diff --git a/.github/workflows/build_display_theme_cards.yml b/.github/workflows/build_display_theme_cards.yml index 2b63cb8214..002b0378e8 100644 --- a/.github/workflows/build_display_theme_cards.yml +++ b/.github/workflows/build_display_theme_cards.yml @@ -15,10 +15,10 @@ jobs: - name: Checkout Repository uses: actions/checkout@v4 - - name: Setup Node.js (v20) + - name: Setup Node.js v24 uses: actions/setup-node@v4 with: - node-version: 20 + node-version: 24 cache: npm cache-dependency-path: packages/modules/display_themes/cards/source/package-lock.json @@ -33,5 +33,9 @@ jobs: git config user.name "${{ github.actor }}" git config user.email "${{ github.actor }}@users.noreply.github.com" git add packages/modules/display_themes/cards/web - git commit -m "Build Display Theme: Cards" - git push + if ! git diff --cached --quiet; then + git commit -m "Build Display Theme: Cards" + git push + else + echo "No changes to commit." + fi diff --git a/.github/workflows/build_display_theme_colors.yml b/.github/workflows/build_display_theme_colors.yml index 67c4dfe93d..c1853cfdd1 100644 --- a/.github/workflows/build_display_theme_colors.yml +++ b/.github/workflows/build_display_theme_colors.yml @@ -33,5 +33,9 @@ jobs: git config user.name "${{ github.actor }}" git config user.email "${{ github.actor }}@users.noreply.github.com" git add packages/modules/display_themes/colors/web - git commit -m "Build Display Theme: Colors" - git push + if ! git diff --cached --quiet; then + git commit -m "Build Display Theme: Colors" + git push + else + echo "No changes to commit." + fi diff --git a/.github/workflows/build_web_theme_colors.yml b/.github/workflows/build_web_theme_colors.yml new file mode 100644 index 0000000000..f0205e2e92 --- /dev/null +++ b/.github/workflows/build_web_theme_colors.yml @@ -0,0 +1,41 @@ +name: Build Web Theme Colors + +on: + push: + paths: + - packages/modules/web_themes/colors/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js 24 + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + cache-dependency-path: packages/modules/web_themes/colors/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/web_themes/colors/source + npm install + npm run build --if-present + + - name: Commit and Push Changes + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + git add packages/modules/web_themes/colors/web + if ! git diff --cached --quiet; then + git commit -m "Build Web Theme: Colors" + git push + else + echo "No changes to commit." + fi diff --git a/.github/workflows/build_web_theme_koala.yml b/.github/workflows/build_web_theme_koala.yml new file mode 100644 index 0000000000..af99325902 --- /dev/null +++ b/.github/workflows/build_web_theme_koala.yml @@ -0,0 +1,41 @@ +name: Build Web Theme Koala + +on: + push: + paths: + - packages/modules/web_themes/koala/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js 24 + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: npm + cache-dependency-path: packages/modules/web_themes/koala/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/web_themes/koala/source + npm install + npm run build --if-present + + - name: Commit and Push Changes + run: | + git config user.name "${{ github.actor }}" + git config user.email "${{ github.actor }}@users.noreply.github.com" + git add packages/modules/web_themes/koala/web + if ! git diff --cached --quiet; then + git commit -m "Build Web Theme: Koala" + git push + else + echo "No changes to commit." + fi diff --git a/.github/workflows/check_display_theme_cards.yml b/.github/workflows/check_display_theme_cards.yml new file mode 100644 index 0000000000..aa4dbc7546 --- /dev/null +++ b/.github/workflows/check_display_theme_cards.yml @@ -0,0 +1,36 @@ +name: Check Display Theme Cards + +on: + pull_request: + paths: + - packages/modules/display_themes/cards/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node: [ 22, 24 ] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: 'npm' + cache-dependency-path: packages/modules/display_themes/cards/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/display_themes/cards/source + npm install + npm run lint + npm run test:unit + npm run build --if-present diff --git a/.github/workflows/check_display_theme_colors.yml b/.github/workflows/check_display_theme_colors.yml new file mode 100644 index 0000000000..259f4b917e --- /dev/null +++ b/.github/workflows/check_display_theme_colors.yml @@ -0,0 +1,34 @@ +name: Check Display Theme Colors + +on: + pull_request: + paths: + - packages/modules/display_themes/colors/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node: [ 22, 24 ] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: 'npm' + cache-dependency-path: packages/modules/display_themes/colors/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/display_themes/colors/source + npm install + npm run build --if-present diff --git a/.github/workflows/check_web_theme_colors.yml b/.github/workflows/check_web_theme_colors.yml new file mode 100644 index 0000000000..b0bbb9f78b --- /dev/null +++ b/.github/workflows/check_web_theme_colors.yml @@ -0,0 +1,34 @@ +name: Check Web Theme Colors + +on: + pull_request: + paths: + - packages/modules/web_themes/colors/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node: [ 22, 24 ] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: 'npm' + cache-dependency-path: packages/modules/web_themes/colors/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/web_themes/colors/source + npm install + npm run build --if-present diff --git a/.github/workflows/check_web_theme_koala.yml b/.github/workflows/check_web_theme_koala.yml new file mode 100644 index 0000000000..041e9f4acc --- /dev/null +++ b/.github/workflows/check_web_theme_koala.yml @@ -0,0 +1,36 @@ +name: Check Web Theme Koala + +on: + pull_request: + paths: + - packages/modules/web_themes/koala/source/** + branches: + - master + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node: [ 22, 24 ] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ matrix.node }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: 'npm' + cache-dependency-path: packages/modules/web_themes/koala/source/package-lock.json + + - name: Install Dependencies and Build + run: | + cd packages/modules/web_themes/koala/source + npm install + npm run lint + # npm run test:unit + npm run build --if-present diff --git a/.github/workflows/github-actions-python.yml b/.github/workflows/github-actions-python.yml index cff37b6e33..e942a8e893 100644 --- a/.github/workflows/github-actions-python.yml +++ b/.github/workflows/github-actions-python.yml @@ -4,13 +4,13 @@ on: pull_request jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Python 3.9 uses: actions/setup-python@v5 with: - python-version: "3.9.2" + python-version: "3.9.12" - name: Install dependencies run: | pip3 install -r "/home/runner/work/core/core/requirements.txt" diff --git a/README.md b/README.md index 4f70c82783..14b32635c5 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Software: - alternativ kann auch ein x86_64 System (Hardware oder als VM) mit installiertem **Debian 11 "Bullseye"** als Basis verwendet werden. - Eine Installation unter **Debian 12 "Bookworm"** wird noch nicht unterstützt! +- Bitte beachten das **Debian 11 "Bullseye"** nur mit erheblichem Aufwand mit einem Raspberry Pi 5 kompatibel ist. Wir empfehlen die Nutzung von einem Raspberry Pi 3b. In der Shell folgendes eingeben: diff --git a/data/config/mosquitto/openwb_local.conf b/data/config/mosquitto/openwb_local.conf index 13fbd9ec8c..f540b3dcf1 100644 --- a/data/config/mosquitto/openwb_local.conf +++ b/data/config/mosquitto/openwb_local.conf @@ -1,4 +1,4 @@ -# openwb-version:16 +# openwb-version:19 listener 1886 localhost allow_anonymous true @@ -22,12 +22,19 @@ topic openWB/chargepoint/+/set/phases_to_use out 2 topic openWB/chargepoint/+/set/manual_lock out 2 topic openWB/chargepoint/+/set/autolock_state out 2 topic openWB/chargepoint/+/set/rfid out 2 +topic openWB/chargepoint/+/set/charge_template out 2 +topic openWB/chargepoint/+/set/charge_template/# out 2 topic openWB/chargepoint/+/get/# out 2 topic openWB/chargepoint/+/config/# out 2 topic openWB/chargepoint/template/# out 2 topic openWB/internal_chargepoint/# out 2 +topic openWB/io/# out 2 +topic openWB/internal_io/# out 2 + +topic openWB/mqtt/# both 2 + topic openWB/pv/config/configured out 2 topic openWB/pv/get/# out 2 topic openWB/pv/+/config/# out 2 diff --git a/data/config/sudoers/apache2 b/data/config/sudoers/apache2 new file mode 100644 index 0000000000..7d077feec1 --- /dev/null +++ b/data/config/sudoers/apache2 @@ -0,0 +1,2 @@ +# openwb-version:1 +www-data ALL=NOPASSWD: /bin/systemctl restart openwb2.service, /bin/systemctl restart openwbRemoteSupport.service, /bin/systemctl restart mosquitto.service, /bin/systemctl restart mosquitto_local.service diff --git a/docs/EZA-Beispielkonfiguration.png b/docs/EZA-Beispielkonfiguration.png new file mode 100644 index 0000000000..19980e4a5f Binary files /dev/null and b/docs/EZA-Beispielkonfiguration.png differ diff --git "a/docs/Grunds\303\244tzliches zu Z\303\244hlern.md" "b/docs/Grunds\303\244tzliches zu Z\303\244hlern.md" index add8c822a8..b5df17333e 100644 --- "a/docs/Grunds\303\244tzliches zu Z\303\244hlern.md" +++ "b/docs/Grunds\303\244tzliches zu Z\303\244hlern.md" @@ -1,6 +1,6 @@ openWB benötigt zum erfolgreichen PV-Überschussladen die entsprechenden Zählerwerte am EVU-Punkt (EVU=Elektrizitätsversorgungsunternehmen), sprich dem Übergang ins öffentliche Netz. An dieser Stelle muss die Gesamtleistung saldierend erfasst werden. Für eine phasenbasierte Leistungsüberwachung sind auch die einzelnen Ströme und/oder Leistungen der drei Phasen notwendig. Bei einem Zähler im Hausverbrauchs-Zweig muss die Konfiguration wie [hier](https://github.com/openWB/core/wiki/Hausverbrauchs-Zähler) beschrieben erfolgen. -Im einfachsten Fall geschieht dies durch Kauf und Einbau eines [EVU-Kits](##EVU-Kit). Sollten schon digital auslesbare Zähler vorhanden sein, so besteht die Möglichkeit diese Werte an openWB weiterzuleiten, auch mit Hilfe von Hausautomationsservern. +Im einfachsten Fall geschieht dies durch Kauf und Einbau eines [EVU-Kits](#evu-kit). Sollten schon digital auslesbare Zähler vorhanden sein, so besteht die Möglichkeit diese Werte an openWB weiterzuleiten, auch mit Hilfe von Hausautomationsservern. Es gibt viele verschiedene Möglichkeiten, Zähler als auch Wechselrichter in das openWB-System einzufügen. Die Struktur der Zähler muss dann im [Lastmanagement](https://github.com/openWB/core/wiki/Lastmanagement-und-kaskadierte-Zähler) dem System bekanntgegeben werden. Hier können auch [virtuelle Zähler](##Virtuelle Zähler) hinzugefügt werden, welche openWB-intern die untergeordneten Zähler verrechnen. @@ -13,7 +13,7 @@ Der Zähler kommuniziert mit der openWB über Ethernet. Die Kits sind so vorkonf ## MQTT -openWB hat einen MQTT-Broker integriert, welcher unter Port 1883 (ohne Verschlüsselung) und Port 8883 (mit Verschlüsselung) erreichbar ist. Benutzerauthentifizierung ist deaktiviert und auch nicht aktivierbar. Ein Zähler, welcher die benötigten Daten liefert muss sich mit diesem Broker verbinden und dort die Werte unter den entsprechenden Topics publishen. +openWB hat einen MQTT-Broker integriert, welcher unter Port 1883 (ohne Verschlüsselung) und Port 8883 (mit Verschlüsselung) erreichbar ist. Benutzerauthentifizierung ist deaktiviert und auch nicht aktivierbar. Ein Zähler, welcher die benötigten Daten liefert muss sich mit diesem Broker verbinden und dort die Werte unter den entsprechenden Topics veröffentlichen. Folgende Werte können dem MQTT-Zähler übergeben werden. Die ID ist individuell und wird beim Anlegen der MQTT-Komponente angezeigt. Die folgenden Topics sind für einen reibungslosen Betrieb unbedingt erforderlich: @@ -57,10 +57,10 @@ Die Netzfrequenz, Spannungen, Leistungen und Leistungsfaktoren jeder Phase werde ## Huawei Wechselrichter mit DTSU666-H 250A und SDongle Huawei Wechselrichter werden, in der Betriebsart mit Aufzeichnung des Hausverbraucht mit dem _DTSU666-H 250A_ Stromzähler direkt am EVU-Punkt betrieben. Die Kommunikation zwischen Zähler und Wechselrichter findet über RS485 statt. Sofern der Wechselrichter mit dem optionalen SmartDongle FE ausgestattet ist, können über diesen Daten des Wechselrichter ausgelesen werden. -Die Schnittstelle am Dongle ist Modbus-TCP. Dies muss mit Installer-Account am Wechselrichter auf "Unrestrictet" gestellt werden, damit die Daten extern abgerufen werden können. +Die Schnittstelle am Dongle ist Modbus-TCP. Dies muss mit Installer-Account am Wechselrichter auf "Unrestricted" gestellt werden, damit die Daten extern abgerufen werden können. Der Huawei-Wechselrichter kann direkt über die openWB ausgelesen werden. -Eine weitere Möglichkeit des Datenabrufs wird im [openWB-Forum](https://openwb.de/forum/viewtopic.php?t=7029) entwickelt und ist auf [Github](https://github.com/AlexanderMetzger/huawei_openwb_bridge) sowie der [Homepage des Entwicklers](https://lebensraum-wohnraum.de/openwb-kommunikation-mit-dem-huawei-wechselrichter-sun-2000/) zu finden. Hierbei wird das Image auf die SD-karte eines Raspberry-Zero gespiegelt und der Raspberry mit dem Config-WLAN des Wechselrichters verbunden. Die Skripte ziehen sich die entsprechenden Werte in Echtzeit vom Wechselrichter und publishen diese auf die [MQTT](#MQTT) Schnittstelle der openWB. Der Zähler in der openWB muss dementsprechend als MQTT-Zähler eingerichtet sein. +Eine weitere Möglichkeit des Datenabrufs wird im [openWB-Forum](https://openwb.de/forum/viewtopic.php?t=7029) entwickelt und ist auf [Github](https://github.com/AlexanderMetzger/huawei_openwb_bridge) sowie der [Homepage des Entwicklers](https://lebensraum-wohnraum.de/openwb-kommunikation-mit-dem-huawei-wechselrichter-sun-2000/) zu finden. Hierbei wird das Image auf die SD-karte eines Raspberry-Zero gespiegelt und der Raspberry mit dem Config-WLAN des Wechselrichters verbunden. Die Skripte ziehen sich die entsprechenden Werte in Echtzeit vom Wechselrichter und veröffentlichen diese auf die [MQTT](#mqtt) Schnittstelle der openWB. Der Zähler in der openWB muss dementsprechend als MQTT-Zähler eingerichtet sein. ### Solaranzeige @@ -69,7 +69,7 @@ Dieses Projekt unterstützt aktuell (Stand 2024-02) mehr Wechselrichter als open Die Software ist originär dafür vorgesehen auf einen Raspberry per Image installiert zu werden und nach wenigen Konfigurationsschritten lauffähig zu sein. Es gibt auch schon Portierungen für [Docker](https://github.com/DeBaschdi/docker.solaranzeige). Solaranzeige kann mit vielen Wechselrichtern kommunizieren und auch teilweise die angeschlossenen Zähler auslesen. Eine zeitbasierte Datenbank (InfluxDb), Datenweitergabe über einen MQTT-Client sowie eine Visualisierung mit Grafana sind direkt integriert. Es kann aber auch bereits existierende Infrastruktur verwendet werden. In dem Projekt wird (mit Stand von 2021) auch die Möglichkeit dokumentiert die Daten direkt an openWB weiterzuleiten. Dann kann jedoch kein weiterer MQTT-Broker bedient werden. -Alternativ können die Zählerwerte an eine Hausautomationsserver weitergegeben, dort ggf. vorzeichenkorrigiert und dann über einen zweiten MQTT-Client zur openWB geschickt werden. +Alternativ können die Zählerwerte an eine Hausautomationsserver weitergegeben, dort ggf. mit korrigiertem Vorzeichen und dann über einen zweiten MQTT-Client zur openWB geschickt werden. ## Virtuelle Zähler diff --git a/docs/Hausverbrauch.md b/docs/Hausverbrauch.md index 0135f48e5b..a9b0c9cc78 100644 --- a/docs/Hausverbrauch.md +++ b/docs/Hausverbrauch.md @@ -1,5 +1,5 @@ -Der Hausverbrauch kann in der openWB auf zwei verschiedenen Wegen ermittelt werden: die openWB berechnet den Hausverbrauch oder du gibst in den Einstellungen einen Zähler an, der den Hausverbrauch misst. -Eine Mischung der beiden Möglichkeiten, also nicht gemessener, berechneter Hausverbrauch und gemessener Hausverbrauch kann in der openWB nicht abgebildet werden. +Der Hausverbrauch kann in der openWB auf zwei verschiedenen Wegen ermittelt werden: die openWB berechnet den Hausverbrauch oder du gibst in den Einstellungen einen Zähler an, der den Hausverbrauch misst. +Eine Mischung der beiden Möglichkeiten, also nicht gemessener, berechneter Hausverbrauch und gemessener Hausverbrauch kann in der openWB nicht abgebildet werden. Es muss in der Anlage einen Zähler geben, der alle Verbräuche erfasst. Dies kann entweder der EVU-Zähler sein (dieser erfasst auch Wechselrichter und Speicher) oder der Hausverbrauchszähler (Wechselrichter und Zähler werden separat erfasst). ### Möglichkeit 1: Berechnung des Hausverbrauchs durch die openWB @@ -8,14 +8,15 @@ Der Hausverbrauch entspricht der Summer aller nicht gemessenen Verbraucher. Übl ### Möglichkeit 2: Hausverbrauchszähler -Unter `Einstellungen→Konfiguration→Lastmanagement` kann bei Hausverbrauch ein Zähler ausgewählt werden. Diese Einstellung ist nur dann richtig, wenn in der Anlage ein Zähler verbaut ist, der den Hausverbrauch misst. Dies ist bei manchen Systemherstellern wie Kostal(?) üblich. Der Hausverbrauchszähler kann die Ladepunkte messen oder nicht. Dann müssen diese in der Hierachie entsprechend hinter oder neben dem Zähler angeordnet werden. +Unter `Einstellungen→Konfiguration→Lastmanagement` kann bei Hausverbrauch ein Zähler ausgewählt werden. Diese Einstellung ist nur dann richtig, wenn in der Anlage ein Zähler verbaut ist, der den Hausverbrauch misst. Dies ist bei manchen Systemherstellern wie Kostal(?) üblich. Der Hausverbrauchszähler kann die Ladepunkte messen oder nicht. Dann müssen diese in der Hierarchie entsprechend hinter oder neben dem Zähler angeordnet werden. Bezug und Einspeisung ins öffentliche Netz werden dann mit einem virtuellen Zähler aus den Werten des Hausverauchszählers, Wechselrichter und Speicher berechnet. Der virtuelle Zähler addiert die Werte aller in der Struktur dahinter angeordneten Komponenten. Zunächst ein Virtuelles Gerät mit einem virtuellen Zähler anlegen. Die Komponenten müssen in der Hierarchie wie in den Abbildungen unten angeordnet werden. In den Einstellungen für das Lastmanagement beim Punkt Hausverbrauch den Hausverbrauchs-Zähler auswählen. ### Hausverbrauch bei mehreren Zählern -Wenn es einen Zähler am EVU-Punkt und einen Zähler im Hausverbrauchszweig gibt, dann wie unter `Möglichkeit 2` beschrieben, den Zähler, der den Hausverbrauch misst unter `Einstellungen→Konfiguration→Lastmanagement→Hausverbauch` auswählen. -Wenn der Hausverbrauch die Summer mehrerer Zähler in der Anlage ist, müssen diese in einem virtuellen Zähler zusammengefasst werden und dieser wie unter `Möglichkeit 2` als Hausverbrauchs-Zähler ausgewählt werden. Dieser kann nicht der Zähler an der Spitze (EVU-Zähler) sein, da in diesem Zähler immer auch Speicher und PV miteingerechnet werden müssen, um den Überschuss für PV-Laden am EVU-Punkt zu kennen. + +Wenn es einen Zähler am EVU-Punkt und einen Zähler im Hausverbrauchszweig gibt, dann wie unter `Möglichkeit 2` beschrieben, den Zähler, der den Hausverbrauch misst unter `Einstellungen→Konfiguration→Lastmanagement→Hausverbauch` auswählen. +Wenn der Hausverbrauch die Summer mehrerer Zähler in der Anlage ist, müssen diese in einem virtuellen Zähler zusammengefasst werden und dieser wie unter `Möglichkeit 2` als Hausverbrauchs-Zähler ausgewählt werden. Dieser kann nicht der Zähler an der Spitze (EVU-Zähler) sein, da in diesem Zähler immer auch Speicher und PV mit eingerechnet werden müssen, um den Überschuss für PV-Laden am EVU-Punkt zu kennen. Misst der Zähler den Hausverbrauch, ergibt sich folgende Anordnung: diff --git a/docs/Home.md b/docs/Home.md index 684cf4e982..0d58231955 100644 --- a/docs/Home.md +++ b/docs/Home.md @@ -2,7 +2,7 @@ In openWB2 sind Ladepunkte, Module und Fahrzeuge flexibel konfigurierbar. In diesem Wiki wird das zugrundeliegende Konzept und das Zusammenspiel der verschiedenen Einstellungen erläutert. Details zu den einzelnen Einstellungen erhält man durch Klick auf das Fragezeichen neben der Einstellung direkt im User Interface. -Die ersten Schritte in openWB 2 sind [hier](https://openwb.de/main/?page_id=973) erklärt. Eine Anleitung zum Anbinden von Wechselrichtern, Speichern und Zählern findet Ihr [hier](https://openwb.de/main/?page_id=970). +Die ersten Schritte in openWB 2 sind [hier](https://openwb.de/main/?page_id=1942) erklärt. Eine Anleitung zum Anbinden von Wechselrichtern, Speichern und Zählern findet Ihr [hier](https://openwb.de/main/?page_id=970). Die Einstellungsseiten und Konfigurationsmöglichkeiten wurden im Vergleich zu 1.9 grundlegend überarbeitet. Eine Übersicht über die wichtigsten Features findet Ihr [hier](https://openwb.de/forum/viewtopic.php?f=3&t=3170). diff --git "a/docs/IO-Ger\303\244te & -Aktionen.md" "b/docs/IO-Ger\303\244te & -Aktionen.md" new file mode 100644 index 0000000000..05b90d7ad9 --- /dev/null +++ "b/docs/IO-Ger\303\244te & -Aktionen.md" @@ -0,0 +1,39 @@ +### IO-Geräte + +IO/GPIO sind analoge und digitale Ein- und Ausgänge, die man meist als Pin- oder Buchsenleiste auf der Platine findet. openWB software2 kann analoge und digitale Eingänge auslesen und analoge sowie digitale Ausgänge schalten. Die Ein- und Ausgänge befinden sich auf dem konfigurierten IO-Gerät, wie zB dem Dimm- & Control-Kit. Um festzulegen, was mit den Informationen aus den Eingängen gemacht werden soll oder welche Ausgänge geschaltet werden sollen, konfigurierst Du IO-Aktionen. Bei der IO-Aktion gibst Du an, welcher Ein- oder Ausgang dafür verwendet werden soll und ggf weitere Aktions-spezifische Einstellungen. + +#### Dimm-& Control-Kit + +Das Dimm-& Control-Kit besitzt acht analoge Eingänge (AI1-AI8), acht digitale Eingänge (DI1-DI8) und achte digitale Ausgänge (DO1-DO8). Bei den Ausgängen handelt es sich um potentialfreie Relais-Ausgänge mit 5A@28VDC/250VAC. + +#### openWB series2-Modell mit AddOn-Platine + +Die AddOn-Platine stellt 7 Eingänge und 3 Ausgänge zur Verfügung. WICHTIG: In openWB software 1.9 waren den IOs feste Aktionen zugeordnet, die auch auf der Platine beschriftet sind. Diese Zuordnung ist in software2 NICHT vorgegeben. Zur einfachen Zuordnung der Pins hier eine Übersicht: + +| Pin | Beschriftung | +|---------|---------| +| Eingang 21 | RSE 2 | +| Eingang 24 | RSE 1 | +| Eingang 31 | Taster 3 PV | +| Eingang 32 | Taster 1 Sofortladen | +| Eingang 33 | Taster 4 Stop | +| Eingang 36 | Taster 2 Min+PV | +| Eingang 40 | Taster 5 Standby | +| Ausgang 7 | LED 3 | +| Ausgang 16 | LED 2 | +| Ausgang 18 | LED 1 | + +## IO-Aktionen + +### Steuerbare Verbrauchseinrichtungen: Dimmen per EMS, Dimmung per Direkt-Steuerung, RSE + +Ausführliche Informationen findest Du im gesonderten Wiki-Beitrag [Steuerbare Einrichtungen nach § 14a EnGW und § 9 EEG](https://github.com/openWB/core/wiki/Steuerbare-Verbrauchseinrichtungen-nach-§14a) + +### Steuerbare Erzeugungseinrichtungen: Stufenweise Steuerung + +Bitte beachten: Die openWB steuert keinen Wechselrichter an. Sie zeigt lediglich den aktuellen Zustand der Beschränkung an und kann optional das Signal der Eingänge an Ausgänge durchreichen. +Ausführliche Informationen findest Du im gesonderten Wiki-Beitrag [Steuerbare Einrichtungen nach § 14a EnGW und § 9 EEG](https://github.com/openWB/core/wiki/Steuerbare-Verbrauchseinrichtungen-nach-§14a) + +## Manuelles Setzen der Ausgänge + +Die Ausgänge aller IO-Geräte können per MQTT gesetzt werden. Die Topics findet Ihr in den Einstellungen des jeweiligen Geräts als Copy-to-Clipboard-Link. Das manuelle Setzen des Ausgangs überschreibt den Wert, den zB die openWB bei einer IO-Aktion gesetzt hat. diff --git a/docs/Identifikation.md b/docs/Identifikation.md index 2fcb688871..f74b0b9b12 100644 --- a/docs/Identifikation.md +++ b/docs/Identifikation.md @@ -2,9 +2,9 @@ Mit den verschiedenen Identifikations-Möglichkeiten kannst du die openWB grunds Die Identifikation erfolgt über - * RFID-Tags: Setzt einen eingebauten RFID-Reader voraus. Dieser ist als optionales Zubehör für openWB Pro und openWB series2 erhältlich. Der Tag kann nach oder max. 5 Minuten vor dem Anstecken gescannt werden. - * Eingabe einer ID am Display: Setzt ein eingebautes Display voraus. - * Fahrzeugerkennung: Setzt eine openWB Pro und ein Fahrzeug, das diese Funktion unterstützt, voraus. (Permalink zur Übersicht im Forum) Zur Identifikation wird die MAC-Adresse des Fahrzeugs verwendet. Hat die Pro auch einen RFID-Reader, hat bei der Fahrzeug-Zuordnung die MAC-Adresse die höhere Priorität. Beim Entsperren wird beides geprüft. +* RFID-Tags: Setzt einen eingebauten RFID-Reader voraus. Dieser ist als optionales Zubehör für openWB Pro und openWB series2 erhältlich. Der Tag kann nach oder max. 5 Minuten vor dem Anstecken gescannt werden. +* Eingabe einer ID am Display: Setzt ein eingebautes Display voraus. +* Fahrzeugerkennung: Setzt eine openWB Pro und ein Fahrzeug, das diese Funktion unterstützt, voraus. (Permalink zur Übersicht im Forum) Zur Identifikation wird die MAC-Adresse des Fahrzeugs verwendet. Hat die Pro auch einen RFID-Reader, hat bei der Fahrzeug-Zuordnung die MAC-Adresse die höhere Priorität. Beim Entsperren wird beides geprüft. Die beschriebenen Identifikationsverfahren werden in der Software gleich ausgewertet. Es sind unterschiedliche Wege je nach Hardwareausstattung, die Information an die Software zu übergeben. Wenn ID-Tags genutzt werden sollen, dann ist in der Navigationsbar unter Einstellungen - Optionale Hardware unter dem Punkt Identifikation von Fahrzeugen die Option Identifikation aktivieren auf An zu stellen. @@ -18,15 +18,16 @@ Unter Einstellungen → Konfiguration → Ladepunkte → Ladepunkt-Profil kann f Im Menü Einstellungen → Konfiguration → Fahrzeuge können ID-Tags für das Fahrzeug hinterlegt werden. Wird einer dieser Tags erkannt, wird das Fahrzeug dem Ladepunkt zugeordnet. -Im Ladeprofil kann eingestellt werden, ob nach dem Abstecken das Standard-Fahrzeug zugeordnet werden soll. Andernfalls wird nach Abstecken das letzte vorher ausgewählte Fahrzeug zugeordnet. -Die Option Standard nach Abstecken macht nur Sinn, wenn neben dem Standard-Fahrzeug mindestens ein weiteres Fahrzeug und neben dem Standard-Lade-Profil mindestens ein weiteres Lade-Profil angelegt wurde. Dabei ist dem Standard-Fahrzeug das Standard-Lade-Profil und dem weiteren Fahrzeug das weitere Lade-Profil zuzuweisen. Wenn nur mit Identifikation geladen werden soll, muss im Standard-Lade-Profil der aktive Lademodus auf Stop gestellt werden. In den Lade-Profilen der anderen Fahrzeuge muss Standard nach Abstecken aktiviert werden. +Im Ladeprofil kann eingestellt werden, ob nach dem Abstecken das Standard-Fahrzeug zugeordnet werden soll. Andernfalls wird nach Abstecken das letzte vorher ausgewählte Fahrzeug zugeordnet. +Die Option Standard nach Abstecken macht nur Sinn, wenn neben dem Standard-Fahrzeug mindestens ein weiteres Fahrzeug und neben dem Standard-Lade-Profil mindestens ein weiteres Lade-Profil angelegt wurde. Dabei ist dem Standard-Fahrzeug das Standard-Lade-Profil und dem weiteren Fahrzeug das weitere Lade-Profil zuzuweisen. Wenn nur mit Identifikation geladen werden soll, muss im Standard-Lade-Profil der aktive Lademodus auf Stop gestellt werden. In den Lade-Profilen der anderen Fahrzeuge muss Standard nach Abstecken aktiviert werden. Über den ID-Tag wird ein Fahrzeug zugeordnet. Nach Abstecken wechselt die Auswahl dann auf Standardfahrzeug in den Lademodus Stop und der Ladepunkt startet keinen weiteren Ladevorgang, bis die Auswahl entweder händisch über das User Interface oder automatisch per ID-Tag auf ein Fahrzeug geändert wird, das sich z.B. im Lademodus Sofortladen befindet und laden darf. ### Use Cases #### Sperre nach Abstecken - + Sperre nach Abstecken kann an einem Ladepunkt verwendet werden, welcher das Laden gegenüber fremdem Zugriff sichert. Wird der ID-Tag nur zum Sperren/Entsperren des Ladepunktes verwendet, dann startet immer das ausgewählte Fahrzeug den Ladevorgang. Dies kann im privaten Bereich mit nur einem Fahrzeug sinnvoll sein, damit nur dieses Fahrzeug auch laden darf. Die Option ist aber auch für Ladeparks sinnvoll, bei denen die Ladepunkte nur für eine Gruppe von ID-Tags freischaltbar sind und dem ID-Tag zum Entsperren auch gleichzeitig zugeordnet sind. #### Standard nach Abstecken + Standard nach Abstecken kann an einem Ladepunkt verwendet werden, welcher das Laden mehrerer verschiedener Fahrzeuge ermöglichen soll. Werden mehrere Fahrzeuge mit verschiedenen Lade-Profilen und verschiedenen ID-Kennungen neben dem Standard-Fahrzeug angelegt, kann über die ID-Kennung zwischen den einzelnen Fahrzeugen gewechselt werden. Hier bietet sich beispielsweise ein privater Ladepunkt mit zwei Fahrzeugen an oder ein Ladepunkt in einer Firma mit verschiedenen Mitarbeitern. Standard nach Abstecken kann auch dazu verwendet werden, um beispielsweise zwischen zwei Fahrzeugen (und damit Fahrzeug-Profilen und Lade-Profilen) ohne ID-Tag zu wechseln, vor allem wenn nur eines der Fahrzeuge über die ID-Kennung zuverlässig erkannt wird. diff --git a/docs/Lademodi.md b/docs/Lademodi.md index 1d9f20cc3e..9ffb373f52 100644 --- a/docs/Lademodi.md +++ b/docs/Lademodi.md @@ -1,18 +1,19 @@ ### PV-Laden -Beim PV-Laden wird die Ladeleistung anhand des Überschusses am EVU-Punkt geregelt. Um ständiges Starten und Stoppen der Ladung zu verhindern, startet die Ladung, wenn für die Dauer der Einschaltverzögerung die Einschaltschwelle überschritten wurde. Die Ladung wird gestoppt, wenn für die Dauer der Abschaltverzögerung die Abschaltschwelle unterschritten wurde. + +Beim PV-Laden wird die Ladeleistung anhand des Überschusses am EVU-Punkt geregelt. Um ständiges Starten und Stoppen der Ladung zu verhindern, startet die Ladung, wenn für die Dauer der Einschaltverzögerung die Einschaltschwelle überschritten wurde. Die Ladung wird gestoppt, wenn für die Dauer der Abschaltverzögerung die Abschaltschwelle unterschritten wurde. Wenn ein Wechselrichter verbaut ist, bei dem die Einspeiseleistung reduziert wird - auch als 70%-Regelung bekannt -, kann dies mit dem Regelpunkt Einspeisegrenze eingestellt werden. ### Regelmodus -Die Ladeleistung kann nicht mit absoluter Genauigkeit eingestellt werden, sodass am EVU-Punkt nicht auf exakt 0W geregelt werden kann. Einige Fahrzeuge und ältere openWBs können zudem nur in Schritten von 1A regeln (entspricht 230W bei einphasiger Ladung). Der Regelmodus bestimmt, in welchem Bereich (ca. 200-300W) sich der EVU-Überschuss bewegen soll. Beim Regelmodus „Bezug“ darf ein geringer Netzbezug vorhanden sein, bevor nachgeregelt wird. Das Auto lädt dann etwas schneller, aber es wird mehr Netzstrom verbraucht. Im Regelmodus „Einspeisung“ kann etwas Strom ins Netz eingespeist werden, bevor nachgeregelt wird. Dann lädt das Auto etwas langsamer und es wird weniger Netzstrom verbraucht. -Der Regelbereich wird auf den gesamten Überschuss angewendet, bevor die PV-Regelung durchgeführt wird. D.h. der Regelbereich wird auf alle Einstellungen für das PV-Laden angewendet und nur einmal unabhängig von der Anzahl der angesteckten Fahrzeuge. Liegt der Überschuss am EVU-Punkt im vorgegebenen Regelbereich, wird nicht nachgeregelt. Liegt er außerhalb des Bereichs, wird die Lade-Leistung auf die Mitte des Bereichs angepasst. +Die Ladeleistung kann nicht mit absoluter Genauigkeit eingestellt werden, sodass am EVU-Punkt nicht auf exakt 0W geregelt werden kann. Einige Fahrzeuge und ältere openWBs können zudem nur in Schritten von 1A regeln (entspricht 230W bei einphasiger Ladung). Der Regelmodus bestimmt, in welchem Bereich (ca. 200-300W) sich der EVU-Überschuss bewegen soll. Beim Regelmodus „Bezug“ darf ein geringer Netzbezug vorhanden sein, bevor nachgeregelt wird. Das Auto lädt dann etwas schneller, aber es wird mehr Netzstrom verbraucht. Im Regelmodus „Einspeisung“ kann etwas Strom ins Netz eingespeist werden, bevor nachgeregelt wird. Dann lädt das Auto etwas langsamer und es wird weniger Netzstrom verbraucht. +Der Regelbereich wird auf den gesamten Überschuss angewendet, bevor die PV-Regelung durchgeführt wird. D.h. der Regelbereich wird auf alle Einstellungen für das PV-Laden angewendet und nur einmal unabhängig von der Anzahl der angesteckten Fahrzeuge. Liegt der Überschuss am EVU-Punkt im vorgegebenen Regelbereich, wird nicht nachgeregelt. Liegt er außerhalb des Bereichs, wird die Lade-Leistung auf die Mitte des Bereichs angepasst. Bei Speichervorrang erzeugt die Regelung bei Bedarf unabhängig vom eingestellten Regelmodus Einspeisung, damit der Speicher seine Ladeleistung erhöht. Achtung: bei unlogischen Einstellungen kann die Regelung gestört werden! Im Zweifel bitte unsere vordefinierten Modi verwenden. #### Speicherbeachtung -Sofern ein Hausstromspeicher (im Folgenden „Speicher“ genannt) im Energiesystem verbaut ist, kann dieser beim Fahrzeugladen mit berücksichtigt werden. Dies erfolgt passiv über die Berücksichtigung der Speicherleistungswerte und des Speicher-SoC. Eine aktive Speichersteuerung durch openWB ist aktuell mangels Speicherschnittstelle nicht möglich. -Bei Auswahl „Fahrzeuge“ wird der gesamte Überschuss in das EV geladen. Ist die maximale Ladeleistung der Fahrzeuge erreicht und es wird eingespeist, wird dieser Überschuss in den Speicher geladen. -Bei Auswahl „Speicher“ wird der gesamte Überschuss in den Speicher geladen. Ist die maximale Ladeleistung des Speichers erreicht und es wird eingespeist, wird dieser Überschuss unter Beachtung der Einschaltschwelle in die Fahrzeuge geladen. -Bei Auswahl „Mindest-SoC des Speichers“ wird der Überschuss bis zum Mindest-SoC in den Speicher geladen. Ist die maximale Ladeleistung des Speichers erreicht und es wird eingespeist, wird dieser Überschuss in die Fahrzeuge geladen. Wird der Mindest-SoC überschritten, wird der Überschuss ins Fahrzeug geladen. \ No newline at end of file +Sofern ein Hausstromspeicher (im Folgenden „Speicher“ genannt) im Energiesystem verbaut ist, kann dieser beim Fahrzeugladen mit berücksichtigt werden. Dies erfolgt passiv über die Berücksichtigung der Speicherleistungswerte und des Speicher-SoC. Eine aktive Speichersteuerung durch openWB ist aktuell mangels Speicherschnittstelle nicht möglich. +Bei Auswahl „Fahrzeuge“ wird der gesamte Überschuss in das EV geladen. Ist die maximale Ladeleistung der Fahrzeuge erreicht und es wird eingespeist, wird dieser Überschuss in den Speicher geladen. +Bei Auswahl „Speicher“ wird der gesamte Überschuss in den Speicher geladen. Ist die maximale Ladeleistung des Speichers erreicht und es wird eingespeist, wird dieser Überschuss unter Beachtung der Einschaltschwelle in die Fahrzeuge geladen. +Bei Auswahl „Mindest-SoC des Speichers“ wird der Überschuss bis zum Mindest-SoC in den Speicher geladen. Ist die maximale Ladeleistung des Speichers erreicht und es wird eingespeist, wird dieser Überschuss in die Fahrzeuge geladen. Wird der Mindest-SoC überschritten, wird der Überschuss ins Fahrzeug geladen. diff --git a/docs/Ladeprofile.md b/docs/Ladeprofile.md index 1238da39ad..99ce834534 100644 --- a/docs/Ladeprofile.md +++ b/docs/Ladeprofile.md @@ -3,3 +3,13 @@ _Einstellungen -> Konfiguration -> Fahrzeuge -> Lade-Profile_ Unter den Lade-Profilen werden die Einstellungen für das Ladeprofil verwaltet. Die Einstellungen auf der Hauptseite werden aus diesem Profil geladen und dorthin geschrieben. Ist nur ein Fahrzeug vorhanden, so wird in den meisten Fällen nur das Standard-Ladeprofil benötigt. Ausgenommen hiervon ist, wenn per RFID-Tag Ladevorgaben ausgewählt werden. In den fahrzeugspezifischen Einstellungen wird ein Ladeprofil einem Fahrzeug zugeordnet. Werden zwei Fahrzeuge geladen, empfiehlt es sich dazu ein zweites Ladeprofil anzulegen. + +### Temporäre Ladeprofile (ab Version 2.1.8) + +Anpassungen am Ladeprofil, die über die Hauptseite (Web-Themes) oder ein Display (Display-Themes) vorgenommen werden, sind temporär. Die Lade-Profile müssen direkt in den Einstellungen bearbeitet werden. + +Die temporären Einstellungen werden mit dem Ladeprofil aus den Einstellungen überschrieben, wenn ... + +* abgesteckt wird. +* das Fahrzeug gewechselt wird. Das Lade-Profil des neuen Fahrzeugs wird geladen. +* das Ladeprofil in den Einstellungen geändert wird. diff --git a/docs/MQTT.md b/docs/MQTT.md index 43a7ca32a2..a905d54195 100644 --- a/docs/MQTT.md +++ b/docs/MQTT.md @@ -9,7 +9,7 @@ openWB hat einen eigenen MQTT-Broker integriert, über den die Kommunikation lä Als EVU-Zähler können auch Werte über MQTT empfangen werden. Die Integration ist im Abschnitt [Zähler](https://github.com/openWB/core/wiki/Zähler) beschrieben. -## Smarthome +## SmartHome ## Steuerbefehle @@ -32,8 +32,8 @@ SoC-Limit auf z.B. 80% setzen Zielladen `openWB/set/vehicle/template/charge_template/#/chargemode/selected -> scheduled_charging` -Standby -`openWB/set/vehicle/template/charge_template/#/chargemode/selected -> standby` +Eco +`openWB/set/vehicle/template/charge_template/#/chargemode/selected -> eco_charging` Stop `openWB/set/vehicle/template/charge_template/#/chargemode/selected -> stop` @@ -50,7 +50,7 @@ openWB/vehicle/template/charge_template/1 Setzen von min_Current für min+PV nachbauen: `openWB/set/vehicle/template/charge_template/#/chargemode/pv_charging/min_current` -Setzen des Lademodus: (Werte die zu senden sind: instant_charging, pv_charging, scheduled_charging, standby, stop) +Setzen des Lademodus: (Werte die zu senden sind: instant_charging, pv_charging, scheduled_charging, eco_charging, stop) `openWB/set/vehicle/template/charge_template/#/chargemode/selected` Ladepunkt sperren für Priosteuerung der LP: diff --git a/docs/Neues Modul programmieren.md b/docs/Neues Modul programmieren.md index ef3e9fbd44..98e6159a20 100644 --- a/docs/Neues Modul programmieren.md +++ b/docs/Neues Modul programmieren.md @@ -34,7 +34,8 @@ Wenn von der Komponente die Zählerstände für Import und Export gelesen werden Bei Hybrid-Systemen erfolgt die Verrechnung von Speicher-und PV-Leistung automatisiert, wenn Speicher und Wechselrichter in der Hierarchie wie [hier](https://github.com/openWB/core/wiki/Hybrid-System-aus-Wechselrichter-und-Speicher) beschrieben angeordnet sind. Wenn noch weitere spezifische Berechnungen erforderlich sind, müsst Ihr die Komponenten wie unter sample_request_per_device abfragen. Die update-Methode der Komponenten wird dann in eine get- und set-Methode aufgeteilt. Die get-Methode liefert den Component-State zurück, dieser wird in der update_components-Methode des Geräts verrechnet und dann die set-Methode der Komponente aufgerufen, die die store-Methode der Komponente aufruft. #### Schnittstelle für die Speicher-Steuerung -Bei Speichern, die eine aktive Steuerung unterstützen, kann mit der Methode `set_power_limit` die Speicherleistung gesetzt werden. Die Speicher erben von der Klasse `AbstractBat`, die die abstrakte Methode `set_power_limit` beinhaltet. Bei der Implementierung des Speichers kannst Du diese Methode überschreiben. Die Regelung prüft am Ende, ob die Methode für den jeweiligen Speicher implementiert ist und ruft diese auf. Als Variable wird die Speicherleistung in Watt oder `None` übergeben, dann wird der Speicher nicht mehr aktiv von der openWB gesteuert und soll selbst anhand des EVU-Punktes regeln. + +Ob ein Speicher die aktive Speichersteuerung unterstützt, wird in der Methode `power_limit_controllable` implementiert. Ist diese Methode nicht im Speicher implementiert, wird die Methode aus der geerbten Klasse `AbstractBat` aufgerufen und die Steuerbarkeit auf `False` gesetzt. Bei Speichern, die eine aktive Steuerung unterstützen, kann mit der Methode `set_power_limit` die Speicherleistung gesetzt werden. Als Variable wird die Speicherleistung in Watt oder `None` übergeben, dann wird der Speicher nicht mehr aktiv von der openWB gesteuert und soll selbst anhand des EVU-Punktes regeln. ### Neues Fahrzeug programmieren diff --git a/docs/NextCloud als Sicherungs-Cloud einrichten.md b/docs/NextCloud als Sicherungs-Cloud einrichten.md index 9077e27424..d5fed7c2fd 100644 --- a/docs/NextCloud als Sicherungs-Cloud einrichten.md +++ b/docs/NextCloud als Sicherungs-Cloud einrichten.md @@ -10,13 +10,13 @@ Diesen Link in das Feld Cloud-URL in der openWB unter System -> Datenverwaltung Alternativ: Falls es Probleme gibt kann der Link auch folgendermaßen eingetragen werden: -Freigabelink z.B.: https://kim.nl.tab.digital/s/tUbHHrEdGltSRgx +Freigabelink z.B.: Wahl: BackupCloud: NextCloud Unterpunkt: Einstellungen für Backup-Cloud Modul "NextCloud" Cloud-URL: z.B.: OHNE /s/ oder / Benutzername: dein shared token, also z.B.: tUbHHrEdGltSRgx -Passwort: kann leergelassen oder irgendetwas eingetragen werden, spielt keine Rolle +Passwort: kann leer gelassen oder irgendetwas eingetragen werden, spielt keine Rolle Beide Varianten wurden getestet und funktionieren. diff --git a/docs/RSE-Beispielkonfiguration.png b/docs/RSE-Beispielkonfiguration.png new file mode 100644 index 0000000000..5d1e14a646 Binary files /dev/null and b/docs/RSE-Beispielkonfiguration.png differ diff --git a/docs/SoC-BMW-19-settings-1.PNG b/docs/SoC-BMW-19-settings-1.PNG new file mode 100755 index 0000000000..570b17f1d4 Binary files /dev/null and b/docs/SoC-BMW-19-settings-1.PNG differ diff --git a/docs/SoC-BMW-19-settings-2.PNG b/docs/SoC-BMW-19-settings-2.PNG new file mode 100755 index 0000000000..544329616e Binary files /dev/null and b/docs/SoC-BMW-19-settings-2.PNG differ diff --git a/docs/SoC-BMW-19-settings-3.PNG b/docs/SoC-BMW-19-settings-3.PNG new file mode 100755 index 0000000000..2c89899efe Binary files /dev/null and b/docs/SoC-BMW-19-settings-3.PNG differ diff --git a/docs/SoC-BMW-19-settings-4.PNG b/docs/SoC-BMW-19-settings-4.PNG new file mode 100755 index 0000000000..25bdca3c50 Binary files /dev/null and b/docs/SoC-BMW-19-settings-4.PNG differ diff --git a/docs/SoC-BMW-20-settings-1.PNG b/docs/SoC-BMW-20-settings-1.PNG new file mode 100755 index 0000000000..9cf8d9c3c2 Binary files /dev/null and b/docs/SoC-BMW-20-settings-1.PNG differ diff --git a/docs/SoC-BMW-20-settings-2.PNG b/docs/SoC-BMW-20-settings-2.PNG new file mode 100755 index 0000000000..a7c373bc50 Binary files /dev/null and b/docs/SoC-BMW-20-settings-2.PNG differ diff --git a/docs/SoC-BMW-20-settings-3.PNG b/docs/SoC-BMW-20-settings-3.PNG new file mode 100755 index 0000000000..e089d91292 Binary files /dev/null and b/docs/SoC-BMW-20-settings-3.PNG differ diff --git a/docs/SoC-BMW-Mini-Settings.png b/docs/SoC-BMW-Mini-Settings.png new file mode 100644 index 0000000000..2257a25e78 Binary files /dev/null and b/docs/SoC-BMW-Mini-Settings.png differ diff --git a/docs/SoC-BMW-Mini.md b/docs/SoC-BMW-Mini.md new file mode 100644 index 0000000000..00f96b7c0f --- /dev/null +++ b/docs/SoC-BMW-Mini.md @@ -0,0 +1,80 @@ +# SoC-Modul BMW & Mini + +Das Modul gibt es in openWB 1.x und 2.x; es funktioniert in beiden Systemen ähnlich. + +## Konfiguration in openWB 1.x +Die Konfiguration des SoC-Moduls erfolgt in Einstellungen - Modulkonfiguration - Ladepunkte: +![BMW-Settings 1.9-1](SoC-BMW-19-settings-1.PNG) +![BMW-Settings 1.9-2](SoC-BMW-19-settings-2.PNG) +![BMW-Settings 1.9-3](SoC-BMW-19-settings-3.PNG) +![BMW-Settings 1.9-4](SoC-BMW-19-settings-4.PNG) + +## Konfiguration in openWB 2.x +Die Konfiguration des SoC-Moduls erfolgt in Einstellungen - Konfiguration - Fahrzeuge: +![BMW-Settings 2.x-1](SoC-BMW-20-settings-1.PNG) +![BMW-Settings 2.x-2](SoC-BMW-20-settings-2.PNG) +![BMW-Settings 2.x-3](SoC-BMW-20-settings-3.PNG) + +Die Hilfe zu den Feldern kann in openWB 2.x durch Click auf das (?) angezeigt werden. + +In der Konfiguration des SoC-Moduls BMW & Mini ist Folgendes einzugeben: + +- SoC-Modul: BMW & Mini +- Intervalle zur Aktualisierung der Fahrzeugdaten +- Auswahl: Nur Aktualisieren wenn angesteckt +- Benutzername des Connected-Drive Accounts +- Kennwort des Benutzers +- VIN des Fahrzeuges +- Captcha-Token +- Auswahl: SoC-Berechnung während des Ladens + +Das Captcha-Token muss durch Lösen eines Captcha ermittelt werden. + +Nach Eingabe des Captcha-Token und Sichern der Einstellung muss der SoC für das Fahrzeug sofort abgerufen werden. Dies geschieht mittels des Kreispfeils neben der SoC-Anzeige auf der Hauptseite. + +Falls das nicht auf Anhieb klappt, muss es wiederholt werden. + +### openWB 2.x: Mehrere Fahrzeuge in einem Account +Wenn mehrere Fahrzeuge in einem Connected-Drive-Account vorhanden sind, ist dies in openWB 2.x wie folgt zu konfigurieren: + +Das erste Fahrzeug - mit der niedrigsten ID - wird wie wie oben beschrieben konfiguriert. + +Ab dem 2. Fahrzeug wird als Captcha-Token der Wert SECONDARY (genau so in Grossbuchstaben) eingegeben. + +## Hinweise +Nach Neustart bzw. Änderung der LP-Konfiguration werden im EV-Soc-Log Fehler ausgegeben (permission oder fehlende Datei). + + Diese Fehler sind normal und können ignoriert werden. Leider wird im Debug Mode 0 keine Positiv-Meldung ausgegeben. + Empfehlung: + - In Einstellungen - System - Fehlersuche bzw. Debugging dies einstellen: Debug Mode 1/Regelwerte bzw. Info + - dann einen manuellen SOC-Abruf durchführen (im Dashboard auf Kreispfeil klicken). + - danach sollte im EV-SOC-Log eine Zeile ähnlich dieser kommen: + + `2023-02-12 11:57:14 INFO:soc_bmwbc:Lp1 SOC: 61%@2023-02-12T11:53:20` + + Diese Zeile zeigt folgende Information: + + `2023-02-12 11:57:14 - Timestamp des SOC-Abrufs` + + `INFO - Debug Level INFO` + + `soc_bmwbc - SOC-Modul` + + `Lp1 - Ladepunkt` + + `SOC: 61% - SOC Stand` + + `@2023-02-12T11:53:20 - Timestamp des Updates vom EV zum VW Cloud-Server` + +Falls diese Schritte nicht zum Erfolg führen, das Problem im [Support-Seite im openWB Forum](https://forum.openwb.de/viewtopic.php?t=4870) posten mit Angabe relevanter Daten + - oWB SW Version + - oWB gekauft oder selbst installiert + - wenn selbst installiert: welches OS(Stretch/Buster) + - welches Fahrzeug + - falls vorhanden Angaben über Firewall, VPN, etc., also Appliances, die den Internetzugang limitieren könnten + - relevante Abschnitte der Logs, vor allem Fehlermeldungen, als CODE-blocks (). + +Das SoC-Log mit evtl. Fehlermeldungen kann wie folgt eingesehen werden: + - openWB 1.x (Status - EV SoC Log) + - openWB 2.x (Einstellungen - System - Fehlersuche) + diff --git a/docs/SoC-Cupra-settings-1.png b/docs/SoC-Cupra-settings-1.png new file mode 100644 index 0000000000..5064108227 Binary files /dev/null and b/docs/SoC-Cupra-settings-1.png differ diff --git a/docs/SoC-Cupra-settings-2.png b/docs/SoC-Cupra-settings-2.png new file mode 100644 index 0000000000..219e090f75 Binary files /dev/null and b/docs/SoC-Cupra-settings-2.png differ diff --git a/docs/SoC-Cupra.md b/docs/SoC-Cupra.md new file mode 100644 index 0000000000..fbea412a55 --- /dev/null +++ b/docs/SoC-Cupra.md @@ -0,0 +1,82 @@ +# SoC-Modul Cupra + +Das SoC-Modul Cupra gibt es in openWB 2.x. + +## Konfiguration + +Die Konfiguration erfolgt im Bereich Einstellungen - Konfiguration - Fahrzeuge: + +![Allgemeine Konfiguration](SoC-Cupra-settings-1.png) + +![Spezielle Konfiguration](SoC-Cupra-settings-2.png) + +## Hinweise + +Für nicht-Cupra Fahrzeuge (Audi, Skoda, VW, etc.) funktioniert das Modul nicht. + +Erfolgreich getestet u.a. für folgende Fahrzeuge: Cupra Born. + +**Wichtig für alle Fahrzeuge:** +Es muss ein aktives Konto im Seat ID Portal vorhanden sein und die "My SEAT App" muss eingerichtet sein. Es muss explizit die "My SEAT App" eingerichtet werden und nicht die "My CUPRA App". + +**WICHTIG:** +Seat/Cupra ändert gelegentlich die Bedingungen für die Nutzung der Online-Services. + +Diese müssen bestätigt werden. Wenn der SOC-Abruf plötzlich nicht mehr funktioniert, VOR dem Posten bitte Schritt 1 ausführen. + +Bei Problemen zunächst bitte diese Schritte durchführen: + +1. sicherstellen, dass auf dieser Seat-Seite alle Einverständnisse gegeben wurden. + + + + In einigen Fällen wurden die Einverständnisse gegeben und trotzdem funktionierte die Abfrage nicht. + Hier hat folgendes Vorgehen geholfen: Im Seat Konto das Land temporär umstellen, d.h. + - auf ein anderes Land als das eigene ändern + - sichern + - zurück auf das eigene Land ändern + - sichern. + +2. Nach einem manuellen SOC-Abruf (Kreispfeil hinter dem SOC klicken) auf der Status - Seite EV-SOC Log und Debug log auf Fehler kontrollieren + +3. Falls im Ev-Soc Log Fehler 303 (unknown redirect) gemeldet wird: + - Ursache 1: Bestimmte Sonderzeichen im Passwort funktionieren nicht mit dem Modul. Bitte das Passwort auf eines ohne Sonderzeichen ändern und testen. + - Ursache 2: Falsche Email, Passwort oder VIN eingegeben. Alle 3 löschen, speichern, neu eingeben, speichern und testen. + +4. Falls eine Firewall im Spiel ist: Es gab einzelne Probleme beim Internet-Zugriff der openWB auf Python Archive und Fahrzeug-Server wenn IPV6 aktiv ist. + +5. Nach Neustart bzw. Änderung der LP-Konfiguration werden im EV-Soc-Log Fehler ausgegeben (permission oder fehlende Datei). + + Diese Fehler sind normal und können ignoriert werden. Leider wird im Debug Mode 0 keine Positiv-Meldung ausgegeben. + Empfehlung: + - In Einstellungen - System - Fehlersuche dies einstellen: Debug Level/Details + - dann einen manuellen SOC-Abruf durchführen (im Dashboard auf Kreispfeil klicken). + - danach sollte im EV-SOC-Log eine Zeile ähnlich dieser kommen: + + `2023-02-12 11:57:14 INFO:soc_skoda:Lp1 SOC: 61%@2023-02-12T11:53:20` + + Diese Zeile zeigt folgende Information: + + `2023-02-12 11:57:14` *- Timestamp des SOC-Abrufs* + + `INFO` *- Debug Level INFO* + + `soc_skoda` *- SOC-Modul* + + `Lp1` *- Ladepunkt* + + `SOC: 61%` *- SOC Stand* + + `@2025-02-12T11:53:20` *- Timestamp des Updates vom EV zum Seat Cloud-Server* + +6. Falls diese Schritte nicht zum Erfolg führen, das Problem im [Support Thema](https://forum.openwb.de/viewforum.php?f=12) mit Angabe relevanter Daten posten + - oWB SW Version + - oWB gekauft oder selbst installiert + - wenn selbst installiert: welches OS(Stretch/Buster) + - welches Fahrzeug + - falls vorhanden Angaben über Firewall, VPN, etc., also Appliances, die den Internetzugang limitieren könnten + - relevante Abschnitte der Logs, vor allem Fehlermeldungen, als CODE-blocks (). + +Das SoC-Log mit evtl. Fehlermeldungen kann wie folgt eingesehen werden: + +- Einstellungen - System - Fehlersuche diff --git a/docs/SoC-OVMS-19-settings-1.PNG b/docs/SoC-OVMS-19-settings-1.PNG new file mode 100755 index 0000000000..7f453c26c5 Binary files /dev/null and b/docs/SoC-OVMS-19-settings-1.PNG differ diff --git a/docs/SoC-OVMS-19-settings-2.PNG b/docs/SoC-OVMS-19-settings-2.PNG new file mode 100755 index 0000000000..b4801a594b Binary files /dev/null and b/docs/SoC-OVMS-19-settings-2.PNG differ diff --git a/docs/SoC-OVMS-Settings.png b/docs/SoC-OVMS-Settings.png new file mode 100644 index 0000000000..e99dd003c2 Binary files /dev/null and b/docs/SoC-OVMS-Settings.png differ diff --git a/docs/SoC-OVMS.md b/docs/SoC-OVMS.md new file mode 100644 index 0000000000..ba0cb6ac95 --- /dev/null +++ b/docs/SoC-OVMS.md @@ -0,0 +1,76 @@ +# SoC-Modul OVMS + +Das OVMS-Modul im Fahrzeug sendet je nach Ausführung die Daten über Mobilnetz und/oder WLAN an den OVMS-Server (z.B. ovms.dexters-web.de). + +Die OVMS Smartphone-Apps (Android/ios) verbinden sich mit dem gewählten OVMS-Server. + +Das SoC-Modul holt die Daten auch vom OVMS Server. + + +Das OVMS SoC-Modul ist in openWB 1.x und 2.x vefügbar. + +Das SoC-Modul OVMS wird wie folgt im Fahrzeug konfiguriert: + +## Konfiguration in openWB 1.x + +Die Konfiguration des SoC-Moduls erfolgt in Einstellungen - Modulkonfiguration - Ladepunkte: + +![1](SoC-OVMS-19-settings-1.PNG) +![2](SoC-OVMS-19-settings-2.PNG) + +## Konfiguration in openWB 2.x + +Die Konfiguration des SoC-Moduls erfolgt in Einstellungen - Konfiguration - Fahrzeuge: + +![SoC-OVMS-Einstellungen](SoC-OVMS-Settings.png) + +Die Hilfe zu jedem Feld kann in 2.x durch Click auf das (?) angezeigt werden! + +Nach den allgemeinen Einstellungen ist in den speziellen Einstellungen des SoC-Moduls OVMS Folgendes einzutragen: + +- Server URL(incl. Port, z.B. `https://ovms.dexters-web.de:6869`) +- User Id des Accounts im OVMS-Server +- Passwort des Accounts +- VehicleId des Fahrzeuges (wird bei der Einrichtung des OVMS-Moduls vergeben) +- Abfrage-Intervall wenn nicht geladen wird +- Abfrage-Intervall wenn geladen wird. + +## Hinweise + +Nach Neustart bzw. Änderung der LP-Konfiguration werden im EV-Soc-Log Fehler ausgegeben (permission oder fehlende Datei). + +Diese Fehler sind normal und können ignoriert werden. Leider wird im Debug Mode 0 keine Positiv-Meldung ausgegeben. + +Empfehlung: + +- In Einstellungen - System - Fehlersuche bzw. Debugging dies einstellen: Debug Mode 1/Regelwerte bzw. Info +- dann einen manuellen SOC-Abruf durchführen (im Dashboard auf Kreispfeil klicken). +- danach sollte im EV-SOC-Log eine Zeile ähnlich dieser kommen: + + `2023-02-12 11:57:14 INFO:soc_ovms:Lp1 SOC: 61%@2023-02-12T11:53:20` + +Diese Zeile zeigt folgende Information: + +```text +2023-02-12 11:57:14 - Timestamp des SOC-Abrufs +INFO - Debug Level INFO +soc_ovms - SOC-Modul +Lp1 - Ladepunkt +SOC: 61% - SOC Stand +@2023-02-12T11:53:20 - Timestamp des Updates vom EV zum OVMS Cloud-Server +``` + +Falls diese Schritte nicht zum Erfolg führen, das Problem im [Support-Seite im openWB Forum](https://forum.openwb.de/viewtopic.php?t=9278) posten mit Angabe relevanter Daten + +- oWB SW Version +- oWB gekauft oder selbst installiert +- wenn selbst installiert: welches OS(Stretch/Buster) +- welches Fahrzeug +- falls vorhanden Angaben über Firewall, VPN, etc., also Appliances, die den Internetzugang limitieren könnten +- relevante Abschnitte der Logs, vor allem Fehlermeldungen, als CODE-blocks (). + +Das SoC-Log mit evtl. Fehlermeldungen kann wie folgt eingesehen werden: + +- openWB 1.x (Status - EV SoC Log) +- openWB 2.x (Einstellungen - System - Fehlersuche) + diff --git a/docs/SoC-Skoda-settings-1.PNG b/docs/SoC-Skoda-settings-1.PNG new file mode 100644 index 0000000000..892f4d24e6 Binary files /dev/null and b/docs/SoC-Skoda-settings-1.PNG differ diff --git a/docs/SoC-Skoda-settings-2.PNG b/docs/SoC-Skoda-settings-2.PNG new file mode 100644 index 0000000000..6805cea332 Binary files /dev/null and b/docs/SoC-Skoda-settings-2.PNG differ diff --git a/docs/SoC-Skoda.md b/docs/SoC-Skoda.md new file mode 100644 index 0000000000..c4a0f335d3 --- /dev/null +++ b/docs/SoC-Skoda.md @@ -0,0 +1,82 @@ +# SoC-Modul Skoda + +Das SoC-Modul Skoda gibt es in openWB 2.x. + +## Konfiguration + +Die Konfiguration erfolgt im Bereich Einstellungen - Konfiguration - Fahrzeuge: + +![Allgemeine Konfiguration](SoC-Skoda-settings-1.PNG) + +![Spezielle Konfiguration](SoC-Skoda-settings-2.PNG) + +## Hinweise + +Für nicht-Skoda Fahrzeuge (Audi, VW, etc.) funktioniert das Modul nicht. + +Erfolgreich getestet u.a. für folgende Fahrzeuge: Enyaq, Elroq. + +**Wichtig für alle Fahrzeuge:** +Es muss ein aktives Konto im Skoda ID Portal vorhanden sein und die "MySkoda App" muss eingerichtet sein. + +**WICHTIG:** +Skoda ändert gelegentlich die Bedingungen für die Nutzung der Online-Services. + +Diese müssen bestätigt werden. Wenn der SOC-Abruf plötzlich nicht mehr funktioniert, VOR dem Posten bitte Schritt 1 ausführen. + +Bei Problemen zunächst bitte diese Schritte durchführen: + +1. sicherstellen, dass auf dieser Skoda-Seite alle Einverständnisse gegeben wurden. + + + + In einigen Fällen wurden die Einverständnisse gegeben und trotzdem funktionierte die Abfrage nicht. + Hier hat folgendes Vorgehen geholfen: Im Skoda Konto das Land temporär umstellen, d.h. + - auf ein anderes Land als das eigene ändern + - sichern + - zurück auf das eigene Land ändern + - sichern. + +2. Nach einem manuellen SOC-Abruf (Kreispfeil hinter dem SOC klicken) auf der Status - Seite EV-SOC Log und Debug log auf Fehler kontrollieren + +3. Falls im Ev-Soc Log Fehler 303 (unknown redirect) gemeldet wird: + - Ursache 1: Bestimmte Sonderzeichen im Passwort funktionieren nicht mit dem Modul. Bitte das Passwort auf eines ohne Sonderzeichen ändern und testen. + - Ursache 2: Falsche Email, Passwort oder VIN eingegeben. Alle 3 löschen, speichern, neu eingeben, speichern und testen. + +4. Falls eine Firewall im Spiel ist: Es gab einzelne Probleme beim Internet-Zugriff der openWB auf Python Archive und Fahrzeug-Server wenn IPV6 aktiv ist. + +5. Nach Neustart bzw. Änderung der LP-Konfiguration werden im EV-Soc-Log Fehler ausgegeben (permission oder fehlende Datei). + + Diese Fehler sind normal und können ignoriert werden. Leider wird im Debug Mode 0 keine Positiv-Meldung ausgegeben. + Empfehlung: + - In Einstellungen - System - Fehlersuche dies einstellen: Debug Level/Details + - dann einen manuellen SOC-Abruf durchführen (im Dashboard auf Kreispfeil klicken). + - danach sollte im EV-SOC-Log eine Zeile ähnlich dieser kommen: + + `2023-02-12 11:57:14 INFO:soc_skoda:Lp1 SOC: 61%@2023-02-12T11:53:20` + + Diese Zeile zeigt folgende Information: + + `2023-02-12 11:57:14` *- Timestamp des SOC-Abrufs* + + `INFO` *- Debug Level INFO* + + `soc_skoda` *- SOC-Modul* + + `Lp1` *- Ladepunkt* + + `SOC: 61%` *- SOC Stand* + + `@2025-02-12T11:53:20` *- Timestamp des Updates vom EV zum VW Cloud-Server* + +6. Falls diese Schritte nicht zum Erfolg führen, das Problem im [Support Thema](https://forum.openwb.de/viewforum.php?f=12) mit Angabe relevanter Daten posten + - oWB SW Version + - oWB gekauft oder selbst installiert + - wenn selbst installiert: welches OS(Stretch/Buster) + - welches Fahrzeug + - falls vorhanden Angaben über Firewall, VPN, etc., also Appliances, die den Internetzugang limitieren könnten + - relevante Abschnitte der Logs, vor allem Fehlermeldungen, als CODE-blocks (). + +Das SoC-Log mit evtl. Fehlermeldungen kann wie folgt eingesehen werden: + +- Einstellungen - System - Fehlersuche diff --git a/docs/SoC-VWId-19-settings-1.PNG b/docs/SoC-VWId-19-settings-1.PNG new file mode 100755 index 0000000000..9ed159043c Binary files /dev/null and b/docs/SoC-VWId-19-settings-1.PNG differ diff --git a/docs/SoC-VWId-19-settings-2.PNG b/docs/SoC-VWId-19-settings-2.PNG new file mode 100755 index 0000000000..1a6346958b Binary files /dev/null and b/docs/SoC-VWId-19-settings-2.PNG differ diff --git a/docs/SoC-VWId-20-settings-1.PNG b/docs/SoC-VWId-20-settings-1.PNG new file mode 100755 index 0000000000..31a03d78de Binary files /dev/null and b/docs/SoC-VWId-20-settings-1.PNG differ diff --git a/docs/SoC-VWId-20-settings-2.PNG b/docs/SoC-VWId-20-settings-2.PNG new file mode 100755 index 0000000000..610e160807 Binary files /dev/null and b/docs/SoC-VWId-20-settings-2.PNG differ diff --git a/docs/SoC-VWId.md b/docs/SoC-VWId.md new file mode 100644 index 0000000000..3f3d17a962 --- /dev/null +++ b/docs/SoC-VWId.md @@ -0,0 +1,97 @@ +# SoC-Modul VWId + +Das SoC-Modul VWId gibt es in openWB 1.x und 2.x +Die Konfiguration ist in beiden Varianten sehr ähnlich: + +## Konfiguration in openWB 1.x + +Die Konfiguration in openWB 1.x erfolgt im Bereich Einstellungen - Modulkonfiguration - Ladepunkte: + +![Allgemeine Konfiguration-1x](SoC-VWId-19-settings-1.PNG) + +![Spezielle Konfiguration-1x](SoC-VWId-19-settings-2.PNG) + +## Konfiguration in openWB 2.x + +Die Konfiguration in openWB 2.x erfolgt im Bereich Einstellungen - Konfiguration - Fahrzeuge: + +![Allgemeine Konfiguration-2x](SoC-VWId-20-settings-1.PNG) + +![Spezielle Konfiguration-2x](SoC-VWId-20-settings-2.PNG) + +## Hinweise + +Erfolgreich getestet u.a. für folgende Fahrzeuge: ID3, ID4, ID5, ID7, eGolf, Golf 8 GTE, Passat GTE, eUP, T7 Multivan eHybrid. + +Für nicht-VW Fahrzeuge (Audi, Skoda, etc.) funktioniert das Modul nicht. + +**Wichtig für alle Fahrzeuge:** +Es muss ein aktives Konto in myVolkswagen vorhanden sein und die "Volkswagen App" muss eingerichtet sein. + +**WICHTIG:** +VW ändert gelegentlich die Bedingungen für die Nutzung der Online-Services. + +Diese müssen bestätigt werden. Wenn der SOC-Abruf plötzlich nicht mehr funktioniert, VOR dem Posten bitte Schritt 1 ausführen. + +Bei Problemen zunächst bitte diese Schritte durchführen: + +1. sicherstellen, dass auf diesen VW-Seiten alle Einverständnisse gegeben wurden. + + + + + + In einigen Fällen wurden die Einverständnisse gegeben und trotzdem funktionierte die Abfrage nicht. + Hier hat folgendes Vorgehen geholfen: Im Volkswagen Konto das Land temporär umstellen, d.h. + - auf ein anderes Land als das eigene ändern + - sichern + - zurück auf das eigene Land ändern + - sichern. + +2. Nach einem manuellen SOC-Abruf (Kreispfeil hinter dem SOC klicken) auf der Status - Seite EV-SOC Log und Debug log auf Fehler kontrollieren +3. Falls im Ev-SOC Log ein fehlendes Modul gemeldet wird, z.B. + + `ImportError: No module named 'lxml' (LV0)` + - in openWB 1.9 sicherstellen, dass 1.9.304 (Nightly) installiert ist. + - einen Reboot durchführen (Einstellungen - System - Reboot, Fahrzeug dazu abstecken!) + Bei dem Neustart sollten fehlende Module installiert werden. + Falls das nicht hilft gibt es den Support im Forum; dazu den Log der Startphase (1.9: Debug Log, 2.x Main Log) als code block () posten. +4. Falls im log evcc erwähnt wird: von VW ID (alt) auf VW ID umstellen. +5. Falls im Ev-Soc Log Fehler 303 (unknown redirect) gemeldet wird: + - Ursache 1: Bestimmte Sonderzeichen im Passwort funktionieren nicht mit dem Modul. Bitte das Passwort auf eines ohne Sonderzeichen ändern und testen. + - Ursache 2: Falsche Email, Passwort oder VIN eingegeben. Alle 3 löschen, speichern, neu eingeben, speichern und testen. +6. Falls eine Firewall im Spiel ist: Es gab einzelne Probleme beim Internet-Zugriff der openWB auf Python Archive und Fahrzeug-Server wenn IPV6 aktiv ist. +7. Nach Neustart bzw. Änderung der LP-Konfiguration werden im EV-Soc-Log Fehler ausgegeben (permission oder fehlende Datei). + + Diese Fehler sind normal und können ignoriert werden. Leider wird im Debug Mode 0 keine Positiv-Meldung ausgegeben. + Empfehlung: + - In Einstellungen - System - Debugging dies einstellen: Debug Mode 1/Regelwerte + - dann einen manuellen SOC-Abruf durchführen (im Dashboard auf Kreispfeil klicken). + - danach sollte im EV-SOC-Log eine Zeile ähnlich dieser kommen: + + `2023-02-12 11:57:14 INFO:soc_vwid:Lp1 SOC: 61%@2023-02-12T11:53:20` + + Diese Zeile zeigt folgende Information: + + ```text + 2023-02-12 11:57:14 - Timestamp des SOC-Abrufs + INFO - Debug Level INFO + soc_vwid - SOC-Modul + Lp1 - Ladepunkt + SOC: 61% - SOC Stand + @2023-02-12T11:53:20 - Timestamp des Updates vom EV zum VW Cloud-Server + ``` + +8. Falls diese Schritte nicht zum Erfolg führen, das Problem im [Support Thema](https://forum.openwb.de/viewtopic.php?t=4803) posten mit Angabe relevanter Daten + - oWB SW Version + - oWB gekauft oder selbst installiert + - wenn selbst installiert: welches OS(Stretch/Buster) + - welches Fahrzeug + - falls vorhanden Angaben über Firewall, VPN, etc., also Appliances, die den Internetzugang limitieren könnten + - relevante Abschnitte der Logs, vor allem Fehlermeldungen, als CODE-blocks (). + +Das SoC-Log mit evtl. Fehlermeldungen kann wie folgt eingesehen werden: + +- openWB 1.x (Status - EV SoC Log) +- openWB 2.x (Einstellungen - System - Fehlersuche) + diff --git a/docs/Speichersteuerung-SolarEdge.md b/docs/Speichersteuerung-SolarEdge.md new file mode 100644 index 0000000000..be303f2853 --- /dev/null +++ b/docs/Speichersteuerung-SolarEdge.md @@ -0,0 +1,18 @@ +# Speichersteuerung openWB + + Um die Speicher-Entladung ins Fahrzeug zu steuern muss zunächst unter "Ladeeinstellungen -> Übergreifendes" die Speicher-Entladung ins Fahrzeug konfiguriert werden: + + ![openWB Speichersteuerung](pictures/Speichersteuerung_openWB-Einstellungen.png "openWB Speichersteuerung") + +## SolarEdge und Mindest-SoC bzw. SoC-Reserve + +SolarEdge entlädt nicht alle Speicher bis 0%, sondern nur bis zu einem Mindest-SoC (Ab hier als SoC-Reserve bezeichnet). Um Probleme bei der Speichersteuerung mit SolarEdge zu verhindern, muss diese SoC-Reserve der openWB bekannt sein. Bei Unterschreiten dieser SoC-Reserve erfolgt ggf. eine Zwangsladung des Speichers durch das SolarEdge Portal, um die Tiefentladung des Speichers zu verhindern. Diese kann nur erfolgen, wenn die Speichersteuerung durch openWB beendet wurde. +Die SoC-Reserve ist je nach verwendetem Speicher unterschiedlich. Bei BYD LVS Speichern beträgt sie z.B. meist 10%, kann je nach Typ aber auch bei 15% liegen. Einige LG-Speicher verwenden 5% und die HomeBattery von SolarEdge kann bis zu 0% entladen werden. +Um einen zusätzlichen Konfigurationsaufwand pro Speicher zu vermeiden, verwendet die Speichersteuerung als Default-Wert für die SoC-Reserve 15%. Nach einem Reboot der openWB wird nun ständig der SoC-Wert des Speichers überwacht und bei Auftreten eines geringeren Wertes als 15% wird dieser als SoC-Reserve übernommen. Nach einem Neustart wird der gelernte Wert wieder verworfen, damit im Fehlerfall nicht ein falscher Wert dauerhaft erhalten bleibt. +Dies führt dazu, dass sich nach einem Neustart der openWB die Speichersteuerung sicherheitshalber bei 15% beendet, wenn noch kein niedrigerer Wert gelernt wurde. In diesem Fall wird dann der Rest vom Speicher, wie in der Standardkonfiguration, immer ins Fahrzeug entladen. Wenn dies einmal erfolgt ist, bleibt die neu gelernte SoC-Reserve bis zum nächsten Neustart erhalten. + +### Backup-Modul +Systeme mit einem Backup-Modul können mit einer Backup-Reserve konfiguriert werden, die für den Backup-Fall reserviert bleibt. Wenn diese Konfiguration erfolgt ist, so hat sie Vorrang vor dem oben beschriebenen Vorgang und wird immer berücksichtigt. + +Feedback gerne im Forum: +https://forum.openwb.de/viewtopic.php?t=9934 \ No newline at end of file diff --git "a/docs/Steuerbare Verbrauchseinrichtungen nach \302\24714a.md" "b/docs/Steuerbare Verbrauchseinrichtungen nach \302\24714a.md" new file mode 100644 index 0000000000..77a573797b --- /dev/null +++ "b/docs/Steuerbare Verbrauchseinrichtungen nach \302\24714a.md" @@ -0,0 +1,39 @@ +## Steuerbare Verbrauchseinrichtungen (SteuVE) nach § 14a EnGW + +Der Gesetzgeber sieht verschiedene Möglichkeiten für steuerbare Verbrauchseinrichtungen vor. Für jede steuerbare Verbrauchseinrichtung kann eine andere Option angemeldet werden. Bei der Konfiguration muss deshalb auch immer der/die Ladepunkte angegeben werden, für die die IO-Aktion angewendet werden soll. + +### Dimmen per EMS + +Beim Dimmen wird eine maximale Bezugsleistung für alle steuerbaren Verbrauchseinrichtungen nach einer vorgegebene Formel ermittelt. Das Ergebnis dieser Formel muss bei der IO-Aktion `Dimmen` in der Einstellung `maximale Bezugsleistung` eingetragen werden. ACHTUNG: Die openWB kann aktuell nur die Ladepunkte berücksichtigen. Sind noch weitere steuerbare Verbraucher angemeldet, können diese über einen digitalen Ausgang angebunden werden. Da openWB die Leistung dieser Geräte nicht kennt, werden 4,2kW angenommen. Muss der Verbraucher seine Leistung begrenzen, wird der Ausgang auf 0V gesetzt. Für die korrekte Ermittlung der maximalen Bezugsleistung ist der Betreiber, nicht openWB oder die software2 verantwortlich. +Vorhandener Überschuss kann zusätzlich zur maximalen Bezugsleistung verwendet werden. + +### Dimmung per Direkt-Steuerung + +Bei der Dimmung per Direkt-Steuerung wird jede steuerbare Verbrauchseinrichtung separat angesteuert und ihr Leistungsbezug auf 4,2kW gedimmt. +Pro steuerbarer Verbrauchseinrichtung muss eine IO-Aktion konfiguriert werden und dort der Ladepunkt und der zugehörige Eingang angegeben werden. + +### Rundsteuer-Empfänger-Kontakt (RSE) + +Für den RSE-Kontakt können Muster aus verschiedenen Eingängen angegeben werden. Es kann frei festgelegt werden, bei welchem Muster die zugeordneten Ladepunkte Gesperrt oder freigegeben sind. + +In der abgebildeten Konfiguration werden die Ladepunkte nur freigegeben, wenn beide Kontakte DI1 und DI2 geschlossen sind. Ist auch nur einer geöffnet, wird gesperrt. + +![RSE-Beispielkonfiguration](RSE-Beispielkonfiguration.png) + +## Steuerbare Erzeugungsanlagen (EZA) nach § 9 EEG + +Bitte beachten: Die openWB steuert keinen Wechselrichter an. Sie zeigt lediglich den aktuellen Zustand der Beschränkung an und kann optional das Signal der Eingänge an Ausgänge durchreichen. + +Die Einspeise- oder Erzeugungsleistung der EZA (abhängig von der Implementierung in der EZA) wird über drei potentialfreie Signalkontakte der FNN-Steuerbox geregelt. Die openWB übernimmt dabei keine direkte Steuerung des Wechselrichters, sondern visualisiert lediglich und protokolliert den aktuellen Steuerzustand. + +Das Signalkabel der FNN-Steuerbox muss daher beispielsweise über ein Koppelrelais mit zwei separaten Schließer-/Wechselkontakten mit dem I/O-Modul der openWB und der Erzeugungsanlage verbunden werden. Falls dies nicht möglich ist, kann die Steuerbox über einfache Koppelrelais mit dem I/O-Modul der openWB verbunden werden und das empfangene Signal an vorhandene Ausgänge des I/O-Moduls (falls vorhanden) durchgereicht werden. + +Die Signalkontakte bilden folgende Zustände ab: +S1 -> 60% der EZA +S2 -> 30% der EZA +W3 -> 0% der EZA +alle Kontakte offen -> 100% der EZA + +Die Eingangsmuster sind so zu konfigurieren, dass auch bei mehreren geschlossenen Kontakten eine eindeutige Funktion gewährleistet wird. In der abgebildeten Konfiguration hat z.B. der Eingang DI5 für Begrenzung auf 0% Priorität, sodass dieses Muster auch erkannt wird, falls noch einer der Eingänge DI3 oder DI4 geschlossen sind. + +![EZA-Beispielkonfiguration](EZA-Beispielkonfiguration.png) diff --git a/docs/Strompreisbasiertes Laden.md b/docs/Strompreisbasiertes Laden.md index 61457f74cb..3de589cb1a 100644 --- a/docs/Strompreisbasiertes Laden.md +++ b/docs/Strompreisbasiertes Laden.md @@ -1,11 +1,11 @@ -Einige Stromanbierter berechnen die Strompreise stundenweise anhand des Strompreises an der Börse. openWB bietet die Möglichkeit, die günstigen Zeiten optimal zu nutzen. Eine Übersicht über die unterstützten Anbieter findest Du hier: +Einige Stromanbierter berechnen die Strompreise stundenweise anhand des Strompreises an der Börse. openWB bietet die Möglichkeit, die günstigen Zeiten optimal zu nutzen. Eine Übersicht über die unterstützten Anbieter findest Du hier: Unter `Einstellungen → Ladeeinstellungen → Übergreifendes` muss der Stromanbieter konfiguriert werden. Die dort abgefragten Preise werden dann auch zur Berechnung der Ladekosten für den Netzanteil im Ladeprotokoll verwendet. Im Ladeprofil des Fahrzeugs muss das strompreisbasierte Laden aktiviert werden. Die Berücksichtigung des Strompreises in den verschiedenen Lademodi erfolgt folgendermaßen: - * Sofort- und Zeitladen: Es wird nur geladen, wenn der Strompreis unter dem maximalen angegeben Strompreis liegt. - * Zielladen: Die openWB berechnet anhand der eingestellten Stromstärke und Phasenanzahl für diesen Lademodus, wie lange geladen werden, muss um den konfigurierten SoC oder die konfigurierte Energiemenge zu erreichen. Dieses Ladefenster wird automatisch auf die günstigsten Stunden des Stromtarifs gelegt. Ist PV-Überschuss vorhanden, wird dieser immer zuerst genutzt und verkürzt das besagte Ladefenster. - * PV-Laden: keine Berücksichtigung des Strompreises +* Sofort- und Zeitladen: Es wird nur geladen, wenn der Strompreis unter dem maximalen angegeben Strompreis liegt. +* Zielladen: Die openWB berechnet anhand der eingestellten Stromstärke und Phasenanzahl für diesen Lademodus, wie lange geladen werden, muss um den konfigurierten SoC oder die konfigurierte Energiemenge zu erreichen. Dieses Ladefenster wird automatisch auf die günstigsten Stunden des Stromtarifs gelegt. Ist PV-Überschuss vorhanden, wird dieser immer zuerst genutzt und verkürzt das besagte Ladefenster. +* PV-Laden: keine Berücksichtigung des Strompreises -Wenn keine Preise abgefragt werden können, wird bei Sofort- und Zeitladen immer geladen und bei Zielladen zunächst mit PV-Überschuss und zum Erreichen des Zieltermins mit Netzstrom. \ No newline at end of file +Wenn keine Preise abgefragt werden können, wird bei Sofort- und Zeitladen immer geladen und bei Zielladen zunächst mit PV-Überschuss und zum Erreichen des Zieltermins mit Netzstrom. diff --git "a/docs/Typische-Anwendungsf\303\244lle.md" "b/docs/Typische-Anwendungsf\303\244lle.md" index 6d2bcf7ed2..ea2f18874c 100644 --- "a/docs/Typische-Anwendungsf\303\244lle.md" +++ "b/docs/Typische-Anwendungsf\303\244lle.md" @@ -1,7 +1,20 @@ +## Übersicht + +| Szenario | Konfiguration | +|---------------|---------------| +|Ich muss schnell weg und brauche ein voll geladenes Fahrzeug. | Sofortladen | +|Ich stecke mittags an, möchte PV nutzen und brauch am nächsten Morgen um 8:00 mindestens 75% SoC. | Zielladen | +|Ich habe einen dynamischen Stromtarif und stecke mittags an, möchte PV nutzen und brauch am nächsten Morgen um 8:00 mindestens 75% SoC. | Zielladen: Anhand der berechneten Ladedauer werden die günstigsten Stunden ermittelt und dann geladen.| +|Ich habe einen dynamischen Stromtarif und möchte nur laden, wenn Überschuss vorhanden ist oder der Strompreis unter einer von mir festgelegten Grenze liegt. | Eco | +|Ich möchte nur Überschuss laden. | PV | +|Ich möchte Überschuss laden. Falls ich spontan fahren möchte, brauche ich immer mind 40% SoC. | PV mit Mindest-SoC | +|Ich möchte Überschuss laden. Ich habe kein SoC-Modul und möchte das mein Auto immer etwas geladen wird, falls ich spontan fahren möchte. | PV mit Begrenzung Energiemenge | +|Für meinen Arbeitstag brauche ich mind 60% SoC. Falls Überschuss da ist, soll der auch in mein Auto geladen werden. Mein Auto soll immer mindestens 25% haben, falls ich spontan fahren möchte. | Zielladen und Zeitladen mit Begrenzung SoC | + ## Privater Haushalt, ein E-Auto und PV-Anlage In diesem Szenario sind die Ziele meistens, das Auto morgens für den Weg zur Arbeit fahrbereit zu haben, aber bis dahin möglichst viel Energie aus der PV-Anlage zum Laden zu nutzen. -Hierfür ist die Funktion *Zielladen* zu nutzen. Auch, wenn es vom Namen her scheint, dass nur zu einem festen Zeitpunkt eine definierte Energiemenge in das EV geladen sein soll, wird dennoch bis zum Beginn dieses erzwingenen Ladevorgangs PV-Energie, sofern vorhanden, genutzt. +Hierfür ist die Funktion *Zielladen* zu nutzen. Auch, wenn es vom Namen her scheint, dass nur zu einem festen Zeitpunkt eine definierte Energiemenge in das EV geladen sein soll, wird dennoch bis zum Beginn dieses erzwungenen Ladevorgangs PV-Energie, sofern vorhanden, genutzt. ![Zielladen](pictures/Anwendungsfaelle_zielladen.jpg) @@ -9,7 +22,7 @@ Einstellbar ist der Ziel SoC, der in vielen Fällen auf 80% eingestellt wird, da In dem oben gezeigten Beispiel ist der Ladestrom mit 13A eingestellt. Somit bleibt bei einem 3-phasigen 11kW-Lader noch Reserve, um die Stromstärke kurz vor Ende ggf. noch zu erhöhen. Als Zielzeit ist die Abfahrtzeit Abfahrt einzustellen. Die Regelung berechnet aus dem aktuellen SoC des Fahrzeugs, sowie aus den zwingend korrekt anzugebenden Maximalwerten der Ladeströme im Ladeprofil, den Zeitpunkt, an dem die Ladung starten muss. -Es empfielt sich den Ladestrom im Ladeprofil unter Zielladen etwas niedriger als die Möglichkeiten der Wallbox und des Fahrzeugs anzugeben, damit etwas Puffer vorhanden ist, falls das Auto zu spät angesteckt worden ist. +Es empfiehlt sich den Ladestrom im Ladeprofil unter Zielladen etwas niedriger als die Möglichkeiten der Wallbox und des Fahrzeugs anzugeben, damit etwas Puffer vorhanden ist, falls das Auto zu spät angesteckt worden ist. Falls das EV durch eine Standheizung vor Fahrtbeginn vorgeheizt werden soll, kann hierfür ein Zeitslot mit _Laden nach Zeitplan_ konfiguriert werden. Zeitladen kann zusätzlich zum gewählten Lademodus, hier Zielladen, aktiviert werden. So wird der Akku durch die Standheizung nicht belastet, sondern der Strom kommt aus dem EVU-Netz. Hier kann dann ein minimaler Strom von z.B. 6A gewählt werden, da die Leistungsaufnahme für die Heizung meist nicht mehr als 1kW benötigt. @@ -17,7 +30,7 @@ Falls das EV durch eine Standheizung vor Fahrtbeginn vorgeheizt werden soll, kan Wird das Fahrzeug außer der Reihe benötigt und es soll kurzfristig viel Energie in den Akku geladen werden, ist die openWB auf der Startseite auf *Sofortladen* zu stellen. Hier ist es möglich, mit der maximal verfügbaren Leistung den Akku so schnell wie möglich aufzuladen. -### Ausnutzen der PV-Anlage bei wechseldem Wetter +### Ausnutzen der PV-Anlage bei wechselndem Wetter Insbesondere im Frühling und Herbst kann die PV-Leistung bei bewölktem Himmel stark schwanken. Um ein häufiges Beenden und Starten des Ladevorgangs zu vermeiden, kann bei dem Modus PV ein *Minimaler Dauerstrom* eingestellt werden. Ist die Einstellung auf 0A, so wird ausschließlich mit solarem Überschuss geladen. Steigt die Netzeinspeisung über den in _Konfiguration->Ladeeinstellungen ->PV-Laden_ eingestellten Grenzwert, wird nach Ablauf der Einschaltverzögerung die Ladung gestartet. Mit der nächstgrößeren Einstellmöglichkeit 6A, wird (z.B. bei einphasigem Laden) kontinuierlich mit ~1,3kW geladen. Wird ins Netz eingespeist, wird die Ladeleistung hochgeregelt, sodass möglichst weder Strom eingespeist noch bezogen wird und, je nach Möglichkeiten des Fahrzeugs, auf 3-phasiges Laden umgeschaltet. @@ -26,9 +39,9 @@ Ist die Einstellung auf 0A, so wird ausschließlich mit solarem Überschuss gela ## Integration in Hausautomation -openWB eignet sich hervorragend zur Integration in eine bestehende Hausautomatins-Infrastruktur, da über den integrierten MQTT-Broker Befehle sowie Statusmeldungen ausgetauscht werden können. +openWB eignet sich hervorragend zur Integration in eine bestehende Hausautomation, da über den integrierten MQTT-Broker Befehle sowie Statusmeldungen ausgetauscht werden können. ### MQTT Zum Debugging empfiehlt sich das Programm [MQTT-Explorer?](http://mqtt-explorer.com/). -Eine detailierte Erklärung ist auf der [MQTT-Seite](https://github.com/openWB/core/wiki/MQTT) zu finden. +Eine detaillierte Erklärung ist auf der [MQTT-Seite](https://github.com/openWB/core/wiki/MQTT) zu finden. diff --git a/docs/WiCAN.md b/docs/WiCAN.md new file mode 100644 index 0000000000..e4a29e3d62 --- /dev/null +++ b/docs/WiCAN.md @@ -0,0 +1,155 @@ +# WiCAN-OBD2 mit manuellem SoC Modul der openWB + +Mit Hilfe des WiCAN OBD2 Dongles von meatPi Electronics können die Werte des Fahrzeugs über die OBD2-Schnittstelle ausgelesen und im WLAN per MQTT an die openWB Wallbox gesendet werden. +In Verbindung mit dem manuellem SoC Modul der openWB ist hiermit die Nutzung ohne Cloud-Dienste der Fahrzeughersteller möglich. + + +Die erste Umsetzung erfolgte von zut im openWB Forum, der hierfür auch eine Unterstützung von Spritmonitor umgesetzt hat. Hierfür ist ein weiteres Gerät im Netzwerk nötig, auf dem ein kleines Programm (soc_helper) laufen kann: + + +Im Laufe der weiteren Entwicklung wurde die Möglichkeit des automatischen Auslesen (AutoPID) in die Firmware des Gerätes integriert. Hierfür ist nun keine zusätzliche Hardware mehr nötig, da der Dongle die Daten direkt per MQTT an die openWB sendet. Die Unterstützung von Spritmonitor ist hiermit jedoch nicht möglich. Die Einrichtung dieser Funktion soll hier näher beschrieben werden. + +## 1. Voraussetzungen + +* WiCAN Dongle mit Firmware 3.48, 4.03 oder neuer (4.00 ist fehlerhaft). +* Konfiguration des Manuellen SoC Moduls im Fahrzeug der openWB. +* WLAN Empfang im Bereich der openWB bzw. des Fahrzeugs. +* Ggf. Beachtung einer Alarmanlage, die den OBD2 Port überwacht. + +## 2. WiCAN Dongle + +Die Beschaffung der Hardware ist zunächst die größte Hürde, da die Firma meatPi Electronics Ihren Sitz in Australien hat. +Es sind aktuell 2 Bezugsquellen bekannt: + +Es ist hierbei darauf zu achten, dass nur der WiCAN-OBD2 oder der WiCAN PRO (Coming soon) verwendet werden kann. Bei Bestellung über Mouser erfolgt der Versand ab 50€ Versandkostenfrei, so dass sich idealerweise 2 oder mehr Besteller zu einer Sammelbestellung zusammentun können. +Achtung: Die angegeben Preise sind Nettopreise. + +## 3. Konfiguration manueller SoC in openWB + +* In der Fahrzeugkonfiguration der openWB muss das manuelle SoC Modul eingerichtet werden. Für die spätere Konfiguration der MQTT-Topics wird die Fahrzeug-ID benötigt. Diese kann man am besten auf der Status Seite der openWB auslesen: + + ![Status](pictures/WiCAN_openWB-Status.png "openWB Status") + +## 4. Konfiguration WiCAN Settings + +* Zunächst muss der WiCAN ins Heim-WLAN geholt werden: + + +* Nun werden die weiteren Parameter konfiguriert: +![WiCAN Settings](pictures/WiCAN_Settings.png "WiCAN Settings") + +* Protocol AutoPID wird zur automatischen Abfrage der PIDs benötigt (Bereich Automate) +* MQTT zur openWB erfolgt mit leerem User und Passwort +* RX und Status Topic müssen mit others/ beginnen, damit sie von openWB beachtet werden. Sie dienen nur der Fehleranalyse und Überwachung mit MQTT-Explorer. Für die eigentliche Funktion werden sie nicht benötigt. + +--- + +* Sleep Mode (Bereich Power Saving): Bei Unterschreiten der Sleep Voltage schaltet sich der WiCAN ab. Hierdurch wird ein Entladen der 12V Batterie verhindert. +![WiCAN Power Saving](pictures/WiCAN_PowerSaving.png "WiCAN Settings") + +## 5. Ermittlung der PID Parameter aus dem Vehicle Profile bei meatPi + +Es werden bei meatPi die benötigten Parameter verschiedener Fahrzeuge in Vehicle-Profilen gesammelt. +Die Informationen aus diesen Profilen können wir verwenden, um die Parameter, die wir für die openWB benötigen, zu ermitteln. + +Die Profile sind hier einzeln verfügbar: + + +Anhand des Beispiels eines VW:ID, die wichtigen Informationen (fettgedruckt). +Wir benötigen zwingend den SoC, optional können wir auch die Reichweite (Range) gebrauchen: + +--- +{ + "car_model":"VW: ID", + **"init":"ATST96;ATFCSD300000;ATFCSM1;",** + "pids":[ + { + **"pid":"22028C1",** + **"pid_init":"ATSP7;ATCP17;ATSHFC007B;ATFCSH17FC007B;ATCRA17FE007B;",** + "parameters":[ + { + "class":"battery", + **"expression":"B4\*0.4425-6.1947",** + **"name":"soc",** + "unit":"%" + } + ] + }, + { + **"pid":"222AB62",** + **"pid_init":"ATSP6;ATCP18;ATSH710;ATFCSH710;ATCRA77A;",** + "parameters":[ + { + "class":"none", + **"expression":"[B5:B6]",** + **"name":"range",** + "unit":"km" + } + ] + } + +## 6. Übernahme der Werte für die Custom PIDs (Automate-Tab) + +die gefundenen Werte werden nun im Bereich Automate bei den Custom PIDs eingetragen: +![WiCAN Automate Tab](pictures/WiCAN_Automate.png "WiCAN Automate") + +* Das Feld "Custom Initialization" wird aus "**init**" vom Vehicle-Profile übernommen. +* Das Feld Name muss für den SoC "manual_soc" lauten, für die Reichweite "range". +* Falls vorhanden wird das Feld "Init" aus "**pid_init**" vom Vehicle-Profile übernommen. Dieses Feld kann auch leer sein, wenn für die einzelnen PIDs keine besondere Initialisierung benötigt wird. +* Das Feld PID wird aus "**pid**" übernommen. +* Das Feld Expression wird aus "**expression**" übernommen. +* Period(ms) ist das Intervall, in denen die Werte abgefragt werden. +* Im Feld "Destination Type" muss für den SoC "MQTT_Topic", für die Reichweite jedoch "MQTT_Wallbox" eingestellt werden. +* Im Feld Send_to muss die Fahrzeug ID aus der openWB, die wir in [Punkt 3](#3-konfiguration-manueller-soc-in-openwb) ermittelt haben, eingesetzt werden (...vehicle/**x**/...): + +Die Werte lauten für den SoC: +openWB/set/vehicle/**x**/soc_module/calculated_soc_state +Für die Reichweite: +openWB/set/vehicle/**x**/get/range + +Das Ergebnis sieht dann z.B. so aus: + +Name|Init|PID|Expression|Period(ms)|Type|Send_to +-|-|-|-|-|-|- +manual_soc|ATSP7;ATSHFC007B;ATCP17;ATCRA17FE007B;ATFCSH17FC007B;|22028C1|B4*0.4425‑6.1947|10000|MQTT_Topic|openWB/set/vehicle/**3**/soc_module/calculated_soc_state +range|ATSP6;ATSH710;ATCP18; ATCRA77A;ATFCSH710;|222AB62|[B5:B6]|10000|MQTT_Topic|openWB/set/vehicle/**3**/get/range + +### Ergänzung zur openWB 1.9 + +Bei openWB 1.9 wird das SOC Modul Manuell+Berechnung am Ladepunkt konfiguriert, das Topic benötigt hier den Typ MQTT_Wallbox. +Am Beispiel Ladepunkt 1 würde dies dann so aussehen: + +Name|Init|PID|Expression|Period(ms)|Type|Send_to +-|-|-|-|-|-|- +manualSoC|ATSP7;ATSHFC007B;ATCP17;ATCRA17FE007B;ATFCSH17FC007B;|22028C1|B4*0.4425‑6.1947|60000|MQTT_Wallbox|openWB/set/lp/**1** + +Nachtrag: openWB 1.9 akzeptiert nur Ganzzahlige SOC-Werte und kann daher aktuell nicht verwendet werden. + +## 7. Alarmanlage des Fahrzeugs + +Bei Fahrzeugen mit einer Alarmanlage wird häufig auch der OBD2-Port überwacht, so dass bei einer Abfrage die Alarmanlage auslöst. +Um dieses Problem zu umgehen sind momentan folgende Lösungen bekannt: + +* Bei einigen Fahrzeugen liegt an PIN1 des OBD2-Ports nur bei eingeschalteter Zündung eine Spannung von 12V an. Diese kann dann zur Versorgung des WiCAN Dongles verwendet werden, so dass dieser bei abgeschalteter Zündung gar keine Spannungsversorgung hat. Normalerweise liegt an PIN16 die Versorgungsspannung für den WiCAN an. +Es kann also in diesem Fall mit einem entsprechend "manipuliertem" Adapterkabel z.B. PIN1 und PIN16 getauscht werden: + +* Ggf. ist eine Anpassung der OBD2-Überwachung der Alarmanlage möglich: + + +Beide Lösungen dienen nur zur Info und erfolgen stets auf eigene Gefahr + +## 8. Übersicht erfolgreich getesteter Fahrzeuge + +Abschliessend noch eine kurze Auflistung von bereits erfolgreich getesteten Fahrzeugen. +Gerne kann und sollte diese Liste erweitert werden. +Zur besseren Übersicht werden hier nur die tatsächlich verwendeten Fahrzeuge (am besten mit Modelljahr) sowie nur der SoC aufgeführt: + +Fahrzeug|Custom Initialization|Name|Init|PID|Expression +-|-|-|-|-|- +CUPRA Born 2022|ATST96;ATFCSD300000;ATFCSM1;|manual_soc|ATSP7;ATCP17;ATSHFC007B;ATFCSH17FC007B;ATCRA17FE007B;|22028C1|B4*0.4425‑6.1947 +Hyundai Ioniq (28 kWh) 2017|ATSP6;ATSH7E4;ATST96;|manual_soc||2105|B39/2 +Peugeot iOn|ATSP6;ATFCSH761;ATFCSD300000;ATFCSM1;ATSH761;ATCRA762;|manual_soc||2101|(B4/2)‑5 + +Anmerkungen zur Tabelle: + +Um einen Zeilenumbruch zu verhindern, müssen Leerzeichen durch einen "no-break space character" (\ ) und ein Minuszeichen durch "no-break hyphen" (\‑) im Markdown Quelltext ersetzt werden. diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md index 0cce569b42..a748e2d8da 100644 --- a/docs/_Sidebar.md +++ b/docs/_Sidebar.md @@ -5,17 +5,27 @@ * [Ladepunkte](https://github.com/openWB/core/wiki/Ladepunkte) * [Fahrzeuge](https://github.com/openWB/core/wiki/Fahrzeuge) * [Manueller SoC](https://github.com/openWB/core/wiki/Manueller-SoC) + * [SoC BMW & Mini](https://github.com/openWB/core/wiki/SoC-BMW-Mini) + * [SoC Cupra](https://github.com/openWB/core/wiki/SoC-Cupra) + * [SoC OVMS](https://github.com/openWB/core/wiki/SoC-OVMS) + * [SoC Skoda](https://github.com/openWB/core/wiki/SoC-Skoda) + * [SoC VWId](https://github.com/openWB/core/wiki/SoC-VWId) + * [WiCAN OBD2-Dongle mit manuellem SoC](https://github.com/openWB/core/wiki/WiCAN) * [Lademodi](https://github.com/openWB/core/wiki/Lademodi) * Zähler * [Grundsätzliches zu Zählern](https://github.com/openWB/core/wiki/Grundsätzliches-zu-Zählern) * [Lastmanagement und kaskadierte Zähler](https://github.com/openWB/core/wiki/Lastmanagement-und-kaskadierte-Zähler) * [Ermittlung des Hausverbrauchs](https://github.com/openWB/core/wiki/Hausverbrauch) +* Speicher + * [Speichersteuerung SolarEdge](https://github.com/openWB/core/wiki/speichersteuerung-solaredge) * Oberfläche * [Anzeige - Steuerung](https://github.com/openWB/core/wiki/Anzeige-Steuerung) * Features + * [Identifikation](https://github.com/openWB/core/wiki/Identifikation) + * [IO-Geräte & -Aktionen](https://github.com/openWB/core/wiki/IO-Geräte-&--Aktionen) * [OCPP](https://github.com/openWB/core/wiki/OCPP) * [Strompreisbasiertes Laden](https://github.com/openWB/core/wiki/Strompreisbasiertes-Laden) - * [Identifikation](https://github.com/openWB/core/wiki/Identifikation) + * [Steuerbare Verbrauchseinrichtungen](https://github.com/openWB/core/wiki/Steuerbare-Verbrauchseinrichtungen-nach-§14a) * Szenarien * [Typische Anwendungsfälle](https://github.com/openWB/core/wiki/Typische-Anwendungsfälle) * [Hybrid-System aus Wechselrichter und Speicher](https://github.com/openWB/core/wiki/Hybrid-System-aus-Wechselrichter-und-Speicher) diff --git a/docs/pictures/Speichersteuerung_openWB-Einstellungen.png b/docs/pictures/Speichersteuerung_openWB-Einstellungen.png new file mode 100644 index 0000000000..8a17ba2f18 Binary files /dev/null and b/docs/pictures/Speichersteuerung_openWB-Einstellungen.png differ diff --git a/docs/pictures/WiCAN_Automate.png b/docs/pictures/WiCAN_Automate.png new file mode 100644 index 0000000000..760998c681 Binary files /dev/null and b/docs/pictures/WiCAN_Automate.png differ diff --git a/docs/pictures/WiCAN_Automate_348.png b/docs/pictures/WiCAN_Automate_348.png new file mode 100644 index 0000000000..387bbfd914 Binary files /dev/null and b/docs/pictures/WiCAN_Automate_348.png differ diff --git a/docs/pictures/WiCAN_PowerSaving.png b/docs/pictures/WiCAN_PowerSaving.png new file mode 100644 index 0000000000..29400936b9 Binary files /dev/null and b/docs/pictures/WiCAN_PowerSaving.png differ diff --git a/docs/pictures/WiCAN_Settings.png b/docs/pictures/WiCAN_Settings.png new file mode 100644 index 0000000000..01d3bf45b7 Binary files /dev/null and b/docs/pictures/WiCAN_Settings.png differ diff --git a/docs/pictures/WiCAN_Settings_348.png b/docs/pictures/WiCAN_Settings_348.png new file mode 100644 index 0000000000..11efb8f568 Binary files /dev/null and b/docs/pictures/WiCAN_Settings_348.png differ diff --git a/docs/pictures/WiCAN_openWB-Status.png b/docs/pictures/WiCAN_openWB-Status.png new file mode 100644 index 0000000000..714f225933 Binary files /dev/null and b/docs/pictures/WiCAN_openWB-Status.png differ diff --git a/docs/samples/sample_electricity_tariff/tariff.py b/docs/samples/sample_electricity_tariff/tariff.py index ea23689b6d..fab002574f 100644 --- a/docs/samples/sample_electricity_tariff/tariff.py +++ b/docs/samples/sample_electricity_tariff/tariff.py @@ -5,7 +5,6 @@ from modules.common import req from modules.common.abstract_device import DeviceDescriptor from modules.common.component_state import TariffState -from modules.common.configurable_tariff import ConfigurableTariff log = logging.getLogger(__name__) diff --git a/docs/samples/sample_modbus/__init__.py b/docs/samples/sample_modbus/__init__ .py similarity index 100% rename from docs/samples/sample_modbus/__init__.py rename to docs/samples/sample_modbus/__init__ .py diff --git a/docs/samples/sample_modbus/bat.py b/docs/samples/sample_modbus/bat.py deleted file mode 100644 index eaee6a3f48..0000000000 --- a/docs/samples/sample_modbus/bat.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -from typing import Optional -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractBat -from modules.common.component_state import BatState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.simcount import SimCounter -from modules.common.store import get_bat_value_store -from modules.devices.sample_modbus.config import SampleBatSetup - - -class SampleBat(AbstractBat): - def __init__(self, device_id: int, component_config: SampleBatSetup, client: ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleBatSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - self.store = get_bat_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.client = client - - def update(self) -> None: - power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) - imported, exported = self.sim_counter.sim_count(power) - - bat_state = BatState( - power=power, - soc=soc, - imported=imported, - exported=exported - ) - self.store.set(bat_state) - - def set_power_limit(self, power_limit: Optional[int]) -> None: - # Methode entfernen, falls der Speicher keine Steuerung der Ladeleistung unterstützt - # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive - # Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird - # None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen. - self.client.write_registers(reg, power_limit) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_modbus/config.py b/docs/samples/sample_modbus/config.py deleted file mode 100644 index 0d011cd9f3..0000000000 --- a/docs/samples/sample_modbus/config.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Optional -from helpermodules.auto_str import auto_str -from modules.common.component_setup import ComponentSetup - - -@auto_str -class SampleConfiguration: - def __init__(self, ip_address: Optional[str] = None): - self.ip_address = ip_address - - -@auto_str -class Sample: - def __init__(self, - name: str = "Sample", - type: str = "sample", - id: int = 0, - configuration: SampleConfiguration = None) -> None: - self.name = name - self.type = type - self.id = id - self.configuration = configuration or SampleConfiguration() - - -@auto_str -class SampleBatConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): - def __init__(self, - name: str = "Sample Speicher", - type: str = "bat", - id: int = 0, - configuration: SampleBatConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleBatConfiguration()) - - -@auto_str -class SampleCounterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): - def __init__(self, - name: str = "Sample Zähler", - type: str = "counter", - id: int = 0, - configuration: SampleCounterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleCounterConfiguration()) - - -@auto_str -class SampleInverterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): - def __init__(self, - name: str = "Sample Wechselrichter", - type: str = "inverter", - id: int = 0, - configuration: SampleInverterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_modbus/counter.py b/docs/samples/sample_modbus/counter.py deleted file mode 100644 index 5b9650c742..0000000000 --- a/docs/samples/sample_modbus/counter.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.simcount import SimCounter -from modules.common.store import get_counter_value_store -from modules.devices.sample_modbus.config import SampleCounterSetup - - -class SampleCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: SampleCounterSetup, client: ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleCounterSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - self.store = get_counter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.client = client - - def update(self): - power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) - imported, exported = self.sim_counter.sim_count(power) - - counter_state = CounterState( - currents=currents, - imported=imported, - exported=exported, - power=power, - frequency=frequency, - power_factors=power_factors, - powers=powers, - voltages=voltages - ) - self.store.set(counter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_modbus/device.py b/docs/samples/sample_modbus/device.py deleted file mode 100644 index 07f9a88026..0000000000 --- a/docs/samples/sample_modbus/device.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env python3 -import logging -from typing import Iterable, Optional, Union, List - -from helpermodules.cli import run_using_positional_cli_args -from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext -from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater -from modules.common.modbus import ModbusTcpClient_ -from modules.devices.sample_modbus import bat, counter, inverter -from modules.devices.sample_modbus.bat import SampleBat -from modules.devices.sample_modbus.config import Sample, SampleConfiguration, SampleBatSetup, SampleCounterSetup, SampleInverterSetup -from modules.devices.sample_modbus.counter import SampleCounter -from modules.devices.sample_modbus.inverter import SampleInverter - -log = logging.getLogger(__name__) - - -def create_device(device_config: Sample): - def create_bat_component(component_config: SampleBatSetup): - return SampleBat(device_config.id, component_config, device_config.configuration.ip_address, client) - - def create_counter_component(component_config: SampleCounterSetup): - return SampleCounter(device_config.id, component_config, device_config.configuration.ip_address, client) - - def create_inverter_component(component_config: SampleInverterSetup): - return SampleInverter(device_config.id, component_config, device_config.configuration.ip_address, client) - - def update_components(components: Iterable[Union[SampleBat, SampleCounter, SampleInverter]]): - with client: - for component in components: - with SingleComponentUpdateContext(component.fault_state): - component.update() - - try: - client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") - return ConfigurableDevice( - device_config=device_config, - component_factory=ComponentFactoryByType( - bat=create_bat_component, - counter=create_counter_component, - inverter=create_inverter_component, - ), - component_updater=MultiComponentUpdater(update_components) - ) - - -COMPONENT_TYPE_TO_MODULE = { - "bat": bat, - "counter": counter, - "inverter": inverter -} - - -def read_legacy(component_type: str, ip_address: str, id: int, num: Optional[int]) -> None: - device_config = Sample(configuration=SampleConfiguration(ip_address=ip_address, id=id)) - dev = create_device(device_config) - if component_type in COMPONENT_TYPE_TO_MODULE: - component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() - else: - raise Exception( - "illegal component type " + component_type + ". Allowed values: " + - ','.join(COMPONENT_TYPE_TO_MODULE.keys()) - ) - component_config.id = num - dev.add_component(component_config) - - log.debug('Sample IP-Adresse: ' + ip_address) - log.debug('Sample ID: ' + str(id)) - - dev.update() - - -def main(argv: List[str]): - run_using_positional_cli_args(read_legacy, argv) - - -device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_modbus/inverter.py b/docs/samples/sample_modbus/inverter.py deleted file mode 100644 index 4cf593294a..0000000000 --- a/docs/samples/sample_modbus/inverter.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractInverter -from modules.common.component_state import InverterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.simcount import SimCounter -from modules.common.store import get_inverter_value_store -from modules.devices.sample_modbus.config import SampleInverterSetup - - -class SampleInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: SampleInverterSetup, client: ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleInverterSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - self.store = get_inverter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.client = client - - def update(self) -> None: - power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) - exported = self.sim_counter.sim_count(power)[1] - - inverter_state = InverterState( - currents=currents, - power=power, - exported=exported, - dc_power=dc_power - ) - self.store.set(inverter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/docs/samples/sample_request_by_component/__init__.py b/docs/samples/sample_modbus/sample_modbus/__init__.py similarity index 100% rename from docs/samples/sample_request_by_component/__init__.py rename to docs/samples/sample_modbus/sample_modbus/__init__.py diff --git a/docs/samples/sample_modbus/sample_modbus/bat.py b/docs/samples/sample_modbus/sample_modbus/bat.py new file mode 100644 index 0000000000..2b1a20a768 --- /dev/null +++ b/docs/samples/sample_modbus/sample_modbus/bat.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +from typing import Optional, TypedDict, Any +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.sample_modbus.sample_modbus.config import SampleBatSetup + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class SampleBat(AbstractBat): + def __init__(self, component_config: SampleBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + unit = self.component_config.configuration.modbus_id + power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) + soc = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + def set_power_limit(self, power_limit: Optional[int]) -> None: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive + # Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird + # None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen. + self.client.write_registers(reg, power_limit) + # Wenn der Speicher keine Steuerung der Ladeleistung unterstützt + pass + + def power_limit_controllable(self) -> bool: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. + return True + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_modbus/sample_modbus/config.py b/docs/samples/sample_modbus/sample_modbus/config.py new file mode 100644 index 0000000000..0e3640f49e --- /dev/null +++ b/docs/samples/sample_modbus/sample_modbus/config.py @@ -0,0 +1,74 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class SampleConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502): + self.ip_address = ip_address + self.port = port + + +@auto_str +class Sample: + def __init__(self, + name: str = "Sample", + type: str = "sample", + id: int = 0, + configuration: SampleConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or SampleConfiguration() + + +@auto_str +class SampleBatConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): + def __init__(self, + name: str = "Sample Speicher", + type: str = "bat", + id: int = 0, + configuration: SampleBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleBatConfiguration()) + + +@auto_str +class SampleCounterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): + def __init__(self, + name: str = "Sample Zähler", + type: str = "counter", + id: int = 0, + configuration: SampleCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleCounterConfiguration()) + + +@auto_str +class SampleInverterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): + def __init__(self, + name: str = "Sample Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: SampleInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_modbus/sample_modbus/counter.py b/docs/samples/sample_modbus/sample_modbus/counter.py new file mode 100644 index 0000000000..206d9bdbb8 --- /dev/null +++ b/docs/samples/sample_modbus/sample_modbus/counter.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.sample_modbus.sample_modbus.config import SampleCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class SampleCounter(AbstractCounter): + def __init__(self, component_config: SampleCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + frequency=frequency, + power_factors=power_factors, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_modbus/sample_modbus/device.py b/docs/samples/sample_modbus/sample_modbus/device.py new file mode 100644 index 0000000000..f78bc2f76a --- /dev/null +++ b/docs/samples/sample_modbus/sample_modbus/device.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.sample_modbus.sample_modbus.bat import SampleBat +from modules.devices.sample_modbus.sample_modbus.config import Sample, SampleBatSetup, SampleCounterSetup, SampleInverterSetup +from modules.devices.sample_modbus.sample_modbus.counter import SampleCounter +from modules.devices.sample_modbus.sample_modbus.inverter import SampleInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Sample): + client = None + + def create_bat_component(component_config: SampleBatSetup): + nonlocal client + return SampleBat(component_config, device_id=device_config.id, client=client) + + def create_counter_component(component_config: SampleCounterSetup): + nonlocal client + return SampleCounter(component_config, device_id=device_config.id, client=client) + + def create_inverter_component(component_config: SampleInverterSetup): + nonlocal client + return SampleInverter(component_config, device_id=device_config.id, client=client) + + def update_components(components: Iterable[Union[SampleBat, SampleCounter, SampleInverter]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_modbus/sample_modbus/inverter.py b/docs/samples/sample_modbus/sample_modbus/inverter.py new file mode 100644 index 0000000000..6fda28c90f --- /dev/null +++ b/docs/samples/sample_modbus/sample_modbus/inverter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.sample_modbus.sample_modbus.config import SampleInverterSetup + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class SampleInverter(AbstractInverter): + def __init__(self, component_config: SampleInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + power = self.client.read_holding_registers(reg, ModbusDataType.INT_32, unit=unit) + exported = self.sim_counter.sim_count(power)[1] + + inverter_state = InverterState( + currents=currents, + power=power, + exported=exported, + dc_power=dc_power + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/docs/samples/sample_modbus/vendor.py b/docs/samples/sample_modbus/vendor.py new file mode 100644 index 0000000000..9741383ef5 --- /dev/null +++ b/docs/samples/sample_modbus/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Sample" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/docs/samples/sample_request_by_device/__init__.py b/docs/samples/sample_request_by_component/__init__ .py similarity index 100% rename from docs/samples/sample_request_by_device/__init__.py rename to docs/samples/sample_request_by_component/__init__ .py diff --git a/docs/samples/sample_request_by_component/bat.py b/docs/samples/sample_request_by_component/bat.py deleted file mode 100644 index 0623ba06c7..0000000000 --- a/docs/samples/sample_request_by_component/bat.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common import req -from modules.common.abstract_device import AbstractBat -from modules.common.component_state import BatState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_bat_value_store -from modules.devices.sample_request_by_component.config import SampleBatSetup, SampleConfiguration - - -class SampleBat(AbstractBat): - def __init__(self, device_id: int, component_config: SampleBatSetup, ip_address: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleBatSetup, component_config) - self.ip_address = ip_address - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - self.store = get_bat_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self) -> None: - resp = req.get_http_session().get(self.ip_address) - imported, exported = self.sim_counter.sim_count(power) - - bat_state = BatState( - power=power, - soc=soc, - imported=imported, - exported=exported - ) - self.store.set(bat_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_request_by_component/config.py b/docs/samples/sample_request_by_component/config.py deleted file mode 100644 index 0d011cd9f3..0000000000 --- a/docs/samples/sample_request_by_component/config.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Optional -from helpermodules.auto_str import auto_str -from modules.common.component_setup import ComponentSetup - - -@auto_str -class SampleConfiguration: - def __init__(self, ip_address: Optional[str] = None): - self.ip_address = ip_address - - -@auto_str -class Sample: - def __init__(self, - name: str = "Sample", - type: str = "sample", - id: int = 0, - configuration: SampleConfiguration = None) -> None: - self.name = name - self.type = type - self.id = id - self.configuration = configuration or SampleConfiguration() - - -@auto_str -class SampleBatConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): - def __init__(self, - name: str = "Sample Speicher", - type: str = "bat", - id: int = 0, - configuration: SampleBatConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleBatConfiguration()) - - -@auto_str -class SampleCounterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): - def __init__(self, - name: str = "Sample Zähler", - type: str = "counter", - id: int = 0, - configuration: SampleCounterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleCounterConfiguration()) - - -@auto_str -class SampleInverterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): - def __init__(self, - name: str = "Sample Wechselrichter", - type: str = "inverter", - id: int = 0, - configuration: SampleInverterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_request_by_component/counter.py b/docs/samples/sample_request_by_component/counter.py deleted file mode 100644 index 3dff0611ee..0000000000 --- a/docs/samples/sample_request_by_component/counter.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common import req -from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_counter_value_store -from modules.devices.sample_request_by_component.config import SampleCounterSetup, SampleConfiguration - - -class SampleCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: SampleCounterSetup, ip_address: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleCounterSetup, component_config) - self.ip_address = ip_address - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - self.store = get_counter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self): - resp = req.get_http_session().get(self.ip_address) - imported, exported = self.sim_counter.sim_count(power) - - counter_state = CounterState( - currents=currents, - imported=imported, - exported=exported, - power=power, - frequency=frequency, - power_factors=power_factors, - powers=powers, - voltages=voltages - ) - self.store.set(counter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_request_by_component/device.py b/docs/samples/sample_request_by_component/device.py deleted file mode 100644 index a6804f877a..0000000000 --- a/docs/samples/sample_request_by_component/device.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python3 -import logging -from typing import Optional, List - -from helpermodules.cli import run_using_positional_cli_args -from modules.common.abstract_device import DeviceDescriptor -from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater -from modules.devices.sample_request_by_component import bat, counter, inverter -from modules.devices.sample_request_by_component.bat import SampleBat -from modules.devices.sample_request_by_component.config import Sample, SampleConfiguration, SampleBatSetup, SampleCounterSetup, SampleInverterSetup -from modules.devices.sample_request_by_component.counter import SampleCounter -from modules.devices.sample_request_by_component.inverter import SampleInverter - -log = logging.getLogger(__name__) - - -def create_device(device_config: Sample): - def create_bat_component(component_config: SampleBatSetup): - return SampleBat(device_config.id, component_config, device_config.configuration.ip_address) - - def create_counter_component(component_config: SampleCounterSetup): - return SampleCounter(device_config.id, component_config, device_config.configuration.ip_address) - - def create_inverter_component(component_config: SampleInverterSetup): - return SampleInverter(device_config.id, component_config, device_config.configuration.ip_address) - - return ConfigurableDevice( - device_config=device_config, - component_factory=ComponentFactoryByType( - bat=create_bat_component, - counter=create_counter_component, - inverter=create_inverter_component, - ), - component_updater=IndependentComponentUpdater(lambda component: component.update()) - ) - - -COMPONENT_TYPE_TO_MODULE = { - "bat": bat, - "counter": counter, - "inverter": inverter -} - - -def read_legacy(component_type: str, ip_address: str, id: int, num: Optional[int]) -> None: - device_config = Sample(configuration=SampleConfiguration(ip_address=ip_address, id=id)) - dev = create_device(device_config) - if component_type in COMPONENT_TYPE_TO_MODULE: - component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() - else: - raise Exception( - "illegal component type " + component_type + ". Allowed values: " + - ','.join(COMPONENT_TYPE_TO_MODULE.keys()) - ) - component_config.id = num - dev.add_component(component_config) - - log.debug('Sample IP-Adresse: ' + ip_address) - log.debug('Sample ID: ' + str(id)) - - dev.update() - - -def main(argv: List[str]): - run_using_positional_cli_args(read_legacy, argv) - - -device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_request_by_component/inverter.py b/docs/samples/sample_request_by_component/inverter.py deleted file mode 100644 index 963e5e77bc..0000000000 --- a/docs/samples/sample_request_by_component/inverter.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common import req -from modules.common.abstract_device import AbstractInverter -from modules.common.component_state import InverterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_inverter_value_store -from modules.devices.sample_request_by_component.config import SampleInverterSetup, SampleConfiguration - - -class SampleInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: SampleInverterSetup, ip_address: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleInverterSetup, component_config) - self.ip_address = ip_address - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - self.store = get_inverter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self) -> None: - resp = req.get_http_session().get(self.ip_address) - exported = self.sim_counter.sim_count(power)[1] - - inverter_state = InverterState( - currents=currents, - power=power, - exported=exported, - dc_power=dc_power - ) - self.store.set(inverter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/packages/modules/ripple_control_receivers/dimm_kit/__init__.py b/docs/samples/sample_request_by_component/sample_request_by_component/__init__.py similarity index 100% rename from packages/modules/ripple_control_receivers/dimm_kit/__init__.py rename to docs/samples/sample_request_by_component/sample_request_by_component/__init__.py diff --git a/docs/samples/sample_request_by_component/sample_request_by_component/bat.py b/docs/samples/sample_request_by_component/sample_request_by_component/bat.py new file mode 100644 index 0000000000..7a9de128f4 --- /dev/null +++ b/docs/samples/sample_request_by_component/sample_request_by_component/bat.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +from typing import Optional, TypedDict, Any +from modules.common import req +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.sample_request_by_component.sample_request_by_component.config import SampleBatSetup, SampleConfiguration + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + +class SampleBat(AbstractBat): + def __init__(self, component_config: SampleBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + resp = req.get_http_session().get(self.ip_address) + power = resp.json().get("power") + soc = resp.json().get("soc") + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + def set_power_limit(self, power_limit: Optional[int]) -> None: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive + # Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird + # None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen. + self.client.write_registers(reg, power_limit) + # Wenn der Speicher keine Steuerung der Ladeleistung unterstützt + pass + + def power_limit_controllable(self) -> bool: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. + return True + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_request_by_component/sample_request_by_component/config.py b/docs/samples/sample_request_by_component/sample_request_by_component/config.py new file mode 100644 index 0000000000..0a9b5d4328 --- /dev/null +++ b/docs/samples/sample_request_by_component/sample_request_by_component/config.py @@ -0,0 +1,73 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class SampleConfiguration: + def __init__(self, ip_address: Optional[str] = None): + self.ip_address = ip_address + + +@auto_str +class Sample: + def __init__(self, + name: str = "Sample", + type: str = "sample", + id: int = 0, + configuration: SampleConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or SampleConfiguration() + + +@auto_str +class SampleBatConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): + def __init__(self, + name: str = "Sample Speicher", + type: str = "bat", + id: int = 0, + configuration: SampleBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleBatConfiguration()) + + +@auto_str +class SampleCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): + def __init__(self, + name: str = "Sample Zähler", + type: str = "counter", + id: int = 0, + configuration: SampleCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleCounterConfiguration()) + + +@auto_str +class SampleInverterConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): + def __init__(self, + name: str = "Sample Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: SampleInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_request_by_component/sample_request_by_component/counter.py b/docs/samples/sample_request_by_component/sample_request_by_component/counter.py new file mode 100644 index 0000000000..d069831e56 --- /dev/null +++ b/docs/samples/sample_request_by_component/sample_request_by_component/counter.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common import req +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.sample_request_by_component.sample_request_by_component.config import SampleCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + +class SampleCounter(AbstractCounter): + def __init__(self, component_config: SampleCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + resp = req.get_http_session().get(self.ip_address) + power = resp.json().get("power") + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + frequency=frequency, + power_factors=power_factors, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_request_by_component/sample_request_by_component/device.py b/docs/samples/sample_request_by_component/sample_request_by_component/device.py new file mode 100644 index 0000000000..508546fbc9 --- /dev/null +++ b/docs/samples/sample_request_by_component/sample_request_by_component/device.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import logging + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater +from modules.devices.sample_request_by_component.sample_request_by_component.bat import SampleBat +from modules.devices.sample_request_by_component.sample_request_by_component.config import Sample, SampleBatSetup, SampleCounterSetup, SampleInverterSetup +from modules.devices.sample_request_by_component.sample_request_by_component.counter import SampleCounter +from modules.devices.sample_request_by_component.sample_request_by_component.inverter import SampleInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Sample): + session = None + + def create_bat_component(component_config: SampleBatSetup): + return SampleBat(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) + + def create_counter_component(component_config: SampleCounterSetup): + return SampleCounter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) + + def create_inverter_component(component_config: SampleInverterSetup): + return SampleInverter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) + + def initializer(): + nonlocal session + session = req.get_http_session() + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=IndependentComponentUpdater(lambda component: component.update(session)) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_request_by_component/sample_request_by_component/inverter.py b/docs/samples/sample_request_by_component/sample_request_by_component/inverter.py new file mode 100644 index 0000000000..9ea314435a --- /dev/null +++ b/docs/samples/sample_request_by_component/sample_request_by_component/inverter.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common import req +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.sample_request_by_component.sample_request_by_component.config import SampleInverterSetup + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + +class SampleInverter(AbstractInverter): + def __init__(self, component_config: SampleInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + resp = req.get_http_session().get(self.ip_address) + power = resp.json().get("power") + currents = resp.json().get("currents") + dc_power = resp.json().get("dc_power") + exported = self.sim_counter.sim_count(power)[1] + + inverter_state = InverterState( + currents=currents, + power=power, + exported=exported, + dc_power=dc_power + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/docs/samples/sample_request_by_component/vendor.py b/docs/samples/sample_request_by_component/vendor.py new file mode 100644 index 0000000000..9741383ef5 --- /dev/null +++ b/docs/samples/sample_request_by_component/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Sample" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/ripple_control_receivers/gpio/__init__.py b/docs/samples/sample_request_by_device/__init__ .py similarity index 100% rename from packages/modules/ripple_control_receivers/gpio/__init__.py rename to docs/samples/sample_request_by_device/__init__ .py diff --git a/docs/samples/sample_request_by_device/bat.py b/docs/samples/sample_request_by_device/bat.py deleted file mode 100644 index 8c84673029..0000000000 --- a/docs/samples/sample_request_by_device/bat.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractBat -from modules.common.component_state import BatState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_bat_value_store -from modules.devices.sample_request_by_device.config import SampleBatSetup - - -class SampleBat(AbstractBat): - def __init__(self, device_id: int, component_config: SampleBatSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleBatSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - self.store = get_bat_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self, response) -> None: - # hier die Werte aus der response parsen - imported, exported = self.sim_counter.sim_count(power) - - bat_state = BatState( - power=power, - soc=soc, - imported=imported, - exported=exported - ) - self.store.set(bat_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_request_by_device/config.py b/docs/samples/sample_request_by_device/config.py deleted file mode 100644 index 0d011cd9f3..0000000000 --- a/docs/samples/sample_request_by_device/config.py +++ /dev/null @@ -1,70 +0,0 @@ -from typing import Optional -from helpermodules.auto_str import auto_str -from modules.common.component_setup import ComponentSetup - - -@auto_str -class SampleConfiguration: - def __init__(self, ip_address: Optional[str] = None): - self.ip_address = ip_address - - -@auto_str -class Sample: - def __init__(self, - name: str = "Sample", - type: str = "sample", - id: int = 0, - configuration: SampleConfiguration = None) -> None: - self.name = name - self.type = type - self.id = id - self.configuration = configuration or SampleConfiguration() - - -@auto_str -class SampleBatConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): - def __init__(self, - name: str = "Sample Speicher", - type: str = "bat", - id: int = 0, - configuration: SampleBatConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleBatConfiguration()) - - -@auto_str -class SampleCounterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): - def __init__(self, - name: str = "Sample Zähler", - type: str = "counter", - id: int = 0, - configuration: SampleCounterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleCounterConfiguration()) - - -@auto_str -class SampleInverterConfiguration: - def __init__(self): - pass - - -@auto_str -class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): - def __init__(self, - name: str = "Sample Wechselrichter", - type: str = "inverter", - id: int = 0, - configuration: SampleInverterConfiguration = None) -> None: - super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_request_by_device/counter.py b/docs/samples/sample_request_by_device/counter.py deleted file mode 100644 index fbb409b3dd..0000000000 --- a/docs/samples/sample_request_by_device/counter.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_counter_value_store -from modules.devices.sample_request_by_device.config import SampleCounterSetup - - -class SampleCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: SampleCounterSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleCounterSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - self.store = get_counter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self, response): - # hier die Werte aus der response parsen - imported, exported = self.sim_counter.sim_count(power) - - counter_state = CounterState( - currents=currents, - imported=imported, - exported=exported, - power=power, - frequency=frequency, - power_factors=power_factors, - powers=powers, - voltages=voltages - ) - self.store.set(counter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_request_by_device/device.py b/docs/samples/sample_request_by_device/device.py deleted file mode 100644 index bfc3d414e3..0000000000 --- a/docs/samples/sample_request_by_device/device.py +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env python3 -import logging -from typing import Iterable, Optional, Union, List - -from helpermodules.cli import run_using_positional_cli_args -from modules.common import req -from modules.common.abstract_device import DeviceDescriptor -from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater -from modules.devices.sample_request_by_device import bat, counter, inverter -from modules.devices.sample_request_by_device.bat import SampleBat -from modules.devices.sample_request_by_device.config import Sample, SampleConfiguration, SampleBatSetup, SampleCounterSetup, SampleInverterSetup -from modules.devices.sample_request_by_device.counter import SampleCounter -from modules.devices.sample_request_by_device.inverter import SampleInverter - -log = logging.getLogger(__name__) - - -def create_device(device_config: Sample): - def create_bat_component(component_config: SampleBatSetup): - return SampleBat(device_config.id, component_config) - - def create_counter_component(component_config: SampleCounterSetup): - return SampleCounter(device_config.id, component_config) - - def create_inverter_component(component_config: SampleInverterSetup): - return SampleInverter(device_config.id, component_config) - - def update_components(components: Iterable[Union[SampleBat, SampleCounter, SampleInverter]]): - response = req.get_http_session().get(device_config.configuration.ip_address, timeout=5).json() - for component in components: - component.update(response) - - return ConfigurableDevice( - device_config=device_config, - component_factory=ComponentFactoryByType( - bat=create_bat_component, - counter=create_counter_component, - inverter=create_inverter_component, - ), - component_updater=MultiComponentUpdater(update_components) - ) - - -COMPONENT_TYPE_TO_MODULE = { - "bat": bat, - "counter": counter, - "inverter": inverter -} - - -def read_legacy(component_type: str, ip_address: str, id: int, num: Optional[int]) -> None: - device_config = Sample(configuration=SampleConfiguration(ip_address=ip_address, id=id)) - dev = create_device(device_config) - if component_type in COMPONENT_TYPE_TO_MODULE: - component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() - else: - raise Exception( - "illegal component type " + component_type + ". Allowed values: " + - ','.join(COMPONENT_TYPE_TO_MODULE.keys()) - ) - component_config.id = num - dev.add_component(component_config) - - log.debug('Sample IP-Adresse: ' + ip_address) - log.debug('Sample ID: ' + str(id)) - - dev.update() - # Hier kann es notwendig sein, für 1.9 eine eigene Update-Methode zu implemenitieren, die die Werte wie benötigt miteinander verrechnet. - # Hier muss auch bei Hybrid-Systemen die Speicher-und PV-Leistung verrechnet werden. - - -def main(argv: List[str]): - run_using_positional_cli_args(read_legacy, argv) - - -device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_request_by_device/inverter.py b/docs/samples/sample_request_by_device/inverter.py deleted file mode 100644 index 3ee036e764..0000000000 --- a/docs/samples/sample_request_by_device/inverter.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractInverter -from modules.common.component_state import InverterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.simcount import SimCounter -from modules.common.store import get_inverter_value_store -from modules.devices.sample_request_by_device.config import SampleInverterSetup - - -class SampleInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: SampleInverterSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SampleInverterSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - self.store = get_inverter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def update(self, response) -> None: - # hier die Werte aus der response parsen - exported = self.sim_counter.sim_count(power)[1] - - inverter_state = InverterState( - currents=currents, - power=power, - exported=exported, - dc_power=dc_power - ) - self.store.set(inverter_state) - - -component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/packages/modules/vehicles/skodaconnect/__init__.py b/docs/samples/sample_request_by_device/sample_request_by_device/__init__.py old mode 100755 new mode 100644 similarity index 100% rename from packages/modules/vehicles/skodaconnect/__init__.py rename to docs/samples/sample_request_by_device/sample_request_by_device/__init__.py diff --git a/docs/samples/sample_request_by_device/sample_request_by_device/bat.py b/docs/samples/sample_request_by_device/sample_request_by_device/bat.py new file mode 100644 index 0000000000..2d19e594aa --- /dev/null +++ b/docs/samples/sample_request_by_device/sample_request_by_device/bat.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +from typing import Optional, TypedDict, Any +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.sample_request_by_device.sample_request_by_device.config import SampleBatSetup + + +class KwargsDict(TypedDict): + device_id: int + + +class SampleBat(AbstractBat): + def __init__(self, component_config: SampleBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self, response) -> None: + # hier die Werte aus der response parsen + power = response.get("power") + soc = response.get("soc") + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + def set_power_limit(self, power_limit: Optional[int]) -> None: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss bei Übergabe einer Zahl auf aktive + # Speichersteurung umgeschaltet werden, sodass der Speicher mit der übergebenen Leistung lädt/entlädt. Wird + # None übergeben, muss der Speicher die Null-Punkt-Ausregelung selbst übernehmen. + self.client.write_registers(reg, power_limit) + # Wenn der Speicher keine Steuerung der Ladeleistung unterstützt + pass + + def power_limit_controllable(self) -> bool: + # Wenn der Speicher die Steuerung der Ladeleistung unterstützt, muss True zurückgegeben werden. + return True + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleBatSetup) diff --git a/docs/samples/sample_request_by_device/sample_request_by_device/config.py b/docs/samples/sample_request_by_device/sample_request_by_device/config.py new file mode 100644 index 0000000000..0a9b5d4328 --- /dev/null +++ b/docs/samples/sample_request_by_device/sample_request_by_device/config.py @@ -0,0 +1,73 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class SampleConfiguration: + def __init__(self, ip_address: Optional[str] = None): + self.ip_address = ip_address + + +@auto_str +class Sample: + def __init__(self, + name: str = "Sample", + type: str = "sample", + id: int = 0, + configuration: SampleConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or SampleConfiguration() + + +@auto_str +class SampleBatConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleBatSetup(ComponentSetup[SampleBatConfiguration]): + def __init__(self, + name: str = "Sample Speicher", + type: str = "bat", + id: int = 0, + configuration: SampleBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleBatConfiguration()) + + +@auto_str +class SampleCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleCounterSetup(ComponentSetup[SampleCounterConfiguration]): + def __init__(self, + name: str = "Sample Zähler", + type: str = "counter", + id: int = 0, + configuration: SampleCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleCounterConfiguration()) + + +@auto_str +class SampleInverterConfiguration: + def __init__(self): + pass + + +@auto_str +class SampleInverterSetup(ComponentSetup[SampleInverterConfiguration]): + def __init__(self, + name: str = "Sample Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: SampleInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SampleInverterConfiguration()) diff --git a/docs/samples/sample_request_by_device/sample_request_by_device/counter.py b/docs/samples/sample_request_by_device/sample_request_by_device/counter.py new file mode 100644 index 0000000000..bb92a8eba4 --- /dev/null +++ b/docs/samples/sample_request_by_device/sample_request_by_device/counter.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.sample_request_by_device.sample_request_by_device.config import SampleCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + + +class SampleCounter(AbstractCounter): + def __init__(self, component_config: SampleCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self, response): + # hier die Werte aus der response parsen + power = response.get("power") + currents = response.get("currents") + frequency = response.get("frequency") + power_factors = response.get("power_factors") + powers = response.get("powers") + voltages = response.get("voltages") + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + frequency=frequency, + power_factors=power_factors, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleCounterSetup) diff --git a/docs/samples/sample_request_by_device/sample_request_by_device/device.py b/docs/samples/sample_request_by_device/sample_request_by_device/device.py new file mode 100644 index 0000000000..0a0f912dde --- /dev/null +++ b/docs/samples/sample_request_by_device/sample_request_by_device/device.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common import req +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.devices.sample_request_by_device.sample_request_by_device.bat import SampleBat +from modules.devices.sample_request_by_device.sample_request_by_device.config import Sample, SampleBatSetup, SampleCounterSetup, SampleInverterSetup +from modules.devices.sample_request_by_device.sample_request_by_device.counter import SampleCounter +from modules.devices.sample_request_by_device.sample_request_by_device.inverter import SampleInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Sample): + def create_bat_component(component_config: SampleBatSetup): + return SampleBat(component_config, device_id=device_config.id) + + def create_counter_component(component_config: SampleCounterSetup): + return SampleCounter(component_config, device_id=device_config.id) + + def create_inverter_component(component_config: SampleInverterSetup): + return SampleInverter(component_config, device_id=device_config.id) + + def update_components(components: Iterable[Union[SampleBat, SampleCounter, SampleInverter]]): + response = req.get_http_session().get(device_config.configuration.ip_address, timeout=5).json() + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update(response) + + return ConfigurableDevice( + device_config=device_config, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Sample) diff --git a/docs/samples/sample_request_by_device/sample_request_by_device/inverter.py b/docs/samples/sample_request_by_device/sample_request_by_device/inverter.py new file mode 100644 index 0000000000..20ce4fe886 --- /dev/null +++ b/docs/samples/sample_request_by_device/sample_request_by_device/inverter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.sample_request_by_device.sample_request_by_device.config import SampleInverterSetup + + +class KwargsDict(TypedDict): + device_id: int + + +class SampleInverter(AbstractInverter): + def __init__(self, component_config: SampleInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self, response) -> None: + # hier die Werte aus der response parsen + power = response.get("power") + currents = response.get("currents") + dc_power = response.get("dc_power") + exported = self.sim_counter.sim_count(power)[1] + + inverter_state = InverterState( + currents=currents, + power=power, + exported=exported, + dc_power=dc_power + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SampleInverterSetup) diff --git a/docs/samples/sample_request_by_device/vendor.py b/docs/samples/sample_request_by_device/vendor.py new file mode 100644 index 0000000000..9741383ef5 --- /dev/null +++ b/docs/samples/sample_request_by_device/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Sample" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 9e36453c2a..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "core", - "lockfileVersion": 3, - "requires": true, - "packages": {} -} diff --git a/packages/conftest.py b/packages/conftest.py index a66e5bbc15..abfa8dc5ea 100644 --- a/packages/conftest.py +++ b/packages/conftest.py @@ -14,8 +14,12 @@ from control.counter import Set as CounterSet from control.counter_all import CounterAll from control.pv import Pv, PvData +from control.pv import Config as PvConfig from control.pv import Get as PvGet from helpermodules import hardware_configuration, pub, timecheck +from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule +from modules.common.component_state import ChargepointState +from modules.common.store._api import LoggingValueStore @pytest.fixture(autouse=True) @@ -118,26 +122,56 @@ def data_() -> None: get=Mock(spec=Get, currents=[30, 0, 0], power=6900, daily_imported=10000, daily_exported=0, imported=56000, fault_state=0), - set=Mock(spec=Set, loadmanagement_available=True))), + set=Mock(spec=Set, loadmanagement_available=True)), + chargepoint_module=Mock(spec=ChargepointModule, + store=Mock(spec=LoggingValueStore, + delegate=Mock(spec=LoggingValueStore, + state=ChargepointState(currents=[30, 0, 0], + power=6900, + plug_state=False, + charge_state=False, + imported=None, + exported=None, + phases_in_use=0))))), "cp4": Mock(spec=Chargepoint, data=Mock(spec=ChargepointData, config=Mock(spec=Config, phase_1=2), get=Mock(spec=Get, currents=[0, 15, 15], power=6900, daily_imported=10000, daily_exported=0, imported=60000, fault_state=0), - set=Mock(spec=Set, loadmanagement_available=True))), + set=Mock(spec=Set, loadmanagement_available=True)), + chargepoint_module=Mock(spec=ChargepointModule, + store=Mock(spec=LoggingValueStore, + delegate=Mock(spec=LoggingValueStore, + state=ChargepointState(currents=[0, 15, 15], + power=6900, + plug_state=False, + charge_state=False, + imported=None, + exported=None, + phases_in_use=0))))), "cp5": Mock(spec=Chargepoint, data=Mock(spec=ChargepointData, config=Mock(spec=Config, phase_1=3), get=Mock(spec=Get, currents=[10]*3, power=6900, daily_imported=10000, daily_exported=0, imported=62000, fault_state=0), - set=Mock(spec=Set, loadmanagement_available=True)))} + set=Mock(spec=Set, loadmanagement_available=True)), + chargepoint_module=Mock(spec=ChargepointModule, + store=Mock(spec=LoggingValueStore, + delegate=Mock(spec=LoggingValueStore, + state=ChargepointState(currents=[10]*3, + power=6900, + plug_state=False, + charge_state=False, + imported=None, + exported=None, + phases_in_use=0)))))} data.data.bat_data.update({"bat2": Mock(spec=Bat, num=2, data=Mock(spec=BatData, get=Mock( spec=BatGet, power=-5000, daily_imported=7000, daily_exported=3000, imported=12000, exported=10000, currents=None, fault_state=0), set=Mock(spec=BatSet, power_limit=None)))}) data.data.pv_data.update({"pv1": Mock(spec=Pv, data=Mock( spec=PvData, get=Mock(spec=PvGet, power=-10000, daily_exported=6000, exported=27000, currents=None, - fault_state=0)))}) + fault_state=0), config=Mock(spec=PvConfig, max_ac_out=10000)))}) data.data.counter_data.update({ "counter0": Mock(spec=Counter, data=Mock(spec=CounterData, get=Mock( spec=CounterGet, currents=[40]*3, power=6200, daily_imported=45000, daily_exported=3000, fault_state=0))), diff --git a/packages/control/algorithm/additional_current.py b/packages/control/algorithm/additional_current.py index ad92724e14..1cc76de58e 100644 --- a/packages/control/algorithm/additional_current.py +++ b/packages/control/algorithm/additional_current.py @@ -2,12 +2,11 @@ from control.algorithm import common from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT -from control.loadmanagement import LimitingValue, Loadmanagement -from control.counter import Counter +from control.limiting_value import LoadmanagementLimit +from control.loadmanagement import Loadmanagement from control.chargepoint.chargepoint import Chargepoint from control.algorithm.filter_chargepoints import (get_chargepoints_by_mode_and_counter, get_preferenced_chargepoint_charging) -from modules.common.utils.component_parser import get_component_name_by_id log = logging.getLogger(__name__) @@ -28,14 +27,14 @@ def set_additional_current(self) -> None: while len(preferenced_chargepoints): cp = preferenced_chargepoints[0] missing_currents, counts = common.get_missing_currents_left(preferenced_chargepoints) - available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter) + available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, cp) log.debug(f"cp {cp.num} available currents {available_currents} missing currents " - f"{missing_currents} limit {limit}") + f"{missing_currents} limit {limit.message}") cp.data.control_parameter.limit = limit available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents) current = common.get_current_to_set( cp.data.set.current, available_for_cp, cp.data.set.target_current) - self._set_loadmangement_message(current, limit, cp, counter) + self._set_loadmangement_message(current, limit, cp) common.set_current_counterdiff( cp.data.control_parameter.min_current, current, @@ -48,9 +47,8 @@ def set_additional_current(self) -> None: # tested def _set_loadmangement_message(self, current: float, - limit: LimitingValue, - chargepoint: Chargepoint, - counter: Counter) -> None: + limit: LoadmanagementLimit, + chargepoint: Chargepoint) -> None: # Strom muss an diesem Zähler geändert werden log.debug( f"current {current} target {chargepoint.data.set.target_current} set current {chargepoint.data.set.current}" @@ -60,4 +58,4 @@ def _set_loadmangement_message(self, round(current, 2) != round(max( chargepoint.data.control_parameter.required_currents), 2)): chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" - f"{limit.value.format(get_component_name_by_id(counter.num))}") + f"{limit.message}") diff --git a/packages/control/algorithm/additional_current_test.py b/packages/control/algorithm/additional_current_test.py index 89c552aeff..e1f57b6309 100644 --- a/packages/control/algorithm/additional_current_test.py +++ b/packages/control/algorithm/additional_current_test.py @@ -1,4 +1,3 @@ -from unittest.mock import Mock import pytest from control.algorithm import additional_current @@ -7,22 +6,23 @@ from control.chargepoint.control_parameter import ControlParameter from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue @pytest.mark.parametrize( "set_current, limit, expected_msg", - [pytest.param(7, None, None, id="unverändert"), + [pytest.param(7, LoadmanagementLimit(None, None), None, id="unverändert"), pytest.param( - 6, LimitingValue.CURRENT, + 6, LoadmanagementLimit(LimitingValue.CURRENT.value.format('Garage'), LimitingValue.CURRENT), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.CURRENT.value.format('Garage')}", id="begrenzt durch Strom"), pytest.param( - 6, LimitingValue.POWER, + 6, LoadmanagementLimit(LimitingValue.POWER.value.format('Garage'), LimitingValue.POWER), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.POWER.value.format('Garage')}", id="begrenzt durch Leistung"), pytest.param( - 6, LimitingValue.UNBALANCED_LOAD, + 6, LoadmanagementLimit(LimitingValue.UNBALANCED_LOAD.value.format('Garage'), LimitingValue.UNBALANCED_LOAD), f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" f"{LimitingValue.UNBALANCED_LOAD.value.format('Garage')}", id="begrenzt durch Schieflast"), @@ -30,15 +30,13 @@ def test_set_loadmangement_message(set_current, limit, expected_msg, monkeypatch): # setup ev = Ev(0) - ev.charge_template = ChargeTemplate(0) + ev.charge_template = ChargeTemplate() cp1 = Chargepoint(1, None) cp1.data = ChargepointData(set=Set(current=set_current), control_parameter=ControlParameter(required_currents=[8]*3)) - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) # execution - additional_current.AdditionalCurrent()._set_loadmangement_message(7, limit, cp1, Mock()) + additional_current.AdditionalCurrent()._set_loadmangement_message(7, limit, cp1) # evaluation assert cp1.data.get.state_str == expected_msg diff --git a/packages/control/algorithm/algorithm.py b/packages/control/algorithm/algorithm.py index 6c4af1008f..4c4ed46aa5 100644 --- a/packages/control/algorithm/algorithm.py +++ b/packages/control/algorithm/algorithm.py @@ -4,6 +4,7 @@ from control import data from control.algorithm import common from control.algorithm.additional_current import AdditionalCurrent +from control.algorithm.bidi_charging import Bidi from control.algorithm.min_current import MinCurrent from control.algorithm.no_current import NoCurrent from control.algorithm.surplus_controlled import SurplusControlled @@ -14,6 +15,7 @@ class Algorithm: def __init__(self): self.additional_current = AdditionalCurrent() + self.bidi = Bidi() self.min_current = MinCurrent() self.no_current = NoCurrent() self.surplus_controlled = SurplusControlled() @@ -32,15 +34,17 @@ def calc_current(self) -> None: log.info("**Soll-Strom setzen**") common.reset_current_to_target_current() self.additional_current.set_additional_current() + self.surplus_controlled.set_required_current_to_max() + log.info("**PV-geführten Strom setzen**") counter.limit_raw_power_left_to_surplus(self.evu_counter.calc_raw_surplus()) - self.surplus_controlled.check_switch_on() if self.evu_counter.data.set.surplus_power_left > 0: - log.info("**PV-geführten Strom setzen**") common.reset_current_to_target_current() - self.surplus_controlled.set_required_current_to_max() self.surplus_controlled.set_surplus_current() else: - log.info("**Keine Leistung für PV-geführtes Laden übrig.**") + log.info("Keine Leistung für PV-geführtes Laden übrig.") + log.info("**Bidi-(Ent-)Lade-Strom setzen**") + counter.set_raw_surplus_power_left() + self.bidi.set_bidi() self.no_current.set_no_current() self.no_current.set_none_current() except Exception: @@ -62,6 +66,7 @@ def _check_auto_phase_switch_delay(self) -> None: # wurden, wieder zurückgegeben. log.debug(f"Ladepunkt {cp.num}: Prüfen, ob Phasenumschaltung durchgeführt werden soll.") phases, current, message = charging_ev.auto_phase_switch( + cp.data.set.charge_template, cp.data.control_parameter, cp.num, cp.data.get.currents, diff --git a/packages/control/algorithm/bidi_charging.py b/packages/control/algorithm/bidi_charging.py new file mode 100644 index 0000000000..b30e3a65f7 --- /dev/null +++ b/packages/control/algorithm/bidi_charging.py @@ -0,0 +1,45 @@ +import logging +from control import data +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE +from control.algorithm.filter_chargepoints import get_chargepoints_by_mode + +log = logging.getLogger(__name__) + + +class Bidi: + def __init__(self): + pass + + def set_bidi(self): + grid_counter = data.data.counter_all_data.get_evu_counter() + log.debug(f"Nullpunktanpassung {grid_counter.data.set.surplus_power_left}W") + zero_point_adjustment = grid_counter + for mode_tuple in CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE: + preferenced_cps = get_chargepoints_by_mode(mode_tuple) + if preferenced_cps: + log.info( + f"Mode-Tuple {mode_tuple[0]} - {mode_tuple[1]} - {mode_tuple[2]}, Zähler {grid_counter.num}") + while len(preferenced_cps): + cp = preferenced_cps[0] + zero_point_adjustment = grid_counter.data.set.surplus_power_left / len(preferenced_cps) + log.debug(f"Nullpunktanpassung für LP{cp.num}: verbleibende Leistung {zero_point_adjustment}W") + missing_currents = [zero_point_adjustment / cp.data.get.phases_in_use / + 230 for i in range(0, cp.data.get.phases_in_use)] + missing_currents += [0] * (3 - len(missing_currents)) + if zero_point_adjustment > 0: + if cp.data.set.charging_ev_data.charge_template.bidi_charging_allowed( + cp.data.control_parameter.current_plan, cp.data.set.charging_ev_data.data.get.soc): + for index in range(0, 3): + missing_currents[index] = min(cp.data.control_parameter.required_current, + missing_currents[index]) + else: + log.info(f"LP{cp.num}: Nur bidirektional entladen erlaubt, da SoC-Limit erreicht.") + missing_currents = [0, 0, 0] + else: + for index in range(0, 3): + missing_currents[index] = cp.check_min_max_current(missing_currents[index], + cp.data.get.phases_in_use) + grid_counter.update_surplus_values_left(missing_currents, cp.data.get.voltages) + cp.data.set.current = missing_currents[0] + log.info(f"LP{cp.num}: Stromstärke {missing_currents}A") + preferenced_cps.pop(0) diff --git a/packages/control/algorithm/chargemodes.py b/packages/control/algorithm/chargemodes.py index 65e16a9645..0ebfa07cf3 100644 --- a/packages/control/algorithm/chargemodes.py +++ b/packages/control/algorithm/chargemodes.py @@ -8,19 +8,26 @@ (None, Chargemode.TIME_CHARGING, False), (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, True), (Chargemode.INSTANT_CHARGING, Chargemode.INSTANT_CHARGING, False), + (Chargemode.ECO_CHARGING, Chargemode.INSTANT_CHARGING, True), + (Chargemode.ECO_CHARGING, Chargemode.INSTANT_CHARGING, False), (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, True), (Chargemode.PV_CHARGING, Chargemode.INSTANT_CHARGING, False), (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, True), (Chargemode.SCHEDULED_CHARGING, Chargemode.PV_CHARGING, False), + (Chargemode.ECO_CHARGING, Chargemode.PV_CHARGING, True), + (Chargemode.ECO_CHARGING, Chargemode.PV_CHARGING, False), (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, True), (Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, False), - (None, Chargemode.STANDBY, True), - (None, Chargemode.STANDBY, False), + # niedrigere Priorität soll nachrangig geladen, aber zuerst entladen werden + (Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING, False), + (Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING, True), (None, Chargemode.STOP, True), (None, Chargemode.STOP, False)) -CONSIDERED_CHARGE_MODES_SURPLUS = CHARGEMODES[0:2] + CHARGEMODES[6:12] -CONSIDERED_CHARGE_MODES_PV_ONLY = CHARGEMODES[8:12] -CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT = CHARGEMODES[0:8] -CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-1] -CONSIDERED_CHARGE_MODES_NO_CURRENT = CHARGEMODES[12:16] +CONSIDERED_CHARGE_MODES_SURPLUS = CHARGEMODES[0:2] + CHARGEMODES[6:16] +CONSIDERED_CHARGE_MODES_PV_ONLY = CHARGEMODES[10:16] +CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT = CHARGEMODES[0:10] +CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-4] +CONSIDERED_CHARGE_MODES_NO_CURRENT = CHARGEMODES[18:20] +CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE = CHARGEMODES[16:18] +CONSIDERED_CHARGE_MODES_CHARGING = CHARGEMODES[0:16] diff --git a/packages/control/algorithm/common.py b/packages/control/algorithm/common.py index 2e5e2b66f8..1cb9c0f37b 100644 --- a/packages/control/algorithm/common.py +++ b/packages/control/algorithm/common.py @@ -3,6 +3,7 @@ from control import data from control.algorithm.filter_chargepoints import get_chargepoints_by_mode +from control.algorithm.utils import get_medium_charging_current from control.chargepoint.chargepoint import Chargepoint from control.counter import Counter from helpermodules.timecheck import check_timestamp @@ -40,6 +41,7 @@ def mode_and_counter_generator(chargemodes: List) -> Iterable[Tuple[Tuple[Option counter = data.data.counter_data[f"counter{element['id']}"] yield mode_tuple, counter + # tested @@ -58,7 +60,7 @@ def get_min_current(chargepoint: Chargepoint) -> Tuple[List[float], List[int]]: # tested -def set_current_counterdiff(diff_curent: float, +def set_current_counterdiff(diff_current: float, current: float, chargepoint: Chargepoint, surplus: bool = False) -> None: @@ -66,15 +68,16 @@ def set_current_counterdiff(diff_curent: float, considered_current = consider_less_charging_chargepoint_in_loadmanagement( chargepoint, current) # gar nicht ladende Autos? - diff = max(considered_current - diff_curent, 0) + diff = max(considered_current - diff_current, 0) diffs = [diff if required_currents[i] != 0 else 0 for i in range(3)] if max(diffs) > 0: counters = data.data.counter_all_data.get_counters_to_check(chargepoint.num) for counter in counters: if surplus: - data.data.counter_data[counter].update_surplus_values_left(diffs) + data.data.counter_data[counter].update_surplus_values_left(diffs, chargepoint.data.get.voltages) else: - data.data.counter_data[counter].update_values_left(diffs) + data.data.counter_data[counter].update_values_left(diffs, chargepoint.data.get.voltages) + data.data.io_actions.dimming_set_import_power_left({"type": "cp", "id": chargepoint.num}, sum(diffs)*230) chargepoint.data.set.current = current log.info(f"LP{chargepoint.num}: Stromstärke {current}A") @@ -143,20 +146,22 @@ def update_raw_data(preferenced_chargepoints: List[Chargepoint], counters = data.data.counter_all_data.get_counters_to_check(chargepoint.num) for counter in counters: if surplus: - data.data.counter_data[counter].update_surplus_values_left(diffs) + data.data.counter_data[counter].update_surplus_values_left(diffs, chargepoint.data.get.voltages) else: - data.data.counter_data[counter].update_values_left(diffs) + data.data.counter_data[counter].update_values_left(diffs, chargepoint.data.get.voltages) + data.data.io_actions.dimming_set_import_power_left({"type": "cp", "id": chargepoint.num}, sum(diffs)*230) def consider_less_charging_chargepoint_in_loadmanagement(cp: Chargepoint, set_current: float) -> bool: if (data.data.counter_all_data.data.config.consider_less_charging is False and ((set_current - - cp.data.set.charging_ev_data.ev_template.data.nominal_difference) > max(cp.data.get.currents) and + cp.data.set.charging_ev_data.ev_template.data.nominal_difference) > get_medium_charging_current( + cp.data.get.currents) and cp.data.control_parameter.timestamp_charge_start is not None and check_timestamp(cp.data.control_parameter.timestamp_charge_start, LESS_CHARGING_TIMEOUT) is False)): log.debug( f"LP {cp.num} lädt deutlich unter dem Sollstrom und wird nur mit {cp.data.get.currents}A berücksichtigt.") - return max(cp.data.get.currents) + return get_medium_charging_current(cp.data.get.currents) else: return set_current # tested diff --git a/packages/control/algorithm/common_test.py b/packages/control/algorithm/common_test.py index 5435c4ef15..2ae8679e98 100644 --- a/packages/control/algorithm/common_test.py +++ b/packages/control/algorithm/common_test.py @@ -9,12 +9,14 @@ from control.ev.ev import Ev from control.counter import Counter from control.counter_all import CounterAll +from control.io_device import IoActions @pytest.fixture(autouse=True) def cp() -> None: data.data_init(Mock()) data.data.cp_data = {"cp0": Chargepoint(0, None)} + data.data.io_actions = IoActions() @pytest.mark.parametrize("set_current, expected_current", diff --git a/packages/control/algorithm/integration_test/bidi_charging_test.py b/packages/control/algorithm/integration_test/bidi_charging_test.py new file mode 100644 index 0000000000..0a065ac734 --- /dev/null +++ b/packages/control/algorithm/integration_test/bidi_charging_test.py @@ -0,0 +1,59 @@ +import pytest +from unittest.mock import Mock + +from control import data +from control.algorithm.algorithm import Algorithm +from control.chargemode import Chargemode + + +@pytest.fixture() +def bidi_cps(): + def _setup(*cps): + for cp in cps: + data.data.cp_data[cp].data.get.max_discharge_power = -11000 + data.data.cp_data[cp].data.get.max_charge_power = 11000 + data.data.cp_data[cp].data.get.phases_in_use = 3 + control_parameter = data.data.cp_data[cp].data.control_parameter + control_parameter.min_current = data.data.cp_data[cp].data.set.charging_ev_data.ev_template.data.min_current + control_parameter.phases = 3 + control_parameter.required_currents = [16]*3 + control_parameter.required_current = 16 + control_parameter.chargemode = Chargemode.SCHEDULED_CHARGING + control_parameter.submode = Chargemode.BIDI_CHARGING + return _setup + + +@pytest.mark.parametrize("grid_power, expected_current", + [pytest.param(-2000, 2.898550724637681, id="bidi charge"), + pytest.param(2000, -2.898550724637681, id="bidi discharge")]) +def test_cp3_bidi(grid_power: float, expected_current: float, bidi_cps, all_cp_not_charging, monkeypatch): + # setup + bidi_cps("cp3") + data.data.counter_data["counter0"].data.get.power = grid_power + return_mock = Mock(reurn_value=True) + monkeypatch.setattr( + data.data.cp_data["cp3"].data.set.charging_ev_data.charge_template, "bidi_charging_allowed", return_mock) + + # execution + Algorithm().calc_current() + + # evaluation + assert data.data.cp_data["cp3"].data.set.current == expected_current + assert data.data.cp_data["cp4"].data.set.current == 0 + assert data.data.cp_data["cp5"].data.set.current == 0 + assert data.data.counter_data["counter0"].data.set.surplus_power_left == 0 + + +def test_cp3_cp4_bidi_discharge(bidi_cps, all_cp_not_charging, monkeypatch): + # setup + bidi_cps("cp3", "cp4") + data.data.counter_data["counter0"].data.get.power = 4000 + + # execution + Algorithm().calc_current() + + # evaluation + assert data.data.cp_data["cp3"].data.set.current == -2.898550724637681 + assert data.data.cp_data["cp4"].data.set.current == -2.898550724637681 + assert data.data.cp_data["cp5"].data.set.current == 0 + assert data.data.counter_data["counter0"].data.set.surplus_power_left == 0 diff --git a/packages/control/algorithm/integration_test/conftest.py b/packages/control/algorithm/integration_test/conftest.py index abf0f02e18..22eac7fd59 100644 --- a/packages/control/algorithm/integration_test/conftest.py +++ b/packages/control/algorithm/integration_test/conftest.py @@ -7,9 +7,11 @@ from control.bat import Bat from control.bat_all import BatAll from control.chargepoint.chargepoint import Chargepoint +from control.chargepoint.chargepoint_template import CpTemplate from control.counter_all import CounterAll from control.counter import Counter from control.ev.ev import Ev +from control.io_device import IoActions from control.pv import Pv from control.chargepoint.chargepoint_state import ChargepointState from test_utils.default_hierarchies import NESTED_HIERARCHY @@ -23,6 +25,7 @@ def data_() -> None: "cp4": Chargepoint(4, None), "cp5": Chargepoint(5, None)} for i in range(3, 6): + data.data.cp_data[f"cp{i}"].template = CpTemplate() data.data.cp_data[f"cp{i}"].data.config.phase_1 = i-2 data.data.cp_data[f"cp{i}"].data.set.charging_ev = i data.data.cp_data[f"cp{i}"].data.set.charging_ev_data = Ev(i) @@ -48,6 +51,7 @@ def data_() -> None: data.data.counter_all_data = CounterAll() data.data.counter_all_data.data.get.hierarchy = NESTED_HIERARCHY data.data.counter_all_data.data.config.consider_less_charging = True + data.data.io_actions = IoActions() @dataclass diff --git a/packages/control/algorithm/integration_test/instant_charging_test.py b/packages/control/algorithm/integration_test/instant_charging_test.py index f0582261d5..0042910933 100644 --- a/packages/control/algorithm/integration_test/instant_charging_test.py +++ b/packages/control/algorithm/integration_test/instant_charging_test.py @@ -3,10 +3,9 @@ from unittest.mock import Mock import pytest -from control.algorithm import additional_current from control.algorithm.integration_test.conftest import ParamsExpectedSetCurrent, assert_expected_current from control.chargemode import Chargemode -from control import data +from control import data, loadmanagement from control.algorithm.algorithm import Algorithm from control.limiting_value import LimitingValue from dataclass_utils.factories import currents_list_factory @@ -63,8 +62,6 @@ def test_start_instant_charging(all_cp_instant_charging_1p, all_cp_not_charging, data.data.counter_data["counter0"].data.set.raw_power_left = 21310 data.data.counter_data["counter0"].data.set.raw_currents_left = [32, 30, 31] data.data.counter_data["counter6"].data.set.raw_currents_left = [16, 12, 14] - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) # execution Algorithm().calc_current() @@ -122,8 +119,8 @@ def test_instant_charging_limit(params: ParamsLimit, all_cp_instant_charging_1p, data.data.counter_data["counter0"].data.set.raw_power_left = params.raw_power_left data.data.counter_data["counter0"].data.set.raw_currents_left = params.raw_currents_left_counter0 data.data.counter_data["counter6"].data.set.raw_currents_left = params.raw_currents_left_counter6 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) + mock_get_component_name_by_id = Mock(return_value="Garage") + monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mock_get_component_name_by_id) # execution Algorithm().calc_current() @@ -199,8 +196,8 @@ def test_control_parameter_instant_charging(params: ParamsControlParameter, all_ data.data.counter_data["counter0"].data.set.raw_power_left = 22080 data.data.counter_data["counter0"].data.set.raw_currents_left = [32]*3 data.data.counter_data["counter6"].data.set.raw_currents_left = [16]*3 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) + mock_get_component_name_by_id = Mock(return_value="Garage") + monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mock_get_component_name_by_id) # execution Algorithm().calc_current() diff --git a/packages/control/algorithm/integration_test/pv_charging_test.py b/packages/control/algorithm/integration_test/pv_charging_test.py index c15eda4952..c21eae69dc 100644 --- a/packages/control/algorithm/integration_test/pv_charging_test.py +++ b/packages/control/algorithm/integration_test/pv_charging_test.py @@ -3,12 +3,10 @@ from unittest.mock import Mock import pytest -from control.algorithm import additional_current, surplus_controlled from control.algorithm.integration_test.conftest import ParamsExpectedSetCurrent, assert_expected_current from control.chargemode import Chargemode -from control import data +from control import data, loadmanagement from control.algorithm.algorithm import Algorithm -from control.algorithm.algorithm import data as algorithm_data from control.chargepoint.chargepoint_template import CpTemplate from control.chargepoint.chargepoint_state import ChargepointState from dataclass_utils.factories import currents_list_factory @@ -117,12 +115,10 @@ def assert_counter_set(params: ParamsExpectedCounterSet): def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch): # alle 3 im PV-laden, keine Ladung -> bei zwei die Verzögerung starten, für den 3. reicht es nicht # setup - data.data.counter_data["counter0"].data.set.raw_power_left = 31775 + data.data.counter_data["counter0"].data.set.raw_power_left = 31975 data.data.counter_data["counter0"].data.set.raw_currents_left = [32, 30, 31] data.data.counter_data["counter6"].data.set.raw_currents_left = [16, 12, 14] data.data.counter_data["counter0"].data.set.reserved_surplus = 0 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) # execution Algorithm().calc_current() @@ -131,13 +127,13 @@ def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch) for i in range(3, 6): assert data.data.cp_data[f"cp{i}"].data.set.current == 0 assert data.data.cp_data[ - "cp3"].data.control_parameter.timestamp_switch_on_off == 1652683252.0 + "cp3"].data.control_parameter.timestamp_switch_on_off is None assert data.data.cp_data[ "cp4"].data.control_parameter.timestamp_switch_on_off == 1652683252.0 assert data.data.cp_data[ - "cp5"].data.control_parameter.timestamp_switch_on_off is None - assert data.data.counter_data["counter0"].data.set.raw_power_left == 31775 - assert data.data.counter_data["counter0"].data.set.surplus_power_left == 9890 + "cp5"].data.control_parameter.timestamp_switch_on_off == 1652683252.0 + assert data.data.counter_data["counter0"].data.set.raw_power_left == 31975 + assert data.data.counter_data["counter0"].data.set.surplus_power_left == -690 assert data.data.counter_data["counter0"].data.set.reserved_surplus == 9000 @@ -159,8 +155,6 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc "cp4"].data.control_parameter.state = ChargepointState.SWITCH_ON_DELAY data.data.cp_data[ "cp5"].data.control_parameter.timestamp_switch_on_off = None - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id) # execution Algorithm().calc_current() @@ -176,7 +170,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc assert data.data.cp_data[ "cp5"].data.control_parameter.timestamp_switch_on_off is None assert data.data.counter_data["counter0"].data.set.raw_power_left == 24300 - assert data.data.counter_data["counter0"].data.set.surplus_power_left == 2415 + assert data.data.counter_data["counter0"].data.set.surplus_power_left == -690 assert data.data.counter_data["counter0"].data.set.reserved_surplus == 0 @@ -190,7 +184,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc expected_current_cp4=8, expected_current_cp5=8, expected_raw_power_left=34820, - expected_surplus_power_left=6035.0, + expected_surplus_power_left=1090, expected_reserved_surplus=0, expected_released_surplus=0), ParamsSurplus(name="reduce current", @@ -202,7 +196,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc expected_current_cp4=7.8731884057971016, expected_current_cp5=7.8731884057971016, expected_raw_power_left=24470, - expected_surplus_power_left=0, + expected_surplus_power_left=1090, expected_reserved_surplus=0, expected_released_surplus=0), ParamsSurplus(name="switch off delay for two of three charging", @@ -214,7 +208,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc expected_current_cp4=6, expected_current_cp5=6, expected_raw_power_left=5635, - expected_surplus_power_left=-16250.0, + expected_surplus_power_left=-8200, expected_reserved_surplus=0, expected_released_surplus=11040), ] @@ -227,8 +221,11 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3 data.data.counter_data["counter0"].data.set.raw_power_left = params.raw_power_left data.data.counter_data["counter0"].data.set.raw_currents_left = params.raw_currents_left_counter0 data.data.counter_data["counter6"].data.set.raw_currents_left = params.raw_currents_left_counter6 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id) + mock_get_component_name_by_id = Mock(return_value="Garage") + monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mock_get_component_name_by_id) + data.data.cp_data["cp3"].data.set.charge_template.data.chargemode.pv_charging.phases_to_use = 1 + data.data.cp_data["cp4"].data.set.charge_template.data.chargemode.pv_charging.phases_to_use = 1 + data.data.cp_data["cp5"].data.set.charge_template.data.chargemode.pv_charging.phases_to_use = 1 # execution Algorithm().calc_current() @@ -250,7 +247,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3 expected_current_cp4=6, expected_current_cp5=6, expected_raw_power_left=17400, - expected_surplus_power_left=-4485, + expected_surplus_power_left=-690, expected_reserved_surplus=0, expected_released_surplus=0), ParamsPhaseSwitch(name="phase switch 1p->3p", @@ -264,7 +261,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3 expected_current_cp4=6, expected_current_cp5=6, expected_raw_power_left=37520.0, - expected_surplus_power_left=10575.0, + expected_surplus_power_left=3000, expected_reserved_surplus=0, expected_released_surplus=0) ] @@ -275,10 +272,6 @@ def test_phase_switch(all_cp_pv_charging_3p, all_cp_charging_3p, monkeypatch): data.data.counter_data["counter0"].data.set.raw_power_left = cases_phase_switch[0].raw_power_left data.data.counter_data["counter0"].data.set.raw_currents_left = cases_phase_switch[0].raw_currents_left_counter0 data.data.counter_data["counter6"].data.set.raw_currents_left = cases_phase_switch[0].raw_currents_left_counter6 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id) - mockget_get_phases_chargemode = Mock(return_value=0) - monkeypatch.setattr(algorithm_data.data.general_data, "get_phases_chargemode", mockget_get_phases_chargemode) data.data.cp_data[ "cp3"].data.control_parameter.state = ChargepointState.CHARGING_ALLOWED data.data.cp_data[ @@ -300,10 +293,6 @@ def test_phase_switch_1p_3p(all_cp_pv_charging_1p, monkeypatch): data.data.counter_data["counter0"].data.set.raw_power_left = cases_phase_switch[1].raw_power_left data.data.counter_data["counter0"].data.set.raw_currents_left = cases_phase_switch[1].raw_currents_left_counter0 data.data.counter_data["counter6"].data.set.raw_currents_left = cases_phase_switch[1].raw_currents_left_counter6 - mockget_component_name_by_id = Mock(return_value="Garage") - monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id) - mockget_get_phases_chargemode = Mock(return_value=0) - monkeypatch.setattr(algorithm_data.data.general_data, "get_phases_chargemode", mockget_get_phases_chargemode) data.data.cp_data["cp3"].data.get.currents = [32, 0, 0] data.data.cp_data["cp3"].data.get.power = 7360 data.data.cp_data["cp3"].data.control_parameter.timestamp_last_phase_switch = 1652682252 diff --git a/packages/control/algorithm/min_current.py b/packages/control/algorithm/min_current.py index 87d7d9a134..8dada55ce0 100644 --- a/packages/control/algorithm/min_current.py +++ b/packages/control/algorithm/min_current.py @@ -1,10 +1,11 @@ import logging +from control import data from control.algorithm import common -from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_MIN_CURRENT +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_MIN_CURRENT, CONSIDERED_CHARGE_MODES_PV_ONLY +from control.chargepoint.chargepoint_state import ChargepointState from control.loadmanagement import Loadmanagement from control.algorithm.filter_chargepoints import get_chargepoints_by_mode_and_counter -from modules.common.utils.component_parser import get_component_name_by_id log = logging.getLogger(__name__) @@ -24,7 +25,8 @@ def set_min_current(self) -> None: cp = preferenced_chargepoints[0] missing_currents, counts = common.get_min_current(cp) if max(missing_currents) > 0: - available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter) + available_currents, limit = Loadmanagement().get_available_currents( + missing_currents, counter, cp) cp.data.control_parameter.limit = limit available_for_cp = common.available_current_for_cp( cp, counts, available_currents, missing_currents) @@ -34,13 +36,19 @@ def set_min_current(self) -> None: common.set_current_counterdiff(-(cp.data.set.current or 0), 0, cp) if limit: cp.set_state_and_log( - "Ladung kann nicht gestartet werden" - f"{limit.value.format(get_component_name_by_id(counter.num))}") + f"Ladung kann nicht gestartet werden{limit.message}") else: common.set_current_counterdiff( cp.data.set.target_current, cp.data.control_parameter.min_current, cp) else: + if mode_tuple in CONSIDERED_CHARGE_MODES_PV_ONLY: + try: + if (cp.data.control_parameter.state == ChargepointState.NO_CHARGING_ALLOWED or + cp.data.control_parameter.state == ChargepointState.SWITCH_ON_DELAY): + data.data.counter_all_data.get_evu_counter().switch_on_threshold_reached(cp) + except Exception: + log.exception(f"Fehler in der PV-gesteuerten Ladung bei {cp.num}") cp.data.set.current = 0 preferenced_chargepoints.pop(0) diff --git a/packages/control/algorithm/surplus_controlled.py b/packages/control/algorithm/surplus_controlled.py index 51e0aaa1c7..d4ceadca27 100644 --- a/packages/control/algorithm/surplus_controlled.py +++ b/packages/control/algorithm/surplus_controlled.py @@ -3,15 +3,17 @@ from control import data from control.algorithm import common -from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_PV_ONLY, CONSIDERED_CHARGE_MODES_SURPLUS +from control.algorithm.chargemodes import (CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE, CONSIDERED_CHARGE_MODES_PV_ONLY, + CONSIDERED_CHARGE_MODES_SURPLUS) from control.algorithm.filter_chargepoints import (get_chargepoints_by_chargemodes, get_chargepoints_by_mode_and_counter, get_preferenced_chargepoint_charging) +from control.algorithm.utils import get_medium_charging_current from control.chargepoint.charging_type import ChargingType from control.chargepoint.chargepoint import Chargepoint from control.chargepoint.chargepoint_state import ChargepointState, CHARGING_STATES -from modules.common.utils.component_parser import get_component_name_by_id from control.counter import ControlRangeState, Counter +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue, Loadmanagement @@ -52,8 +54,10 @@ def _set(self, cp = chargepoints[0] missing_currents, counts = common.get_missing_currents_left(chargepoints) available_currents, limit = Loadmanagement().get_available_currents_surplus(missing_currents, + cp.data.get.voltages, counter, - feed_in_yield) + cp, + feed_in=feed_in_yield) cp.data.control_parameter.limit = limit available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents) if counter.get_control_range_state(feed_in_yield) == ControlRangeState.MIDDLE: @@ -61,8 +65,9 @@ def _set(self, dif_to_old_current = available_for_cp + cp.data.set.target_current - cp.data.set.current_prev # Wenn die Differenz zwischen altem und neuem Soll-Strom größer als der Regelbereich ist, trotzdem # nachregeln, auch wenn der Regelbereich eingehalten wird. Sonst würde zB nicht berücksichtigt werden, - # wenn noch ein Fahrzeug dazu kommmt. - if (pv_charging.control_range[1] - pv_charging.control_range[0]) / 230 < abs(dif_to_old_current): + # wenn noch ein Fahrzeug dazu kommt. + if ((pv_charging.control_range[1] - pv_charging.control_range[0]) / + (sum(counter.data.get.voltages) / len(counter.data.get.voltages)) < abs(dif_to_old_current)): current = available_for_cp else: # Nicht mehr freigeben, wie das Lastmanagement vorgibt @@ -71,8 +76,8 @@ def _set(self, current = available_for_cp current = common.get_current_to_set(cp.data.set.current, current, cp.data.set.target_current) - self._set_loadmangement_message(current, limit, cp, counter) - limited_current = self._limit_adjust_current(cp, current) + self._set_loadmangement_message(current, limit, cp) + limited_current = limit_adjust_current(cp, current) common.set_current_counterdiff( cp.data.control_parameter.min_current, limited_current, @@ -82,55 +87,25 @@ def _set(self, def _set_loadmangement_message(self, current: float, - limit: LimitingValue, - chargepoint: Chargepoint, - counter: Counter) -> None: + limit: LoadmanagementLimit, + chargepoint: Chargepoint) -> None: # Strom muss an diesem Zähler geändert werden if (current != chargepoint.data.set.current and # Strom erreicht nicht die vorgegebene Stromstärke current != max(chargepoint.data.control_parameter.required_currents) and # im PV-Laden wird der Strom immer durch die Leistung begrenzt - limit != LimitingValue.POWER): + limit.limiting_value != LimitingValue.POWER): chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden" - f"{limit.value.format(get_component_name_by_id(counter.num))}") + f"{limit.message}") # tested def filter_by_feed_in_limit(self, chargepoints: List[Chargepoint]) -> Tuple[List[Chargepoint], List[Chargepoint]]: - cp_with_feed_in = list(filter(lambda cp: cp.data.set.charging_ev_data.charge_template.data.chargemode. + cp_with_feed_in = list(filter(lambda cp: cp.data.set.charge_template.data.chargemode. pv_charging.feed_in_limit is True, chargepoints)) - cp_without_feed_in = list(filter(lambda cp: cp.data.set.charging_ev_data.charge_template.data.chargemode. + cp_without_feed_in = list(filter(lambda cp: cp.data.set.charge_template.data.chargemode. pv_charging.feed_in_limit is False, chargepoints)) return cp_with_feed_in, cp_without_feed_in - # tested - def _limit_adjust_current(self, chargepoint: Chargepoint, new_current: float) -> float: - if chargepoint.template.data.charging_type == ChargingType.AC.value: - MAX_CURRENT = 5 - else: - MAX_CURRENT = 30 - msg = None - nominal_difference = chargepoint.data.set.charging_ev_data.ev_template.data.nominal_difference - if chargepoint.data.set.charging_ev_data.chargemode_changed or chargepoint.data.get.charge_state is False: - return new_current - else: - # Um max. +/- 5A pro Zyklus regeln - if (-MAX_CURRENT-nominal_difference - < new_current - max(chargepoint.data.get.currents) - < MAX_CURRENT+nominal_difference): - current = new_current - else: - if new_current < max(chargepoint.data.get.currents): - current = max(chargepoint.data.get.currents) - MAX_CURRENT - msg = f"Es darf um max {MAX_CURRENT}A unter den aktuell genutzten Strom geregelt werden." - - else: - current = max(chargepoint.data.get.currents) + MAX_CURRENT - msg = f"Es darf um max {MAX_CURRENT}A über den aktuell genutzten Strom geregelt werden." - chargepoint.set_state_and_log(msg) - return max(current, - chargepoint.data.control_parameter.min_current, - chargepoint.data.set.target_current) - def _fix_deviating_evse_current(self, chargepoint: Chargepoint) -> float: """Wenn Autos nicht die volle Ladeleistung nutzen, wird unnötig eingespeist. Dann kann um den noch nicht genutzten Soll-Strom hochgeregelt werden. Wenn Fahrzeuge entgegen der Norm mehr Ladeleistung beziehen, als @@ -139,7 +114,7 @@ def _fix_deviating_evse_current(self, chargepoint: Chargepoint) -> float: Wenn die Soll-Stromstärke nicht angepasst worden ist, nicht den ungenutzten EVSE-Strom aufschlagen.""" evse_current = chargepoint.data.get.evse_current if evse_current and chargepoint.data.set.current != chargepoint.data.set.current_prev: - offset = evse_current - max(chargepoint.data.get.currents) + offset = evse_current - get_medium_charging_current(chargepoint.data.get.currents) current_with_offset = chargepoint.data.set.current + offset current = min(current_with_offset, chargepoint.data.control_parameter.required_current) if current != chargepoint.data.set.current: @@ -150,56 +125,87 @@ def check_submode_pv_charging(self) -> None: evu_counter = data.data.counter_all_data.get_evu_counter() for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY): - def phase_switch_necessary() -> bool: - return cp.cp_ev_chargemode_support_phase_switch() and cp.data.get.phases_in_use != 1 - control_parameter = cp.data.control_parameter - if cp.data.set.charging_ev_data.chargemode_changed or cp.data.set.charging_ev_data.submode_changed: - if control_parameter.state == ChargepointState.CHARGING_ALLOWED: - if (cp.data.set.charging_ev_data.ev_template.data.prevent_charge_stop is False and - phase_switch_necessary() is False): - threshold = evu_counter.calc_switch_off_threshold(cp)[0] - if evu_counter.calc_raw_surplus() - cp.data.set.required_power < threshold: - control_parameter.required_currents = [0]*3 - control_parameter.state = ChargepointState.NO_CHARGING_ALLOWED + try: + def phase_switch_necessary() -> bool: + return cp.cp_ev_chargemode_support_phase_switch() and cp.data.get.phases_in_use != 1 + control_parameter = cp.data.control_parameter + if cp.chargemode_changed or cp.submode_changed: + if control_parameter.state == ChargepointState.CHARGING_ALLOWED: + if cp.data.set.charging_ev_data.ev_template.data.prevent_charge_stop is False: + threshold = evu_counter.calc_switch_off_threshold(cp)[0] + if evu_counter.calc_raw_surplus() - cp.data.set.required_power < threshold: + control_parameter.required_currents = [0]*3 + control_parameter.state = ChargepointState.NO_CHARGING_ALLOWED + else: + control_parameter.required_currents = [0]*3 else: - control_parameter.required_currents = [0]*3 - else: - if ((control_parameter.state == ChargepointState.CHARGING_ALLOWED or - control_parameter.state == ChargepointState.SWITCH_OFF_DELAY) and - phase_switch_necessary() is False): - evu_counter.switch_off_check_threshold(cp) - if control_parameter.state == ChargepointState.SWITCH_OFF_DELAY: - evu_counter.switch_off_check_timer(cp) - if control_parameter.state == ChargepointState.SWITCH_ON_DELAY: - # Wenn charge_state False und set_current > 0, will Auto nicht laden - evu_counter.switch_on_timer_expired(cp) - if control_parameter.state not in CHARGING_STATES: - control_parameter.required_currents = [0]*3 - - def check_switch_on(self) -> None: - for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_PV_ONLY): - if (cp.data.control_parameter.state == ChargepointState.NO_CHARGING_ALLOWED or - cp.data.control_parameter.state == ChargepointState.SWITCH_ON_DELAY): - data.data.counter_all_data.get_evu_counter().switch_on_threshold_reached(cp) + if ((control_parameter.state == ChargepointState.CHARGING_ALLOWED or + control_parameter.state == ChargepointState.SWITCH_OFF_DELAY) and + phase_switch_necessary() is False): + evu_counter.switch_off_check_threshold(cp) + if control_parameter.state == ChargepointState.SWITCH_OFF_DELAY: + evu_counter.switch_off_check_timer(cp) + if control_parameter.state == ChargepointState.SWITCH_ON_DELAY: + # Wenn charge_state False und set_current > 0, will Auto nicht laden + evu_counter.switch_on_timer_expired(cp) + if control_parameter.state not in CHARGING_STATES: + control_parameter.required_currents = [0]*3 + except Exception: + log.exception(f"Fehler in der PV-gesteuerten Ladung bei {cp.num}") def set_required_current_to_max(self) -> None: - for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS): - charging_ev_data = cp.data.set.charging_ev_data - required_currents = cp.data.control_parameter.required_currents - control_parameter = cp.data.control_parameter + for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS + + CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE): + try: + charging_ev_data = cp.data.set.charging_ev_data + required_currents = cp.data.control_parameter.required_currents + control_parameter = cp.data.control_parameter - if control_parameter.phases == 1: - max_current = charging_ev_data.ev_template.data.max_current_single_phase - else: - max_current = charging_ev_data.ev_template.data.max_current_multi_phases - - if cp.template.data.charging_type == ChargingType.AC.value: if control_parameter.phases == 1: max_current = charging_ev_data.ev_template.data.max_current_single_phase else: max_current = charging_ev_data.ev_template.data.max_current_multi_phases - else: - max_current = charging_ev_data.ev_template.data.dc_max_current - control_parameter.required_currents = [max_current if required_currents[i] != 0 else 0 for i in range(3)] - control_parameter.required_current = max_current + if cp.template.data.charging_type == ChargingType.AC.value: + if control_parameter.phases == 1: + max_current = charging_ev_data.ev_template.data.max_current_single_phase + else: + max_current = charging_ev_data.ev_template.data.max_current_multi_phases + else: + max_current = charging_ev_data.ev_template.data.dc_max_current + + control_parameter.required_currents = [ + max_current if required_currents[i] != 0 else 0 for i in range(3)] + control_parameter.required_current = max_current + except Exception: + log.exception(f"Fehler in der PV-gesteuerten Ladung bei {cp.num}") + + +# tested +def limit_adjust_current(chargepoint: Chargepoint, new_current: float) -> float: + if chargepoint.template.data.charging_type == ChargingType.AC.value: + MAX_CURRENT = 5 + else: + MAX_CURRENT = 30 + msg = None + nominal_difference = chargepoint.data.set.charging_ev_data.ev_template.data.nominal_difference + if chargepoint.chargemode_changed or chargepoint.data.get.charge_state is False: + return new_current + else: + # Um max. +/- 5A pro Zyklus regeln + if (-MAX_CURRENT-nominal_difference + < new_current - get_medium_charging_current(chargepoint.data.get.currents) + < MAX_CURRENT+nominal_difference): + current = new_current + else: + if new_current < get_medium_charging_current(chargepoint.data.get.currents): + current = get_medium_charging_current(chargepoint.data.get.currents) - MAX_CURRENT + msg = f"Es darf um max {MAX_CURRENT}A unter den aktuell genutzten Strom geregelt werden." + + else: + current = get_medium_charging_current(chargepoint.data.get.currents) + MAX_CURRENT + msg = f"Es darf um max {MAX_CURRENT}A über den aktuell genutzten Strom geregelt werden." + chargepoint.set_state_and_log(msg) + return max(current, + chargepoint.data.control_parameter.min_current, + chargepoint.data.set.target_current) diff --git a/packages/control/algorithm/surplus_controlled_test.py b/packages/control/algorithm/surplus_controlled_test.py index 6e50210534..5963e6d491 100644 --- a/packages/control/algorithm/surplus_controlled_test.py +++ b/packages/control/algorithm/surplus_controlled_test.py @@ -5,13 +5,13 @@ from control import data from control.algorithm import surplus_controlled from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes -from control.algorithm.surplus_controlled import CONSIDERED_CHARGE_MODES_PV_ONLY, SurplusControlled +from control.algorithm.surplus_controlled import (CONSIDERED_CHARGE_MODES_PV_ONLY, SurplusControlled, + limit_adjust_current) from control.chargemode import Chargemode from control.chargepoint.chargepoint import Chargepoint, ChargepointData from control.chargepoint.chargepoint_data import Get, Set from control.chargepoint.chargepoint_template import CpTemplate from control.chargepoint.control_parameter import ControlParameter -from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev @@ -40,10 +40,8 @@ def test_filter_by_feed_in_limit(feed_in_limit_1: bool, expected_sorted: int): # setup def setup_cp(cp: Chargepoint, feed_in_limit: bool) -> Chargepoint: - ev = Ev(0) - ev.charge_template = ChargeTemplate(0) - ev.charge_template.data.chargemode.pv_charging.feed_in_limit = feed_in_limit - cp.data = ChargepointData(set=Set(charging_ev_data=ev)) + cp.data = ChargepointData() + cp.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit = feed_in_limit return cp cp1 = setup_cp(mock_cp1, feed_in_limit_1) @@ -69,7 +67,7 @@ def test_limit_adjust_current(new_current: float, expected_current: float, monke monkeypatch.setattr(Chargepoint, "set_state_and_log", Mock()) # execution - current = SurplusControlled()._limit_adjust_current(cp, new_current) + current = limit_adjust_current(cp, new_current) # evaluation assert current == expected_current diff --git a/packages/control/algorithm/utils.py b/packages/control/algorithm/utils.py new file mode 100644 index 0000000000..24f422bb60 --- /dev/null +++ b/packages/control/algorithm/utils.py @@ -0,0 +1,10 @@ +from typing import List + + +def get_medium_charging_current(currents: List[float]) -> float: + """Ermittelt den mittleren Ladestrom der Phasen, auf denen geladen wird. + """ + if any(x >= 0.5 for x in currents): + return sum(x for x in currents if x >= 0.5) / len([x for x in currents if x >= 0.5]) + else: + return 0.0 diff --git a/packages/control/auto_phase_switch_test.py b/packages/control/auto_phase_switch_test.py index 25531d2e96..dd31a25895 100644 --- a/packages/control/auto_phase_switch_test.py +++ b/packages/control/auto_phase_switch_test.py @@ -1,10 +1,12 @@ -import threading import pytest +from threading import Event from typing import List, Optional from unittest.mock import Mock from control.chargepoint.control_parameter import ControlParameter from control.counter import Counter, CounterData, Set +from control.limiting_value import LoadmanagementLimit +from control.ev.charge_template import ChargeTemplate from control.pv_all import PvAll from control.bat_all import BatAll from control.general import General @@ -21,7 +23,7 @@ def vehicle() -> Ev: @pytest.fixture(autouse=True) def data_module() -> None: - data.data_init(threading.Event()) + data.data_init(Event()) data.data.general_data = General() data.data.pv_all_data = PvAll() data.data.bat_all_data = BatAll() @@ -31,7 +33,7 @@ class Params: def __init__(self, name: str, max_current_single_phase: int, - timestamp_last_phase_switch: Optional[float], + timestamp_phase_switch_buffer_start: Optional[float], phases_to_use: int, required_current: float, evu_surplus: int, @@ -44,7 +46,7 @@ def __init__(self, expected_message: Optional[str] = None) -> None: self.name = name self.max_current_single_phase = max_current_single_phase - self.timestamp_last_phase_switch = timestamp_last_phase_switch + self.timestamp_phase_switch_buffer_start = timestamp_phase_switch_buffer_start self.phases_to_use = phases_to_use self.required_current = required_current self.available_power = evu_surplus @@ -58,54 +60,55 @@ def __init__(self, cases = [ - Params("1to3, enough power, start timer", max_current_single_phase=16, timestamp_last_phase_switch=1652683202, + Params("1to3, enough power, start timer", max_current_single_phase=16, timestamp_phase_switch_buffer_start=None, phases_to_use=1, required_current=6, evu_surplus=800, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=1, expected_current=6, - expected_message=Ev.PHASE_SWITCH_DELAY_TEXT.format("Umschaltung von 1 auf 3", "4 Min. 10 Sek."), + expected_message=Ev.PHASE_SWITCH_DELAY_TEXT.format("Umschaltung von 1 auf 3", "30 Sek."), expected_state=ChargepointState.PHASE_SWITCH_DELAY), - Params("1to3, not enough power, start timer", max_current_single_phase=16, timestamp_last_phase_switch=1652683202, + Params("1to3, not enough power, start timer", max_current_single_phase=16, timestamp_phase_switch_buffer_start=None, phases_to_use=1, required_current=6, evu_surplus=300, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=1, expected_current=6, expected_state=ChargepointState.CHARGING_ALLOWED), Params("1to3, enough power, timer not expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652683202.0, phases_to_use=1, required_current=6, + timestamp_phase_switch_buffer_start=1652683232.0, phases_to_use=1, required_current=6, evu_surplus=1460, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=6, - expected_message=Ev.PHASE_SWITCH_DELAY_TEXT.format("Umschaltung von 1 auf 3", "4 Min. 10 Sek."), + expected_message=Ev.PHASE_SWITCH_DELAY_TEXT.format("Umschaltung von 1 auf 3", "10 Sek."), expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("1to3, not enough power, timer not expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652683202.0, phases_to_use=1, required_current=6, + timestamp_phase_switch_buffer_start=1652683202.0, phases_to_use=1, required_current=6, evu_surplus=460, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=6, expected_message=f"Verzögerung für die Umschaltung von 1 auf 3 Phasen abgebrochen{Ev.NOT_ENOUGH_POWER}", expected_state=ChargepointState.CHARGING_ALLOWED), Params("1to3, enough power, timer expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652682802.0, phases_to_use=1, required_current=6, + timestamp_phase_switch_buffer_start=1652682802.0, phases_to_use=1, required_current=6, evu_surplus=1640, get_currents=[15.6, 0, 0], get_power=3450, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, expected_state=ChargepointState.PHASE_SWITCH_AWAITED), - Params("3to1, not enough power, start timer", max_current_single_phase=16, timestamp_last_phase_switch=1652683202, + Params("3to1, not enough power, start timer", max_current_single_phase=16, + timestamp_phase_switch_buffer_start=1652683202, phases_to_use=3, required_current=6, evu_surplus=0, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.CHARGING_ALLOWED, expected_phases_to_use=3, expected_current=6, - expected_message="Umschaltung von 3 auf 1 Phasen in 4 Min. 10 Sek..", + expected_message="Umschaltung von 3 auf 1 Phasen in 10 Sek..", expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("3to1, not enough power, timer not expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652683202.0, + timestamp_phase_switch_buffer_start=1652683202.0, phases_to_use=3, required_current=6, evu_surplus=-460, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, - expected_message="Umschaltung von 3 auf 1 Phasen in 4 Min. 10 Sek..", + expected_message="Umschaltung von 3 auf 1 Phasen in 10 Sek..", expected_state=ChargepointState.PHASE_SWITCH_DELAY), Params("3to1, enough power, timer not expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652683202.0, phases_to_use=3, required_current=6, + timestamp_phase_switch_buffer_start=1652683202.0, phases_to_use=3, required_current=6, evu_surplus=860, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=3, expected_current=6, expected_message=f"Verzögerung für die Umschaltung von 3 auf 1 Phasen abgebrochen{Ev.ENOUGH_POWER}", expected_state=ChargepointState.CHARGING_ALLOWED), Params("3to1, not enough power, timer expired", max_current_single_phase=16, - timestamp_last_phase_switch=1652682802.0, phases_to_use=3, required_current=6, + timestamp_phase_switch_buffer_start=1652682802.0, phases_to_use=3, required_current=6, evu_surplus=-460, get_currents=[4.5, 4.4, 5.8], get_power=3381, state=ChargepointState.PHASE_SWITCH_DELAY, expected_phases_to_use=1, expected_current=16, expected_state=ChargepointState.PHASE_SWITCH_AWAITED), @@ -125,19 +128,21 @@ def test_auto_phase_switch(monkeypatch, vehicle: Ev, params: Params): vehicle.ev_template.data.max_current_single_phase = params.max_current_single_phase control_parameter = ControlParameter() - control_parameter.timestamp_last_phase_switch = params.timestamp_last_phase_switch + control_parameter.timestamp_last_phase_switch = 1652682802 + control_parameter.timestamp_phase_switch_buffer_start = params.timestamp_phase_switch_buffer_start control_parameter.phases = params.phases_to_use control_parameter.required_current = params.required_current control_parameter.state = params.state # execution - phases_to_use, current, message = vehicle.auto_phase_switch(control_parameter, + phases_to_use, current, message = vehicle.auto_phase_switch(ChargeTemplate(), + control_parameter, 0, params.get_currents, params.get_power, 32, 3, - None) + LoadmanagementLimit(None, None)) # evaluation assert phases_to_use == params.expected_phases_to_use diff --git a/packages/control/bat_all.py b/packages/control/bat_all.py index c58c8a396c..ac0cbdc90e 100644 --- a/packages/control/bat_all.py +++ b/packages/control/bat_all.py @@ -20,15 +20,14 @@ from dataclasses import dataclass, field from enum import Enum import logging -from typing import List +from typing import List, Optional from control import data -from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_CHARGING from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes -from control.bat import Bat +from control.pv import Pv from helpermodules.constants import NO_ERROR from modules.common.abstract_device import AbstractDevice -from modules.common.fault_state import FaultStateLevel log = logging.getLogger(__name__) @@ -50,6 +49,7 @@ class Config: configured: bool = field(default=False, metadata={"topic": "config/configured"}) power_limit_mode: str = field(default=BatPowerLimitMode.NO_LIMIT.value, metadata={"topic": "config/power_limit_mode"}) + bat_control_permitted: bool = field(default=False, metadata={"topic": "config/bat_control_permitted"}) def config_factory() -> Config: @@ -75,9 +75,8 @@ def get_factory() -> Get: @dataclass class Set: - charging_power_left: float = field( - default=0, metadata={"topic": "set/charging_power_left"}) - power_limit: float = field(default=0, metadata={"topic": "set/power_limit"}) + charging_power_left: float = field(default=0, metadata={"topic": "set/charging_power_left"}) + power_limit: Optional[float] = field(default=None, metadata={"topic": "set/power_limit"}) regulate_up: bool = field(default=False, metadata={"topic": "set/regulate_up"}) @@ -114,7 +113,10 @@ def calc_power_for_all_components(self): for battery in data.data.bat_data.values(): try: if battery.data.get.fault_state < 2: - power += battery.data.get.power + try: + power += battery.data.get.power + except Exception: + log.exception(f"Fehler im Bat-Modul {battery.num}") imported += battery.data.get.imported exported += battery.data.get.exported soc_sum += battery.data.get.soc @@ -144,48 +146,32 @@ def calc_power_for_all_components(self): except Exception: log.exception("Fehler im Bat-Modul") - def _max_bat_power_hybrid_system(self, battery: Bat) -> float: + def _inverter_limited_power(self, inverter: Pv) -> float: """gibt die maximale Entladeleistung des Speichers zurück, bis die maximale Ausgangsleistung des WR erreicht ist.""" # tested - parent = data.data.counter_all_data.get_entry_of_parent(battery.num) - if parent.get("type") == "inverter": - parent_data = data.data.pv_data[f"pv{parent['id']}"].data - # Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR - # genutzt werden. - if parent_data.config.max_ac_out > 0: - max_bat_discharge_power = parent_data.config.max_ac_out + \ - parent_data.get.power + battery.data.get.power - return max_bat_discharge_power, True - else: - battery.data.get.fault_state = FaultStateLevel.ERROR.value - battery.data.get.fault_str = self.ERROR_CONFIG_MAX_AC_OUT - raise ValueError(self.ERROR_CONFIG_MAX_AC_OUT) + # Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR + # genutzt werden. + if inverter.data.config.max_ac_out > 0: + return max(inverter.data.get.power * -1 - inverter.data.config.max_ac_out, 0) else: - # Kein Hybrid-WR - # Maximal die Speicher-Leistung als Entladeleistung nutzen, um nicht unnötig Bezug zu erzeugen. - return abs(battery.data.get.power) + 50, False + return 0 def _limit_bat_power_discharge(self, required_power): """begrenzt die für den Algorithmus benötigte Entladeleistung des Speichers, wenn die maximale Ausgangsleistung des WR erreicht ist.""" - available_power = 0 - hybrid = False + inverter_limited_power = 0 if required_power > 0: # Nur wenn der Speicher entladen werden soll, fließt Leistung durch den WR. - for battery in data.data.bat_data.values(): + for inverter in data.data.pv_data.values(): try: - available_power_bat, hybrid_bat = self._max_bat_power_hybrid_system(battery) - if hybrid_bat: - hybrid = True - available_power += available_power_bat + inverter_limited_power += self._inverter_limited_power(inverter) except Exception: - log.exception(f"Fehler im Bat-Modul {battery.num}") - if hybrid: - if required_power > available_power: - log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {available_power}W" - " begrenzt.") - return min(required_power, available_power) + log.exception(f"Fehler im Bat-Modul {inverter.num}") + if inverter_limited_power > 0: + required_power = max(required_power-inverter_limited_power, 0) + log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W" + " begrenzt.") return required_power def setup_bat(self): @@ -223,6 +209,7 @@ def _get_charging_power_left(self): else: charging_power_left = 0 self.data.set.regulate_up = True if self.data.get.soc < 100 else False + # ev wird nach Speicher geladen elif config.bat_mode == BatConsiderationMode.EV_MODE.value: # Speicher sollte weder ge- noch entladen werden. charging_power_left = self.data.get.power @@ -303,27 +290,38 @@ def set_power_limit_controllable(self): self.data.get.power_limit_controllable = False def get_power_limit(self): - if (self.data.config.power_limit_mode != BatPowerLimitMode.NO_LIMIT.value - and len(get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT)) > 0 and + if self.data.config.bat_control_permitted is False: + return + chargepoint_by_chargemodes = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_CHARGING) + # Falls aktive Steuerung an und Fahrzeuge laden und kein Überschuss im System ist, + # dann Speichereistung begrenzen. + if (self.data.config.power_limit_mode != BatPowerLimitMode.NO_LIMIT.value and + len(chargepoint_by_chargemodes) > 0 and + data.data.cp_all_data.data.get.power > 100 and self.data.get.power_limit_controllable and - # Nur wenn kein Überschuss im System ist, Speicherleistung begrenzen. self.data.get.power <= 0 and - data.data.counter_all_data.get_evu_counter().data.get.power >= 0): + data.data.counter_all_data.get_evu_counter().data.get.power >= -100): if self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_STOP.value: self.data.set.power_limit = 0 elif self.data.config.power_limit_mode == BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value: - self.data.set.power_limit = data.data.counter_all_data.data.set.home_consumption + self.data.set.power_limit = data.data.counter_all_data.data.set.home_consumption * -1 log.debug(f"Speicher-Leistung begrenzen auf {self.data.set.power_limit/1000}kW") else: self.data.set.power_limit = None - if len(get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT)) == 0: + control_range_low = data.data.general_data.data.chargemode_config.pv_charging.control_range[0] + control_range_high = data.data.general_data.data.chargemode_config.pv_charging.control_range[1] + control_range_center = control_range_high - (control_range_high - control_range_low) / 2 + if len(chargepoint_by_chargemodes) == 0: log.debug("Speicher-Leistung nicht begrenzen, " "da keine Ladepunkte in einem Lademodus mit Netzbezug sind.") + elif data.data.cp_all_data.data.get.power <= 100: + log.debug("Speicher-Leistung nicht begrenzen, da kein Ladepunkt mit Netzubezug lädt.") elif self.data.get.power_limit_controllable is False: log.debug("Speicher-Leistung nicht begrenzen, da keine regelbaren Speicher vorhanden sind.") elif self.data.get.power > 0: log.debug("Speicher-Leistung nicht begrenzen, da kein Speicher entladen wird.") - elif data.data.counter_all_data.get_evu_counter().data.get.power < 0: + elif data.data.counter_all_data.get_evu_counter().data.get.power < control_range_center + 80: + # Wenn der Regelbereich zB auf Bezug steht, darf auch die Leistung des Regelbereichs entladen werden. log.debug("Speicher-Leistung nicht begrenzen, da EVU-Überschuss vorhanden ist.") else: log.debug("Speicher-Leistung nicht begrenzen.") @@ -332,10 +330,7 @@ def get_power_limit(self): if self.data.set.power_limit is None: power_limit = None else: - power_limit = min(self._max_bat_power_hybrid_system( - data.data.bat_data[f"bat{bat_component.component_config.id}"])[0], remaining_power_limit) - remaining_power_limit -= power_limit - remaining_power_limit = max(remaining_power_limit, 0) + power_limit = self._limit_bat_power_discharge(remaining_power_limit) data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit @@ -346,6 +341,6 @@ def get_controllable_bat_components() -> List: if isinstance(value, AbstractDevice): for comp_value in value.components.values(): if "bat" in comp_value.component_config.type: - if "set_power_limit" in type(comp_value).__dict__: + if comp_value.power_limit_controllable(): bat_components.append(comp_value) return bat_components diff --git a/packages/control/bat_all_test.py b/packages/control/bat_all_test.py index 35379d64ae..2256629529 100644 --- a/packages/control/bat_all_test.py +++ b/packages/control/bat_all_test.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field from typing import List, Optional -from unittest.mock import Mock +from unittest.mock import MagicMock, Mock import pytest from packages.conftest import hierarchy_standard from control import bat_all @@ -27,66 +27,40 @@ def data_fixture() -> None: @pytest.mark.parametrize( - "parent, bat_power, pv_power, expected_power_hybrid", + "max_ac_out, power, expected_result", [ - pytest.param({"id": 6, "type": "counter", "children": [ - {"id": 2, "type": "bat", "children": []}]}, 100, -6400, (150, False), - id="kein Hybrid-System, Speicher wird geladen"), - pytest.param({"id": 6, "type": "counter", "children": [ - {"id": 2, "type": "bat", "children": []}]}, -100, -6400, (150, False), - id="kein Hybrid-System, Speicher wird entladen"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -6400, (2000, True), - id="Speicher lädt mit 1200W, max 2000W zusätzliche Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -6400, (1400, True), - id="Speicher lädt mit 600W, max 1400W zusätzliche Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -6400, (800, True), - id="Speicher neutral, max 800W Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, -600, -6400, (200, True), - id="Speicher entlädt mit 600W, max 200W Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, -800, -6400, (0, True), - id="Speicher entlädt mit 800W, maximale Entladeleistung des WR erreicht"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7200, (1200, True), - id="Speicher lädt mit 1200W, max 1200W zusätzliche Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7200, (600, True), - id="Speicher lädt mit 600W, max 600W zusätzliche Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -7200, (0, True), - id="Speicher neutral, maximale Entladeleistung des WR erreicht"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7800, (600, True), - id="Speicher lädt mit 1200W, max 600W zusätzliche Entladeleistung bis WR max"), - pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7800, (0, True), - id="Speicher lädt mit 600W, maximale Entladeleistung des WR erreicht"), - ]) -def test_max_bat_power_hybrid_system(parent, bat_power, pv_power, expected_power_hybrid, data_fixture, monkeypatch): - # setup - # pv1-Data: max_ac_out 7200 - mock_get_entry_of_parent = Mock(return_value=parent) - monkeypatch.setattr(data.data.counter_all_data, "get_entry_of_parent", mock_get_entry_of_parent) - data.data.pv_data["pv1"].data.get.power = pv_power - - b = BatAll() - bat2 = Bat(2) - bat2.data.get.power = bat_power - - # execution - power = b._max_bat_power_hybrid_system(bat2) - - # evaluation - assert power == expected_power_hybrid + pytest.param(5000, -6000, 1000, id="Leistung überschreitet max_ac_out"), + pytest.param(5000, -4000, 0, id="Leistung liegt unter max_ac_out"), + pytest.param(5000, 0, 0, id="Keine Leistung (power = 0)"), + pytest.param(0, -6000, 0, id="max_ac_out ist 0"), + ], +) +def test_inverter_limited_power(max_ac_out, power, expected_result): + # Mock für die Pv-Klasse + inverter = Pv(1) + inverter.data.config.max_ac_out = max_ac_out + inverter.data.get.power = power + bat_all = BatAll() + + # Aufruf der zu testenden Funktion + result = bat_all._inverter_limited_power(inverter) + + # Überprüfung des Ergebnisses + assert result == expected_result @pytest.mark.parametrize( - "required_power, return_max_bat_power_hybrid_system, expected_power", + "required_power, return_inverter_limited_power, expected_power", [ - pytest.param(1000, (1100, True), 1000, id="maximale Entladeleistung nicht erreicht"), - pytest.param(1000, (900, True), 900, id="maximale Entladeleistung erreicht"), - pytest.param(-1000, (10, True), -1000, id="Speicher soll nicht mehr entladen werden"), - pytest.param(1000, (900, False), 1000, id="kein Hybrid-System"), + pytest.param(1000, 0, 1000, id="maximale Entladeleistung nicht erreicht"), + pytest.param(1000, 100, 900, id="maximale Entladeleistung erreicht"), + pytest.param(-1000, 10, -1000, id="Speicher soll nicht mehr entladen werden"), ]) -def test_limit_bat_power_discharge(required_power, return_max_bat_power_hybrid_system, expected_power, monkeypatch): +def test_limit_bat_power_discharge(required_power, return_inverter_limited_power, expected_power, monkeypatch): # setup - data.data.bat_data = {"bat2": Bat(2)} - mock_max_bat_power_hybrid_system = Mock(return_value=return_max_bat_power_hybrid_system) - monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock_max_bat_power_hybrid_system) + data.data.pv_data = {"pv2": Pv(2)} + mock_inverter_limited_power = Mock(return_value=return_inverter_limited_power) + monkeypatch.setattr(BatAll, "_inverter_limited_power", mock_inverter_limited_power) b = BatAll() @@ -157,8 +131,8 @@ class Params: ] -@ pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) -def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypatch): +@pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) +def test_get_charging_power_left(params: Params, caplog, data_, monkeypatch): # setup b_all = BatAll() b_all.data.get.power = params.power @@ -168,8 +142,8 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat b.data.get.power = params.power data.data.bat_data["bat0"] = b data.data.general_data.data.chargemode_config.pv_charging = params.config - mock__max_bat_power_hybrid_system = Mock(return_value=(params.power, None)) - monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock__max_bat_power_hybrid_system) + mock_limit_bat_power_discharge = MagicMock(side_effect=lambda x: x) + monkeypatch.setattr(BatAll, "_limit_bat_power_discharge", mock_limit_bat_power_discharge) # execution b_all._get_charging_power_left() @@ -180,7 +154,9 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat def default_chargepoint_factory() -> List[Chargepoint]: - return [Chargepoint(3, None)] + cp = Chargepoint(3, None) + cp.data.get.power = 1400 + return [cp] @dataclass @@ -202,10 +178,10 @@ class PowerLimitParams: power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), PowerLimitParams("Begrenzung immer, Speicher lädt", None, bat_power=100, power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), - PowerLimitParams("Begrenzung immer,Einspeisung", None, evu_power=-100, + PowerLimitParams("Begrenzung immer,Einspeisung", None, evu_power=-110, power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), PowerLimitParams("Begrenzung immer", 0, power_limit_mode=BatPowerLimitMode.LIMIT_STOP.value), - PowerLimitParams("Begrenzung Hausverbrauch", 456, + PowerLimitParams("Begrenzung Hausverbrauch", -456, power_limit_mode=BatPowerLimitMode.LIMIT_TO_HOME_CONSUMPTION.value), ] @@ -213,11 +189,13 @@ class PowerLimitParams: @pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) def test_get_power_limit(params: PowerLimitParams, data_, monkeypatch): b_all = BatAll() + b_all.data.config.bat_control_permitted = True b_all.data.config.power_limit_mode = params.power_limit_mode b_all.data.get.power_limit_controllable = params.power_limit_controllable b_all.data.get.power = params.bat_power data.data.counter_all_data = hierarchy_standard() data.data.counter_all_data.data.set.home_consumption = 456 + data.data.cp_all_data.data.get.power = 1400 data.data.counter_data["counter0"].data.get.power = params.evu_power data.data.bat_all_data = b_all @@ -225,7 +203,7 @@ def test_get_power_limit(params: PowerLimitParams, data_, monkeypatch): monkeypatch.setattr(bat_all, "get_chargepoints_by_chargemodes", get_chargepoints_by_chargemodes_mock) get_evu_counter_mock = Mock(return_value=data.data.counter_data["counter0"]) monkeypatch.setattr(data.data.counter_all_data, "get_evu_counter", get_evu_counter_mock) - get_controllable_bat_components_mock = Mock(return_value=[MqttBat(MqttBatSetup(id=2))]) + get_controllable_bat_components_mock = Mock(return_value=[MqttBat(MqttBatSetup(id=2), device_id=0)]) monkeypatch.setattr(bat_all, "get_controllable_bat_components", get_controllable_bat_components_mock) data.data.bat_all_data.get_power_limit() diff --git a/packages/control/chargelog/chargelog.py b/packages/control/chargelog/chargelog.py index db69551e7e..4dcad2e8b2 100644 --- a/packages/control/chargelog/chargelog.py +++ b/packages/control/chargelog/chargelog.py @@ -414,9 +414,7 @@ def calculate_charge_cost(cp, create_log_entry: bool = False): raise TypeError(f"Unbekannter Referenz-Zeitpunkt {reference}") log.debug(f'power source {energy_source_entry["energy_source"]}') log.debug(f"charged_energy {charged_energy}") - costs = _calc(energy_source_entry["energy_source"], - charged_energy, - (data.data.optional_data.et_module is not None)) + costs = _calc(energy_source_entry["energy_source"], charged_energy) cp.data.set.log.costs += costs log.debug(f"current costs {costs}, total costs {cp.data.set.log.costs}") Pub().pub(f"openWB/set/chargepoint/{cp.num}/set/log", asdict(cp.data.set.log)) @@ -492,14 +490,14 @@ def get_daily_log(day): return [] -def _calc(energy_source: Dict[str, float], charged_energy_last_hour: float, et_active: bool) -> float: +def _calc(energy_source: Dict[str, float], charged_energy_last_hour: float) -> float: prices = data.data.general_data.data.prices bat_costs = prices.bat * charged_energy_last_hour * energy_source["bat"] cp_costs = prices.cp * charged_energy_last_hour * energy_source["cp"] - if et_active: + try: grid_costs = data.data.optional_data.et_get_current_price() * charged_energy_last_hour * energy_source["grid"] - else: + except Exception: grid_costs = prices.grid * charged_energy_last_hour * energy_source["grid"] pv_costs = prices.pv * charged_energy_last_hour * energy_source["pv"] diff --git a/packages/control/chargemode.py b/packages/control/chargemode.py index 6b0fada216..4ff3dfea22 100644 --- a/packages/control/chargemode.py +++ b/packages/control/chargemode.py @@ -6,5 +6,6 @@ class Chargemode(Enum): TIME_CHARGING = "time_charging" INSTANT_CHARGING = "instant_charging" PV_CHARGING = "pv_charging" - STANDBY = "standby" + ECO_CHARGING = "eco_charging" + BIDI_CHARGING = "bidi_charging" STOP = "stop" diff --git a/packages/control/chargepoint/chargepoint.py b/packages/control/chargepoint/chargepoint.py index cf01d0cb67..6ed200beee 100644 --- a/packages/control/chargepoint/chargepoint.py +++ b/packages/control/chargepoint/chargepoint.py @@ -17,25 +17,30 @@ from dataclasses import asdict import dataclasses import logging -import threading +from threading import Thread, Event import traceback from typing import Dict, Optional, Tuple +from control.algorithm.utils import get_medium_charging_current from control.chargelog import chargelog from control import data from control.chargemode import Chargemode from control.chargepoint.chargepoint_data import ChargepointData, ConnectedConfig, ConnectedInfo, ConnectedSoc, Get, Log from control.chargepoint.chargepoint_template import CpTemplate -from control.chargepoint.control_parameter import ControlParameter, control_parameter_factory +from control.chargepoint.control_parameter import control_parameter_factory from control.chargepoint.charging_type import ChargingType from control.chargepoint.rfid import ChargepointRfidMixin +from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev from control import phase_switch from control.chargepoint.chargepoint_state import CHARGING_STATES, ChargepointState +from control.text import BidiState +from helpermodules.broker import BrokerClient from helpermodules.phase_mapping import convert_single_evu_phase_to_cp_phase from helpermodules.pub import Pub from helpermodules import timecheck from helpermodules.utils import thread_handler +from modules.chargepoints.openwb_pro.chargepoint_module import EvseSignaling from modules.common.abstract_chargepoint import AbstractChargepoint from helpermodules.timecheck import check_timestamp, create_timestamp @@ -63,13 +68,15 @@ def get_chargepoint_get_default() -> Dict: class Chargepoint(ChargepointRfidMixin): """ geht alle Ladepunkte durch, prüft, ob geladen werden darf und ruft die Funktion des angesteckten Autos auf. """ - MAX_FAILED_PHASE_SWITCHES = 3 + MAX_FAILED_PHASE_SWITCHES = 2 - def __init__(self, index: int, event: Optional[threading.Event]): + def __init__(self, index: int, event: Optional[Event]): try: self.template: CpTemplate = None self.chargepoint_module: AbstractChargepoint = None self.num = index + self.chargemode_changed = False + self.submode_changed = False # bestehende Daten auf dem Broker nicht zurücksetzen, daher nicht veröffentlichen self.data: ChargepointData = ChargepointData() self.data.set_event(event) @@ -106,22 +113,6 @@ def _is_grid_protection_inactive(self) -> Tuple[bool, Optional[str]]: message = "Ladepunkt gesperrt, da der Netzschutz aktiv ist." return state, message - def _is_ripple_control_receiver_inactive(self) -> Tuple[bool, Optional[str]]: - """ prüft, dass der Rundsteuerempfängerkontakt nicht geschlossen ist. - """ - state = True - message = None - general_data = data.data.general_data.data - if general_data.ripple_control_receiver.module: - if general_data.ripple_control_receiver.get.override_value == 0: - state = False - message = "Ladepunkt gesperrt, da der Rundsteuerempfängerkontakt geschlossen ist." - elif general_data.ripple_control_receiver.get.fault_state == 2: - state = False - message = ("Ladepunkt gesperrt, da der Rundsteuerempfänger ein Problem meldet. " - "Bitte im Status des RSE nachsehen.") - return state, message - def _is_loadmanagement_available(self) -> Tuple[bool, Optional[str]]: """ prüft, ob Lastmanagement verfügbar ist. Wenn keine Werte vom EVU-Zähler empfangen werden, darf nicht geladen werden. @@ -191,15 +182,13 @@ def is_charging_possible(self) -> Tuple[bool, Optional[str]]: try: charging_possible, message = self._is_grid_protection_inactive() if charging_possible: - charging_possible, message = self._is_ripple_control_receiver_inactive() + charging_possible, message = self._is_loadmanagement_available() if charging_possible: - charging_possible, message = self._is_loadmanagement_available() + charging_possible, message = self._is_manual_lock_inactive() if charging_possible: - charging_possible, message = self._is_manual_lock_inactive() + charging_possible, message = self._is_ev_plugged() if charging_possible: - charging_possible, message = self._is_ev_plugged() - if charging_possible: - charging_possible, message = self._is_autolock_inactive() + charging_possible, message = self._is_autolock_inactive() except Exception: log.exception("Fehler in der Ladepunkt-Klasse von "+str(self.num)) return False, "Keine Ladung, da ein interner Fehler aufgetreten ist: "+traceback.format_exc() @@ -226,7 +215,7 @@ def _process_charge_stop(self) -> None: if not self.data.get.plug_state: self.data.control_parameter = control_parameter_factory() # Standardprofil nach Abstecken laden - if data.data.ev_data["ev"+str(self.data.set.charging_ev_prev)].charge_template.data.load_default: + if self.data.set.charge_template.data.load_default: self.data.config.ev = 0 Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/config/ev", 0) # Ladepunkt nach Abstecken sperren @@ -235,6 +224,8 @@ def _process_charge_stop(self) -> None: Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/manual_lock", True) log.debug("/set/manual_lock True") # Ev wurde noch nicht aktualisiert. + # Ladeprofil aus den Einstellungen laden. + self.update_charge_template(data.data.ev_data["ev"+str(self.data.set.charging_ev_prev)].charge_template) chargelog.save_and_reset_data(self, data.data.ev_data["ev"+str(self.data.set.charging_ev_prev)]) self.data.set.charging_ev_prev = -1 Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charging_ev_prev", @@ -257,16 +248,7 @@ def setup_values_at_start(self): self._reset_values_at_start() self._set_values_at_start() - def reset_control_parameter(self): - """ setzt alle Werte zurück, die während des Algorithmus gesetzt werden. - """ - try: - log.debug(f"ControlParameter an LP {self.num} zurückgesetzt.") - self.data.control_parameter = ControlParameter() - except Exception: - log.exception("Fehler im LP-Modul "+str(self.num)) - - def set_control_parameter(self, submode: str, required_current: float): + def set_control_parameter(self, submode: str): """ setzt die Regel-Parameter, die der Algorithmus verwendet. Parameter @@ -280,9 +262,8 @@ def set_control_parameter(self, submode: str, required_current: float): self.data.control_parameter.chargemode = Chargemode.TIME_CHARGING else: self.data.control_parameter.chargemode = Chargemode( - self.data.set.charging_ev_data.charge_template.data.chargemode.selected) - self.data.control_parameter.prio = self.data.set.charging_ev_data.charge_template.data.prio - self.data.control_parameter.required_current = required_current + self.data.set.charge_template.data.chargemode.selected) + self.data.control_parameter.prio = self.data.set.charge_template.data.prio if self.template.data.charging_type == ChargingType.AC.value: self.data.control_parameter.min_current = self.data.set.charging_ev_data.ev_template.data.min_current else: @@ -300,10 +281,14 @@ def _set_values_at_start(self): self.data.set.plug_time) def remember_previous_values(self): + self.data.set.charge_state_prev = self.data.get.charge_state self.data.set.plug_state_prev = self.data.get.plug_state self.data.set.current_prev = self.data.set.current + self.data.set.ev_prev = self.data.config.ev + Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charge_state_prev", self.data.set.charge_state_prev) Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/plug_state_prev", self.data.set.plug_state_prev) Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/current_prev", self.data.set.current_prev) + Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/ev_prev", self.data.set.ev_prev) def reset_log_data_chargemode_switch(self) -> None: reset_log = Log() @@ -322,8 +307,6 @@ def reset_control_parameter_at_charge_stop(self) -> None: # Wenn die Ladung zB wegen Autolock gestoppt wird, Zählerstände beibehalten, damit nicht nochmal die Ladung # gestartet wird. control_parameter = control_parameter_factory() - control_parameter.imported_at_plan_start = self.data.control_parameter.imported_at_plan_start - control_parameter.imported_instant_charging = self.data.control_parameter.imported_instant_charging self.data.control_parameter = control_parameter def initiate_control_pilot_interruption(self): @@ -337,7 +320,7 @@ def initiate_control_pilot_interruption(self): # Wird die Ladung gestartet? if self.data.set.current_prev == 0 and self.data.set.current != 0: # Die CP-Unterbrechung erfolgt in Threads, da diese länger als ein Zyklus dauert. - if thread_handler(threading.Thread( + if thread_handler(Thread( target=self.chargepoint_module.interrupt_cp, args=(charging_ev.ev_template.data.control_pilot_interruption_duration,), name=f"cp{self.chargepoint_module.config.id}")): @@ -355,28 +338,33 @@ def check_deviating_contactor_states(self, phase_a: int, phase_b: int) -> bool: def _is_phase_switch_required(self) -> bool: phase_switch_required = False + if self.data.get.evse_signaling == EvseSignaling.HLC: + return False + if (self.data.control_parameter.state == ChargepointState.WAIT_FOR_USING_PHASES and + (self.data.set.current != 0 and self.data.set.current_prev != 0)): + phase_switch_required = False # Manche EVs brauchen nach der Umschaltung mehrere Zyklen, bis sie mit den drei Phasen laden. Dann darf # nicht zwischendurch eine neue Umschaltung getriggert werden. - if ((((self.data.control_parameter.state == ChargepointState.PHASE_SWITCH_AWAITED or + elif ((((self.data.control_parameter.state == ChargepointState.PHASE_SWITCH_AWAITED or self.data.control_parameter.state == ChargepointState.SWITCH_OFF_DELAY) and # Nach Ablauf der Laden aktiv halten Zeit, sollte mit der vorgegebenen Phasenzahl geladen werden. - self.check_deviating_contactor_states(self.data.set.phases_to_use, self.data.get.phases_in_use)) or + self.check_deviating_contactor_states(self.data.set.phases_to_use, self.data.get.phases_in_use)) or # Vorgegebene Phasenzahl hat sich geändert und es wird geladen - (self.check_deviating_contactor_states(self.data.set.phases_to_use, - self.data.control_parameter.phases) and + (self.check_deviating_contactor_states(self.data.set.phases_to_use, + self.data.control_parameter.phases) and self.data.control_parameter.state in CHARGING_STATES)) and # Wenn ein Soll-Strom vorgegeben ist, muss das Auto auch laden, damit umgeschaltet wird, sonst # wird zB bei automatischer Umschaltung ständig versucht auf 1 Phase zurück zu schalten, wenn # das Auto bei 3 Phasen voll ist. - ((self.data.set.current != 0 and self.data.get.charge_state) or - (self.data.set.current != 0 and self.data.set.current_prev == 0) or - self.data.set.current == 0)): + ((self.data.set.current != 0 and self.data.get.charge_state) or + (self.data.set.current != 0 and self.data.set.current_prev == 0) or + self.data.set.current == 0)): phase_switch_required = True - if (self.data.control_parameter.state == ChargepointState.NO_CHARGING_ALLOWED and - (self.check_deviating_contactor_states(self.data.set.phases_to_use, self.data.get.phases_in_use) or + elif (self.data.control_parameter.state == ChargepointState.NO_CHARGING_ALLOWED and + (self.check_deviating_contactor_states(self.data.set.phases_to_use, self.data.get.phases_in_use) or # Vorgegebene Phasenzahl hat sich geändert - self.check_deviating_contactor_states(self.data.set.phases_to_use, - self.data.control_parameter.phases)) and + self.check_deviating_contactor_states(self.data.set.phases_to_use, + self.data.control_parameter.phases)) and # Wenn der Ladevorgang gestartet wird, muss vor dem ersten Laden umgeschaltet werden. self.data.set.current != 0): phase_switch_required = True @@ -389,13 +377,16 @@ def _is_phase_switch_required(self) -> bool: self.set_state_and_log( "Keine Phasenumschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde. Die " "aktuelle Phasenzahl wird bis zum Abstecken beibehalten.") + self.data.control_parameter.failed_phase_switches += 1 else: - phase_switch_required = False - self.set_state_and_log( - "Keine Phasenumschaltung, da wiederholtes Anstoßen der Umschaltung in den übergreifenden " - "Ladeeinstellungen deaktiviert wurde. Die aktuelle " - "Phasenzahl wird bis zum Abstecken beibehalten.") - self.data.control_parameter.failed_phase_switches += 1 + # Umschaltung vor Ladestart zulassen + if self.data.set.log.imported_since_plugged != 0: + phase_switch_required = False + self.set_state_and_log( + "Keine Phasenumschaltung, da wiederholtes Anstoßen der Umschaltung in den übergreifenden " + "Ladeeinstellungen deaktiviert wurde. Die aktuelle " + "Phasenzahl wird bis zum Abstecken beibehalten.") + self.data.control_parameter.failed_phase_switches += 1 return phase_switch_required STOP_CHARGING = ", dafür wird die Ladung unterbrochen." @@ -437,11 +428,14 @@ def check_phase_switch_completed(self): self.set_state_and_log(message) return if self.data.control_parameter.state == ChargepointState.WAIT_FOR_USING_PHASES: - if (phase_switch.phase_switch_thread_alive(self.num) is False and - check_timestamp(self.data.control_parameter.timestamp_charge_start, - charging_ev.ev_template.data.keep_charge_active_duration) is False): - self.data.control_parameter.state = ChargepointState.PHASE_SWITCH_AWAITED - if self._is_phase_switch_required() is False: + if check_timestamp(self.data.control_parameter.timestamp_charge_start, + charging_ev.ev_template.data.keep_charge_active_duration) is False: + if self.cp_ev_support_phase_switch() and self.failed_phase_switches_reached(): + if phase_switch.phase_switch_thread_alive(self.num) is False: + self.data.control_parameter.state = ChargepointState.PHASE_SWITCH_AWAITED + if self._is_phase_switch_required() is False: + self.data.control_parameter.state = ChargepointState.CHARGING_ALLOWED + else: self.data.control_parameter.state = ChargepointState.CHARGING_ALLOWED except Exception: log.exception("Fehler in der Ladepunkt-Klasse von "+str(self.num)) @@ -450,6 +444,8 @@ def initiate_phase_switch(self): """prüft, ob eine Phasenumschaltung erforderlich ist und führt diese durch. """ try: + if self.data.get.evse_signaling == EvseSignaling.HLC: + return evu_counter = data.data.counter_all_data.get_evu_counter() charging_ev = self.data.set.charging_ev_data # Wenn noch kein Eintrag im Protokoll erstellt wurde, wurde noch nicht geladen und die Phase kann noch @@ -471,7 +467,8 @@ def initiate_phase_switch(self): log.debug( f"Lp {self.num}: Ladung aktiv halten " f"{charging_ev.ev_template.data.keep_charge_active_duration}s") - phase_switch.thread_phase_switch(self) + if phase_switch.thread_phase_switch(self) is False: + return log.debug("start phase switch phases_to_use " + str(self.data.set.phases_to_use) + " control_parameter phases " + @@ -504,18 +501,11 @@ def initiate_phase_switch(self): except Exception: log.exception("Fehler in der Ladepunkt-Klasse von "+str(self.num)) - def get_phases_by_selected_chargemode(self) -> int: + def get_phases_by_selected_chargemode(self, phases_chargemode: int) -> int: charging_ev = self.data.set.charging_ev_data - # Zeitladen kann nicht als Lademodus ausgewählt werden. Ob Zeitladen aktiv ist, lässt sich aus dem Submode - # erkennen. - if self.data.control_parameter.submode == "time_charging": - mode = "time_charging" - else: - mode = charging_ev.charge_template.data.chargemode.selected - chargemode = data.data.general_data.get_phases_chargemode(mode, self.data.control_parameter.submode) - - if (chargemode is None or - (self.data.config.auto_phase_switch_hw is False and self.data.get.charge_state) or + if self.data.get.evse_signaling == EvseSignaling.HLC: + phases = self.data.get.phases_in_use + elif ((self.data.config.auto_phase_switch_hw is False and self.data.get.charge_state) or self.data.control_parameter.failed_phase_switches > self.MAX_FAILED_PHASE_SWITCHES): # Wenn keine Umschaltung verbaut ist, die Phasenzahl nehmen, mit der geladen wird. Damit werden zB auch # einphasige EV an dreiphasigen openWBs korrekt berücksichtigt. @@ -523,7 +513,7 @@ def get_phases_by_selected_chargemode(self) -> int: elif self.data.control_parameter.state == ChargepointState.PERFORMING_PHASE_SWITCH: phases = self.data.set.phases_to_use log.debug(f"Umschaltung wird durchgeführt, Phasenzahl nicht ändern {phases}") - elif chargemode == 0: + elif phases_chargemode == 0: # Wenn die Lademodus-Phasen 0 sind, wird die bisher genutzte Phasenzahl weiter genutzt, # bis der Algorithmus eine Umschaltung vorgibt, zB weil der gewählte Lademodus eine # andere Phasenzahl benötigt oder bei PV-Laden die automatische Umschaltung aktiv ist. @@ -542,10 +532,10 @@ def get_phases_by_selected_chargemode(self) -> int: phases = self.data.config.connected_phases log.debug(f"Phasenzahl Lademodus: {phases}") else: - if chargemode == 0: + if phases_chargemode == 0: phases = self.data.control_parameter.phases else: - phases = chargemode + phases = phases_chargemode return phases def get_max_phase_hw(self) -> int: @@ -559,6 +549,16 @@ def get_max_phase_hw(self) -> int: log.debug(f"Anzahl angeschlossener Phasen beschränkt die nutzbaren Phasen auf {phases}") return phases + def hw_bidi_capable(self) -> BidiState: + if self.data.get.evse_signaling is None: + return BidiState.CP_NOT_BIDI_CAPABLE + elif self.data.get.evse_signaling != "HLC": + return BidiState.CP_WRONG_PROTOCOL + elif self.data.set.charging_ev_data.ev_template.data.bidi is False: + return BidiState.EV_NOT_BIDI_CAPABLE + else: + return BidiState.BIDI_CAPABLE + def set_phases(self, phases: int) -> int: charging_ev = self.data.set.charging_ev_data @@ -581,28 +581,47 @@ def set_phases(self, phases: int) -> int: self.data.control_parameter.phases = phases return phases - def check_min_max_current(self, required_current: float, phases: int, pv: bool = False) -> float: - required_current_prev = required_current - required_current, msg = self.data.set.charging_ev_data.check_min_max_current( - self.data.control_parameter, - required_current, - phases, - self.template.data.charging_type, - pv) + def check_cp_max_current(self, required_current: float, phases: int) -> float: + sign = 1 if required_current >= 0 else -1 + abs_current = abs(required_current) if self.template.data.charging_type == ChargingType.AC.value: if phases == 1: - required_current = min(required_current, self.template.data.max_current_single_phase) + abs_current = min(abs_current, self.template.data.max_current_single_phase) else: - required_current = min(required_current, self.template.data.max_current_multi_phases) + abs_current = min(abs_current, self.template.data.max_current_multi_phases) else: - required_current = min(required_current, self.template.data.dc_max_current) + abs_current = min(abs_current, self.template.data.dc_max_current) + return sign * abs_current + + def check_min_max_current(self, required_current: float, phases: int) -> float: + required_current_prev = required_current + msg = None + if self.data.control_parameter.submode == Chargemode.BIDI_CHARGING: + if required_current < 0: + if self.data.get.max_discharge_power / phases / 230 > required_current: + required_current = self.data.get.max_discharge_power / phases / 230 + msg = f"Die vom Auto übertragene Entladeleistung begrenzt den Strom auf " \ + f"maximal {round(required_current, 2)} A." + else: + if self.data.get.max_charge_power / phases / 230 < required_current: + required_current = self.data.get.max_charge_power / phases / 230 + msg = f"Die vom Auto übertragene Ladeleistung begrenzt den Strom auf " \ + f"maximal {round(required_current, 2)} A." + required_current = self.check_cp_max_current(required_current, phases) + else: + required_current, msg = self.data.set.charging_ev_data.check_min_max_current( + required_current, + phases, + self.template.data.charging_type) + required_current = self.check_cp_max_current(required_current, phases) if required_current != required_current_prev and msg is None: msg = ("Die Einstellungen in dem Ladepunkt-Profil beschränken den Strom auf " - f"maximal {required_current} A.") + f"maximal {round(required_current, 2)} A.") self.set_state_and_log(msg) return required_current def set_required_currents(self, required_current: float) -> None: + self.data.control_parameter.required_current = required_current control_parameter = self.data.control_parameter try: for i in range(0, control_parameter.phases): @@ -613,7 +632,8 @@ def set_required_currents(self, required_current: float) -> None: self.set_state_and_log("Bitte in den Ladepunkt-Einstellungen die Einstellung 'Phase 1 des Ladekabels'" + " angeben. Andernfalls wird der benötigte Strom auf allen 3 Phasen vorgehalten, " + "was ggf eine unnötige Reduktion der Ladeleistung zur Folge hat.") - self.data.set.required_power = sum(control_parameter.required_currents) * 230 + self.data.set.required_power = sum( + [c * v for c, v in zip(control_parameter.required_currents, self.data.get.voltages)]) def set_timestamp_charge_start(self): # Beim Ladestart Timer laufen lassen, manche Fahrzeuge brauchen sehr lange. @@ -624,6 +644,19 @@ def set_timestamp_charge_start(self): elif self.data.set.current == 0: self.data.control_parameter.timestamp_charge_start = None + def set_chargemode_changed(self, submode: str) -> None: + if ((submode == "time_charging" and self.data.control_parameter.chargemode != "time_charging") or + (submode != "time_charging" and + self.data.control_parameter.chargemode != self.data.set.charge_template.data.chargemode.selected)): + self.chargemode_changed = True + log.debug("Änderung des Lademodus") + self.data.control_parameter.timestamp_chargemode_changed = create_timestamp() + else: + self.chargemode_changed = False + + def set_submode_changed(self, submode: str) -> None: + self.submode_changed = (submode != self.data.control_parameter.submode) + def update_ev(self, ev_list: Dict[str, Ev]) -> None: self._validate_rfid() charging_possible = self.is_charging_possible()[0] @@ -636,6 +669,8 @@ def update_ev(self, ev_list: Dict[str, Ev]) -> None: else: vehicle = -1 self._pub_configured_ev(ev_list) + if self.data.config.ev != self.data.set.ev_prev: + self.update_charge_template(ev_list[f"ev{self.data.config.ev}"].charge_template) def update(self, ev_list: Dict[str, Ev]) -> None: try: @@ -652,36 +687,37 @@ def update(self, ev_list: Dict[str, Ev]) -> None: if charging_possible: try: charging_ev = self._get_charging_ev(vehicle, ev_list) - max_phase_hw = self.get_max_phase_hw() - self.data.control_parameter.phases = min( - self.get_phases_by_selected_chargemode(), max_phase_hw) state, message_ev, submode, required_current, phases = charging_ev.get_required_current( + self.data.set.charge_template, self.data.control_parameter, - self.data.get.imported, - max_phase_hw, + self.get_max_phase_hw(), self.cp_ev_support_phase_switch(), - self.template.data.charging_type) + self.template.data.charging_type, + self.data.control_parameter.timestamp_chargemode_changed or create_timestamp(), + self.data.set.log.imported_since_plugged, + self.hw_bidi_capable(), + self.data.get.phases_in_use) + phases = self.get_phases_by_selected_chargemode(phases) phases = self.set_phases(phases) self._pub_connected_vehicle(charging_ev) required_current = self.chargepoint_module.add_conversion_loss_to_current(required_current) + self.set_chargemode_changed(submode) + self.set_submode_changed(submode) + self.set_control_parameter(submode) # Einhaltung des Minimal- und Maximalstroms prüfen - required_current = self.check_min_max_current( - required_current, self.data.control_parameter.phases) + required_current = self.check_min_max_current(required_current, self.data.control_parameter.phases) required_current = self.chargepoint_module.add_conversion_loss_to_current(required_current) - charging_ev.set_chargemode_changed(self.data.control_parameter, submode) - charging_ev.set_submode_changed(self.data.control_parameter, submode) - self.set_control_parameter(submode, required_current) self.set_required_currents(required_current) self.check_phase_switch_completed() - if charging_ev.chargemode_changed or charging_ev.submode_changed: + if self.chargemode_changed or self.submode_changed: data.data.counter_all_data.get_evu_counter().reset_switch_on_off( self, charging_ev) charging_ev.reset_phase_switch(self.data.control_parameter) message = message_ev if message_ev else message # Ein Eintrag muss nur erstellt werden, wenn vorher schon geladen wurde und auch danach noch # geladen werden soll. - if charging_ev.chargemode_changed and self.data.set.log.imported_since_mode_switch != 0 and state: + if self.chargemode_changed and self.data.set.log.imported_since_mode_switch != 0 and state: chargelog.save_interim_data(self, charging_ev) # Wenn die Nachrichten gesendet wurden, EV wieder löschen, wenn das EV im Algorithmus nicht @@ -697,7 +733,7 @@ def update(self, ev_list: Dict[str, Ev]) -> None: str(self.num)+"/set/charging_ev", -1) log.debug(f'LP {self.num}, EV: {self.data.set.charging_ev_data.data.name}' f' (EV-Nr.{vehicle}): Lademodus ' - f'{charging_ev.charge_template.data.chargemode.selected}, Submodus: ' + f'{self.data.set.charge_template.data.chargemode.selected}, Submodus: ' f'{self.data.control_parameter.submode}') else: if (self.data.control_parameter.state == ChargepointState.SWITCH_ON_DELAY and @@ -707,11 +743,11 @@ def update(self, ev_list: Dict[str, Ev]) -> None: log.info( f"LP {self.num}, EV: {self.data.set.charging_ev_data.data.name} (EV-Nr.{vehicle}): " f"Theoretisch benötigter Strom {required_current}A, Lademodus " - f"{charging_ev.charge_template.data.chargemode.selected}, Submodus: " + f"{self.data.set.charge_template.data.chargemode.selected}, Submodus: " f"{self.data.control_parameter.submode}, Phasen: " f"{self.data.control_parameter.phases}" - f", Priorität: {charging_ev.charge_template.data.prio}" - f", max. Ist-Strom: {max(self.data.get.currents)}") + f", Priorität: {self.data.control_parameter.prio}" + f", mittlerer Ist-Strom: {get_medium_charging_current(self.data.get.currents)}") except Exception: log.exception("Fehler im Prepare-Modul für Ladepunkt "+str(self.num)) self.data.control_parameter.submode = "stop" @@ -719,8 +755,21 @@ def update(self, ev_list: Dict[str, Ev]) -> None: self._process_charge_stop() if vehicle != -1: self._pub_connected_vehicle(ev_list[f"ev{vehicle}"]) + if self.data.set.charge_template.data.id != ev_list[f"ev{vehicle}"].charge_template.data.id: + self.update_charge_template(ev_list[f"ev{vehicle}"].charge_template) else: self._pub_configured_ev(ev_list) + if self.data.set.charge_template.data.id != ev_list[ + f"ev{self.data.config.ev}"].charge_template.data.id: + self.update_charge_template(ev_list[f"ev{self.data.config.ev}"].charge_template) + try: + # check für charging stop or charging interruption, if so force a soc query for the ev + if self.data.set.charge_state_prev and self.data.get.charge_state is False: + Pub().pub(f"openWB/set/vehicle/{self.data.config.ev}/get/force_soc_update", True) + log.info(f"SoC-Abfrage nach Ladeunterbrechung, cp{self.num}, ev{self.data.config.ev}") + except Exception: + log.exception(f"Fehler bei Ladestop,cp{self.num}") + # OCPP Start Transaction nach Anstecken if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or (self.data.set.ocpp_transaction_id is None and self.data.get.charge_state)): @@ -732,6 +781,8 @@ def update(self, ev_list: Dict[str, Ev]) -> None: self.data.get.imported) Pub().pub("openWB/set/chargepoint/"+str(self.num) + "/set/ocpp_transaction_id", self.data.set.ocpp_transaction_id) + if self.data.get.plug_state and self.data.set.plug_state_prev is False: + self.data.control_parameter.timestamp_chargemode_changed = create_timestamp() # SoC nach Anstecken aktualisieren if ((self.data.get.plug_state and self.data.set.plug_state_prev is False) or (self.data.get.plug_state is False and self.data.set.plug_state_prev) or @@ -760,9 +811,12 @@ def _get_charging_ev(self, vehicle: int, ev_list: Dict[str, Ev]) -> Ev: " verwendet.") charging_ev = ev_list["ev0"] vehicle = 0 - if self.data.set.charging_ev != vehicle and self.data.set.charging_ev_prev != vehicle: + if self.data.set.charging_ev_prev != vehicle: Pub().pub(f"openWB/set/vehicle/{charging_ev.num}/get/force_soc_update", True) log.debug("SoC nach EV-Wechsel") + self.update_charge_template(charging_ev.charge_template) + if self.data.set.charge_template.data.id != charging_ev.charge_template.data.id: + self.update_charge_template(charging_ev.charge_template) self.data.set.charging_ev_data = charging_ev self.data.set.charging_ev = vehicle Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charging_ev", vehicle) @@ -770,6 +824,21 @@ def _get_charging_ev(self, vehicle: int, ev_list: Dict[str, Ev]) -> Ev: Pub().pub("openWB/set/chargepoint/"+str(self.num)+"/set/charging_ev_prev", vehicle) return charging_ev + def _clear_template_topics(self, topic: str) -> None: + def on_connect(client, userdata, flags, rc): + client.subscribe(topic, 2) + + def __get_payload(client, userdata, msg): + received_topics.append(msg.topic) + received_topics = [] + BrokerClient("processBrokerBranch", on_connect, __get_payload).start_finite_loop() + for topic in received_topics: + Pub().pub(topic, "") + + def update_charge_template(self, charge_template: ChargeTemplate) -> None: + Pub().pub(f"openWB/set/chargepoint/{self.num}/set/charge_template", + dataclasses.asdict(charge_template.data)) + def _pub_connected_vehicle(self, vehicle: Ev): """ published die Daten, die zur Anzeige auf der Hauptseite benötigt werden. @@ -793,57 +862,70 @@ def _pub_connected_vehicle(self, vehicle: Ev): soc_obj.range = vehicle.data.get.range info_obj = ConnectedInfo(id=vehicle.num, name=vehicle.data.name) - if (vehicle.charge_template.data.chargemode.selected == "time_charging" or - vehicle.charge_template.data.chargemode.selected == "scheduled_charging"): + if (self.data.set.charge_template.data.chargemode.selected == "time_charging" or + self.data.set.charge_template.data.chargemode.selected == "scheduled_charging"): current_plan = self.data.control_parameter.current_plan else: current_plan = None config_obj = ConnectedConfig( - charge_template=vehicle.charge_template.ct_num, - ev_template=vehicle.ev_template.et_num, - chargemode=vehicle.charge_template.data.chargemode.selected, - priority=vehicle.charge_template.data.prio, + charge_template=self.data.set.charge_template.data.id, + ev_template=vehicle.ev_template.data.id, + chargemode=self.data.set.charge_template.data.chargemode.selected, + priority=self.data.set.charge_template.data.prio, current_plan=current_plan, average_consumption=vehicle.ev_template.data.average_consump, time_charging_in_use=True if (self.data.control_parameter.submode == "time_charging") else False) if soc_obj != self.data.get.connected_vehicle.soc: - Pub().pub("openWB/chargepoint/"+str(self.num) + - "/get/connected_vehicle/soc", dataclasses.asdict(soc_obj)) + Pub().pub(f"openWB/chargepoint/{self.num}/get/connected_vehicle/soc", dataclasses.asdict(soc_obj)) if info_obj != self.data.get.connected_vehicle.info: - Pub().pub("openWB/chargepoint/"+str(self.num) + - "/get/connected_vehicle/info", dataclasses.asdict(info_obj)) + Pub().pub(f"openWB/chargepoint/{self.num}/get/connected_vehicle/info", dataclasses.asdict(info_obj)) if config_obj != self.data.get.connected_vehicle.config: - Pub().pub("openWB/chargepoint/"+str(self.num) + - "/get/connected_vehicle/config", dataclasses.asdict(config_obj)) + Pub().pub(f"openWB/chargepoint/{self.num}/get/connected_vehicle/config", + dataclasses.asdict(config_obj)) except Exception: log.exception("Fehler im Prepare-Modul") def cp_ev_chargemode_support_phase_switch(self) -> bool: + if (self.cp_ev_support_phase_switch() and + self.data.get.charge_state and + self.chargemode_support_phase_switch() and + (self.data.control_parameter.state == ChargepointState.CHARGING_ALLOWED or + self.data.control_parameter.state == ChargepointState.PHASE_SWITCH_DELAY)): + return self.failed_phase_switches_reached() + else: + return False + + def cp_ev_support_phase_switch(self) -> bool: + return (self.data.config.auto_phase_switch_hw and + self.data.get.evse_signaling != EvseSignaling.HLC and + self.data.set.charging_ev_data.ev_template.data.prevent_phase_switch is False) + + def chargemode_support_phase_switch(self) -> bool: control_parameter = self.data.control_parameter - pv_auto_switch = (control_parameter.chargemode == Chargemode.PV_CHARGING and - data.data.general_data.get_phases_chargemode( - Chargemode.PV_CHARGING.value, - control_parameter.submode) == 0) + pv_auto_switch = ((control_parameter.chargemode == Chargemode.PV_CHARGING or + control_parameter.chargemode == Chargemode.ECO_CHARGING) and + control_parameter.submode == Chargemode.PV_CHARGING and + self.data.set.charge_template.data.chargemode.pv_charging.phases_to_use == 0) + for p in self.data.set.charge_template.data.chargemode.scheduled_charging.plans: + if p.id == self.data.control_parameter.current_plan: + phases_to_use_pv = p.phases_to_use_pv + break + else: + phases_to_use_pv = 1 scheduled_auto_switch = ( control_parameter.chargemode == Chargemode.SCHEDULED_CHARGING and control_parameter.submode == Chargemode.PV_CHARGING and - data.data.general_data.get_phases_chargemode(Chargemode.SCHEDULED_CHARGING.value, - control_parameter.submode) == 0) + phases_to_use_pv == 0) + return (pv_auto_switch or scheduled_auto_switch) + + def failed_phase_switches_reached(self) -> bool: if ((data.data.general_data.data.chargemode_config.retry_failed_phase_switches and - self.data.control_parameter.failed_phase_switches > self.MAX_FAILED_PHASE_SWITCHES) or - (data.data.general_data.data.chargemode_config.retry_failed_phase_switches is False and - self.data.control_parameter.failed_phase_switches == 1)): - failed_phase_switches_reached = True + self.data.control_parameter.failed_phase_switches > self.MAX_FAILED_PHASE_SWITCHES) or + (data.data.general_data.data.chargemode_config.retry_failed_phase_switches is False and + self.data.control_parameter.failed_phase_switches == 1)): + self.set_state_and_log( + "Keine automatische Umschaltung, da die maximale Anzahl an Fehlversuchen erreicht wurde. ") + return False else: - failed_phase_switches_reached = False - return (self.cp_ev_support_phase_switch() and - self.data.get.charge_state and - (pv_auto_switch or scheduled_auto_switch) and - (control_parameter.state == ChargepointState.CHARGING_ALLOWED or - control_parameter.state == ChargepointState.PHASE_SWITCH_DELAY) and - failed_phase_switches_reached is False) - - def cp_ev_support_phase_switch(self) -> bool: - return (self.data.config.auto_phase_switch_hw and - self.data.set.charging_ev_data.ev_template.data.prevent_phase_switch is False) + return True diff --git a/packages/control/chargepoint/chargepoint_all.py b/packages/control/chargepoint/chargepoint_all.py index d2fd2e5075..219227b039 100644 --- a/packages/control/chargepoint/chargepoint_all.py +++ b/packages/control/chargepoint/chargepoint_all.py @@ -69,13 +69,14 @@ def get_cp_sum(self): """ imported, exported, power = 0, 0, 0 try: - for cp in data.data.cp_data: + for cp in data.data.cp_data.values(): + try: + imported = imported + cp.data.get.imported + exported = exported + cp.data.get.exported + except Exception: + log.exception("Fehler in der allgemeinen Ladepunkt-Klasse für Ladepunkt "+cp) try: - if "cp" in cp: - chargepoint = data.data.cp_data[cp] - power = power + chargepoint.data.get.power - imported = imported + chargepoint.data.get.imported - exported = exported + chargepoint.data.get.exported + power = power + cp.data.get.power except Exception: log.exception("Fehler in der allgemeinen Ladepunkt-Klasse für Ladepunkt "+cp) self.data.get.power = power diff --git a/packages/control/chargepoint/chargepoint_data.py b/packages/control/chargepoint/chargepoint_data.py index 9d2f9bb02c..4d433a8205 100644 --- a/packages/control/chargepoint/chargepoint_data.py +++ b/packages/control/chargepoint/chargepoint_data.py @@ -1,12 +1,14 @@ from dataclasses import dataclass, field -import threading +from threading import Event from typing import Dict, List, Optional, Protocol from control.chargepoint.chargepoint_template import CpTemplate from control.chargepoint.control_parameter import ControlParameter, control_parameter_factory +from control.ev.charge_template import ChargeTemplate from control.ev.ev import Ev from dataclass_utils.factories import currents_list_factory, empty_dict_factory, voltages_list_factory from helpermodules.constants import NO_ERROR +from modules.chargepoints.openwb_pro.chargepoint_module import EvseSignaling from modules.common.abstract_chargepoint import AbstractChargepoint @@ -94,19 +96,26 @@ class Get: charging_power: Optional[float] = 0 charging_voltage: Optional[float] = 0 connected_vehicle: ConnectedVehicle = field(default_factory=connected_vehicle_factory) + current_branch: Optional[str] = None + current_commit: Optional[str] = None currents: List[float] = field(default_factory=currents_list_factory) daily_imported: float = 0 daily_exported: float = 0 error_timestamp: int = 0 evse_current: Optional[float] = None + # kann auch zur Laufzeit geändert werden + evse_signaling: Optional[EvseSignaling] = None exported: float = 0 fault_str: str = NO_ERROR fault_state: int = 0 imported: float = 0 + max_charge_power: Optional[float] = None + max_discharge_power: Optional[float] = None max_evse_current: Optional[int] = None phases_in_use: int = 0 plug_state: bool = False power: float = 0 + powers: List[float] = field(default_factory=currents_list_factory) rfid_timestamp: Optional[float] = None rfid: Optional[int] = None serial_number: Optional[str] = None @@ -114,9 +123,14 @@ class Get: soc_timestamp: Optional[int] = None state_str: Optional[str] = None vehicle_id: Optional[str] = None + version: Optional[str] = None voltages: List[float] = field(default_factory=voltages_list_factory) +def charge_template_factory() -> ChargeTemplate: + return ChargeTemplate() + + def ev_factory() -> Ev: return Ev(0) @@ -129,8 +143,10 @@ def log_factory() -> Log: class Set: charging_ev: int = -1 charging_ev_prev: int = -1 + charge_template: ChargeTemplate = field(default_factory=charge_template_factory) current: float = 0 energy_to_charge: float = 0 + ev_prev: int = 0 loadmanagement_available: bool = True log: Log = field(default_factory=log_factory) manual_lock: bool = False @@ -145,6 +161,7 @@ class Set: target_current: float = 0 # Soll-Strom aus fest vorgegebener Stromstärke charging_ev_data: Ev = field(default_factory=ev_factory) ocpp_transaction_id: Optional[int] = None + charge_state_prev: bool = False @dataclass @@ -162,7 +179,7 @@ class Config: ocpp_chargebox_id: Optional[str] = None def __post_init__(self): - self.event_update_state: threading.Event + self.event_update_state: Event @property def ev(self) -> int: @@ -205,7 +222,7 @@ class ChargepointData: set: Set = field(default_factory=set_factory) config: Config = field(default_factory=config_factory) - def set_event(self, event: Optional[threading.Event] = None) -> None: + def set_event(self, event: Optional[Event] = None) -> None: self.event_update_state = event if event: self.config.event_update_state = event diff --git a/packages/control/chargepoint/chargepoint_state_update.py b/packages/control/chargepoint/chargepoint_state_update.py index 0f6c608189..2cdc4e2e48 100644 --- a/packages/control/chargepoint/chargepoint_state_update.py +++ b/packages/control/chargepoint/chargepoint_state_update.py @@ -1,7 +1,6 @@ import copy import logging -from threading import Thread -import threading +from threading import Thread, Event from typing import Dict from control.chargepoint.chargepoint import Chargepoint @@ -14,13 +13,13 @@ class ChargepointStateUpdate: def __init__(self, index: int, - event_copy_data: threading.Event, - event_global_data_initialized: threading.Event, + event_copy_data: Event, + event_global_data_initialized: Event, cp_template_data: Dict, ev_data: Dict, ev_charge_template_data: Dict, ev_template_data: Dict) -> None: - self.event_update_state = threading.Event() + self.event_update_state = Event() self.event_copy_data = event_copy_data self.event_global_data_initialized = event_global_data_initialized self.chargepoint: Chargepoint = Chargepoint(index, self.event_update_state) diff --git a/packages/control/chargepoint/chargepoint_template.py b/packages/control/chargepoint/chargepoint_template.py index 1e70a34af4..f028416714 100644 --- a/packages/control/chargepoint/chargepoint_template.py +++ b/packages/control/chargepoint/chargepoint_template.py @@ -1,12 +1,12 @@ from dataclasses import asdict, dataclass, field import logging import traceback -from typing import Dict, List +from typing import List from control import data from control.ev import ev as ev_module from control.chargepoint.charging_type import ChargingType -from dataclass_utils.factories import empty_dict_factory, empty_list_factory +from dataclass_utils.factories import empty_list_factory from helpermodules.abstract_plans import AutolockPlan from helpermodules import timecheck @@ -16,18 +16,13 @@ def get_chargepoint_template_default(): default = asdict(CpTemplateData()) - default["autolock"].pop("plans") return default -def get_autolock_plan_default(): - return asdict(AutolockPlan()) - - @dataclass class Autolock: active: bool = False - plans: Dict[int, AutolockPlan] = field(default_factory=empty_dict_factory) + plans: List[AutolockPlan] = field(default_factory=empty_list_factory) wait_for_charging_end: bool = False diff --git a/packages/control/chargepoint/chargepoint_test.py b/packages/control/chargepoint/chargepoint_test.py index a976fb547c..4bdac31c1d 100644 --- a/packages/control/chargepoint/chargepoint_test.py +++ b/packages/control/chargepoint/chargepoint_test.py @@ -1,8 +1,11 @@ +from dataclasses import dataclass from typing import List from unittest.mock import Mock import pytest +from control import data from control.chargepoint.chargepoint import Chargepoint +from control.chargepoint.chargepoint_state import ChargepointState from control.chargepoint.chargepoint_template import CpTemplate from control.ev.ev import Ev @@ -49,3 +52,93 @@ def test_check_min_max_current(required_current, phases, expected_required_curre # assertion assert ret == expected_required_current + + +@dataclass +class Params: + name: str + state: ChargepointState = ChargepointState.CHARGING_ALLOWED + set_current: float = 0 + set_current_prev: float = 0 + phases_to_use: int = 1 + phases_in_use: int = 1 + control_parameter_phases: int = 1 + charge_state: bool = True + failed_phase_switches: int = 0 + retry_failed_phase_switches: bool = False + phase_switch_required: bool = False + + +params = [ + # PV-Laden + Params( + name="Wartezeit", + state=ChargepointState.SWITCH_ON_DELAY, + charge_state=False, + phase_switch_required=False + ), + Params( + name="Einschalten nach Wartzeit, Umschaltung erforderlich", + state=ChargepointState.WAIT_FOR_USING_PHASES, + phases_to_use=3, + phases_in_use=3, + control_parameter_phases=1, + set_current=6, + charge_state=False, + phase_switch_required=True + ), + Params( + name="Einschalten nach Wartzeit, keine Umschaltung erforderlich", + state=ChargepointState.WAIT_FOR_USING_PHASES, + phases_to_use=3, + phases_in_use=3, + control_parameter_phases=3, + set_current=6, + charge_state=False, + phase_switch_required=False + ), + Params( + name="keine Umschaltung während Warten auf Phasennutzung", + state=ChargepointState.WAIT_FOR_USING_PHASES, + phases_to_use=1, + phases_in_use=3, + control_parameter_phases=1, + set_current=6, + set_current_prev=6, + charge_state=True, + phase_switch_required=False + ), + Params( + name="Umschaltung, wenn sich während der Ladung die Phasenvorgabe ändert", + state=ChargepointState.CHARGING_ALLOWED, + phases_to_use=3, + phases_in_use=3, + control_parameter_phases=1, + set_current=6, + set_current_prev=6, + charge_state=True, + phase_switch_required=True + ), +] + + +@pytest.mark.parametrize("params", params, ids=[p.name for p in params]) +def test_is_phase_switch_required(params: Params): + # setup + cp = Chargepoint(0, None) + cp.data.control_parameter.state = params.state + cp.data.set.current = params.set_current + cp.data.set.current_prev = params.set_current_prev + cp.data.set.phases_to_use = params.phases_to_use + cp.data.get.phases_in_use = params.phases_in_use + cp.data.control_parameter.phases = params.control_parameter_phases + cp.data.get.charge_state = params.charge_state + cp.data.control_parameter.failed_phase_switches = params.failed_phase_switches + data.data_init(Mock()) + data.data.general_data.data.chargemode_config.retry_failed_phase_switches = params.retry_failed_phase_switches + + # evaluation + ret = cp._is_phase_switch_required() + + # assertion + assert ret == params.phase_switch_required diff --git a/packages/control/chargepoint/control_parameter.py b/packages/control/chargepoint/control_parameter.py index f419f4f012..e1bbe8c4a5 100644 --- a/packages/control/chargepoint/control_parameter.py +++ b/packages/control/chargepoint/control_parameter.py @@ -3,7 +3,7 @@ from control.chargepoint.chargepoint_state import ChargepointState from control.chargemode import Chargemode as Chargemode_enum -from control.limiting_value import LimitingValue +from control.limiting_value import LoadmanagementLimit, loadmanagement_limit_factory from dataclass_utils.factories import currents_list_factory @@ -13,11 +13,8 @@ class ControlParameter: "topic": "control_parameter/chargemode"}) current_plan: Optional[str] = field(default=None, metadata={"topic": "control_parameter/current_plan"}) failed_phase_switches: int = field(default=0, metadata={"topic": "control_parameter/failed_phase_switches"}) - imported_at_plan_start: Optional[float] = field( - default=None, metadata={"topic": "control_parameter/imported_at_plan_start"}) - imported_instant_charging: Optional[float] = field( - default=None, metadata={"topic": "control_parameter/imported_instant_charging"}) - limit: Optional[LimitingValue] = field(default=None, metadata={"topic": "control_parameter/limit"}) + limit: Optional[LoadmanagementLimit] = field(default_factory=loadmanagement_limit_factory, metadata={ + "topic": "control_parameter/limit"}) min_current: int = field(default=6, metadata={"topic": "control_parameter/min_current"}) phases: int = field(default=0, metadata={"topic": "control_parameter/phases"}) prio: bool = field(default=False, metadata={"topic": "control_parameter/prio"}) @@ -28,8 +25,12 @@ class ControlParameter: submode: Chargemode_enum = field(default=Chargemode_enum.STOP, metadata={"topic": "control_parameter/submode"}) timestamp_charge_start: Optional[float] = field( default=None, metadata={"topic": "control_parameter/timestamp_charge_start"}) + timestamp_chargemode_changed: Optional[float] = field( + default=None, metadata={"topic": "control_parameter/timestamp_chargemode_changed"}) timestamp_last_phase_switch: float = field( default=0, metadata={"topic": "control_parameter/timestamp_last_phase_switch"}) + timestamp_phase_switch_buffer_start: Optional[float] = field( + default=None, metadata={"topic": "control_parameter/timestamp_phase_switch_buffer_start"}) timestamp_switch_on_off: Optional[float] = field( default=None, metadata={"topic": "control_parameter/timestamp_switch_on_off"}) diff --git a/packages/control/chargepoint/get_phases_test.py b/packages/control/chargepoint/get_phases_test.py index 9dd39adead..555a0529bd 100644 --- a/packages/control/chargepoint/get_phases_test.py +++ b/packages/control/chargepoint/get_phases_test.py @@ -1,4 +1,4 @@ -import threading +from threading import Event from unittest.mock import Mock from typing import Optional import pytest @@ -21,7 +21,7 @@ def cp() -> Chargepoint: @pytest.fixture(autouse=True) def general() -> None: - data.data_init(threading.Event()) + data.data_init(Event()) data.data.general_data = General() @@ -81,11 +81,8 @@ def __init__(self, @pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) -def test_get_phases_by_selected_chargemode(monkeypatch, cp: Chargepoint, params: Params): +def test_get_phases_by_selected_chargemode(cp: Chargepoint, params: Params): # setup - mock_chargemode_phases = Mock(name="chargemode_phases", return_value=params.chargemode_phases) - monkeypatch.setattr(data.data.general_data, "get_phases_chargemode", mock_chargemode_phases) - cp.data.config.connected_phases = params.connected_phases cp.data.config.auto_phase_switch_hw = params.auto_phase_switch_hw cp.data.get.charge_state = params.charge_state @@ -98,7 +95,7 @@ def test_get_phases_by_selected_chargemode(monkeypatch, cp: Chargepoint, params: cp.data.control_parameter.phases = params.phases_in_use # execution - phases = cp.get_phases_by_selected_chargemode() + phases = cp.get_phases_by_selected_chargemode(params.chargemode_phases) # evaluation assert phases == params.expected_phases @@ -134,36 +131,36 @@ def __init__(self, prevent_phase_switch: bool, phases_in_use: int, imported_since_plugged: float, - phase_switch_suppported: bool, + phase_switch_supported: bool, expected_phases: int) -> None: self.name = name self.phases = phases self.prevent_phase_switch = prevent_phase_switch self.phases_in_use = phases_in_use self.imported_since_plugged = imported_since_plugged - self.phase_switch_suppported = phase_switch_suppported + self.phase_switch_supported = phase_switch_supported self.expected_phases = expected_phases cases_set_phases = [ SetPhasesParams(name="Phases don't change", phases=1, phases_in_use=1, prevent_phase_switch=True, - imported_since_plugged=0, phase_switch_suppported=True, expected_phases=1), + imported_since_plugged=0, phase_switch_supported=True, expected_phases=1), SetPhasesParams(name="Charging didn't started yet", phases=1, phases_in_use=3, prevent_phase_switch=True, - imported_since_plugged=0, phase_switch_suppported=True, expected_phases=1), + imported_since_plugged=0, phase_switch_supported=True, expected_phases=1), SetPhasesParams(name="EV doesn't support phase wich", phases=1, phases_in_use=3, prevent_phase_switch=True, - imported_since_plugged=1, phase_switch_suppported=True, expected_phases=3), + imported_since_plugged=1, phase_switch_supported=True, expected_phases=3), SetPhasesParams(name="Switch phases", phases=1, phases_in_use=3, prevent_phase_switch=False, - imported_since_plugged=1, phase_switch_suppported=True, expected_phases=1), + imported_since_plugged=1, phase_switch_supported=True, expected_phases=1), SetPhasesParams(name="Phase switch not supported by cp", phases=1, phases_in_use=3, prevent_phase_switch=False, - imported_since_plugged=1, phase_switch_suppported=False, expected_phases=1) + imported_since_plugged=1, phase_switch_supported=False, expected_phases=1) ] @pytest.mark.parametrize("params", cases_set_phases, ids=[c.name for c in cases_set_phases]) def test_set_phases(monkeypatch, cp: Chargepoint, params: SetPhasesParams): # setup - mock_phase_switch_suppported = Mock(name="phase_switch_suppported", return_value=params.phase_switch_suppported) - monkeypatch.setattr(Chargepoint, "cp_ev_support_phase_switch", mock_phase_switch_suppported) + mock_phase_switch_supported = Mock(name="phase_switch_supported", return_value=params.phase_switch_supported) + monkeypatch.setattr(Chargepoint, "cp_ev_support_phase_switch", mock_phase_switch_supported) cp.data.get.phases_in_use = params.phases_in_use cp.data.set.log.imported_since_plugged = params.imported_since_plugged charging_ev_data = cp.data.set.charging_ev_data diff --git a/packages/control/counter.py b/packages/control/counter.py index 80531f12a9..d82b106be1 100644 --- a/packages/control/counter.py +++ b/packages/control/counter.py @@ -7,6 +7,9 @@ from typing import List, Optional, Tuple from control import data +from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE +from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes +from control.algorithm.utils import get_medium_charging_current from control.chargemode import Chargemode from control.ev.ev import Ev from control.chargepoint.chargepoint import Chargepoint @@ -150,7 +153,7 @@ def _set_current_left(self, loadmanagement_available: bool) -> None: chargepoint.data.config.phase_1, chargepoint.data.get.currents) except KeyError: - element_current = [max(chargepoint.data.get.currents)]*3 + element_current = [get_medium_charging_current(chargepoint.data.get.currents)]*3 currents_raw = list(map(operator.sub, currents_raw, element_current)) currents_raw = list(map(operator.sub, self.data.config.max_currents, currents_raw)) if min(currents_raw) < 0: @@ -191,17 +194,17 @@ def _set_power_left(self, loadmanagement_available: bool) -> None: else: self.data.set.raw_power_left = None - def update_values_left(self, diffs) -> None: + def update_values_left(self, diffs, cp_voltages: List[float]) -> None: self.data.set.raw_currents_left = list(map(operator.sub, self.data.set.raw_currents_left, diffs)) if self.data.set.raw_power_left: - self.data.set.raw_power_left -= sum(diffs) * 230 + self.data.set.raw_power_left -= sum([c * v for c, v in zip(diffs, cp_voltages)]) log.debug(f'Zähler {self.num}: {self.data.set.raw_currents_left}A verbleibende Ströme, ' f'{self.data.set.raw_power_left}W verbleibende Leistung') - def update_surplus_values_left(self, diffs) -> None: + def update_surplus_values_left(self, diffs, cp_voltages: List[float]) -> None: self.data.set.raw_currents_left = list(map(operator.sub, self.data.set.raw_currents_left, diffs)) if self.data.set.surplus_power_left: - self.data.set.surplus_power_left -= sum(diffs) * 230 + self.data.set.surplus_power_left -= sum([c * v for c, v in zip(diffs, cp_voltages)]) log.debug(f'Zähler {self.num}: {self.data.set.raw_currents_left}A verbleibende Ströme, ' f'{self.data.set.surplus_power_left}W verbleibender Überschuss') @@ -253,19 +256,19 @@ def get_usable_surplus(self, feed_in_yield: float) -> float: return (-self.calc_surplus() - self.data.set.released_surplus + self.data.set.reserved_surplus - feed_in_yield) - SWITCH_ON_FALLEN_BELOW = "Einschaltschwelle während der Einschaltverzögerung unterschritten." - SWITCH_ON_WAITING = "Die Ladung wird gestartet, sobald in {} die Einschaltverzögerung abgelaufen ist." + SWITCH_ON_FALLEN_BELOW = "Einschaltschwelle während der Wartezeit unterschritten." + SWITCH_ON_WAITING = "Die Ladung wird gestartet, sobald in {} die Wartezeit abgelaufen ist." SWITCH_ON_NOT_EXCEEDED = ("Die Ladung kann nicht gestartet werden, da die Einschaltschwelle nicht erreicht " "wird.") - SWITCH_ON_EXPIRED = "Einschaltschwelle für die Dauer der Einschaltverzögerung überschritten." + SWITCH_ON_EXPIRED = "Einschaltschwelle für die Dauer der Wartezeit überschritten." SWITCH_ON_MAX_PHASES = "Der Überschuss ist ausreichend, um direkt mit {} Phasen zu laden." def calc_switch_on_power(self, chargepoint: Chargepoint) -> Tuple[float, float]: - surplus = self.data.set.surplus_power_left - self.data.set.reserved_surplus + surplus = self.calc_raw_surplus() - self.data.set.reserved_surplus control_parameter = chargepoint.data.control_parameter pv_config = data.data.general_data.data.chargemode_config.pv_charging - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: threshold = pv_config.feed_in_yield else: threshold = pv_config.switch_on_threshold*control_parameter.phases @@ -275,7 +278,7 @@ def switch_on_threshold_reached(self, chargepoint: Chargepoint) -> None: try: message = None control_parameter = chargepoint.data.control_parameter - feed_in_limit = chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.\ + feed_in_limit = chargepoint.data.set.charge_template.data.chargemode.pv_charging.\ feed_in_limit pv_config = data.data.general_data.data.chargemode_config.pv_charging timestamp_switch_on_off = control_parameter.timestamp_switch_on_off @@ -325,6 +328,7 @@ def switch_on_timer_expired(self, chargepoint: Chargepoint) -> None: msg = None pv_config = data.data.general_data.data.chargemode_config.pv_charging control_parameter = chargepoint.data.control_parameter + charging_ev_data = chargepoint.data.set.charging_ev_data # Timer ist noch nicht abgelaufen if timecheck.check_timestamp(control_parameter.timestamp_switch_on_off, pv_config.switch_on_delay): @@ -337,14 +341,14 @@ def switch_on_timer_expired(self, chargepoint: Chargepoint) -> None: msg = self.SWITCH_ON_EXPIRED.format(pv_config.switch_on_threshold) control_parameter.state = ChargepointState.WAIT_FOR_USING_PHASES - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: feed_in_yield = pv_config.feed_in_yield else: feed_in_yield = 0 - ev_template = chargepoint.data.set.charging_ev_data.ev_template + ev_template = charging_ev_data.ev_template max_phases_power = ev_template.data.min_current * ev_template.data.max_phases * 230 - if (data.data.general_data.get_phases_chargemode(Chargemode.PV_CHARGING.value, - control_parameter.submode) == 0 and + if (control_parameter.submode == Chargemode.PV_CHARGING and + chargepoint.data.set.charge_template.data.chargemode.pv_charging.phases_to_use == 0 and chargepoint.cp_ev_support_phase_switch() and self.get_usable_surplus(feed_in_yield) > max_phases_power): control_parameter.phases = ev_template.data.max_phases @@ -353,8 +357,8 @@ def switch_on_timer_expired(self, chargepoint: Chargepoint) -> None: except Exception: log.exception("Fehler im allgemeinen PV-Modul") - SWITCH_OFF_STOP = "Ladevorgang nach Ablauf der Abschaltverzögerung gestoppt." - SWITCH_OFF_WAITING = "Ladevorgang wird nach Ablauf der Abschaltverzögerung in {} gestoppt." + SWITCH_OFF_STOP = "Ladevorgang nach Ablauf der Wartezeit gestoppt." + SWITCH_OFF_WAITING = "Ladevorgang wird nach Ablauf der Wartezeit in {} gestoppt." SWITCH_OFF_NO_STOP = ("Der Ladevorgang wird trotz fehlenden Überschusses nicht gestoppt, da in dem Fahrzeug-Profil " "die Einstellung 'Ladung aktiv halten' aktiviert ist.") SWITCH_OFF_EXCEEDED = "Abschaltschwelle während der Verzögerung überschritten." @@ -385,7 +389,7 @@ def switch_off_check_timer(self, chargepoint: Chargepoint) -> None: def calc_switch_off_threshold(self, chargepoint: Chargepoint) -> Tuple[float, float]: pv_config = data.data.general_data.data.chargemode_config.pv_charging control_parameter = chargepoint.data.control_parameter - if chargepoint.data.set.charging_ev_data.charge_template.data.chargemode.pv_charging.feed_in_limit: + if chargepoint.data.set.charge_template.data.chargemode.pv_charging.feed_in_limit: # Der EVU-Überschuss muss ggf um die Einspeisegrenze bereinigt werden. # Wnn die Leistung nicht Einspeisegrenze + Einschaltschwelle erreicht, darf die Ladung nicht pulsieren. # Abschaltschwelle um Einschaltschwelle reduzieren. @@ -441,7 +445,7 @@ def switch_off_check_threshold(self, chargepoint: Chargepoint) -> bool: # Einen nach dem anderen abschalten, bis Ladeleistung des Speichers erreicht ist # und wieder eingespeist wird. self.data.set.reserved_surplus == 0)) - if switch_off_condition and max(chargepoint.data.get.currents) <= min_current: + if switch_off_condition and get_medium_charging_current(chargepoint.data.get.currents) <= min_current: if not charging_ev_data.ev_template.data.prevent_charge_stop: # EV, die ohnehin nicht laden, wird direkt die Ladefreigabe entzogen. # Würde man required_power vom released_evu_surplus subtrahieren, würden keine anderen EVs @@ -513,3 +517,17 @@ def limit_raw_power_left_to_surplus(surplus) -> None: counter.data.set.surplus_power_left = surplus log.debug(f'Zähler {counter.num}: Begrenzung der verbleibenden Leistung auf ' f'{counter.data.set.surplus_power_left}W') + + +def set_raw_surplus_power_left() -> None: + """ Bei surplus power left ist auch Leistung drin, die Autos zugeteilt bekommen, aber nicht ziehen und dann wird + ins Netz eingespeist. + beim Bidi-Laden den Regelmodus rausrechnen, da sonst zum Regelmodus und nicht zum Nullpunkt geregelt wird. + """ + grid_counter = data.data.counter_all_data.get_evu_counter() + bidi_power = 0 + chargepoint_by_chargemodes = get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE) + for cp in chargepoint_by_chargemodes: + bidi_power += cp.data.get.power + grid_counter.data.set.surplus_power_left = grid_counter.data.get.power * -1 + bidi_power + log.debug(f"Nullpunktanpassung {grid_counter.data.set.surplus_power_left}W") diff --git a/packages/control/counter_all.py b/packages/control/counter_all.py index 4a5b64a40f..2eeb156d18 100644 --- a/packages/control/counter_all.py +++ b/packages/control/counter_all.py @@ -471,9 +471,15 @@ def check_and_add(type_name: ComponentType, data_structure): # Falls EVU-Zähler fehlt, zuerst hinzufügen. check_and_add(ComponentType.COUNTER, data.data.counter_data) - check_and_add(ComponentType.BAT, data.data.bat_data) - check_and_add(ComponentType.CHARGEPOINT, data.data.cp_data) - check_and_add(ComponentType.INVERTER, data.data.pv_data) + try: + self.get_id_evu_counter() + check_and_add(ComponentType.BAT, data.data.bat_data) + check_and_add(ComponentType.CHARGEPOINT, data.data.cp_data) + check_and_add(ComponentType.INVERTER, data.data.pv_data) + except TypeError: + pub_system_message({}, ("Es konnte kein Zähler gefunden werden, der als EVU-Zähler an die Spitze des " + "Lastmanagements gesetzt werden kann. Bitte zuerst einen EVU-Zähler hinzufügen."), + MessageType.ERROR) def get_max_id_in_hierarchy(current_entry: List, max_id: int) -> int: diff --git a/packages/control/counter_test.py b/packages/control/counter_test.py index 4b10d1335f..4b3e59466a 100644 --- a/packages/control/counter_test.py +++ b/packages/control/counter_test.py @@ -140,7 +140,7 @@ def test_switch_on_threshold_reached(params: Params, caplog, general_data_fixtur cp.data.control_parameter.phases = 1 cp.data.control_parameter.state = params.state cp.data.control_parameter.timestamp_switch_on_off = params.timestamp_switch_on_off - ev.data.charge_template = ChargeTemplate(0) + ev.data.charge_template = ChargeTemplate() ev.data.charge_template.data.chargemode.pv_charging.feed_in_limit = params.feed_in_limit cp.data.set.charging_ev_data = ev mock_calc_switch_on_power = Mock(return_value=[params.surplus, params.threshold]) diff --git a/packages/control/data.py b/packages/control/data.py index 68bf8b7da4..1d7c779d01 100644 --- a/packages/control/data.py +++ b/packages/control/data.py @@ -4,7 +4,7 @@ """ import copy import logging -import threading +from threading import Event, Lock from functools import wraps from typing import Dict from control.bat import Bat @@ -24,29 +24,33 @@ from control.ev.ev import Ev from control.ev.ev_template import EvTemplate from control.general import General +from control.io_device import IoActions, IoStates from control.optional import Optional from modules.common.abstract_device import AbstractDevice +from modules.common.abstract_io import AbstractIoDevice log = logging.getLogger(__name__) -bat_data_lock = threading.Lock() -bat_all_data_lock = threading.Lock() -graph_data_lock = threading.Lock() -counter_data_lock = threading.Lock() -counter_all_data_lock = threading.Lock() -cp_data_lock = threading.Lock() -cp_all_data_lock = threading.Lock() -cp_template_data_lock = threading.Lock() -ev_charge_template_data_lock = threading.Lock() -ev_data_lock = threading.Lock() -ev_template_data_lock = threading.Lock() -general_data_lock = threading.Lock() -optional_data_lock = threading.Lock() -pv_data_lock = threading.Lock() -pv_all_data_lock = threading.Lock() -system_data_lock = threading.Lock() - - -def locked(lock: threading.Lock): +bat_data_lock = Lock() +bat_all_data_lock = Lock() +graph_data_lock = Lock() +counter_data_lock = Lock() +counter_all_data_lock = Lock() +cp_data_lock = Lock() +cp_all_data_lock = Lock() +cp_template_data_lock = Lock() +ev_charge_template_data_lock = Lock() +ev_data_lock = Lock() +ev_template_data_lock = Lock() +general_data_lock = Lock() +io_actions_lock = Lock() +io_states_lock = Lock() +optional_data_lock = Lock() +pv_data_lock = Lock() +pv_all_data_lock = Lock() +system_data_lock = Lock() + + +def locked(lock: Lock): def decorate(method): @wraps(method) def inner(*args, **kwargs): @@ -61,7 +65,7 @@ def inner(*args, **kwargs): class Data: - def __init__(self, event_module_update_completed: threading.Event): + def __init__(self, event_module_update_completed: Event): self.event_module_update_completed = event_module_update_completed self._bat_data: Dict[str, Bat] = {} self._bat_all_data = BatAll() @@ -75,6 +79,8 @@ def __init__(self, event_module_update_completed: threading.Event): self._ev_template_data: Dict[str, EvTemplate] = {} self._general_data = General() self._graph_data = Graph() + self._io_actions: IoActions = {} + self._io_states: Dict[str, IoStates] = {} self._optional_data = Optional() self._pv_data: Dict[str, Pv] = {} self._pv_all_data = PvAll() @@ -197,6 +203,24 @@ def general_data(self) -> General: def general_data(self, value): self._general_data = value + @property + def io_actions(self) -> IoActions: + return self._io_actions + + @io_actions.setter + @locked(io_actions_lock) + def io_actions(self, value): + self._io_actions = value + + @property + def io_states(self) -> Dict[str, IoStates]: + return self._io_states + + @io_states.setter + @locked(io_states_lock) + def io_states(self, value): + self._io_states = value + @property def optional_data(self) -> Optional: return self._optional_data @@ -247,11 +271,14 @@ def print_all(self): log.info(f"general_data\n{self._general_data.data}") log.info(f"general_data-display\n{self._general_data.data.extern_display_mode}") log.info(f"graph_data\n{self._graph_data.data}") + self._print_io_actions(self._io_actions) + self._print_dictionaries(self._io_states) log.info(f"optional_data\n{self._optional_data.data}") self._print_dictionaries(self._pv_data) log.info(f"pv_all_data\n{self._pv_all_data.data}") self._print_dictionaries(self._system_data) self._print_device_config(self._system_data) + self._print_io_device_config(self._system_data) log.info("\n") def _print_dictionaries(self, data): @@ -284,6 +311,21 @@ def _print_device_config(self, data: Dict[str, AbstractDevice]): except Exception: log.exception("Fehler im Data-Modul") + def _print_io_device_config(self, data: Dict[str, AbstractIoDevice]): + for key, value in data.items(): + try: + if isinstance(value, AbstractIoDevice): + log.info(f"{key}\n{dataclass_utils.asdict(value.config)}") + except Exception: + log.exception("Fehler im Data-Modul") + + def _print_io_actions(self, data: IoActions): + for key, value in data.actions.items(): + try: + log.info(f"{key}\n{dataclass_utils.asdict(value.config)}") + except Exception: + log.exception("Fehler im Data-Modul") + def copy_system_data(self) -> None: with ModuleDataReceivedContext(self.event_module_update_completed): self.__copy_system_data() @@ -298,7 +340,8 @@ def __copy_system_data(self) -> None: # werden, sodass die Nutzung einer Referenz vorerst funktioniert. self.system_data = { "system": copy.deepcopy(SubData.system_data["system"])} | { - k: SubData.system_data[k] for k in SubData.system_data if "device" in k} + k: SubData.system_data[k] for k in SubData.system_data if "device" in k} | { + k: SubData.system_data[k] for k in SubData.system_data if "io" in k} self.general_data = copy.deepcopy(SubData.general_data) self.__copy_cp_data() except Exception: @@ -370,7 +413,7 @@ def __copy_module_data(self) -> None: break if stop: break - self.pv_all_data = copy.deepcopy(SubData.pv_all_data) + self.pv_all_data = copy.deepcopy(SubData.pv_all_data) self.bat_data.clear() for bat in SubData.bat_data: stop = False @@ -383,7 +426,7 @@ def __copy_module_data(self) -> None: break if stop: break - self.bat_all_data = copy.deepcopy(SubData.bat_all_data) + self.bat_all_data = copy.deepcopy(SubData.bat_all_data) except Exception: log.exception("Fehler im Prepare-Modul") @@ -393,6 +436,8 @@ def copy_data(self) -> None: with ModuleDataReceivedContext(self.event_module_update_completed): try: self.general_data = copy.deepcopy(SubData.general_data) + self.io_actions = copy.deepcopy(SubData.io_actions) + self.io_states = copy.deepcopy(SubData.io_states) self.optional_data = copy.deepcopy(SubData.optional_data) self.__copy_ev_data() self.__copy_cp_data() @@ -444,7 +489,7 @@ def __exit__(self, exception_type, exception, exception_traceback) -> bool: data: Data -def data_init(event_module_update_completed: threading.Event): +def data_init(event_module_update_completed: Event): """instanziiert die Data-Klasse. """ global data diff --git a/packages/control/ev/charge_template.py b/packages/control/ev/charge_template.py index ed0493350c..66c7b94661 100644 --- a/packages/control/ev/charge_template.py +++ b/packages/control/ev/charge_template.py @@ -1,12 +1,16 @@ from dataclasses import asdict, dataclass, field +import datetime import logging import traceback -from typing import Dict, Optional, Tuple +from typing import List, Optional, Tuple from control import data +from control.chargepoint.chargepoint_state import CHARGING_STATES from control.chargepoint.charging_type import ChargingType +from control.chargepoint.control_parameter import ControlParameter from control.ev.ev_template import EvTemplate -from dataclass_utils.factories import empty_dict_factory +from control.text import BidiState +from dataclass_utils.factories import empty_list_factory from helpermodules.abstract_plans import Limit, limit_factory, ScheduledChargingPlan, TimeChargingPlan from helpermodules import timecheck log = logging.getLogger(__name__) @@ -14,47 +18,63 @@ def get_new_charge_template() -> dict: ct_default = asdict(ChargeTemplateData()) - ct_default["chargemode"]["scheduled_charging"].pop("plans") - ct_default["time_charging"].pop("plans") return ct_default def get_charge_template_default() -> dict: ct_default = asdict(ChargeTemplateData(name="Standard-Lade-Profil")) - ct_default["chargemode"]["scheduled_charging"].pop("plans") - ct_default["time_charging"].pop("plans") return ct_default @dataclass class ScheduledCharging: - plans: Dict[int, ScheduledChargingPlan] = field(default_factory=empty_dict_factory, metadata={ - "topic": ""}) + plans: List[ScheduledChargingPlan] = field(default_factory=empty_list_factory, metadata={ + "topic": ""}) # Dict[int,ScheduledChargingPlan] wird bei der dict to dataclass Konvertierung nicht unterstützt @dataclass class TimeCharging: active: bool = False - plans: Dict[int, TimeChargingPlan] = field(default_factory=empty_dict_factory, metadata={ - "topic": ""}) + plans: List[TimeChargingPlan] = field(default_factory=empty_list_factory, metadata={ + "topic": ""}) # Dict[int, TimeChargingPlan] wird bei der dict to dataclass Konvertierung nicht unterstützt + + +def scheduled_charging_plan_factory() -> ScheduledChargingPlan: + return ScheduledChargingPlan() + + +@dataclass +class EcoCharging: + current: int = 6 + dc_current: float = 145 + limit: Limit = field(default_factory=limit_factory) + max_price: float = 0.0002 + phases_to_use: int = 3 @dataclass class InstantCharging: - current: int = 10 + current: int = 16 dc_current: float = 145 limit: Limit = field(default_factory=limit_factory) + phases_to_use: int = 3 @dataclass class PvCharging: dc_min_current: float = 145 dc_min_soc_current: float = 145 - min_soc_current: int = 10 - min_current: int = 0 feed_in_limit: bool = False + limit: Limit = field(default_factory=limit_factory) + min_current: int = 0 + min_soc_current: int = 10 min_soc: int = 0 - max_soc: int = 100 + phases_to_use: int = 0 + phases_to_use_min_soc: int = 3 + + +def eco_charging_factory() -> EcoCharging: + return EcoCharging() def pv_charging_factory() -> PvCharging: @@ -71,7 +91,8 @@ def instant_charging_factory() -> InstantCharging: @dataclass class Chargemode: - selected: str = "stop" + selected: str = "instant_charging" + eco_charging: EcoCharging = field(default_factory=eco_charging_factory) pv_charging: PvCharging = field(default_factory=pv_charging_factory) scheduled_charging: ScheduledCharging = field(default_factory=scheduled_charging_factory) instant_charging: InstantCharging = field(default_factory=instant_charging_factory) @@ -85,22 +106,12 @@ def chargemode_factory() -> Chargemode: return Chargemode() -@dataclass -class Et: - active: bool = False - max_price: float = 0.0002 - - -def et_factory() -> Et: - return Et() - - @dataclass class ChargeTemplateData: + id: int = 0 name: str = "Lade-Profil" prio: bool = False load_default: bool = False - et: Et = field(default_factory=et_factory) time_charging: TimeCharging = field(default_factory=time_charging_factory) chargemode: Chargemode = field(default_factory=chargemode_factory) @@ -112,243 +123,354 @@ def charge_template_data_factory() -> ChargeTemplateData: @dataclass class SelectedPlan: remaining_time: float = 0 - available_current: float = 14 duration: float = 0 - max_current: int = 16 missing_amount: float = 0 phases: int = 1 - id: int = 0 + plan: Optional[ScheduledChargingPlan] = None @dataclass class ChargeTemplate: """ Klasse der Lade-Profile """ - ct_num: int data: ChargeTemplateData = field(default_factory=charge_template_data_factory, metadata={ "topic": ""}) BUFFER = -1200 # nach mehr als 20 Min Überschreitung wird der Termin als verpasst angesehen - CHARGING_PRICE_EXCEEDED = "Keine Ladung, da der aktuelle Strompreis über dem maximalen Strompreis liegt." + CHARGING_PRICE_EXCEEDED = ("Der aktuelle Strompreis liegt über dem maximalen Strompreis. ") + CHARGING_PRICE_LOW = "Laden, da der aktuelle Strompreis unter dem maximalen Strompreis liegt." - TIME_CHARGING_NO_PLAN_CONFIGURED = "Keine Ladung, da keine Zeitfenster für Zeitladen konfiguriert sind." - TIME_CHARGING_NO_PLAN_ACTIVE = "Keine Ladung, da kein Zeitfenster für Zeitladen aktiv ist." - TIME_CHARGING_SOC_REACHED = "Kein Zeitladen, da der Soc bereits erreicht wurde." - TIME_CHARGING_AMOUNT_REACHED = "Kein Zeitladen, da die Energiemenge bereits geladen wurde." + TIME_CHARGING_NO_PLAN_CONFIGURED = "Zeitladen aktiviert, aber keine Zeitfenster konfiguriert." + TIME_CHARGING_NO_PLAN_ACTIVE = "Kein Zeitfenster für Zeitladen aktiv." + TIME_CHARGING_SOC_REACHED = "Das Ladeziel für das Zeitladen wurde erreicht." + TIME_CHARGING_AMOUNT_REACHED = "Die gewünschte Energiemenge für das Zeitladen wurde geladen." def time_charging(self, soc: Optional[float], used_amount_time_charging: float, - charging_type: str) -> Tuple[int, str, Optional[str], Optional[str]]: + charging_type: str) -> Tuple[int, str, Optional[str], Optional[str], int]: """ prüft, ob ein Zeitfenster aktiv ist und setzt entsprechend den Ladestrom """ message = None + sub_mode = "time_charging" + id = None + phases = None try: if self.data.time_charging.plans: plan = timecheck.check_plans_timeframe(self.data.time_charging.plans) if plan is not None: current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current - if self.data.et.active and data.data.optional_data.et_provider_available(): - if not data.data.optional_data.et_price_lower_than_limit(self.data.et.max_price): - return 0, "stop", self.CHARGING_PRICE_EXCEEDED, plan.id - if plan.limit.selected == "none": # kein Limit konfiguriert, mit konfigurierter Stromstärke laden - return current, "time_charging", message, plan.id - elif plan.limit.selected == "soc": # SoC Limit konfiguriert - if soc: - if soc < plan.limit.soc: - return current, "time_charging", message, plan.id # Limit nicht erreicht - else: - return 0, "stop", self.TIME_CHARGING_SOC_REACHED, plan.id # Limit erreicht - else: - return plan.current, "time_charging", message, plan.id - elif plan.limit.selected == "amount": # Energiemengenlimit konfiguriert - if used_amount_time_charging < plan.limit.amount: - return current, "time_charging", message, plan.id # Limit nicht erreicht - else: - return 0, "stop", self.TIME_CHARGING_AMOUNT_REACHED, plan.id # Limit erreicht - else: - raise TypeError(f'{plan.limit.selected} unbekanntes Zeitladen-Limit.') + phases = plan.phases_to_use + id = plan.id + if plan.limit.selected == "soc" and soc and soc >= plan.limit.soc: + # SoC-Limit erreicht + current = 0 + sub_mode = "stop" + message = self.TIME_CHARGING_SOC_REACHED + elif plan.limit.selected == "amount" and used_amount_time_charging >= plan.limit.amount: + # Energie-Limit erreicht + current = 0 + sub_mode = "stop" + message = self.TIME_CHARGING_AMOUNT_REACHED else: message = self.TIME_CHARGING_NO_PLAN_ACTIVE + current = 0 + sub_mode = "stop" else: message = self.TIME_CHARGING_NO_PLAN_CONFIGURED - log.debug(message) - return 0, "stop", message, None + current = 0 + sub_mode = "stop" + return current, sub_mode, message, id, phases except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) - return 0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), None + log.exception("Fehler im ev-Modul "+str(self.data.id)) + return (0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), None, + 0) - INSTANT_CHARGING_SOC_REACHED = "Kein Sofortladen, da der Soc bereits erreicht wurde." - INSTANT_CHARGING_AMOUNT_REACHED = "Kein Sofortladen, da die Energiemenge bereits geladen wurde." + SOC_REACHED = "Keine Ladung, da das Ladeziel bereits erreicht wurde." + AMOUNT_REACHED = "Keine Ladung, da die Energiemenge bereits geladen wurde." def instant_charging(self, soc: Optional[float], - imported_instant_charging: float, - charging_type: str) -> Tuple[int, str, Optional[str]]: + used_amount: float, + charging_type: str) -> Tuple[int, str, Optional[str], int]: """ prüft, ob die Lademengenbegrenzung erreicht wurde und setzt entsprechend den Ladestrom. """ message = None + sub_mode = "instant_charging" try: instant_charging = self.data.chargemode.instant_charging + phases = instant_charging.phases_to_use if charging_type == ChargingType.AC.value: current = instant_charging.current else: current = instant_charging.dc_current - if self.data.et.active and data.data.optional_data.et_provider_available(): - if not data.data.optional_data.et_price_lower_than_limit(self.data.et.max_price): - return 0, "stop", self.CHARGING_PRICE_EXCEEDED - if instant_charging.limit.selected == "none": - return current, "instant_charging", message - elif instant_charging.limit.selected == "soc": - if soc: - if soc < instant_charging.limit.soc: - return current, "instant_charging", message - else: - return 0, "stop", self.INSTANT_CHARGING_SOC_REACHED - else: - return current, "instant_charging", message + + if instant_charging.limit.selected == "soc" and soc and soc >= instant_charging.limit.soc: + current = 0 + sub_mode = "stop" + message = self.SOC_REACHED elif instant_charging.limit.selected == "amount": - if imported_instant_charging < self.data.chargemode.instant_charging.limit.amount: - return current, "instant_charging", message - else: - return 0, "stop", self.INSTANT_CHARGING_AMOUNT_REACHED - else: - raise TypeError(f'{instant_charging.limit.selected} unbekanntes Sofortladen-Limit.') + if used_amount >= self.data.chargemode.instant_charging.limit.amount: + current = 0 + sub_mode = "stop" + message = self.AMOUNT_REACHED + return current, sub_mode, message, phases except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) - return 0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc() + log.exception("Fehler im ev-Modul "+str(self.data.id)) + return 0, "stop", "Keine Ladung, da da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), 0 - PV_CHARGING_SOC_REACHED = "Keine Ladung, da der maximale Soc bereits erreicht wurde." PV_CHARGING_SOC_CHARGING = ("Ladung evtl. auch ohne PV-Überschuss, da der Mindest-SoC des Fahrzeugs noch nicht " "erreicht wurde.") PV_CHARGING_MIN_CURRENT_CHARGING = "Ladung evtl. auch ohne PV-Überschuss, da minimaler Dauerstrom aktiv ist." - def pv_charging(self, soc: Optional[float], min_current: int, charging_type: str) -> Tuple[int, str, Optional[str]]: + def pv_charging(self, + soc: Optional[float], + min_current: int, + charging_type: str, + used_amount: float) -> Tuple[int, str, Optional[str], int]: """ prüft, ob Min-oder Max-Soc erreicht wurden und setzt entsprechend den Ladestrom. """ message = None + sub_mode = "pv_charging" try: pv_charging = self.data.chargemode.pv_charging - if soc is None or soc < pv_charging.max_soc: - if pv_charging.min_soc != 0 and soc is not None: - if soc < pv_charging.min_soc: - if charging_type == ChargingType.AC.value: - current = pv_charging.min_soc_current - else: - current = pv_charging.dc_min_soc_current - return current, "instant_charging", self.PV_CHARGING_SOC_CHARGING - if charging_type == ChargingType.AC.value: - pv_min_current = pv_charging.min_current - else: - pv_min_current = pv_charging.dc_min_current - if pv_min_current == 0: + phases = pv_charging.phases_to_use + min_pv_current = (pv_charging.min_current if charging_type == ChargingType.AC.value + else pv_charging.dc_min_current) + if pv_charging.limit.selected == "soc" and soc and soc > pv_charging.limit.soc: + current = 0 + sub_mode = "stop" + message = self.SOC_REACHED + elif pv_charging.limit.selected == "amount" and used_amount >= pv_charging.limit.amount: + current = 0 + sub_mode = "stop" + message = self.AMOUNT_REACHED + else: + if pv_charging.min_soc != 0 and soc is not None and soc < pv_charging.min_soc: + if charging_type == ChargingType.AC.value: + current = pv_charging.min_soc_current + else: + current = pv_charging.dc_min_soc_current + sub_mode = "instant_charging" + message = self.PV_CHARGING_SOC_CHARGING + phases = pv_charging.phases_to_use_min_soc + elif min_pv_current == 0: # nur PV; Ampere darf nicht 0 sein, wenn geladen werden soll - return min_current, "pv_charging", message + current = min_current + sub_mode = "pv_charging" else: # Min PV - return pv_min_current, "instant_charging", self.PV_CHARGING_MIN_CURRENT_CHARGING - else: - return 0, "stop", self.PV_CHARGING_SOC_REACHED + current = min_pv_current + sub_mode = "instant_charging" + message = self.PV_CHARGING_MIN_CURRENT_CHARGING + return current, sub_mode, message, phases except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) - return 0, "stop", "Keine Ladung, da ein interner Fehler aufgetreten ist: "+traceback.format_exc() - - def scheduled_charging_recent_plan(self, - soc: float, - ev_template: EvTemplate, - phases: int, - used_amount: float, - max_phases: int, - phase_switch_supported: bool, - charging_type: str) -> Optional[SelectedPlan]: - """ prüft, ob der Ziel-SoC oder die Ziel-Energiemenge erreicht wurde und stellt den zur Erreichung nötigen - Ladestrom ein. Um etwas mehr Puffer zu haben, wird bis 20 Min nach dem Zieltermin noch geladen, wenn dieser - nicht eingehalten werden konnte. - """ - if phase_switch_supported: - if charging_type == ChargingType.AC.value: - max_current = ev_template.data.max_current_multi_phases - else: - max_current = ev_template.data.dc_max_current - instant_phases = data.data.general_data.get_phases_chargemode("scheduled_charging", "instant_charging") - if instant_phases == 0: - planned_phases = 3 - else: - planned_phases = instant_phases - planned_phases = min(planned_phases, max_phases) - plan_data = self._search_plan(max_current, soc, ev_template, planned_phases, used_amount, charging_type) - if (plan_data and - charging_type == ChargingType.AC.value and - instant_phases == 0 and - plan_data.remaining_time > 300 and - self.data.et.active is False): - max_current = ev_template.data.max_current_single_phase - plan_data_single_phase = self._search_plan( - max_current, soc, ev_template, 1, used_amount, charging_type) - if plan_data_single_phase: - if plan_data_single_phase.remaining_time > 0: - plan_data = plan_data_single_phase - else: - if charging_type == ChargingType.AC.value: - if phases == 1: - max_current = ev_template.data.max_current_single_phase - else: - max_current = ev_template.data.max_current_multi_phases - else: - max_current = ev_template.data.dc_max_current - plan_data = self._search_plan(max_current, soc, ev_template, phases, used_amount, charging_type) - return plan_data + log.exception("Fehler im ev-Modul "+str(self.data.id)) + return 0, "stop", "Keine Ladung, da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), 1 - def _search_plan(self, - max_current: int, + def eco_charging(self, soc: Optional[float], - ev_template: EvTemplate, - phases: int, + control_parameter: ControlParameter, + charging_type: str, used_amount: float, - charging_type: str) -> Optional[SelectedPlan]: - smallest_remaining_time = float("inf") - missed_date_today_of_plan_with_smallest_remaining_time = False - plan_data: Optional[SelectedPlan] = None - battery_capacity = ev_template.data.battery_capacity - for plan in self.data.chargemode.scheduled_charging.plans.values(): - if plan.active: - if plan.limit.selected == "soc" and soc is None: + max_phases_hw: int) -> Tuple[int, str, Optional[str], int]: + """ prüft, ob Min-oder Max-Soc erreicht wurden und setzt entsprechend den Ladestrom. + """ + message = None + sub_mode = "pv_charging" + try: + eco_charging = self.data.chargemode.eco_charging + phases = eco_charging.phases_to_use + current = eco_charging.current if charging_type == ChargingType.AC.value else eco_charging.dc_current + + if eco_charging.limit.selected == "soc" and soc and soc >= eco_charging.limit.soc: + current = 0 + sub_mode = "stop" + message = self.SOC_REACHED + elif (eco_charging.limit.selected == "amount" and + used_amount >= self.data.chargemode.instant_charging.limit.amount): + current = 0 + sub_mode = "stop" + message = self.AMOUNT_REACHED + elif data.data.optional_data.et_provider_available(): + if data.data.optional_data.et_charging_allowed(eco_charging.max_price): + sub_mode = "instant_charging" + message = self.CHARGING_PRICE_LOW + phases = max_phases_hw + else: + current = control_parameter.min_current + message = self.CHARGING_PRICE_EXCEEDED + if control_parameter.state in CHARGING_STATES: + message += "Lädt mit Überschuss. " + else: + current = control_parameter.min_current + return current, sub_mode, message, phases + except Exception: + log.exception("Fehler im ev-Modul "+str(self.data.id)) + return 0, "stop", "Keine Ladung, da ein interner Fehler aufgetreten ist: "+traceback.format_exc(), 0 + + def _find_recent_plan(self, + plans: List[ScheduledChargingPlan], + soc: float, + ev_template: EvTemplate, + used_amount: float, + max_hw_phases: int, + phase_switch_supported: bool, + charging_type: str, + chargemode_switch_timestamp: float, + control_parameter: ControlParameter, + soc_request_interval_offset: int, + hw_bidi: bool): + plans_diff_end_date = [] + for p in plans: + if p.active: + if p.limit.selected == "soc" and soc is None: raise ValueError("Um Zielladen mit SoC-Ziel nutzen zu können, bitte ein SoC-Modul konfigurieren " - f"oder im Plan {plan.name} als Begrenzung Energie einstellen.") + f"oder im Plan {p.name} als Begrenzung Energie einstellen.") try: - duration, missing_amount = self._calculate_duration( - plan, soc, battery_capacity, used_amount, phases, charging_type, ev_template) - remaining_time, missed_date_today = timecheck.check_duration(plan, duration, self.BUFFER) - if remaining_time: - # Wenn der Zeitpunkt vorüber, aber noch nicht abgelaufen ist oder - # wenn noch gar kein Plan vorhanden ist, - if ((remaining_time < 0 and missed_date_today is False) or - # oder der Zeitpunkt noch nicht vorüber ist - remaining_time > 0): - # Wenn die verbleibende Zeit geringer als die niedrigste bisherige verbleibende Zeit ist - if (remaining_time < smallest_remaining_time or - # oder wenn der Zeitpunkt abgelaufen ist und es noch einen Zeitpunkt gibt, der in - # der Zukunft liegt. - (missed_date_today_of_plan_with_smallest_remaining_time and 0 < remaining_time)): - smallest_remaining_time = remaining_time - missed_date_today_of_plan_with_smallest_remaining_time = missed_date_today - if charging_type == ChargingType.AC.value: - available_current = plan.current - else: - available_current = plan.dc_current - plan_data = SelectedPlan( - remaining_time=remaining_time, - available_current=available_current, - max_current=max_current, - phases=phases, - id=plan.id, - missing_amount=missing_amount, - duration=duration) - log.debug(f"Plan-Nr. {plan.id}: Differenz zum Start {remaining_time}s, Dauer {duration/3600}h, " - f"Termin heute verpasst: {missed_date_today}") + plans_diff_end_date.append( + {p.id: timecheck.check_end_time(p, chargemode_switch_timestamp)}) + log.debug(f"Verbleibende Zeit bis zum Zieltermin [s]: {plans_diff_end_date}") except Exception: - log.exception("Fehler im ev-Modul "+str(self.ct_num)) - return plan_data + log.exception("Fehler im ev-Modul "+str(self.data.id)) + if plans_diff_end_date: + # ermittle den Key vom kleinsten value in plans_diff_end_date + filtered_plans = [d for d in plans_diff_end_date if list(d.values())[0] is not None] + if filtered_plans: + sorted_plans = sorted(filtered_plans, key=lambda x: list(x.values())[0]) + if len(sorted_plans) == 1: + plan_dict = sorted_plans[0] + elif (len(sorted_plans) > 1 and + list(sorted_plans[0].values())[0] < 0 and + list(sorted_plans[1].values())[0] < 43200): + # wenn der erste Plan in der Liste in der Vergangenheit liegt, dann den zweiten nehmen, wenn dessen + # Zielzeit weniger als 12 h entfernt ist. + plan_dict = sorted_plans[1] + else: + plan_dict = sorted_plans[0] + if plan_dict: + plan_id = list(plan_dict.keys())[0] + plan_end_time = list(plan_dict.values())[0] + + for p in plans: + if p.id == plan_id: + plan = p + + remaining_time, missing_amount, phases, duration = self._calc_remaining_time( + plan, plan_end_time, soc, ev_template, used_amount, max_hw_phases, phase_switch_supported, + charging_type, control_parameter.phases, soc_request_interval_offset, hw_bidi) + + return SelectedPlan(remaining_time=remaining_time, + duration=duration, + missing_amount=missing_amount, + phases=phases, + plan=plan) + else: + return None + + def scheduled_charging(self, + soc: float, + ev_template: EvTemplate, + used_amount: float, + max_hw_phases: int, + phase_switch_supported: bool, + charging_type: str, + chargemode_switch_timestamp: float, + control_parameter: ControlParameter, + soc_request_interval_offset: int, + bidi_state: BidiState) -> Optional[SelectedPlan]: + if bidi_state == BidiState.BIDI_CAPABLE and soc is None: + raise Exception("Für den Lademodis Bidi ist zwingend ein SoC-Modul erforderlich. Soll der " + "SoC ausschließlich aus dem Fahrzeug ausgelesen werden, bitte auf " + "manuellen SoC mit Auslesung aus dem Fahrzeug umstellen.") + plan_data = self._find_recent_plan(self.data.chargemode.scheduled_charging.plans, + soc, + ev_template, + used_amount, + max_hw_phases, + phase_switch_supported, + charging_type, + chargemode_switch_timestamp, + control_parameter, + soc_request_interval_offset, + bidi_state) + if plan_data: + control_parameter.current_plan = plan_data.plan.id + else: + control_parameter.current_plan = None + return self.scheduled_charging_calc_current( + plan_data, + soc, + used_amount, + control_parameter.phases, + control_parameter.min_current, + soc_request_interval_offset, + charging_type, + ev_template, + bidi_state) + + def _calc_remaining_time(self, + plan: ScheduledChargingPlan, + plan_end_time: float, + soc: Optional[float], + ev_template: EvTemplate, + used_amount: float, + max_hw_phases: int, + phase_switch_supported: bool, + charging_type: str, + control_parameter_phases: int, + soc_request_interval_offset: int, + bidi_state: BidiState) -> SelectedPlan: + bidi = BidiState.BIDI_CAPABLE and plan.bidi_charging_enabled + if bidi: + duration, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, + used_amount, control_parameter_phases, charging_type, ev_template, bidi) + remaining_time = plan_end_time - duration + phases = control_parameter_phases + elif plan.phases_to_use == 0: + if max_hw_phases == 1: + duration, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, + used_amount, 1, charging_type, ev_template, bidi) + remaining_time = plan_end_time - duration + phases = 1 + elif phase_switch_supported is False: + duration, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, used_amount, control_parameter_phases, + charging_type, ev_template, bidi) + phases = control_parameter_phases + remaining_time = plan_end_time - duration + else: + duration_3p, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, used_amount, 3, + charging_type, ev_template, bidi) + remaining_time_3p = plan_end_time - duration_3p + duration_1p, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, used_amount, 1, + charging_type, ev_template, bidi) + remaining_time_1p = plan_end_time - duration_1p + # Kurz vor dem nächsten Abfragen des SoC, wenn noch der alte SoC da ist, kann es sein, dass die Zeit + # für 1p nicht mehr reicht, weil die Regelung den neuen SoC noch nicht kennt. + if remaining_time_1p - (soc_request_interval_offset if plan.limit.selected == "soc" else 0) < 0: + # Zeit reicht nicht mehr für einphasiges Laden + remaining_time = remaining_time_3p + duration = duration_3p + phases = 3 + else: + remaining_time = remaining_time_1p + duration = duration_1p + phases = 1 + log.debug(f"Dauer 1p: {duration_1p}, Dauer 3p: {duration_3p}") + elif plan.phases_to_use == 3 or plan.phases_to_use == 1: + duration, missing_amount = self._calculate_duration( + plan, soc, ev_template.data.battery_capacity, + used_amount, plan.phases_to_use, charging_type, ev_template, bidi) + remaining_time = plan_end_time - duration + phases = plan.phases_to_use + + log.debug(f"Verbleibende Zeit bis zum Ladestart [s]:{remaining_time}, Dauer [h]: {duration/3600}") + return remaining_time, missing_amount, phases, duration def _calculate_duration(self, plan: ScheduledChargingPlan, @@ -357,7 +479,9 @@ def _calculate_duration(self, used_amount: float, phases: int, charging_type: str, - ev_template: EvTemplate) -> Tuple[float, float]: + ev_template: EvTemplate, + bidi: bool) -> Tuple[float, float]: + if plan.limit.selected == "soc": if soc is not None: missing_amount = ((plan.limit.soc_scheduled - soc) / 100) * battery_capacity @@ -365,99 +489,127 @@ def _calculate_duration(self, raise ValueError("Um Zielladen mit SoC-Ziel nutzen zu können, bitte ein SoC-Modul konfigurieren.") else: missing_amount = plan.limit.amount - used_amount - current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current - current = max(current, ev_template.data.min_current if charging_type == - ChargingType.AC.value else ev_template.data.dc_min_current) - duration = missing_amount/(current * phases*230) * 3600 + if bidi: + duration = missing_amount/plan.bidi_power * 3600 + else: + current = plan.current if charging_type == ChargingType.AC.value else plan.dc_current + current = max(current, ev_template.data.min_current if charging_type == + ChargingType.AC.value else ev_template.data.dc_min_current) + duration = missing_amount/(current * phases*230) * 3600 return duration, missing_amount SCHEDULED_REACHED_LIMIT_SOC = ("Kein Zielladen, da noch Zeit bis zum Zieltermin ist. " "Kein Zielladen mit Überschuss, da das SoC-Limit für Überschuss-Laden " + - "erreicht wurde.") + "erreicht wurde. ") SCHEDULED_CHARGING_REACHED_LIMIT_SOC = ("Kein Zielladen, da das Limit für Fahrzeug Laden mit Überschuss (SoC-Limit)" - " sowie der Fahrzeug-SoC (Ziel-SoC) bereits erreicht wurde.") - SCHEDULED_CHARGING_REACHED_AMOUNT = "Kein Zielladen, da die Energiemenge bereits erreicht wurde." + " sowie der Fahrzeug-SoC (Ziel-SoC) bereits erreicht wurde. ") + SCHEDULED_CHARGING_REACHED_AMOUNT = "Kein Zielladen, da die Energiemenge bereits erreicht wurde. " SCHEDULED_CHARGING_REACHED_SCHEDULED_SOC = ("Falls vorhanden wird mit EVU-Überschuss geladen, da der Ziel-Soc " - "für Zielladen bereits erreicht wurde.") + "für Zielladen bereits erreicht wurde. ") + SCHEDULED_CHARGING_BIDI = ("Der Ziel-Soc für Zielladen wurde bereits erreicht. Das Auto wird " + "bidirektional ge-/entladen, sodass möglichst weder Bezug noch " + "Einspeisung erfolgt. ") SCHEDULED_CHARGING_NO_PLANS_CONFIGURED = "Keine Ladung, da keine Ziel-Termine konfiguriert sind." - SCHEDULED_CHARGING_NO_DATE_PENDING = "Kein Zielladen, da kein Ziel-Termin ansteht." - SCHEDULED_CHARGING_USE_PV = ("Kein Zielladen, da noch Zeit bis zum Zieltermin ist. Falls vorhanden, " - "wird mit Überschuss geladen.") - SCHEDULED_CHARGING_MAX_CURRENT = ("Zielladen mit {}A. Der Ladestrom wurde erhöht, um den Zieltermin zu erreichen. " - "Es wird bis max. 20 Minuten nach dem angegebenen Zieltermin geladen.") + SCHEDULED_CHARGING_NO_DATE_PENDING = "Kein Zielladen, da kein Ziel-Termin ansteht. " + SCHEDULED_CHARGING_USE_PV = "Laden startet {}. Falls vorhanden, wird mit Überschuss geladen. " + SCHEDULED_CHARGING_MAX_CURRENT = "Zielladen mit {}A. Der Ladestrom wurde erhöht, um das Ziel zu erreichen. " SCHEDULED_CHARGING_LIMITED_BY_SOC = 'einen SoC von {}%' SCHEDULED_CHARGING_LIMITED_BY_AMOUNT = '{}kWh geladene Energie' SCHEDULED_CHARGING_IN_TIME = ('Zielladen mit mindestens {}A, um {} um {} zu erreichen. Falls vorhanden wird ' - 'zusätzlich EVU-Überschuss geladen.') - SCHEDULED_CHARGING_CHEAP_HOUR = "Zielladen, da ein günstiger Zeitpunkt zum preisbasierten Laden ist." + 'zusätzlich EVU-Überschuss geladen. ') + SCHEDULED_CHARGING_CHEAP_HOUR = "Zielladen, da ein günstiger Zeitpunkt zum preisbasierten Laden ist. {}" SCHEDULED_CHARGING_EXPENSIVE_HOUR = ("Zielladen ausstehend, da jetzt kein günstiger Zeitpunkt zum preisbasierten " - "Laden ist. Falls vorhanden, wird mit Überschuss geladen.") + "Laden ist. {} Falls vorhanden, wird mit Überschuss geladen. ") def scheduled_charging_calc_current(self, - plan_data: Optional[SelectedPlan], + selected_plan: Optional[SelectedPlan], soc: int, used_amount: float, control_parameter_phases: int, min_current: int, - soc_request_interval_offset: int) -> Tuple[float, str, str, int]: + soc_request_interval_offset: int, + charging_type: str, + ev_template: EvTemplate, + bidi_state: BidiState) -> Tuple[float, str, str, int]: current = 0 submode = "stop" - if plan_data is None: + if selected_plan is None: if len(self.data.chargemode.scheduled_charging.plans) == 0: return current, submode, self.SCHEDULED_CHARGING_NO_PLANS_CONFIGURED, control_parameter_phases else: return current, submode, self.SCHEDULED_CHARGING_NO_DATE_PENDING, control_parameter_phases - current_plan = self.data.chargemode.scheduled_charging.plans[str(plan_data.id)] - limit = current_plan.limit - phases = plan_data.phases - log.debug("Verwendeter Plan: "+str(current_plan.name)) + plan = selected_plan.plan + limit = plan.limit + phases = selected_plan.phases + if charging_type == ChargingType.AC.value: + plan_current = plan.current + max_current = ev_template.data.max_current_multi_phases + else: + plan_current = plan.dc_current + max_current = ev_template.data.dc_max_current + if plan.limit.selected != "soc": + soc_request_interval_offset = 0 + log.debug("Verwendeter Plan: "+str(plan.name)) if limit.selected == "soc" and soc >= limit.soc_limit and soc >= limit.soc_scheduled: message = self.SCHEDULED_CHARGING_REACHED_LIMIT_SOC elif limit.selected == "soc" and limit.soc_scheduled <= soc < limit.soc_limit: - message = self.SCHEDULED_CHARGING_REACHED_SCHEDULED_SOC - current = min_current - submode = "pv_charging" - # bei Überschuss-Laden mit der Phasenzahl aus den control_parameter laden, - # um die Umschaltung zu berücksichtigen. - phases = control_parameter_phases + if plan.bidi_charging_enabled and bidi_state == BidiState.BIDI_CAPABLE: + message = self.SCHEDULED_CHARGING_BIDI + current = min_current + submode = "bidi_charging" + phases = control_parameter_phases + else: + message = self.SCHEDULED_CHARGING_REACHED_SCHEDULED_SOC + if plan.bidi_charging_enabled and bidi_state != BidiState.BIDI_CAPABLE: + message += bidi_state.value + current = min_current + submode = "pv_charging" + # bei Überschuss-Laden mit der Phasenzahl aus den control_parameter laden, + # um die Umschaltung zu berücksichtigen. + phases = plan.phases_to_use_pv elif limit.selected == "amount" and used_amount >= limit.amount: message = self.SCHEDULED_CHARGING_REACHED_AMOUNT - elif 0 - soc_request_interval_offset < plan_data.remaining_time < 300 + soc_request_interval_offset: + elif 0 - soc_request_interval_offset < selected_plan.remaining_time < 300 + soc_request_interval_offset: + # Wenn der SoC ein paar Minuten alt ist, kann der Termin trotzdem gehalten werden. + # Zielladen kann nicht genauer arbeiten, als das Abfrageintervall vom SoC. # 5 Min vor spätestem Ladestart if limit.selected == "soc": limit_string = self.SCHEDULED_CHARGING_LIMITED_BY_SOC.format(limit.soc_scheduled) else: limit_string = self.SCHEDULED_CHARGING_LIMITED_BY_AMOUNT.format(limit.amount/1000) - message = self.SCHEDULED_CHARGING_IN_TIME.format( - plan_data.available_current, limit_string, current_plan.time) - current = plan_data.available_current + message = self.SCHEDULED_CHARGING_IN_TIME.format(plan_current, limit_string, plan.time) + current = plan_current submode = "instant_charging" # weniger als die berechnete Zeit verfügbar - # Ladestart wurde um maximal 20 Min verpasst. - elif plan_data.remaining_time <= 0 - soc_request_interval_offset: - if plan_data.duration + plan_data.remaining_time < 0: - current = plan_data.max_current + elif selected_plan.remaining_time <= 0 - soc_request_interval_offset: + if selected_plan.duration + selected_plan.remaining_time < 0: + current = max_current else: - current = min(plan_data.missing_amount/((plan_data.duration + plan_data.remaining_time) / - 3600)/(phases*230), plan_data.max_current) + current = min(selected_plan.missing_amount/((selected_plan.duration + selected_plan.remaining_time) / + 3600)/(phases*230), max_current) message = self.SCHEDULED_CHARGING_MAX_CURRENT.format(round(current, 2)) submode = "instant_charging" else: # Wenn dynamische Tarife aktiv sind, prüfen, ob jetzt ein günstiger Zeitpunkt zum Laden # ist. - if self.data.et.active and data.data.optional_data.et_provider_available(): - hour_list = data.data.optional_data.et_get_loading_hours(plan_data.duration, plan_data.remaining_time) + if plan.et_active: + hour_list = data.data.optional_data.et_get_loading_hours( + selected_plan.duration, selected_plan.remaining_time) + hours_message = ("Geladen wird zu folgenden Uhrzeiten: " + + ", ".join([datetime.datetime.fromtimestamp(hour).strftime('%-H:%M') + for hour in sorted(hour_list)]) + + ".") log.debug(f"Günstige Ladezeiten: {hour_list}") if timecheck.is_list_valid(hour_list): - message = self.SCHEDULED_CHARGING_CHEAP_HOUR - current = plan_data.available_current + message = self.SCHEDULED_CHARGING_CHEAP_HOUR.format(hours_message) + current = plan_current submode = "instant_charging" elif ((limit.selected == "soc" and soc <= limit.soc_limit) or (limit.selected == "amount" and used_amount < limit.amount)): - message = self.SCHEDULED_CHARGING_EXPENSIVE_HOUR + message = self.SCHEDULED_CHARGING_EXPENSIVE_HOUR.format(hours_message) current = min_current submode = "pv_charging" - phases = control_parameter_phases + phases = plan.phases_to_use_pv else: message = self.SCHEDULED_REACHED_LIMIT_SOC else: @@ -465,14 +617,24 @@ def scheduled_charging_calc_current(self, if limit.selected == "soc" and soc >= limit.soc_limit: message = self.SCHEDULED_REACHED_LIMIT_SOC else: - message = self.SCHEDULED_CHARGING_USE_PV + now = datetime.datetime.today() + start_time = now + datetime.timedelta(seconds=selected_plan.remaining_time) + if start_time.year == now.year and start_time.month == now.month and start_time.day == now.day: + message = self.SCHEDULED_CHARGING_USE_PV.format( + f"um {start_time.strftime('%-H:%M')} Uhr") + else: + message = self.SCHEDULED_CHARGING_USE_PV.format( + f"am {start_time.strftime('%d.%m')} um {start_time.strftime('%-H:%M')} Uhr") current = min_current submode = "pv_charging" - phases = control_parameter_phases + phases = plan.phases_to_use_pv return current, submode, message, phases - def standby(self) -> Tuple[int, str, str]: - return 0, "standby", "Keine Ladung, da der Lademodus Standby aktiv ist." - def stop(self) -> Tuple[int, str, str]: return 0, "stop", "Keine Ladung, da der Lademodus Stop aktiv ist." + + def bidi_charging_allowed(self, selected_plan: int, soc: float): + # Wenn zu über den Limit-SoC geladen wurde, darf nur noch bidirektional entladen werden. + for plan in self.data.chargemode.scheduled_charging.plans: + if plan.id == selected_plan: + return soc <= plan.limit.soc_limit diff --git a/packages/control/ev/charge_template_test.py b/packages/control/ev/charge_template_test.py index a036ffed0a..06c3cb6b40 100644 --- a/packages/control/ev/charge_template_test.py +++ b/packages/control/ev/charge_template_test.py @@ -1,15 +1,18 @@ -from typing import Dict, NamedTuple, Optional, Tuple +import datetime +from typing import Dict, Optional, Tuple from unittest.mock import Mock import pytest from control import data from control import optional +from control.chargepoint.control_parameter import ControlParameter from control.ev.charge_template import SelectedPlan from control.chargepoint.charging_type import ChargingType -from control.ev.ev import ChargeTemplate +from control.ev.charge_template import ChargeTemplate from control.ev.ev_template import EvTemplate, EvTemplateData from control.general import General +from control.text import BidiState from helpermodules import timecheck from helpermodules.abstract_plans import Limit, ScheduledChargingPlan, TimeChargingPlan @@ -24,32 +27,33 @@ def data_module() -> None: @pytest.mark.parametrize( "plans, soc, used_amount_time_charging, plan_found, expected", [ - pytest.param({}, 0, 0, None, (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_CONFIGURED, None), + pytest.param({}, 0, 0, None, (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_CONFIGURED, None, None), id="no plan defined"), pytest.param({"0": TimeChargingPlan(id=0)}, 0, 0, None, - (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_ACTIVE, None), id="no plan active"), + (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_ACTIVE, None, None), id="no plan active"), pytest.param({"0": TimeChargingPlan(id=0)}, 0, 0, TimeChargingPlan(id=0), - (16, "time_charging", None, 0), id="plan active"), + (16, "time_charging", None, 0, 1), id="plan active"), pytest.param({"0": TimeChargingPlan(id=0, limit=Limit(selected="soc"))}, 100, 0, TimeChargingPlan(id=0, limit=Limit(selected="soc")), - (0, "stop", ChargeTemplate.TIME_CHARGING_SOC_REACHED, 0), + (0, "stop", ChargeTemplate.TIME_CHARGING_SOC_REACHED, 0, 1), id="plan active, soc is reached"), pytest.param({"0": TimeChargingPlan(id=0, limit=Limit(selected="soc"))}, 40, 0, TimeChargingPlan(id=0, limit=Limit(selected="soc")), - (16, "time_charging", None, 0), id="plan active, soc is not reached"), + (16, "time_charging", None, 0, 1), id="plan active, soc is not reached"), pytest.param({"0": TimeChargingPlan(id=0, limit=Limit(selected="soc"))}, None, 0, TimeChargingPlan(id=0, limit=Limit(selected="soc")), - (16, "time_charging", None, 0), id="plan active, soc is not defined"), + (16, "time_charging", None, 0, 1), id="plan active, soc is not defined"), pytest.param({"0": TimeChargingPlan(id=0, limit=Limit(selected="amount"))}, 0, 1500, TimeChargingPlan(id=0, limit=Limit(selected="amount")), - (0, "stop", ChargeTemplate.TIME_CHARGING_AMOUNT_REACHED, 0), + (0, "stop", ChargeTemplate.TIME_CHARGING_AMOUNT_REACHED, 0, 1), id="plan active, used_amount_time_charging is reached"), pytest.param({"0": TimeChargingPlan(id=0, limit=Limit(selected="amount"))}, 0, 500, TimeChargingPlan(id=0, limit=Limit(selected="amount")), - (16, "time_charging", None, 0), + (16, "time_charging", None, 0, 1), id="plan active, used_amount_time_charging is not reached"), pytest.param({"0": TimeChargingPlan(id=0)}, 0, 0, None, - (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_ACTIVE, None), id="plan defined but not found"), + (0, "stop", ChargeTemplate.TIME_CHARGING_NO_PLAN_ACTIVE, None, None), + id="plan defined but not found"), ] ) def test_time_charging(plans: Dict[int, TimeChargingPlan], soc: float, used_amount_time_charging: float, @@ -57,7 +61,7 @@ def test_time_charging(plans: Dict[int, TimeChargingPlan], soc: float, used_amou expected: Tuple[int, str, Optional[str], Optional[str]], monkeypatch): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.time_charging.plans = plans check_plans_timeframe_mock = Mock(return_value=plan_found) monkeypatch.setattr(timecheck, "check_plans_timeframe", check_plans_timeframe_mock) @@ -72,20 +76,20 @@ def test_time_charging(plans: Dict[int, TimeChargingPlan], soc: float, used_amou @pytest.mark.parametrize( "selected, current_soc, used_amount, expected", [ - pytest.param("none", 0, 0, (10, "instant_charging", None), id="without limit"), - pytest.param("soc", None, 0, (10, "instant_charging", None), id="limit soc: soc not defined"), - pytest.param("soc", 49, 0, (10, "instant_charging", None), id="limit soc: soc not reached"), - pytest.param("soc", 50, 0, (0, "stop", ChargeTemplate.INSTANT_CHARGING_SOC_REACHED), + pytest.param("none", 0, 0, (16, "instant_charging", None, 3), id="without limit"), + pytest.param("soc", None, 0, (16, "instant_charging", None, 3), id="limit soc: soc not defined"), + pytest.param("soc", 49, 0, (16, "instant_charging", None, 3), id="limit soc: soc not reached"), + pytest.param("soc", 50, 0, (0, "stop", ChargeTemplate.SOC_REACHED, 3), id="limit soc: soc reached"), - pytest.param("amount", 0, 999, (10, "instant_charging", None), id="limit amount: amount not reached"), - pytest.param("amount", 0, 1000, (0, "stop", ChargeTemplate.INSTANT_CHARGING_AMOUNT_REACHED), + pytest.param("amount", 0, 999, (16, "instant_charging", None, 3), id="limit amount: amount not reached"), + pytest.param("amount", 0, 1000, (0, "stop", ChargeTemplate.AMOUNT_REACHED, 3), id="limit amount: amount reached"), ]) def test_instant_charging(selected: str, current_soc: float, used_amount: float, expected: Tuple[int, str, Optional[str]]): # setup data.data.optional_data.data.et.active = False - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.chargemode.instant_charging.limit.selected = selected # execution @@ -96,87 +100,90 @@ def test_instant_charging(selected: str, current_soc: float, used_amount: float, @pytest.mark.parametrize( - "min_soc, min_current, current_soc, expected", + "min_soc, min_current, limit_selected, current_soc, used_amount, expected", [ - pytest.param(0, 0, 100, (0, "stop", ChargeTemplate.PV_CHARGING_SOC_REACHED), id="max soc reached"), - pytest.param(15, 0, 14, (10, "instant_charging", ChargeTemplate.PV_CHARGING_SOC_CHARGING), + pytest.param(0, 0, "amount", 14, 1500, (0, "stop", ChargeTemplate.AMOUNT_REACHED, 0), id="max amount reached"), + pytest.param(0, 0, "soc", 100, 900, (0, "stop", ChargeTemplate.SOC_REACHED, 0), id="max soc reached"), + pytest.param(15, 0, None, 14, 900, (10, "instant_charging", ChargeTemplate.PV_CHARGING_SOC_CHARGING, 3), id="min soc not reached"), - pytest.param(15, 0, None, (6, "pv_charging", None), id="soc not defined"), - pytest.param(15, 8, 15, (8, "instant_charging", ChargeTemplate.PV_CHARGING_MIN_CURRENT_CHARGING), + pytest.param(15, 0, None, None, 900, (6, "pv_charging", None, 0), id="soc not defined"), + pytest.param(15, 8, None, 15, 900, (8, "instant_charging", ChargeTemplate.PV_CHARGING_MIN_CURRENT_CHARGING, 0), id="min current configured"), - pytest.param(15, 0, 15, (6, "pv_charging", None), id="bare pv charging"), + pytest.param(15, 0, None, 15, 900, (6, "pv_charging", None, 0), id="bare pv charging"), ]) -def test_pv_charging(min_soc: int, min_current: int, current_soc: float, - expected: Tuple[int, str, Optional[str]]): +def test_pv_charging(min_soc: int, + min_current: int, + limit_selected: str, + current_soc: float, + used_amount: float, + expected: Tuple[int, str, Optional[str], int]): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() ct.data.chargemode.pv_charging.min_soc = min_soc ct.data.chargemode.pv_charging.min_current = min_current + ct.data.chargemode.pv_charging.phases_to_use = 0 + ct.data.chargemode.pv_charging.phases_to_use_min_soc = 3 + ct.data.chargemode.pv_charging.limit.selected = limit_selected + ct.data.chargemode.pv_charging.limit.soc = 90 data.data.bat_all_data.data.config.configured = True # execution - ret = ct.pv_charging(current_soc, 6, ChargingType.AC.value) + ret = ct.pv_charging(current_soc, 6, ChargingType.AC.value, used_amount) # evaluation assert ret == expected -Params = NamedTuple("Params", [("name", str), - ("phase_switch_supported", bool), - ("chargemode_phases", int), - ("search_plan", Optional[SelectedPlan]), - ("expected_max_current", int), - ("phases", int), - ("max_phases", int), - ("expected_phases", int)]) - -cases = [ - Params(name="no phase switch, one phase", phase_switch_supported=False, chargemode_phases=0, - search_plan=None, phases=1, max_phases=3, expected_max_current=32, expected_phases=1), - Params(name="no phase switch, multi phase", phase_switch_supported=False, chargemode_phases=0, - search_plan=None, phases=3, max_phases=3, expected_max_current=16, expected_phases=3), - Params(name="no automatic mode, multi phase", phase_switch_supported=True, chargemode_phases=2, - search_plan=None, phases=2, max_phases=2, expected_max_current=16, expected_phases=2), - Params(name="select phases, not enough time", phase_switch_supported=True, chargemode_phases=0, search_plan=Mock( - spec=SelectedPlan, remaining_time=300), phases=1, max_phases=3, expected_max_current=16, expected_phases=3), - Params(name="select phases, enough time", phase_switch_supported=True, chargemode_phases=0, search_plan=Mock( - spec=SelectedPlan, remaining_time=301), phases=1, max_phases=3, expected_max_current=32, expected_phases=1) -] - - -@pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) -def test_scheduled_charging_recent_plan(params: Params, monkeypatch): +@pytest.mark.parametrize("phases_to_use, calc_duration, max_hw_phases, phase_switch_supported, expected", + [ + pytest.param(0, [(1000, 3)], 1, True, (5000, 3, 1, 1000), id="automatic, one hw phase"), + pytest.param(0, [(1000, 3)], 3, False, (5000, 3, 2, 1000), + id="automatic, no phase switch"), + pytest.param(0, [(1000, 3), (7000, 3)], 3, True, (5000, 3, 3, 1000), id="automatic, 3p"), + pytest.param(0, [(500, 3), (1500, 3)], 3, True, (4500, 3, 1, 1500), id="automatic, 1p"), + pytest.param(3, [(5000, 3)], 3, True, (1000, 3, 3, 5000), id="3p"), + pytest.param(1, [(5000, 3)], 3, True, (1000, 3, 1, 5000), id="1p"), + ]) +def test_calc_remaining_time(phases_to_use, + calc_duration, + max_hw_phases, + phase_switch_supported, + expected, monkeypatch): # setup - ct = ChargeTemplate(0) - get_phases_chargemode_mock = Mock(return_value=params.chargemode_phases) - monkeypatch.setattr(data.data.general_data, "get_phases_chargemode", get_phases_chargemode_mock) - search_plan_mock = Mock(return_value=params.search_plan) - monkeypatch.setattr(ChargeTemplate, "_search_plan", search_plan_mock) - evt_data = Mock(spec=EvTemplateData, max_current_multi_phases=16, max_current_single_phase=32) - evt = Mock(spec=EvTemplate, data=evt_data) + ct = ChargeTemplate() + plan = ScheduledChargingPlan(phases_to_use=phases_to_use) + calculate_duration_mock = Mock(side_effect=calc_duration) + monkeypatch.setattr(ChargeTemplate, "_calculate_duration", calculate_duration_mock) + evt = Mock(spec=EvTemplate, data=Mock(spec=EvTemplateData, battery_capacity=85)) # execution - ct.scheduled_charging_recent_plan(50, evt, params.phases, 5, params.max_phases, - params.phase_switch_supported, ChargingType.AC.value) + remaining_time, missing_amount, phases, duration = ct._calc_remaining_time( + plan, 6000, 50, evt, 3000, max_hw_phases, phase_switch_supported, ChargingType.AC.value, 2, 0, False) + # end time 16.5.22 10:00 # evaluation - assert search_plan_mock.call_args.args[0] == params.expected_max_current - assert search_plan_mock.call_args.args[3] == params.expected_phases + assert (remaining_time, missing_amount, phases, duration) == expected @pytest.mark.parametrize( - "selected, phases, expected_duration, expected_missing_amount", + "selected, phases, bidi_charging_enabled, expected_duration, expected_missing_amount", [ - pytest.param("soc", 1, 10062.111801242236, 9000, id="soc, one phase"), - pytest.param("amount", 2, 447.2049689440994, 800, id="amount, two phases"), + pytest.param("soc", 1, False, 10062.111801242236, 9000, id="soc, one phase"), + pytest.param("amount", 2, False, 447.2049689440994, 800, id="amount, two phases"), + pytest.param("soc", 2, True, 3240.0, 9000, id="bidi"), ]) -def test_calculate_duration(selected: str, phases: int, expected_duration: float, expected_missing_amount: float): +def test_calculate_duration(selected: str, + phases: int, + bidi_charging_enabled: bool, + expected_duration: float, + expected_missing_amount: float): # setup - ct = ChargeTemplate(0) - plan = ScheduledChargingPlan() + ct = ChargeTemplate() + plan = ScheduledChargingPlan(bidi_charging_enabled=bidi_charging_enabled) plan.limit.selected = selected # execution - duration, missing_amount = ct._calculate_duration(plan, 60, 45000, 200, phases, ChargingType.AC.value, EvTemplate()) + duration, missing_amount = ct._calculate_duration( + plan, 60, 45000, 200, phases, ChargingType.AC.value, EvTemplate(), bidi_charging_enabled) # evaluation assert duration == expected_duration @@ -184,82 +191,92 @@ def test_calculate_duration(selected: str, phases: int, expected_duration: float @pytest.mark.parametrize( - "check_duration_return1, check_duration_return2, expected_plan_num", + "end_time_mock, expected_plan_num", [ - pytest.param((-50, False), (60, False), 0, id="too late, but didn't miss date for today"), - pytest.param((-50, True), (60, False), 1, id="too late and missed date for today"), - pytest.param((-50, True), (-60, True), None, id="missed both"), - pytest.param((50, False), (60, False), 0, id="in time, plan 1"), - pytest.param((50, False), (40, False), 1, id="in time, plan 2"), + pytest.param([1000, 1500, 2000], 0, id="nächster Zieltermin Plan 0"), + pytest.param([-100, 1000, 2000], 1, id="Plan 0 abgelaufen, Plan 1 innerhalb der nächsten 12h"), + pytest.param([-100, 45000, 50000], 0, id="Plan 0 abgelaufen, Plan 1 nicht innerhalb der nächsten 12h"), + pytest.param([1500, 2000, 1000], 2, id="nächster Zieltermin Plan 2"), + pytest.param([None]*3, 0, id="kein Plan"), ]) -def test_search_plan(check_duration_return1: Tuple[Optional[float], bool], - check_duration_return2: Tuple[Optional[float], bool], - expected_plan_num: Optional[int], - monkeypatch): +def test_scheduled_charging_recent_plan(end_time_mock, + expected_plan_num: Optional[int], + monkeypatch): # setup - calculate_duration_mock = Mock(return_value=(100, 200)) - monkeypatch.setattr(ChargeTemplate, "_calculate_duration", calculate_duration_mock) - check_duration_mock = Mock(side_effect=[check_duration_return1, check_duration_return2]) - monkeypatch.setattr(timecheck, "check_duration", check_duration_mock) - ct = ChargeTemplate(0) + calculate_duration_mock = Mock(return_value=(100, 3000, 3, 500)) + monkeypatch.setattr(ChargeTemplate, "_calc_remaining_time", calculate_duration_mock) + check_end_time_mock = Mock(side_effect=end_time_mock) + monkeypatch.setattr(timecheck, "check_end_time", check_end_time_mock) + control_parameter = ControlParameter() + ct = ChargeTemplate() plan_mock_0 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=0, limit=Limit(selected="amount")) plan_mock_1 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=1, limit=Limit(selected="amount")) - ct.data.chargemode.scheduled_charging.plans = {"0": plan_mock_0, "1": plan_mock_1} + plan_mock_2 = Mock(spec=ScheduledChargingPlan, active=True, current=14, id=2, limit=Limit(selected="amount")) + plans = [plan_mock_0, plan_mock_1, plan_mock_2] + # execution - plan_data = ct._search_plan(14, 60, EvTemplate(), 3, 200, ChargingType.AC.value) + selected_plan = ct._find_recent_plan( + plans, 60, EvTemplate(), 200, 3, True, ChargingType.AC.value, 1652688000, control_parameter, 0, False) # evaluation - if expected_plan_num is None: - assert plan_data is None + if selected_plan: + assert selected_plan.plan.id == expected_plan_num else: - assert plan_data.id == expected_plan_num - assert plan_data.duration == 100 + assert selected_plan is None @pytest.mark.parametrize( - "plan_data, soc, used_amount, selected, expected", + "plan_data, soc, used_amount, selected, bidi_charging_enabled, expected", [ - pytest.param(None, 0, 0, "none", (0, "stop", + pytest.param(None, 0, 0, "none", False, (0, "stop", ChargeTemplate.SCHEDULED_CHARGING_NO_DATE_PENDING, 3), id="no date pending"), - pytest.param(SelectedPlan(duration=3600), 90, 0, "soc", (0, "stop", + pytest.param(SelectedPlan(duration=3600), 90, 0, "soc", False, (0, "stop", ChargeTemplate.SCHEDULED_CHARGING_REACHED_LIMIT_SOC, 1), id="reached limit soc"), - pytest.param(SelectedPlan(duration=3600), 80, 0, "soc", (6, "pv_charging", - ChargeTemplate.SCHEDULED_CHARGING_REACHED_SCHEDULED_SOC, 3), id="reached scheduled soc"), - pytest.param(SelectedPlan(phases=3, duration=3600), 0, 1000, "amount", (0, "stop", + pytest.param(SelectedPlan(duration=3600), 80, 0, "soc", False, (6, "pv_charging", + ChargeTemplate.SCHEDULED_CHARGING_REACHED_SCHEDULED_SOC, 0), id="reached scheduled soc"), + pytest.param(SelectedPlan(duration=3600), 80, 0, "soc", True, (6, "bidi_charging", + ChargeTemplate.SCHEDULED_CHARGING_BIDI, 3), id="reached scheduled soc, bidi"), + pytest.param(SelectedPlan(phases=3, duration=3600), 0, 1000, "amount", False, (0, "stop", ChargeTemplate.SCHEDULED_CHARGING_REACHED_AMOUNT, 3), id="reached amount"), pytest.param(SelectedPlan(remaining_time=299, duration=3600), 0, 999, "amount", - (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_IN_TIME.format( + False, (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_IN_TIME.format( 14, ChargeTemplate.SCHEDULED_CHARGING_LIMITED_BY_AMOUNT.format(1.0), "07:00"), 1), id="in time, limited by amount"), pytest.param(SelectedPlan(remaining_time=299, duration=3600), 79, 0, "soc", - (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_IN_TIME.format( + False, (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_IN_TIME.format( 14, ChargeTemplate.SCHEDULED_CHARGING_LIMITED_BY_SOC.format(80), "07:00"), 1), id="in time, limited by soc"), pytest.param(SelectedPlan(remaining_time=-500, duration=3600, missing_amount=9000, phases=3), 79, 0, "soc", - (15.147265077138847, "instant_charging", + False, (15.147265077138847, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_MAX_CURRENT.format(15.15), 3), id="too late, but didn't miss for today"), pytest.param(SelectedPlan(remaining_time=-800, duration=780, missing_amount=4600, phases=3), 79, 0, "soc", - (16, "instant_charging", + False, (16, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_MAX_CURRENT.format(16), 3), id="few minutes too late, but didn't miss for today"), pytest.param(SelectedPlan(remaining_time=301, duration=3600), 79, 0, "soc", - (6, "pv_charging", ChargeTemplate.SCHEDULED_CHARGING_USE_PV, 3), id="too early, use pv"), + False, (6, "pv_charging", ChargeTemplate.SCHEDULED_CHARGING_USE_PV.format("um 8:45 Uhr"), 0), + id="too early, use pv"), ]) def test_scheduled_charging_calc_current(plan_data: SelectedPlan, soc: int, used_amount: float, selected: str, + bidi_charging_enabled: bool, expected: Tuple[float, str, str, int]): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() plan = ScheduledChargingPlan(active=True, id=0) plan.limit.selected = selected + plan.bidi_charging_enabled = bidi_charging_enabled # json verwandelt Keys in strings - ct.data.chargemode.scheduled_charging.plans = {"0": plan} + ct.data.chargemode.scheduled_charging.plans = [plan] + if plan_data: + plan_data.plan = plan # execution - ret = ct.scheduled_charging_calc_current(plan_data, soc, used_amount, 3, 6, 0) + ret = ct.scheduled_charging_calc_current(plan_data, soc, used_amount, 3, 6, + 0, ChargingType.AC.value, EvTemplate(), BidiState.BIDI_CAPABLE) # evaluation assert ret == expected @@ -267,10 +284,11 @@ def test_scheduled_charging_calc_current(plan_data: SelectedPlan, def test_scheduled_charging_calc_current_no_plans(): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() # execution - ret = ct.scheduled_charging_calc_current(None, 63, 5, 3, 6, 0) + ret = ct.scheduled_charging_calc_current( + None, 63, 5, 3, 6, 0, ChargingType.AC.value, EvTemplate(), BidiState.BIDI_CAPABLE) # evaluation assert ret == (0, "stop", ChargeTemplate.SCHEDULED_CHARGING_NO_PLANS_CONFIGURED, 3) @@ -279,25 +297,29 @@ def test_scheduled_charging_calc_current_no_plans(): @pytest.mark.parametrize( "loading_hour, expected", [ - pytest.param(True, (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_CHEAP_HOUR, 3)), - pytest.param(False, (6, "pv_charging", ChargeTemplate.SCHEDULED_CHARGING_EXPENSIVE_HOUR, 3)), + pytest.param(True, (14, "instant_charging", ChargeTemplate.SCHEDULED_CHARGING_CHEAP_HOUR.format( + "Geladen wird zu folgenden Uhrzeiten: 8:00."), 3)), + pytest.param(False, (6, "pv_charging", ChargeTemplate.SCHEDULED_CHARGING_EXPENSIVE_HOUR.format( + "Geladen wird zu folgenden Uhrzeiten: 8:00."), 0)), ]) def test_scheduled_charging_calc_current_electricity_tariff(loading_hour, expected, monkeypatch): # setup - ct = ChargeTemplate(0) + ct = ChargeTemplate() plan = ScheduledChargingPlan(active=True) + plan.et_active = True plan.limit.selected = "soc" - ct.data.chargemode.scheduled_charging.plans = {"0": plan} - ct.data.et.active = True - mock_et_get_loading_hours = Mock(return_value=[]) + ct.data.chargemode.scheduled_charging.plans = [plan] + # für Github-Test keinen Zeitstempel verwenden + mock_et_get_loading_hours = Mock(return_value=[datetime.datetime( + year=2022, month=5, day=16, hour=8, minute=0).timestamp()]) monkeypatch.setattr(data.data.optional_data, "et_get_loading_hours", mock_et_get_loading_hours) - mock_et_provider_available = Mock(return_value=True) - monkeypatch.setattr(data.data.optional_data, "et_provider_available", mock_et_provider_available) mock_is_list_valid = Mock(return_value=loading_hour) monkeypatch.setattr(timecheck, "is_list_valid", mock_is_list_valid) # execution - ret = ct.scheduled_charging_calc_current(SelectedPlan(remaining_time=301, phases=3, duration=3600), 79, 0, 3, 6, 0) + ret = ct.scheduled_charging_calc_current(SelectedPlan( + plan=plan, remaining_time=301, phases=3, duration=3600), + 79, 0, 3, 6, 0, ChargingType.AC.value, EvTemplate(), BidiState.BIDI_CAPABLE) # evaluation assert ret == expected diff --git a/packages/control/ev/ev.py b/packages/control/ev/ev.py index 2f31fd1425..91ed80bb12 100644 --- a/packages/control/ev/ev.py +++ b/packages/control/ev/ev.py @@ -6,22 +6,25 @@ stärke wird auch geprüft, ob sich an diesen Parametern etwas geändert hat. Falls ja, muss das EV in der Regelung neu priorisiert werden und eine neue Zuteilung des Stroms erhalten. """ +from modules.common.configurable_vehicle import ConfigurableVehicle +from modules.common.abstract_vehicle import VehicleUpdateData +from helpermodules.constants import NO_ERROR +from helpermodules import timecheck +from dataclass_utils.factories import empty_list_factory +from control.text import BidiState +from control.limiting_value import LimitingValue, LoadmanagementLimit from dataclasses import dataclass, field import logging from typing import List, Optional, Tuple +from fnmatch import fnmatch from control import data +from control.ev.charge_template import ChargeTemplate +from control.algorithm.utils import get_medium_charging_current from control.chargepoint.chargepoint_state import ChargepointState, PHASE_SWITCH_STATES from control.chargepoint.charging_type import ChargingType from control.chargepoint.control_parameter import ControlParameter -from control.ev.charge_template import ChargeTemplate from control.ev.ev_template import EvTemplate -from control.limiting_value import LimitingValue -from dataclass_utils.factories import empty_list_factory -from helpermodules import timecheck -from helpermodules.constants import NO_ERROR -from modules.common.abstract_vehicle import VehicleUpdateData -from modules.common.configurable_vehicle import ConfigurableVehicle log = logging.getLogger(__name__) @@ -86,10 +89,8 @@ class Ev: def __init__(self, index: int): try: self.ev_template: EvTemplate = EvTemplate() - self.charge_template: ChargeTemplate = ChargeTemplate(0) + self.charge_template: ChargeTemplate = ChargeTemplate() self.soc_module: ConfigurableVehicle = None - self.chargemode_changed = False - self.submode_changed = False self.num = index self.data = EvData() except Exception: @@ -114,11 +115,15 @@ def soc_interval_expired(self, vehicle_update_data: VehicleUpdateData) -> bool: return request_soc def get_required_current(self, + charge_template: ChargeTemplate, control_parameter: ControlParameter, - imported: float, max_phases_hw: int, phase_switch_supported: bool, - charging_type: str) -> Tuple[bool, Optional[str], str, float, int]: + charging_type: str, + chargemode_switch_timestamp: float, + imported_since_plugged: float, + bidi: BidiState, + phases_in_use: int) -> Tuple[bool, Optional[str], str, float, int]: """ ermittelt, ob und mit welchem Strom das EV geladen werden soll (unabhängig vom Lastmanagement) Parameter @@ -140,114 +145,78 @@ def get_required_current(self, required_current = None submode = None message = None + tmp_message = None state = True try: - if self.charge_template.data.chargemode.selected == "scheduled_charging": - if control_parameter.imported_at_plan_start is None: - control_parameter.imported_at_plan_start = imported - used_amount = imported - control_parameter.imported_at_plan_start - plan_data = self.charge_template.scheduled_charging_recent_plan( - self.data.get.soc, - self.ev_template, - control_parameter.phases, - used_amount, - max_phases_hw, - phase_switch_supported, - charging_type) - soc_request_interval_offset = 0 - if plan_data: - # Wenn mit einem neuen Plan geladen wird, muss auch die Energiemenge von neuem gezählt werden. - if (self.charge_template.data.chargemode.scheduled_charging.plans[str(plan_data.id)].limit. - selected == "amount" and - plan_data.id != control_parameter.current_plan): - control_parameter.imported_at_plan_start = imported - # Wenn der SoC ein paar Minuten alt ist, kann der Termin trotzdem gehalten werden. - # Zielladen kann nicht genauer arbeiten, als das Abfrageintervall vom SoC. - if (self.soc_module and - self.charge_template.data.chargemode. - scheduled_charging.plans[str(plan_data.id)].limit.selected == "soc"): - soc_request_interval_offset = self.soc_module.general_config.request_interval_charging - control_parameter.current_plan = plan_data.id + if charge_template.data.chargemode.selected == "stop": + required_current, submode, message = charge_template.stop() + phases = control_parameter.phases or max_phases_hw + else: + # Wenn der SoC ein paar Minuten alt ist, kann der Termin trotzdem gehalten werden. + # Zielladen kann nicht genauer arbeiten, als das Abfrageintervall vom SoC. + if self.soc_module: + soc_request_interval_offset = self.soc_module.general_config.request_interval_charging else: - control_parameter.current_plan = None - required_current, submode, message, phases = self.charge_template.scheduled_charging_calc_current( - plan_data, - self.data.get.soc, - used_amount, - control_parameter.phases, - control_parameter.min_current, - soc_request_interval_offset) - - # Wenn Zielladen auf Überschuss wartet, prüfen, ob Zeitladen aktiv ist. - if (submode != "instant_charging" and - self.charge_template.data.time_charging.active): - if control_parameter.imported_at_plan_start is None: - control_parameter.imported_at_plan_start = imported - used_amount = imported - control_parameter.imported_at_plan_start - tmp_current, tmp_submode, tmp_message, plan_id = self.charge_template.time_charging( - self.data.get.soc, - used_amount, - charging_type - ) - # Info vom Zielladen erhalten - message = f"{message or ''} {tmp_message or ''}".strip() - if tmp_current > 0: - # Wenn mit einem neuen Plan geladen wird, muss auch die Energiemenge von neuem gezählt werden. - if plan_id != control_parameter.current_plan: - control_parameter.imported_at_plan_start = imported - control_parameter.current_plan = plan_id - required_current = tmp_current - submode = tmp_submode - if (required_current == 0) or (required_current is None): - if self.charge_template.data.chargemode.selected == "instant_charging": - # Wenn der Submode auf stop gestellt wird, wird auch die Energiemenge seit Wechsel des Modus - # zurückgesetzt, dann darf nicht die Energiemenge erneute geladen werden. - if control_parameter.imported_instant_charging is None: - control_parameter.imported_instant_charging = imported - used_amount = imported - control_parameter.imported_instant_charging - required_current, submode, message = self.charge_template.instant_charging( + soc_request_interval_offset = 0 + if charge_template.data.chargemode.selected == "scheduled_charging": + required_current, submode, tmp_message, phases = charge_template.scheduled_charging( + self.data.get.soc, + self.ev_template, + imported_since_plugged, + max_phases_hw, + phase_switch_supported, + charging_type, + chargemode_switch_timestamp, + control_parameter, + soc_request_interval_offset, + bidi) + message = f"{tmp_message or ''}".strip() + + # Wenn Zielladen auf Überschuss wartet, prüfen, ob Zeitladen aktiv ist. + if (submode != "instant_charging" and + charge_template.data.time_charging.active): + tmp_current, tmp_submode, tmp_message, plan_id, tmp_phases = charge_template.time_charging( self.data.get.soc, - used_amount, - charging_type) - elif self.charge_template.data.chargemode.selected == "pv_charging": - required_current, submode, message = self.charge_template.pv_charging( - self.data.get.soc, control_parameter.min_current, charging_type) - elif self.charge_template.data.chargemode.selected == "standby": - # Text von Zeit-und Zielladen nicht überschreiben. - if message is None: - required_current, submode, message = self.charge_template.standby() + imported_since_plugged, + charging_type + ) + # Info vom Zielladen erhalten + message = f"{message or ''} {tmp_message or ''}".strip() + if tmp_current > 0: + control_parameter.current_plan = plan_id + required_current = tmp_current + submode = tmp_submode + phases = tmp_phases + if (required_current == 0) or (required_current is None): + if charge_template.data.chargemode.selected == "instant_charging": + required_current, submode, tmp_message, phases = charge_template.instant_charging( + self.data.get.soc, + imported_since_plugged, + charging_type) + elif charge_template.data.chargemode.selected == "pv_charging": + required_current, submode, tmp_message, phases = charge_template.pv_charging( + self.data.get.soc, control_parameter.min_current, charging_type, imported_since_plugged) + elif charge_template.data.chargemode.selected == "eco_charging": + required_current, submode, tmp_message, phases = charge_template.eco_charging( + self.data.get.soc, control_parameter, charging_type, imported_since_plugged, max_phases_hw) else: - required_current, submode, _ = self.charge_template.standby() - elif self.charge_template.data.chargemode.selected == "stop": - required_current, submode, message = self.charge_template.stop() - if submode == "stop" or submode == "standby" or (self.charge_template.data.chargemode.selected == "stop"): + tmp_message = None + message = f"{message or ''} {tmp_message or ''}".strip() + if submode == "stop" or (charge_template.data.chargemode.selected == "stop"): state = False - if phases is None: - phases = control_parameter.phases + if phases is None: + log.debug("Keine Phasenvorgabe durch Lademodus. Behalte Phasenzahl bei.") + phases = control_parameter.phases return state, message, submode, required_current, phases except Exception as e: log.exception("Fehler im ev-Modul "+str(self.num)) return (False, f"Kein Ladevorgang, da ein Fehler aufgetreten ist: {' '.join(e.args)}", "stop", 0, control_parameter.phases) - def set_chargemode_changed(self, control_parameter: ControlParameter, submode: str) -> None: - if ((submode == "time_charging" and control_parameter.chargemode != "time_charging") or - (submode != "time_charging" and - control_parameter.chargemode != self.charge_template.data.chargemode.selected)): - self.chargemode_changed = True - log.debug("Änderung des Lademodus") - else: - self.chargemode_changed = False - - def set_submode_changed(self, control_parameter: ControlParameter, submode: str) -> None: - self.submode_changed = (submode != control_parameter.submode) - def check_min_max_current(self, - control_parameter: ControlParameter, required_current: float, phases: int, - charging_type: str, - pv: bool = False,) -> Tuple[float, Optional[str]]: + charging_type: str) -> Tuple[float, Optional[str]]: """ prüft, ob der gesetzte Ladestrom über dem Mindest-Ladestrom und unter dem Maximal-Ladestrom des EVs liegt. Falls nicht, wird der Ladestrom auf den Mindest-Ladestrom bzw. den Maximal-Ladestrom des EV gesetzt. Wenn PV-Laden aktiv ist, darf die Stromstärke nicht unter den PV-Mindeststrom gesetzt werden. @@ -258,13 +227,10 @@ def check_min_max_current(self, if phases != 0: # EV soll/darf nicht laden if required_current != 0: - if not pv: - if charging_type == ChargingType.AC.value: - min_current = self.ev_template.data.min_current - else: - min_current = self.ev_template.data.dc_min_current + if charging_type == ChargingType.AC.value: + min_current = self.ev_template.data.min_current else: - min_current = control_parameter.required_current + min_current = self.ev_template.data.dc_min_current if required_current < min_current: required_current = min_current msg = ("Die Einstellungen in dem Fahrzeug-Profil beschränken den Strom auf " @@ -289,11 +255,12 @@ def check_min_max_current(self, NOT_ENOUGH_POWER = ", da nicht ausreichend Überschuss für mehrphasiges Laden zur Verfügung steht." def _check_phase_switch_conditions(self, + charge_template: ChargeTemplate, control_parameter: ControlParameter, get_currents: List[float], get_power: float, max_current_cp: int, - limit: LimitingValue) -> Tuple[bool, Optional[str]]: + limit: LoadmanagementLimit) -> Tuple[bool, Optional[str]]: # Manche EV laden mit 6.1A bei 6A Soll-Strom min_current = (max(control_parameter.min_current, control_parameter.required_current) + self.ev_template.data.nominal_difference) @@ -302,16 +269,18 @@ def _check_phase_switch_conditions(self, phases_in_use = control_parameter.phases pv_config = data.data.general_data.data.chargemode_config.pv_charging max_phases_ev = self.ev_template.data.max_phases - if self.charge_template.data.chargemode.pv_charging.feed_in_limit: + if charge_template.data.chargemode.pv_charging.feed_in_limit: feed_in_yield = pv_config.feed_in_yield else: feed_in_yield = 0 all_surplus = data.data.counter_all_data.get_evu_counter().get_usable_surplus(feed_in_yield) required_surplus = control_parameter.min_current * max_phases_ev * 230 - get_power - condition_1_to_3 = (((max(get_currents) > max_current and - all_surplus > required_surplus) or limit == LimitingValue.UNBALANCED_LOAD.value) and + unbalanced_load_limit_reached = limit.limiting_value == LimitingValue.UNBALANCED_LOAD + condition_1_to_3 = (((get_medium_charging_current(get_currents) > max_current and + all_surplus > required_surplus) or unbalanced_load_limit_reached) and phases_in_use == 1) - condition_3_to_1 = max(get_currents) < min_current and all_surplus <= 0 and phases_in_use > 1 + condition_3_to_1 = get_medium_charging_current( + get_currents) < min_current and all_surplus <= 0 and phases_in_use > 1 if condition_1_to_3 or condition_3_to_1: return True, None else: @@ -325,21 +294,21 @@ def _check_phase_switch_conditions(self, PHASE_SWITCH_DELAY_TEXT = '{} Phasen in {}.' def auto_phase_switch(self, + charge_template: ChargeTemplate, control_parameter: ControlParameter, cp_num: int, get_currents: List[float], get_power: float, max_current_cp: int, max_phases: int, - limit: LimitingValue) -> Tuple[int, float, Optional[str]]: + limit: LoadmanagementLimit) -> Tuple[int, float, Optional[str]]: message = None - timestamp_last_phase_switch = control_parameter.timestamp_last_phase_switch current = control_parameter.required_current phases_to_use = control_parameter.phases phases_in_use = control_parameter.phases pv_config = data.data.general_data.data.chargemode_config.pv_charging cm_config = data.data.general_data.data.chargemode_config - if self.charge_template.data.chargemode.pv_charging.feed_in_limit: + if charge_template.data.chargemode.pv_charging.feed_in_limit: feed_in_yield = pv_config.feed_in_yield else: feed_in_yield = 0 @@ -352,19 +321,22 @@ def auto_phase_switch(self, new_phase = max_phases new_current = control_parameter.min_current + waiting_time = pv_config.switch_on_delay else: direction_str = f"Umschaltung von {max_phases} auf 1" # Es kann einphasig mit entsprechend niedriger Leistung gestartet werden. required_reserved_power = 0 new_phase = 1 new_current = self.ev_template.data.max_current_single_phase + waiting_time = pv_config.switch_off_delay log.debug( - f'Genutzter Strom: {max(get_currents)}A, Überschuss: {all_surplus}W, benötigte neue Leistung: ' - f'{required_reserved_power}W') + f'Genutzter Strom: {get_medium_charging_current(get_currents)}A, Überschuss: {all_surplus}W, benötigte ' + f'neue Leistung: {required_reserved_power}W') # Wenn gerade umgeschaltet wird, darf kein Timer gestartet werden. if not self.ev_template.data.prevent_phase_switch: - condition, condition_msg = self._check_phase_switch_conditions(control_parameter, + condition, condition_msg = self._check_phase_switch_conditions(charge_template, + control_parameter, get_currents, get_power, max_current_cp, @@ -377,17 +349,21 @@ def auto_phase_switch(self, ).data.set.reserved_surplus += max(0, required_reserved_power) message = self.PHASE_SWITCH_DELAY_TEXT.format( direction_str, - timecheck.convert_timestamp_delta_to_time_string(timestamp_last_phase_switch, delay)) + self._remaining_phase_switch_time(control_parameter, + waiting_time, + delay)[1]) control_parameter.state = ChargepointState.PHASE_SWITCH_DELAY elif condition_msg: log.debug(f"Keine Phasenumschaltung{condition_msg}") else: if condition: # Timer laufen lassen - if timecheck.check_timestamp(control_parameter.timestamp_last_phase_switch, delay): - message = self.PHASE_SWITCH_DELAY_TEXT.format( - direction_str, - timecheck.convert_timestamp_delta_to_time_string(timestamp_last_phase_switch, delay)) + timestamp_passed, remaining_time = self._remaining_phase_switch_time( + control_parameter, + waiting_time, + delay) + if timestamp_passed is False: + message = self.PHASE_SWITCH_DELAY_TEXT.format(direction_str, remaining_time) else: data.data.counter_all_data.get_evu_counter( ).data.set.reserved_surplus -= max(0, required_reserved_power) @@ -405,6 +381,36 @@ def auto_phase_switch(self, log.info(f"LP {cp_num}: {message}") return phases_to_use, current, message + def _remaining_phase_switch_time(self, control_parameter: ControlParameter, + waiting_time: float, + buffer: float) -> float: + + if control_parameter.timestamp_phase_switch_buffer_start is None: + control_parameter.timestamp_phase_switch_buffer_start = timecheck.create_timestamp() + # Wenn der Puffer seit der letzen Umschaltung abgelaufen ist, warte noch die Umschaltverzögerung ab. ODER + # Wenn der Puffer noch nicht abgelaufen ist und Wartezeit länger als Pufferzeit, dann warte Wartezeit ab + remaining_buffer = (buffer - (control_parameter.timestamp_phase_switch_buffer_start - + control_parameter.timestamp_last_phase_switch)) + if remaining_buffer < 0 or remaining_buffer < waiting_time: + if timecheck.check_timestamp(control_parameter.timestamp_phase_switch_buffer_start, waiting_time): + remaining_time = timecheck.convert_timestamp_delta_to_time_string( + control_parameter.timestamp_phase_switch_buffer_start, waiting_time) + log.debug(f"Warte verbleibende Wartezeit ab: {remaining_time}") + return False, remaining_time + else: + control_parameter.timestamp_phase_switch_buffer_start = None + return True, None + else: + # Puffer noch nicht abgelaufen, verbleibende Pufferzeit ist länger als Wartezeit + if timecheck.check_timestamp(control_parameter.timestamp_last_phase_switch, buffer): + remaining_time = timecheck.convert_timestamp_delta_to_time_string( + control_parameter.timestamp_last_phase_switch, buffer) + log.debug(f"Warte verbleibende Pufferzeit ab: {remaining_time}") + return False, remaining_time + else: + control_parameter.timestamp_phase_switch_buffer_start = None + return True, None + def reset_phase_switch(self, control_parameter: ControlParameter): """ Zurücksetzen der Zeitstempel und reservierten Leistung. @@ -428,13 +434,13 @@ def reset_phase_switch(self, control_parameter: ControlParameter): str(data.data.counter_all_data.get_evu_counter().data.set.reserved_surplus)) -def get_ev_to_rfid(rfid: str, vehicle_id: Optional[str] = None) -> Optional[int]: +def get_ev_to_rfid(rfid: Optional[str] = None, vehicle_id: Optional[str] = None) -> Optional[int]: """ ermittelt zum übergebenen ID-Tag das Fahrzeug Parameter --------- rfid: string - ID-Tag + ID-Tag vom RFID-Leser oder Display vehicle_id: string MAC-Adresse des ID-Tags (nur openWB Pro) @@ -443,15 +449,33 @@ def get_ev_to_rfid(rfid: str, vehicle_id: Optional[str] = None) -> Optional[int] vehicle: int Nummer des EV, das zum Tag gehört """ + if rfid is None and vehicle_id is None: + log.debug("Kein Fahrzeug zugeordnet, da weder RFID noch MAC übergeben wurden.") + return None for vehicle in data.data.ev_data: try: if "ev" in vehicle: - if vehicle_id is not None and vehicle_id in data.data.ev_data[vehicle].data.tag_id: + # exakte matches haben Priorität + # Vergleiche werden case-insensitive durchgeführt + # das vereinfacht die Eingabe, kann aber auch für falsche Treffer sorgen. + lowered_vehicle_tags = [tag.lower() for tag in data.data.ev_data[vehicle].data.tag_id] + if vehicle_id is not None and vehicle_id.lower() in lowered_vehicle_tags: log.debug(f"MAC {vehicle_id} wird EV {data.data.ev_data[vehicle].num} zugeordnet.") return data.data.ev_data[vehicle].num - if rfid in data.data.ev_data[vehicle].data.tag_id: + if rfid is not None and rfid.lower() in lowered_vehicle_tags: log.debug(f"RFID {rfid} wird EV {data.data.ev_data[vehicle].num} zugeordnet.") return data.data.ev_data[vehicle].num + # Prüfung auf ein passendes Muster + # auch 'fnmatch()' ist case-insensitive + for tag_id in data.data.ev_data[vehicle].data.tag_id: + if vehicle_id is not None and fnmatch(vehicle_id, tag_id): + log.debug(f"MAC {vehicle_id} und gespeicherte Tag_ID {tag_id} stimmen überein. " + f"EV {data.data.ev_data[vehicle].num} zugeordnet.") + return data.data.ev_data[vehicle].num + if rfid is not None and fnmatch(rfid, tag_id): + log.debug(f"RFID {rfid} und gespeicherte Tag_ID {tag_id} stimmen überein. " + f"EV {data.data.ev_data[vehicle].num} zugeordnet.") + return data.data.ev_data[vehicle].num except Exception: log.exception("Fehler im ev-Modul "+vehicle) return None diff --git a/packages/control/ev/ev_template.py b/packages/control/ev/ev_template.py index 8da234a1ff..0fb4e53fa1 100644 --- a/packages/control/ev/ev_template.py +++ b/packages/control/ev/ev_template.py @@ -3,8 +3,9 @@ @dataclass class EvTemplateData: - dc_min_current: int = 0 - dc_max_current: int = 0 + dc_min_current: int = 20 + dc_max_current: int = 150 + id: int = 0 name: str = "Fahrzeug-Profil" max_current_multi_phases: int = 16 max_phases: int = 3 @@ -20,6 +21,7 @@ class EvTemplateData: efficiency: float = 90 nominal_difference: float = 1 keep_charge_active_duration: int = 40 + bidi: bool = False def ev_template_data_factory() -> EvTemplateData: @@ -33,4 +35,3 @@ class EvTemplate: data: EvTemplateData = field(default_factory=ev_template_data_factory, metadata={ "topic": "config"}) - et_num: int = 0 diff --git a/packages/control/ev/ev_test.py b/packages/control/ev/ev_test.py index 638d08dec5..e4e91b2bc7 100644 --- a/packages/control/ev/ev_test.py +++ b/packages/control/ev/ev_test.py @@ -3,7 +3,10 @@ import pytest -from control.ev.ev import Ev +from control import data + +from control.chargepoint.control_parameter import ControlParameter +from control.ev.ev import Ev, get_ev_to_rfid from helpermodules import timecheck from modules.common.abstract_vehicle import VehicleUpdateData from modules.vehicles.mqtt.config import MqttSocSetup @@ -35,3 +38,87 @@ def test_soc_interval_expired(check_timestamp: bool, # evaluation assert request_soc == expected_request_soc + + +@pytest.mark.parametrize( + "timestamp_last_phase_switch, timestamp_phase_switch_buffer_start, expected_result", + [ + pytest.param(1652682881, None, (False, "30 Sek."), id="Puffer abgelaufen, Wartezeit noch nicht gestartet"), + pytest.param(1652682881, 1652683232, (False, "10 Sek."), id="Puffer abgelaufen, Wartezeit gestartet"), + pytest.param(1652682881, 1652683212, (True, None), id="Puffer abgelaufen, Wartezeit abgelaufen"), + pytest.param(1652682962, None, (False, "30 Sek."), + id="Puffer noch nicht abgelaufen, Wartezeit länger, Wartezeit noch nicht gestartet"), + pytest.param(1652682962, 1652683237, (False, "15 Sek."), + id="Puffer noch nicht abgelaufen, Wartezeit länger, Wartezeit gestartet"), + pytest.param(1652682932, 1652683220, (True, None), + id="Puffer noch nicht abgelaufen, Wartezeit länger, Wartezeit abgelaufen"), + pytest.param(1652683132, None, (False, "3 Min."), id="Puffer noch nicht abgelaufen, Puffer länger, abwarten"), + pytest.param(1652682950, 1652682972, (True, None), + id="Puffer noch nicht abgelaufen, Puffer länger, abgelaufen"), + ], +) +def test_remaining_phase_switch_time(timestamp_last_phase_switch, + timestamp_phase_switch_buffer_start, + expected_result): + # setup + ev = Ev(0) + control_parameter = ControlParameter() + control_parameter.timestamp_last_phase_switch = timestamp_last_phase_switch + control_parameter.timestamp_phase_switch_buffer_start = timestamp_phase_switch_buffer_start + + # execution + result = ev._remaining_phase_switch_time( + control_parameter=control_parameter, + waiting_time=30, + buffer=300, + ) + + # evaluation + assert result == expected_result + + +@pytest.fixture(autouse=True) +def data_module() -> None: + data.data_init(Mock()) + ev10 = Ev(10) + ev11 = Ev(11) + ev12 = Ev(12) + ev13 = Ev(13) + ev14 = Ev(14) + ev15 = Ev(15) + ev10.data.tag_id = ["0815", "abcxyz"] + ev11.data.tag_id = ["aa:bb:cc:dd:ee:ff", "abc*"] + ev12.data.tag_id = ["123*", "abc?"] + ev13.data.tag_id = ["234?"] + ev14.data.tag_id = ["aa:aa:aa*"] + ev15.data.tag_id = ["bb:bb:bb:??:??:??"] + data.data.ev_data = {"ev10": ev10, "ev11": ev11, "ev12": ev12, "ev13": ev13, "ev14": ev14, "ev15": ev15} + + +@pytest.mark.parametrize( + "rfid, vehicle_id, expected_result", + [ + pytest.param("0815", None, 10), + pytest.param(None, "aa:bb:cc:dd:ee:ff", 11), + pytest.param("1239", None, 12), + pytest.param("1234231cxv23ds23", None, 12), + pytest.param("2345", None, 13), + pytest.param("2348", None, 13), + pytest.param("23489", None, None), + pytest.param("aa:aa:aa124a", None, 14), + pytest.param("aa:aa:aa:bb:bb:bb", None, 14), + pytest.param("bb:bb:bb:ca:bc:de", None, 15), + pytest.param("bb:bb:ba:ca:bc:de", None, None), + pytest.param("abcxyz", None, 10), + pytest.param("unknownTag", None, None), + pytest.param(None, "unknownID", None), + ], +) +def test_ev_identification(rfid, vehicle_id, expected_result): + # setup + + # execution + result = get_ev_to_rfid(rfid, vehicle_id) + + # evaluation + assert result == expected_result diff --git a/packages/control/general.py b/packages/control/general.py index 7950eada3d..cb862a0d05 100644 --- a/packages/control/general.py +++ b/packages/control/general.py @@ -1,33 +1,17 @@ """Allgemeine Einstellungen """ from dataclasses import dataclass, field -from enum import Enum import logging import random -from typing import Dict, List, Optional +from typing import List, Optional from control import data from control.bat_all import BatConsiderationMode -from control.chargemode import Chargemode -from helpermodules.constants import NO_ERROR from helpermodules import timecheck -from modules.common.configurable_ripple_control_receiver import ConfigurableRcr -from modules.ripple_control_receivers.gpio.config import GpioRcr -from modules.ripple_control_receivers.gpio.ripple_control_receiver import create_ripple_control_receiver log = logging.getLogger(__name__) -@dataclass -class InstantCharging: - phases_to_use: int = field(default=1, metadata={ - "topic": "chargemode_config/instant_charging/phases_to_use"}) - - -def instant_charging_factory() -> InstantCharging: - return InstantCharging() - - def control_range_factory() -> List: return [0, 230] @@ -44,8 +28,6 @@ class PvCharging: "topic": "chargemode_config/pv_charging/feed_in_yield"}) phase_switch_delay: int = field(default=7, metadata={ "topic": "chargemode_config/pv_charging/phase_switch_delay"}) - phases_to_use: int = field(default=1, metadata={ - "topic": "chargemode_config/pv_charging/phases_to_use"}) bat_power_discharge: int = field(default=1500, metadata={ "topic": "chargemode_config/pv_charging/bat_power_discharge"}) bat_power_discharge_active: bool = field(default=False, metadata={ @@ -56,7 +38,7 @@ class PvCharging: "topic": "chargemode_config/pv_charging/bat_mode"}) switch_off_delay: int = field(default=60, metadata={ "topic": "chargemode_config/pv_charging/switch_off_delay"}) - switch_off_threshold: int = field(default=5, metadata={ + switch_off_threshold: int = field(default=0, metadata={ "topic": "chargemode_config/pv_charging/switch_off_threshold"}) switch_on_delay: int = field(default=30, metadata={ "topic": "chargemode_config/pv_charging/switch_on_delay"}) @@ -68,39 +50,14 @@ def pv_charging_factory() -> PvCharging: return PvCharging() -@dataclass -class ScheduledCharging: - phases_to_use: int = field(default=0, metadata={ - "topic": "chargemode_config/scheduled_charging/phases_to_use"}) - phases_to_use_pv: int = field(default=0, metadata={ - "topic": "chargemode_config/scheduled_charging/phases_to_use_pv"}) - - -def scheduled_charging_factory() -> ScheduledCharging: - return ScheduledCharging() - - -@dataclass -class TimeCharging: - phases_to_use: int = field(default=1, metadata={ - "topic": "chargemode_config/time_charging/phases_to_use"}) - - -def time_charging_factory() -> TimeCharging: - return TimeCharging() - - @dataclass class ChargemodeConfig: - instant_charging: InstantCharging = field(default_factory=instant_charging_factory) phase_switch_delay: int = field(default=5, metadata={ "topic": "chargemode_config/phase_switch_delay"}) pv_charging: PvCharging = field(default_factory=pv_charging_factory) retry_failed_phase_switches: bool = field( default=False, metadata={"topic": "chargemode_config/retry_failed_phase_switches"}) - scheduled_charging: ScheduledCharging = field(default_factory=scheduled_charging_factory) - time_charging: TimeCharging = field(default_factory=time_charging_factory) unbalanced_load_limit: int = field( default=18, metadata={"topic": "chargemode_config/unbalanced_load_limit"}) unbalanced_load: bool = field(default=False, metadata={ @@ -111,42 +68,6 @@ def chargemode_config_factory() -> ChargemodeConfig: return ChargemodeConfig() -@dataclass -class RippleControlReceiverGet: - fault_state: int = field(default=0, metadata={ - "topic": "ripple_control_receiver/get/fault_state"}) - fault_str: str = field(default=NO_ERROR, metadata={ - "topic": "ripple_control_receiver/get/fault_str"}) - override_value: float = field(default=100, metadata={ - "topic": "ripple_control_receiver/get/override_value"}) - - -def rcr_get_factory() -> RippleControlReceiverGet: - return RippleControlReceiverGet() - - -def gpio_rcr_factory() -> ConfigurableRcr: - return create_ripple_control_receiver(GpioRcr()) - - -class OverrideReference(Enum): - EVU = "evu" - CHARGEPOINT = "chargepoint" - - -@dataclass -class RippleControlReceiver: - get: RippleControlReceiverGet = field(default_factory=rcr_get_factory) - module: Optional[Dict] = field(default=None, metadata={ - "topic": "ripple_control_receiver/module"}) - override_reference: OverrideReference = field(default=OverrideReference.CHARGEPOINT, metadata={ - "topic": "ripple_control_receiver/override_reference"}) - - -def ripple_control_receiver_factory() -> RippleControlReceiver: - return RippleControlReceiver() - - @dataclass class Prices: bat: float = field(default=0.0002, metadata={"topic": "prices/bat"}) @@ -181,7 +102,6 @@ class GeneralData: mqtt_bridge: bool = False prices: Prices = field(default_factory=prices_factory) range_unit: str = "km" - ripple_control_receiver: RippleControlReceiver = field(default_factory=ripple_control_receiver_factory) class General: @@ -190,26 +110,6 @@ class General: def __init__(self): self.data: GeneralData = GeneralData() - self.ripple_control_receiver: ConfigurableRcr = None - - def get_phases_chargemode(self, chargemode: str, submode: str) -> Optional[int]: - """ gibt die Anzahl Phasen zurück, mit denen im jeweiligen Lademodus geladen wird. - Wenn der Lademodus Stop oder Standby ist, wird 0 zurückgegeben, da in diesem Fall - die bisher genutzte Phasenzahl weiter genutzt wird, bis der Algorithmus eine Umschaltung vorgibt. - """ - try: - if chargemode == "stop" or chargemode == "standby": - # bei diesen Lademodi kann die bisherige Phasenzahl beibehalten werden. - return None - elif chargemode == "scheduled_charging" and (submode == "pv_charging" or submode == Chargemode.PV_CHARGING): - # todo Lademodus von String auf Enum umstellen - # Phasenumschaltung bei PV-Ueberschuss nutzen - return getattr(self.data.chargemode_config, chargemode).phases_to_use_pv - else: - return getattr(self.data.chargemode_config, chargemode).phases_to_use - except Exception: - log.exception("Fehler im General-Modul") - return 1 def grid_protection(self): """ Wenn der Netzschutz konfiguriert ist, wird geprüft, ob die Frequenz außerhalb des Normalbereichs liegt diff --git a/packages/control/io_device.py b/packages/control/io_device.py new file mode 100644 index 0000000000..c045a80c92 --- /dev/null +++ b/packages/control/io_device.py @@ -0,0 +1,111 @@ +from dataclasses import dataclass, field +from typing import Dict, Optional, Union +from control import data +from control.limiting_value import LimitingValue +from helpermodules.constants import NO_ERROR +from modules.common.utils.component_parser import get_io_name_by_id +from modules.io_actions.controllable_consumers.dimming.api import Dimming +from modules.io_actions.controllable_consumers.dimming_direct_control.api import DimmingDirectControl +from modules.io_actions.controllable_consumers.ripple_control_receiver.api import RippleControlReceiver +from modules.io_actions.generator_systems.stepwise_control.api import StepwiseControl + + +@dataclass +class Get: + analog_input: Dict[int, float] = None + analog_output: Dict[int, float] = None + digital_input: Dict[int, bool] = None + digital_output: Dict[int, bool] = None + analog_input_prev: Dict[int, float] = None + analog_output_prev: Dict[int, float] = None + digital_input_prev: Dict[int, bool] = None + digital_output_prev: Dict[int, bool] = None + fault_str: str = NO_ERROR + fault_state: int = 0 + + +def get_factory(): + return Get() + + +@dataclass +class Set: + analog_output: Dict[int, float] = None + analog_output_prev: Dict[int, float] = None + digital_output: Dict[int, bool] = None + digital_output_prev: Dict[int, bool] = None + + +def set_factory(): + return Set() + + +@dataclass +class IoDeviceData: + get: Get = field(default_factory=get_factory) + set: Set = field(default_factory=set_factory) + + +class IoStates: + def __init__(self, num: Union[int, str]): + self.num = num + self.data = IoDeviceData() + + +class IoActions: + def __init__(self): + self.actions: Dict[int, Union[Dimming, DimmingDirectControl, RippleControlReceiver, StepwiseControl]] = {} + + def setup(self): + for action in self.actions.values(): + action.setup() + + def _check_fault_state_io_device(self, io_device: int) -> None: + if data.data.io_states[f"io_states{io_device}"].data.get.fault_state == 2: + raise ValueError(LimitingValue.CONTROLLABLE_CONSUMERS_ERROR.value.format(get_io_name_by_id(io_device))) + + def dimming_get_import_power_left(self, device: Dict) -> Optional[float]: + for action in self.actions.values(): + if isinstance(action, Dimming): + for d in action.config.configuration.devices: + if device == d: + self._check_fault_state_io_device(action.config.configuration.io_device) + return action.dimming_get_import_power_left() + else: + return None + + def dimming_set_import_power_left(self, device: Dict, used_power: float) -> Optional[float]: + for action in self.actions.values(): + if isinstance(action, Dimming): + for d in action.config.configuration.devices: + if d == device: + return action.dimming_set_import_power_left(used_power) + + def dimming_via_direct_control(self, device: Dict) -> Optional[float]: + for action in self.actions.values(): + if isinstance(action, DimmingDirectControl): + for d in action.config.configuration.devices: + if device == d: + self._check_fault_state_io_device(action.config.configuration.io_device) + return action.dimming_via_direct_control() + else: + return None + + def ripple_control_receiver(self, device: Dict) -> float: + for action in self.actions.values(): + if isinstance(action, RippleControlReceiver): + for d in action.config.configuration.devices: + if device == d: + self._check_fault_state_io_device(action.config.configuration.io_device) + return action.ripple_control_receiver() + else: + return 1 + + def stepwise_control(self, device_id: int) -> Optional[float]: + for action in self.actions.values(): + if isinstance(action, StepwiseControl): + if device_id in [component["id"] for component in action.config.configuration.devices]: + self._check_fault_state_io_device(action.config.configuration.io_device) + return action.control_stepwise() + else: + return None diff --git a/packages/control/limiting_value.py b/packages/control/limiting_value.py index 6d114caee1..8c19b321d3 100644 --- a/packages/control/limiting_value.py +++ b/packages/control/limiting_value.py @@ -1,7 +1,25 @@ +from dataclasses import dataclass from enum import Enum +from typing import Optional class LimitingValue(Enum): CURRENT = ", da der Maximal-Strom an Zähler {} erreicht ist." POWER = ", da die maximale Leistung an Zähler {} erreicht ist." UNBALANCED_LOAD = ", da die maximale Schieflast an Zähler {} erreicht ist." + DIMMING = ", da die Dimmung die Ladeleistung begrenzt." + DIMMING_VIA_DIRECT_CONTROL = ", da die Dimmung per Direkt-Steuerung die Ladeleistung auf 4,2 kW begrenzt." + RIPPLE_CONTROL_RECEIVER = (", da der Ladepunkt durch den RSE-Kontakt auf {}% der konfigurierten Anschlussleistung " + "reduziert wird.") + CONTROLLABLE_CONSUMERS_ERROR = (", da aufgrund eines Fehlers im IO-Gerät {} die steuerbaren Verbraucher nicht " + "gesteuert werden können. Bitte prüfe die Status-Seite.") + + +@dataclass +class LoadmanagementLimit: + message: Optional[str] + limiting_value: Optional[LimitingValue] + + +def loadmanagement_limit_factory() -> LoadmanagementLimit: + return LoadmanagementLimit(None, None) diff --git a/packages/control/loadmanagement.py b/packages/control/loadmanagement.py index 649a81061b..24f431a055 100644 --- a/packages/control/loadmanagement.py +++ b/packages/control/loadmanagement.py @@ -3,8 +3,10 @@ from typing import List, Optional, Tuple from control import data +from control.chargepoint.chargepoint import Chargepoint from control.counter import Counter -from control.limiting_value import LimitingValue +from control.limiting_value import LimitingValue, LoadmanagementLimit +from modules.common.utils.component_parser import get_component_name_by_id log = logging.getLogger(__name__) @@ -14,84 +16,163 @@ class Loadmanagement: def get_available_currents(self, missing_currents: List[float], counter: Counter, - feed_in: int = 0) -> Tuple[List[float], Optional[LimitingValue]]: + cp: Chargepoint, + feed_in: int = 0) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left = counter.data.set.raw_currents_left - available_currents, limit = self._limit_by_current(missing_currents, raw_currents_left) - available_currents, limit_power = self._limit_by_power( - available_currents, counter.data.set.raw_power_left, feed_in) - if limit_power is not None: - limit = limit_power + try: + available_currents, limit = self._limit_by_dimming_via_direct_control(missing_currents, cp) + + available_currents, new_limit = self._limit_by_dimming(available_currents, cp) + limit = new_limit if new_limit.limiting_value is not None else limit + + available_currents, new_limit = self._limit_by_ripple_control_receiver(available_currents, cp) + limit = new_limit if new_limit.limiting_value is not None else limit + except ValueError as e: + available_currents = [0]*3 + limit = LoadmanagementLimit(e.args[0], e) + + available_currents, new_limit = self._limit_by_current(counter, available_currents, raw_currents_left) + limit = new_limit if new_limit.limiting_value is not None else limit + + available_currents, new_limit = self._limit_by_power( + counter, available_currents, cp.data.get.voltages, counter.data.set.raw_power_left, feed_in) + limit = new_limit if new_limit.limiting_value is not None else limit + if f"counter{counter.num}" == data.data.counter_all_data.get_evu_counter_str(): - available_currents, limit_unbalanced_load = self._limit_by_unbalanced_load( + available_currents, new_limit = self._limit_by_unbalanced_load( counter, available_currents, raw_currents_left, len([value for value in missing_currents if value != 0])) - if limit_unbalanced_load is not None: - limit = limit_unbalanced_load + limit = new_limit if new_limit.limiting_value is not None else limit return available_currents, limit def get_available_currents_surplus(self, missing_currents: List[float], + cp_voltages: List[float], counter: Counter, - feed_in: int = 0) -> Tuple[List[float], Optional[LimitingValue]]: + cp: Chargepoint, + feed_in: int = 0) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left = counter.data.set.raw_currents_left - available_currents, limit = self._limit_by_current(missing_currents, raw_currents_left) - available_currents, limit_power = self._limit_by_power( - available_currents, counter.data.set.surplus_power_left, feed_in) - if limit_power is not None: - limit = limit_power + available_currents, limit = self._limit_by_dimming_via_direct_control(missing_currents, cp) + + available_currents, new_limit = self._limit_by_ripple_control_receiver(available_currents, cp) + limit = new_limit if new_limit.limiting_value is not None else limit + + available_currents, new_limit = self._limit_by_current(counter, available_currents, raw_currents_left) + limit = new_limit if new_limit.limiting_value is not None else limit + + available_currents, new_limit = self._limit_by_power( + counter, available_currents, cp_voltages, counter.data.set.surplus_power_left, feed_in) + limit = new_limit if new_limit.limiting_value is not None else limit + if f"counter{counter.num}" == data.data.counter_all_data.get_evu_counter_str(): - available_currents, limit_unbalanced_load = self._limit_by_unbalanced_load( + available_currents, new_limit = self._limit_by_unbalanced_load( counter, available_currents, raw_currents_left, len([value for value in missing_currents if value != 0])) - if limit_unbalanced_load is not None: - limit = limit_unbalanced_load + limit = new_limit if new_limit.limiting_value is not None else limit return available_currents, limit def _limit_by_unbalanced_load(self, counter: Counter, available_currents: List[float], raw_currents_left: List[float], - phases_to_use: int) -> Tuple[List[float], Optional[LimitingValue]]: + phases_to_use: int) -> Tuple[List[float], LoadmanagementLimit]: raw_currents_left_charging = list(map(operator.sub, raw_currents_left, available_currents)) max_exceeding = counter.get_unbalanced_load_exceeding(raw_currents_left_charging) - limit = None + limit = LoadmanagementLimit(None, None) if max(max_exceeding) > 0: if phases_to_use < 3 and phases_to_use > 0: available_currents = list(map(operator.sub, available_currents, max_exceeding)) log.debug(f"Schieflast {max_exceeding}A korrigieren: {available_currents}") - limit = LimitingValue.UNBALANCED_LOAD + limit = LoadmanagementLimit( + LimitingValue.UNBALANCED_LOAD.value.format(get_component_name_by_id(counter.num)), + LimitingValue.UNBALANCED_LOAD) elif phases_to_use == 3: log.debug("Schieflastkorrektur nicht möglich, da alle Phasen genutzt werden.") return available_currents, limit # tested def _limit_by_power(self, + counter: Counter, available_currents: List[float], + cp_voltages: List[float], raw_power_left: Optional[float], - feed_in: Optional[float]) -> Tuple[List[float], Optional[LimitingValue]]: + feed_in: Optional[float]) -> Tuple[List[float], LoadmanagementLimit]: currents = available_currents.copy() - limit = None + limit = LoadmanagementLimit(None, None) if raw_power_left: if feed_in: raw_power_left = raw_power_left - feed_in log.debug(f"Verbleibende Leistung unter Berücksichtigung der Einspeisegrenze: {raw_power_left}W") - if sum(available_currents)*230 > raw_power_left: + if sum([c * v for c, v in zip(available_currents, cp_voltages)]) > raw_power_left: for i in range(0, 3): # Am meisten belastete Phase trägt am meisten zur Leistungsreduktion bei. - currents[i] = available_currents[i] / sum(available_currents) * raw_power_left / 230 + currents[i] = available_currents[i] / sum(available_currents) * raw_power_left / cp_voltages[i] log.debug(f"Leistungsüberschreitung auf {raw_power_left}W korrigieren: {available_currents}") - limit = LimitingValue.POWER + limit = LoadmanagementLimit(LimitingValue.POWER.value.format(get_component_name_by_id(counter.num)), + LimitingValue.POWER) return currents, limit # tested def _limit_by_current(self, + counter: Counter, missing_currents: List[float], - raw_currents_left: List[float]) -> Tuple[List[float], Optional[LimitingValue]]: + raw_currents_left: List[float]) -> Tuple[List[float], LoadmanagementLimit]: available_currents = [0.0]*3 - limit = None + limit = LoadmanagementLimit(None, None) for i in range(0, 3): available_currents[i] = min(missing_currents[i], raw_currents_left[i]) if available_currents != missing_currents: log.debug(f"Stromüberschreitung {missing_currents}W korrigieren: {available_currents}") - limit = LimitingValue.CURRENT + limit = LoadmanagementLimit(LimitingValue.CURRENT.value.format(get_component_name_by_id(counter.num)), + LimitingValue.CURRENT) return available_currents, limit + + def _limit_by_dimming_via_direct_control(self, + missing_currents: List[float], + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: + if data.data.io_actions.dimming_via_direct_control({"type": "cp", "id": cp.num}): + phases = 3-missing_currents.count(0) + current_per_phase = 4200 / 230 / phases + available_currents = [current_per_phase - + cp.data.set.target_current if c > 0 else 0 for c in missing_currents] + log.debug(f"Dimmung per Direkt-Steuerung: {available_currents}A") + limit = LoadmanagementLimit(LimitingValue.DIMMING_VIA_DIRECT_CONTROL.value, + LimitingValue.DIMMING_VIA_DIRECT_CONTROL) + return available_currents, limit + else: + return missing_currents, LoadmanagementLimit(None, None) + + def _limit_by_dimming(self, + available_currents: List[float], + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: + dimming_power_left = data.data.io_actions.dimming_get_import_power_left({"type": "cp", "id": cp.num}) + if dimming_power_left: + if sum(available_currents)*230 > dimming_power_left: + phases = 3-available_currents.count(0) + overload_per_phase = (sum(available_currents) - dimming_power_left/230)/phases + available_currents = [c - overload_per_phase if c > 0 else 0 for c in available_currents] + log.debug(f"Reduzierung der Ströme durch die Dimmung: {available_currents}A") + return available_currents, LoadmanagementLimit(LimitingValue.DIMMING.value, LimitingValue.DIMMING) + return available_currents, LoadmanagementLimit(None, None) + + def _limit_by_ripple_control_receiver(self, + available_currents: List[float], + cp: Chargepoint) -> Tuple[List[float], LoadmanagementLimit]: + value = data.data.io_actions.ripple_control_receiver({"type": "cp", "id": cp.num}) + if value != 1: + phases = 3-available_currents.count(0) + if phases > 1: + max_current = cp.template.data.max_current_single_phase + else: + max_current = cp.template.data.max_current_multi_phases + # target_current ist das Ergebnis der letzten Iteration. Die Differenz der begrenzten Anschlussleistung und + # der Sollstrom der letzten Iteration dürfen daher nicht größer sein als der aktuell fehlende Strom. + available_currents = [min(max_current*value - cp.data.set.target_current, c) + if c > 0 else 0 for c in available_currents] + log.debug(f"Reduzierung durch RSE-Kontakt auf {value*100}%, maximal {max_current*value}A") + limit = LoadmanagementLimit( + LimitingValue.RIPPLE_CONTROL_RECEIVER.value.format(value*100), + LimitingValue.RIPPLE_CONTROL_RECEIVER) + return available_currents, limit + else: + return available_currents, LoadmanagementLimit(None, None) diff --git a/packages/control/loadmanagement_test.py b/packages/control/loadmanagement_test.py index 0d143dcda5..33f1acd59e 100644 --- a/packages/control/loadmanagement_test.py +++ b/packages/control/loadmanagement_test.py @@ -1,22 +1,36 @@ from typing import List +from unittest.mock import Mock import pytest +from control import loadmanagement +from control.counter import Counter +from control.limiting_value import LoadmanagementLimit from control.loadmanagement import LimitingValue, Loadmanagement +COUNTER_NAME = "test counter" + @pytest.mark.parametrize( "available_currents, raw_power_left, expected_currents", [ - pytest.param([5, 10, 15], 6900, ([5, 10, 15], None)), + pytest.param([5, 10, 15], 6900, ([5, 10, 15], LoadmanagementLimit(None, None))), pytest.param([5, 10, 25], 1000, ([0.5434782608695652, 1.0869565217391304, - 2.717391304347826], LimitingValue.POWER)), + 2.717391304347826], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), + LimitingValue.POWER))), pytest.param([5, 10, 25], 5000, ([2.717391304347826, 5.434782608695652, - 13.58695652173913], LimitingValue.POWER)), + 13.58695652173913], LoadmanagementLimit(LimitingValue.POWER.value.format(COUNTER_NAME), + LimitingValue.POWER))), ]) -def test_limit_by_power(available_currents: List[float], raw_power_left: float, expected_currents: List[float]): - # setup & evaluation - currents = Loadmanagement()._limit_by_power(available_currents, raw_power_left, None) +def test_limit_by_power(available_currents: List[float], + raw_power_left: float, + expected_currents: List[float], + monkeypatch): + # setup + counter_name_mock = Mock(return_value=COUNTER_NAME) + monkeypatch.setattr(loadmanagement, "get_component_name_by_id", counter_name_mock) + # evaluation + currents = Loadmanagement()._limit_by_power(Counter(0), available_currents, [230]*3, raw_power_left, None) # assertion assert currents == expected_currents @@ -25,13 +39,17 @@ def test_limit_by_power(available_currents: List[float], raw_power_left: float, @pytest.mark.parametrize( "missing_currents, raw_currents_left, expected_currents", [ - pytest.param([5, 10, 15], [20]*3, ([5, 10, 15], None)), - pytest.param([5, 10, 15], [5, 8, 5], ([5, 8, 5], LimitingValue.CURRENT)), + pytest.param([5, 10, 15], [20]*3, ([5, 10, 15], LoadmanagementLimit(None, None))), + pytest.param([5, 10, 15], [5, 8, 5], ([5, 8, 5], LoadmanagementLimit( + LimitingValue.CURRENT.value.format(COUNTER_NAME), LimitingValue.CURRENT))), ]) def test_limit_by_current( - missing_currents: List[float], raw_currents_left: List[float], expected_currents: List[float]): - # setup & evaluation - currents = Loadmanagement()._limit_by_current(missing_currents, raw_currents_left) + missing_currents: List[float], raw_currents_left: List[float], expected_currents: List[float], monkeypatch): + # setup + counter_name_mock = Mock(return_value=COUNTER_NAME) + monkeypatch.setattr(loadmanagement, "get_component_name_by_id", counter_name_mock) + # evaluation + currents = Loadmanagement()._limit_by_current(Counter(0), missing_currents, raw_currents_left) # assertion assert currents == expected_currents diff --git a/packages/control/ocpp.py b/packages/control/ocpp.py index 7d5f3f7901..104d1c2dde 100644 --- a/packages/control/ocpp.py +++ b/packages/control/ocpp.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timezone import json import logging @@ -20,7 +20,7 @@ try: class OcppMixin: def _get_formatted_time(self: OptionalProtocol) -> str: - return datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ") + return datetime.now(timezone.utc).isoformat() def _process_call(self: OptionalProtocol, chargebox_id: str, @@ -74,10 +74,10 @@ def start_transaction(self: OptionalProtocol, timestamp=self._get_formatted_time() )) if ws: - tansaction_id = json.loads(ws.messages[0])[2]["transactionId"] - log.debug(f"Transaction ID: {tansaction_id} für Chargebox ID: {chargebox_id} mit Tag: {id_tag} und " - f"Zählerstand: {imported} erhalten.") - return tansaction_id + transaction_id = json.loads(ws.messages[0])[2]["transactionId"] + log.debug(f"Transaction ID: {transaction_id} für Chargebox ID: {chargebox_id} mit Tag: {id_tag} " + f"und Zählerstand: {imported} erhalten.") + return transaction_id except Exception as e: fault_state.from_exception(e) return None diff --git a/packages/control/optional.py b/packages/control/optional.py index dea75d3df8..96c1e66c57 100644 --- a/packages/control/optional.py +++ b/packages/control/optional.py @@ -1,8 +1,8 @@ """Optionale Module """ import logging -from math import ceil # Aufrunden -import threading +from math import ceil +from threading import Thread from typing import List from control import data @@ -39,21 +39,24 @@ def monitoring_stop(self): self.mon_module.stop_monitoring() def et_provider_available(self) -> bool: - return self.et_module is not None and self.data.et.get.fault_state != 2 + return self.et_module is not None - def et_price_lower_than_limit(self, max_price: float): - """ prüft, ob der aktuelle Strompreis unter der festgelegten Preisgrenze liegt. + def et_charging_allowed(self, max_price: float): + """ prüft, ob der aktuelle Strompreis niedriger oder gleich der festgelegten Preisgrenze ist. Return ------ - True: Preis liegt darunter + True: Preis ist gleich oder liegt darunter False: Preis liegt darüber """ try: - if self.et_get_current_price() <= max_price: - return True + if self.et_provider_available(): + if self.et_get_current_price() <= max_price: + return True + else: + return False else: - return False + return True except KeyError: log.exception("Fehler beim strompreisbasierten Laden") self.et_get_prices() @@ -62,7 +65,10 @@ def et_price_lower_than_limit(self, max_price: float): return False def et_get_current_price(self): - return self.data.et.get.prices[str(int(create_unix_timestamp_current_full_hour()))] + if self.et_provider_available(): + return self.data.et.get.prices[str(int(create_unix_timestamp_current_full_hour()))] + else: + raise Exception("Kein Anbieter für strompreisbasiertes Laden konfiguriert.") def et_get_loading_hours(self, duration: float, remaining_time: float) -> List[int]: """ @@ -75,6 +81,8 @@ def et_get_loading_hours(self, duration: float, remaining_time: float) -> List[i ------ list: Key des Dictionary (Unix-Sekunden der günstigen Stunden) """ + if self.et_provider_available() is False: + raise Exception("Kein Anbieter für strompreisbasiertes Laden konfiguriert.") try: prices = self.data.et.get.prices prices_in_scheduled_time = {} @@ -94,7 +102,7 @@ def et_get_loading_hours(self, duration: float, remaining_time: float) -> List[i def et_get_prices(self): try: if self.et_module: - thread_handler(threading.Thread(target=self.et_module.update, args=(), name="electricity tariff")) + thread_handler(Thread(target=self.et_module.update, args=(), name="electricity tariff")) else: # Wenn kein Modul konfiguriert ist, Fehlerstatus zurücksetzen. if self.data.et.get.fault_state != 0 or self.data.et.get.fault_str != NO_ERROR: @@ -106,7 +114,7 @@ def et_get_prices(self): def ocpp_transfer_meter_values(self): try: if self.data.ocpp.active: - thread_handler(threading.Thread(target=self._transfer_meter_values, args=(), name="OCPP Client")) + thread_handler(Thread(target=self._transfer_meter_values, args=(), name="OCPP Client")) except Exception: log.exception("Fehler im OCPP-Optional-Modul") @@ -114,7 +122,7 @@ def _transfer_meter_values(self): for cp in data.data.cp_data.values(): try: if self.data.ocpp.boot_notification_sent is False: - # Boot-Notfification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist + # Boot-Notification nicht in der init-Funktion aufrufen, da noch nicht alles initialisiert ist self.boot_notification(cp.data.config.ocpp_chargebox_id, cp.chargepoint_module.fault_state, cp.chargepoint_module.config.type, diff --git a/packages/control/optional_test.py b/packages/control/optional_test.py index f4ecf1211f..cc517ef7ae 100644 --- a/packages/control/optional_test.py +++ b/packages/control/optional_test.py @@ -1,10 +1,13 @@ +from unittest.mock import Mock from control.optional import Optional -def test_et_get_loading_hours(): +def test_et_get_loading_hours(monkeypatch): # setup opt = Optional() opt.data.et.get.prices = PRICE_LIST + mock_et_provider_available = Mock(return_value=True) + monkeypatch.setattr(opt, "et_provider_available", mock_et_provider_available) # execution loading_hours = opt.et_get_loading_hours(3600, 7200) diff --git a/packages/control/phase_switch.py b/packages/control/phase_switch.py index 40537e7e08..89d0edd407 100644 --- a/packages/control/phase_switch.py +++ b/packages/control/phase_switch.py @@ -1,7 +1,7 @@ """ Modul, das die Phasenumschaltung durchführt. """ import logging -import threading +from threading import Thread import time from control.ev.ev import Ev @@ -16,14 +16,14 @@ def thread_phase_switch(cp) -> None: Phasenumschaltung erfolgt in Threads, da diese länger als ein Zyklus dauert. """ try: - if thread_handler(threading.Thread( - target=_perform_phase_switch, - args=(cp.chargepoint_module, - cp.data.control_parameter.phases, - cp.data.set.charging_ev_data, - cp.data.get.charge_state), - name=f"phase switch cp{cp.chargepoint_module.config.id}")): - log.debug("Thread zur Phasenumschaltung an LP"+str(cp.num)+" gestartet.") + log.debug("Starte Thread zur Phasenumschaltung an LP"+str(cp.num)) + return thread_handler(Thread( + target=_perform_phase_switch, + args=(cp.chargepoint_module, + cp.data.control_parameter.phases, + cp.data.set.charging_ev_data, + cp.data.get.charge_state), + name=f"phase switch cp{cp.chargepoint_module.config.id}")) except Exception: log.exception("Fehler im Phasenumschaltungs-Modul") diff --git a/packages/control/prepare.py b/packages/control/prepare.py index 51a1466693..ddb09b471c 100644 --- a/packages/control/prepare.py +++ b/packages/control/prepare.py @@ -34,6 +34,7 @@ def setup_algorithm(self) -> None: data.data.cp_all_data.get_cp_sum() data.data.cp_all_data.no_charge() data.data.counter_all_data.set_home_consumption() + data.data.io_actions.setup() except Exception: log.exception("Fehler im Prepare-Modul") data.data.print_all() diff --git a/packages/control/process.py b/packages/control/process.py index cce967d51e..f4d06f68b4 100644 --- a/packages/control/process.py +++ b/packages/control/process.py @@ -1,7 +1,7 @@ """ Starten des Lade-Vorgangs """ import logging -import threading +from threading import Thread from typing import List from control.bat_all import get_controllable_bat_components @@ -11,7 +11,11 @@ from control.chargepoint.chargepoint_state import ChargepointState from helpermodules.pub import Pub from helpermodules.utils._thread_handler import joined_thread_handler +from modules.common.abstract_io import AbstractIoDevice from modules.common.fault_state_level import FaultStateLevel +from modules.io_actions.controllable_consumers.dimming.api import Dimming +from modules.io_actions.controllable_consumers.dimming_direct_control.api import DimmingDirectControl +from modules.io_actions.generator_systems.stepwise_control.api import StepwiseControl log = logging.getLogger(__name__) @@ -22,7 +26,7 @@ def __init__(self) -> None: def process_algorithm_results(self) -> None: try: - modules_threads: List[threading.Thread] = [] + modules_threads: List[Thread] = [] log.info("# Ladung starten.") for cp in data.data.cp_data.values(): try: @@ -51,10 +55,12 @@ def process_algorithm_results(self) -> None: Pub().pub("openWB/set/chargepoint/"+str(cp.num)+"/get/state_str", cp.data.get.state_str) else: - Pub().pub( - f"openWB/set/chargepoint/{cp.num}/get/state_str", - "Ladevorgang wurde gestartet... (bei Problemen: Prüfe bitte zuerst in den Einstellungen" - " 'Ladeeinstellungen' und 'Konfiguration'.)") + if cp.data.get.charge_state: + Pub().pub( + f"openWB/set/chargepoint/{cp.num}/get/state_str", "Fahrzeug lädt.") + else: + Pub().pub( + f"openWB/set/chargepoint/{cp.num}/get/state_str", "Ladevorgang wird gestartet... ") if cp.chargepoint_module.fault_state.fault_state != FaultStateLevel.NO_ERROR: cp.chargepoint_module.fault_state.store_error() modules_threads.append(self._start_charging(cp)) @@ -63,11 +69,41 @@ def process_algorithm_results(self) -> None: log.exception("Fehler im Process-Modul für Ladepunkt "+str(cp)) for bat_component in get_controllable_bat_components(): modules_threads.append( - threading.Thread( + Thread( target=bat_component.set_power_limit, args=(data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit,), name=f"set power limit {bat_component.component_config.id}")) - + for action in data.data.io_actions.actions.values(): + if isinstance(action, DimmingDirectControl): + for d in action.config.configuration.devices: + if d["type"] == "io": + data.data.io_states[f"io_states{d['id']}"].data.set.digital_output[d["digital_output"]] = ( + action.dimming_via_direct_control() is None # active output (True) if no dimming + ) + if isinstance(action, Dimming): + for d in action.config.configuration.devices: + if d["type"] == "io": + data.data.io_states[f"io_states{d['id']}"].data.set.digital_output[d["digital_output"]] = ( + not action.dimming_active() # active output (True) if no dimming + ) + if isinstance(action, StepwiseControl): + # check if passthrough is enabled + if action.config.configuration.passthrough_enabled: + # find output pattern by value + for pattern in action.config.configuration.output_pattern: + if pattern["value"] == action.control_stepwise(): + # set digital outputs according to matching output_pattern + for output in pattern["matrix"].keys(): + data.data.io_states[ + f"io_states{action.config.configuration.io_device}" + ].data.set.digital_output[output] = pattern["matrix"][output] + for io in data.data.system_data.values(): + if isinstance(io, AbstractIoDevice): + modules_threads.append( + Thread( + target=io.write, + args=(None, data.data.io_states[f"io_states{io.config.id}"].data.set.digital_output,), + name=f"set output io{io.config.id}")) if modules_threads: joined_thread_handler(modules_threads, 3) except Exception: @@ -109,7 +145,7 @@ def _update_state(self, chargepoint: chargepoint.Chargepoint) -> None: log.info(f"LP{chargepoint.num}: set current {current} A, " f"state {ChargepointState(chargepoint.data.control_parameter.state).name}") - def _start_charging(self, chargepoint: chargepoint.Chargepoint) -> threading.Thread: - return threading.Thread(target=chargepoint.chargepoint_module.set_current, - args=(chargepoint.data.set.current,), - name=f"set current cp{chargepoint.chargepoint_module.config.id}") + def _start_charging(self, chargepoint: chargepoint.Chargepoint) -> Thread: + return Thread(target=chargepoint.chargepoint_module.set_current, + args=(chargepoint.data.set.current,), + name=f"set current cp{chargepoint.chargepoint_module.config.id}") diff --git a/packages/control/pv_all.py b/packages/control/pv_all.py index abe2f50225..e938636a4c 100644 --- a/packages/control/pv_all.py +++ b/packages/control/pv_all.py @@ -58,18 +58,28 @@ def calc_power_for_all_components(self) -> None: exported = 0 power = 0 fault_state = 0 - for module in data.data.pv_data: + for module in data.data.pv_data.values(): try: - if "pv" in module: - module_data = data.data.pv_data[module].data - if module_data.get.fault_state < 2: - power += module_data.get.power - exported += module_data.get.exported - else: - if fault_state < module_data.get.fault_state: - fault_state = module_data.get.fault_state + if module.data.get.fault_state < 2: + try: + power += module.data.get.power + except Exception: + log.exception(f"Fehler im allgemeinen PV-Modul für pv{module.num}") + exported += module.data.get.exported + else: + if fault_state < module.data.get.fault_state: + fault_state = module.data.get.fault_state + limit_value = data.data.io_actions.stepwise_control(module.num) + if limit_value is not None and module.data.get.fault_state == 0: + msg = ( + f"Leistung begrenzt auf {int(limit_value * 100)}%" + if limit_value < 1 + else "Keine Leistungsbegrenzung aktiv." + ) + module.data.get.fault_str = msg + Pub().pub(f"openWB/set/pv/{module.num}/get/fault_str", msg) except Exception: - log.exception("Fehler im allgemeinen PV-Modul für "+str(module)) + log.exception(f"Fehler im allgemeinen PV-Modul für pv{module.num}") if fault_state == 0: self.data.get.exported = exported Pub().pub("openWB/set/pv/get/exported", self.data.get.exported) diff --git a/packages/control/text.py b/packages/control/text.py new file mode 100644 index 0000000000..290893882f --- /dev/null +++ b/packages/control/text.py @@ -0,0 +1,8 @@ +from enum import Enum + + +class BidiState(Enum): + BIDI_CAPABLE = "" + CP_NOT_BIDI_CAPABLE = "Bidirektionales Laden ist nur mit einer openWB Pro oder Pro+ möglich. " + CP_WRONG_PROTOCOL = "Bitte in den Einstellungen der openWB Pro/Pro+ die Charging Version auf 'HLC' stellen. " + EV_NOT_BIDI_CAPABLE = "Das Fahrzeug unterstützt kein bidirektionales Laden. " diff --git a/packages/dataclass_utils/_dataclass_from_dict.py b/packages/dataclass_utils/_dataclass_from_dict.py index e26a91db23..6980d761c7 100644 --- a/packages/dataclass_utils/_dataclass_from_dict.py +++ b/packages/dataclass_utils/_dataclass_from_dict.py @@ -1,7 +1,8 @@ +from enum import Enum import inspect -from inspect import FullArgSpec +from inspect import FullArgSpec, isclass import typing -from typing import TypeVar, Type, Union +from typing import TypeVar, Type, Union, get_args, get_origin T = TypeVar('T') @@ -15,7 +16,14 @@ def dataclass_from_dict(cls: Type[T], args: Union[dict, T]) -> T: In case the supplied `args` is already of the desired type, `args` is returned unchanged """ - if isinstance(args, cls): + if isclass(cls): + if isinstance(args, cls): + return args + elif get_origin(cls): + # Generische Typen wie Dict[int, float] + if isinstance(args, get_origin(cls)): + return args + elif isinstance(args, type(cls)): return args arg_spec = inspect.getfullargspec(cls.__init__) return cls(*[_get_argument_value(arg_spec, index, args) for index in range(1, len(arg_spec.args))]) @@ -38,11 +46,20 @@ def _get_argument_value(arg_spec: FullArgSpec, index: int, parameters: dict): def _dataclass_from_dict_recurse(value, requested_type: Type[T]): - return dataclass_from_dict(requested_type, value) \ - if isinstance(value, dict) and not ( + if get_origin(requested_type) == list: + # Extrahiere den generischen Typ der Liste + if get_args(requested_type): + generic_type = get_args(requested_type)[0] + # Konvertiere jedes Element der Liste in den generischen Typ + return [_dataclass_from_dict_recurse(item, generic_type) for item in value] + + if isinstance(value, dict) and not ( _is_optional_of_dict(requested_type) or - issubclass(requested_type, dict)) \ - else value + issubclass(requested_type if isclass(requested_type) else type(bool), dict)): + return dataclass_from_dict(requested_type, value) + if isinstance(requested_type, type) and issubclass(requested_type, Enum): + return requested_type(value) + return value def _is_optional_of_dict(requested_type): diff --git a/packages/dataclass_utils/_dataclass_from_dict_test.py b/packages/dataclass_utils/_dataclass_from_dict_test.py index 63f6ac5992..012b76f8ff 100644 --- a/packages/dataclass_utils/_dataclass_from_dict_test.py +++ b/packages/dataclass_utils/_dataclass_from_dict_test.py @@ -1,4 +1,4 @@ -from typing import Generic, Optional, Type, TypeVar +from typing import Dict, Generic, Optional, Type, TypeVar import pytest @@ -35,6 +35,12 @@ def __init__(self, a: str, o: Optional[dict] = None): self.o = o +class GenericDict: + def __init__(self, a: str, o: Dict[int, float] = None): + self.a = a + self.o = o + + def test_from_dict_simple(): # execution actual = dataclass_from_dict(SimpleSample, {"b": "bValue", "a": "aValue"}) @@ -82,6 +88,15 @@ def test_from_dict_extends_generic(): assert actual.a == "aValue" +def test_generic_dict(): + # execution + actual = dataclass_from_dict(GenericDict, {"a": "aValue", "o": {1: 1.0}}) + + # evaluation + assert actual.a == "aValue" + assert actual.o == {1: 1.0} + + @pytest.mark.parametrize(["type", "invalid_parameter"], [ pytest.param(SimpleSample, "a", id="class with some default values"), pytest.param(NestedSample, "normal", id="class with no default values"), @@ -91,7 +106,7 @@ def test_from_dict_fails_on_invalid_properties(type: Type[T], invalid_parameter: with pytest.raises(Exception) as e: dataclass_from_dict(type, {"invalid": "dict"}) assert str(e.value) == "Cannot determine value for parameter " + invalid_parameter + \ - ": not given in {'invalid': 'dict'} and no default value specified" + ": not given in {'invalid': 'dict'} and no default value specified" def test_from_dict_wit_optional(): diff --git a/packages/dataclass_utils/factories.py b/packages/dataclass_utils/factories.py index 638cf9bace..acb55a2fa8 100644 --- a/packages/dataclass_utils/factories.py +++ b/packages/dataclass_utils/factories.py @@ -15,3 +15,37 @@ def currents_list_factory() -> List[float]: def voltages_list_factory() -> List[float]: return [230.0]*3 + + +def empty_io_pattern_boolean_factory(): + return [ + { + "value": True, # dimmen + "matrix": {} + }, + { + "value": False, # unbeschränkt + "matrix": {} + } + ] + + +def empty_io_pattern_stepwise_factory(): + return [ + { + "value": 1.0, # keine Begrenzung + "matrix": {} + }, + { + "value": 0.6, # Stufe 1: 60% + "matrix": {} + }, + { + "value": 0.3, # Stufe 2: 30% + "matrix": {} + }, + { + "value": 0.0, # Stufe 3: 0% + "matrix": {} + } + ] diff --git a/packages/helpermodules/abstract_plans.py b/packages/helpermodules/abstract_plans.py index 60586a9944..d6017fb952 100644 --- a/packages/helpermodules/abstract_plans.py +++ b/packages/helpermodules/abstract_plans.py @@ -1,9 +1,14 @@ from dataclasses import dataclass, field +import datetime from typing import List, Optional -def once_factory() -> List: - return ["2021-11-01", "2021-11-05"] # ToDo: aktuelles Datum verwenden +def once_period_factory() -> List: + return [datetime.datetime.today().strftime("%Y-%m-%d"), datetime.datetime.today().strftime("%Y-%m-%d")] + + +def once_date_factory() -> List: + return datetime.datetime.today().strftime("%Y-%m-%d") def weekly_factory() -> List: @@ -26,14 +31,25 @@ def limit_factory() -> Limit: @dataclass -class Frequency: +class FrequencyPeriod: + selected: str = "daily" + once: List[str] = field(default_factory=once_period_factory) + weekly: List[bool] = field(default_factory=weekly_factory) + + +def frequency_period_factory() -> FrequencyPeriod: + return FrequencyPeriod() + + +@dataclass +class FrequencyDate: selected: str = "daily" - once: List[str] = field(default_factory=once_factory) + once: str = field(default_factory=once_date_factory) weekly: List[bool] = field(default_factory=weekly_factory) -def frequency_factory() -> Frequency: - return Frequency() +def frequency_date_factory() -> FrequencyDate: + return FrequencyDate() @dataclass @@ -51,33 +67,41 @@ def scheduled_limit_factory() -> ScheduledLimit: @dataclass class PlanBase: active: bool = True - frequency: Frequency = field(default_factory=frequency_factory) @dataclass class TimeframePlan(PlanBase): time: List[str] = field(default_factory=time_factory) # ToDo: aktuelle Zeit verwenden + 1 Stunde + frequency: FrequencyPeriod = field(default_factory=frequency_period_factory) @dataclass class ScheduledChargingPlan(PlanBase): + bidi_charging_enabled: bool = False + bidi_power: int = 10000 current: int = 14 dc_current: float = 145 + et_active: bool = False + frequency: FrequencyDate = field(default_factory=frequency_date_factory) id: Optional[int] = None name: str = "neuer Zielladen-Plan" limit: ScheduledLimit = field(default_factory=scheduled_limit_factory) + phases_to_use: int = 0 + phases_to_use_pv: int = 0 time: str = "07:00" # ToDo: aktuelle Zeit verwenden @dataclass class TimeChargingPlan(TimeframePlan): - name: str = "neuer Zeitladen-Plan" current: int = 16 dc_current: float = 145 id: Optional[int] = None limit: Limit = field(default_factory=limit_factory) + name: str = "neuer Zeitladen-Plan" + phases_to_use: int = 1 @dataclass class AutolockPlan(TimeframePlan): + id: Optional[int] = None name: str = "neuer Plan für Sperren nach Uhrzeit" diff --git a/packages/helpermodules/broker.py b/packages/helpermodules/broker.py index ab7feb3149..c9b6940c6f 100644 --- a/packages/helpermodules/broker.py +++ b/packages/helpermodules/broker.py @@ -16,14 +16,19 @@ def get_name_suffix() -> str: return f"{serial}-{datetime.datetime.today().timestamp()}" -class InternalBrokerClient: - def __init__(self, name: str, on_connect: Callable, on_message: Callable) -> None: +class BrokerClient: + def __init__(self, + name: str, + on_connect: Callable, + on_message: Callable, + host: str = "localhost", + port: int = 1886) -> None: try: self.name = f"openWB-{name}-{get_name_suffix()}" self.client = mqtt.Client(self.name) self.client.on_connect = on_connect self.client.on_message = on_message - self.client.connect("localhost", 1886) + self.client.connect(host, port) except Exception: log.exception("Fehler beim Abonnieren des internen Brokers") @@ -43,10 +48,10 @@ def disconnect(self) -> None: class InternalBrokerPublisher: def __init__(self) -> None: try: - self.client = mqtt.Client(f"openWB-python-bulkpublisher-{get_name_suffix()}") + self.client = mqtt.Client(f"openWB-python-bulk-publisher-{get_name_suffix()}") self.client.connect("localhost", 1886) except Exception: - log.exception("Fehler beim Verbindungsaufbau zum Bulkpublisher") + log.exception("Fehler beim Verbindungsaufbau zum Bulk-Publisher") def start_loop(self) -> None: self.client.loop_start() diff --git a/packages/helpermodules/changed_values_handler.py b/packages/helpermodules/changed_values_handler.py index 5f202e1980..3239bc520c 100644 --- a/packages/helpermodules/changed_values_handler.py +++ b/packages/helpermodules/changed_values_handler.py @@ -1,11 +1,12 @@ -from dataclasses import asdict, fields, is_dataclass +from dataclasses import fields, is_dataclass from enum import Enum import logging -import threading +from threading import Event from typing import Dict, List, Tuple from control import data from control.data import Data +from dataclass_utils._dataclass_asdict import asdict from helpermodules.pub import Pub @@ -14,12 +15,12 @@ # In den Metadaten wird unter dem Key der Topic-Suffix ab "openWB/ev/2/" angegeben. Der Topic-Prefix ("openWB/ev/2/") # wird automatisch ermittelt. -# Der Kontextmanager muss immer verwendet werden, wenn in den Funktionen Werte geändert werden, die nicht gepublished +# Der Kontextmanager muss immer verwendet werden, wenn in den Funktionen Werte geändert werden, die nicht veröffentlicht # werden. -# Metadaten werden nur für Felder erzeugt, die gepublished werden sollen, dh bei ganzen Klassen für das Feld der -# jeweiligen Klasse. Wenn Werte aus einer instanziierten Klasse gepublished werden sollen, erhält die übergeordnete +# Metadaten werden nur für Felder erzeugt, die veröffentlicht werden sollen, dh bei ganzen Klassen für das Feld der +# jeweiligen Klasse. Wenn Werte aus einer instanziierten Klasse veröffentlicht werden sollen, erhält die übergeordnete # Klasse keine Metadaten (siehe Beispiel unten). -# Damit die geänderten Werte automatisiert gepublished werden können, muss jede Klasse eine bestimmte Form haben: +# Damit die geänderten Werte automatisiert veröffentlicht werden können, muss jede Klasse eine bestimmte Form haben: # # @dataclass # class SampleClass: @@ -43,9 +44,9 @@ # @dataclass # class SampleData: -# # Wenn eine ganze Klasse als Dictionary gepublished werden soll, wie zB bei Konfigurationen, werden Metadaten für -# diese Klasse eingetragen. Die Felder der Konfigurationsklasse bekommen keine Metadaten, da diese nicht einzeln -# gepublished werden. +# # Wenn eine ganze Klasse als Dictionary veröffentlicht werden soll, wie zB bei Konfigurationen, werden Metadaten +# für diese Klasse eingetragen. Die Felder der Konfigurationsklasse bekommen keine Metadaten, da diese nicht einzeln +# veröffentlicht werden. # sample_field_class: SampleClass = field( # default_factory=sample_class, metadata={"topic": "get/field_class"}) # sample_field_int: int = field(default=0, metadata={"topic": "get/field_int"}) @@ -53,8 +54,8 @@ # default=0, metadata={"topic": "get/field_immutable"}) # sample_field_list: List = field(default_factory=currents_list_factory, metadata={ # "topic": "get/field_list"}) -# # Bei verschachtelten Klassen, wo der zu publishende Wert auf einer tieferen Ebene liegt, werden nur für den zu -# publishenden Wert Metadaten erzeugt. +# # Bei verschachtelten Klassen, wo der zu veröffentlichende Wert auf einer tieferen Ebene liegt, werden nur für +# den zu veröffentlichenden Wert Metadaten erzeugt. # sample_field_nested: SampleNested = field(default_factory=sample_nested) @@ -64,7 +65,7 @@ class ChangedValuesHandler: - def __init__(self, event_module_update_completed: threading.Event) -> None: + def __init__(self, event_module_update_completed: Event) -> None: self.prev_data: Data = Data(event_module_update_completed) def store_initial_values(self): @@ -76,7 +77,7 @@ def store_initial_values(self): def pub_changed_values(self): try: - # publishen der geänderten Werte + # veröffentlichen der geänderten Werte self._update_value("openWB/set/bat/", self.prev_data.bat_all_data.data, data.data.bat_all_data.data) self._update_value("openWB/set/chargepoint/", self.prev_data.cp_all_data.data.get, data.data.cp_all_data.data.get) @@ -134,7 +135,7 @@ def _update_value(self, topic_prefix, data_inst_previous, data_inst): class ChangedValuesContext: - def __init__(self, event_module_update_completed: threading.Event): + def __init__(self, event_module_update_completed: Event): self.changed_values_handler = ChangedValuesHandler(event_module_update_completed) def __enter__(self): diff --git a/packages/helpermodules/command.py b/packages/helpermodules/command.py index edba8e6098..d75438229f 100644 --- a/packages/helpermodules/command.py +++ b/packages/helpermodules/command.py @@ -1,11 +1,12 @@ +import copy from dataclasses import asdict import importlib import json import logging import subprocess -import threading +from threading import Event import time -from typing import Dict, List, Optional +from typing import Dict, Optional import re import traceback from pathlib import Path @@ -13,24 +14,24 @@ import paho.mqtt.client as mqtt from control.chargelog import chargelog from control.chargepoint import chargepoint -from control.chargepoint.chargepoint_template import get_autolock_plan_default, get_chargepoint_template_default +from control.chargepoint.chargepoint_template import get_chargepoint_template_default -# ToDo: move to module commands if implemented from control.ev.charge_template import get_new_charge_template from control.ev.ev_template import EvTemplateData from helpermodules import pub -from helpermodules.abstract_plans import ScheduledChargingPlan, TimeChargingPlan +from helpermodules.abstract_plans import AutolockPlan, ScheduledChargingPlan, TimeChargingPlan from helpermodules.utils.run_command import run_command +# ToDo: move to module commands if implemented from modules.backup_clouds.onedrive.api import generateMSALAuthCode, retrieveMSALTokens -from helpermodules.broker import InternalBrokerClient +from helpermodules.broker import BrokerClient from helpermodules.data_migration.data_migration import MigrateData from helpermodules.measurement_logging.process_log import get_daily_log, get_monthly_log, get_yearly_log from helpermodules.messaging import MessageType, pub_user_message from helpermodules.create_debug import create_debug_log from helpermodules.pub import Pub, pub_single from helpermodules.subdata import SubData -from helpermodules.utils.topic_parser import decode_payload +from helpermodules.utils.topic_parser import decode_payload, get_index from control import bat, bridge, data, counter, counter_all, pv from control.ev import ev from modules.chargepoints.internal_openwb.chargepoint_module import ChargepointModule @@ -46,20 +47,25 @@ class Command: """ """ - # Tuple: (Name der maximalen ID, Topic zur Ermittlung, Default-Wert) - MAX_IDS = [("autolock_plan", "chargepoint/template/+/autolock", -1), - ("mqtt_bridge", "system/mqtt/bridge", -1), - ("charge_template", "vehicle/template/charge_template", 0), - ("charge_template_scheduled_plan", - "vehicle/template/charge_template/+/chargemode/scheduled_charging/plans", - -1), - ("charge_template_time_charging_plan", "vehicle/template/charge_template/+/time_charging/plans", -1), - ("chargepoint_template", "chargepoint/template", 0), - ("device", "system/device", -1), - ("ev_template", "vehicle/template/ev_template", 0), - ("vehicle", "vehicle", 0)] - - def __init__(self, event_command_completed: threading.Event): + # Tuple: (Name der maximalen ID, Regex-Topic zur Ermittlung, Default-Wert) + MAX_IDS = { + "nested payload": + [("autolock_plan", "openWB/chargepoint/template/[0-9]+$", -1), + ("charge_template_scheduled_plan", "openWB/vehicle/template/charge_template/[0-9]+$", -1), + ("charge_template_time_charging_plan", "openWB/vehicle/template/charge_template/[0-9]+$", -1)], + "topic": + [("mqtt_bridge", "openWB/system/mqtt/bridge", -1), + ("vehicle", "openWB/vehicle/[0-9]+/name$", 0)], + "payload": + [("charge_template", "openWB/vehicle/template/charge_template/[0-9]+$", 0), + ("chargepoint_template", "openWB/chargepoint/template/[0-9]+$", 0), + ("device", "openWB/system/device/[0-9]+/config$", -1), + ("ev_template", "openWB/vehicle/template/ev_template/[0-9]+$", 0), + ("io_action", "openWB/io/action/[0-9]+/config$", -1), + ("io_device", "openWB/system/io/[0-9]+/config$", -1)], + } + + def __init__(self, event_command_completed: Event): try: self.event_command_completed = event_command_completed self._get_max_ids() @@ -71,18 +77,38 @@ def _get_max_ids(self) -> None: """ ermittelt die maximale ID vom Broker """ try: received_topics = ProcessBrokerBranch("").get_max_id() - for id_topic, topic_str, default in self.MAX_IDS: + for id_topic, topic_str, default in self.MAX_IDS["nested payload"]: + max_id = default + for topic, payload in received_topics.items(): + if re.search(topic_str, topic) is not None: + try: + if id_topic == "autolock_plan": + for plan in payload["autolock"]["plans"]: + max_id = max(plan["id"], max_id) + elif id_topic == "charge_template_scheduled_plan": + for plan in payload["chargemode"]["scheduled_charging"]["plans"]: + max_id = max(plan["id"], max_id) + elif id_topic == "charge_template_time_charging_plan": + for plan in payload["time_charging"]["plans"]: + max_id = max(plan["id"], max_id) + except KeyError: + # überspringe Profile, die keinen Eintrag für Pläne haben. + # Da gab es einen Bug beim Kopieren. + pass + setattr(self, f'max_id_{id_topic}', max_id) + Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) + for id_topic, topic_str, default in self.MAX_IDS["topic"]: + max_id = default + for topic in received_topics.keys(): + if re.search(topic_str, topic) is not None: + max_id = max(int(get_index(topic)), max_id) + setattr(self, f'max_id_{id_topic}', max_id) + Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) + for id_topic, topic_str, default in self.MAX_IDS["payload"]: max_id = default - for topic in received_topics: - search_str = "openWB/" + topic_str.replace("+", "[0-9]+") - result = re.search('^('+search_str+'/*).*$', topic) - if result is not None: - topic_found = result.group(1) - topic_rest = topic.replace(topic_found, "") - current_id_regex = re.search('^([0-9]+)/*.*$', topic_rest) - if current_id_regex is not None: - current_id = int(current_id_regex.group(1)) - max_id = max(current_id, max_id) + for topic, payload in received_topics.items(): + if re.search(topic_str, topic) is not None: + max_id = max(payload["id"], max_id) setattr(self, f'max_id_{id_topic}', max_id) Pub().pub("openWB/set/command/max_id/"+id_topic, max_id) except Exception: @@ -105,7 +131,7 @@ def sub_commands(self): # kurze Pause, damit die ID vom Broker ermittelt werden können. Sonst werden noch vorher die retained # Topics empfangen, was zu doppelten Meldungen im Protokoll führt. time.sleep(1) - self.internal_broker_client = InternalBrokerClient("command", self.on_connect, self.on_message) + self.internal_broker_client = BrokerClient("command", self.on_connect, self.on_message) self.internal_broker_client.start_infinite_loop() except Exception: log.exception("Fehler im Command-Modul") @@ -165,11 +191,68 @@ def removeDevice(self, connection_id: str, payload: dict) -> None: pub_user_message(payload, connection_id, f'Gerät mit ID \'{payload["data"]["id"]}\' gelöscht.', MessageType.SUCCESS) else: - pub_user_message( + log.error( payload, connection_id, f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ID \'{self.max_id_device}\'.', MessageType.ERROR) + def addIoAction(self, connection_id: str, payload: dict) -> None: + new_id = self.max_id_io_action + 1 + dev = importlib.import_module(f".io_actions.{'.'.join(payload['data']['type'])}.api", + "modules") + descriptor = dev.device_descriptor.configuration_factory() + device_default = dataclass_utils.asdict(descriptor) + device_default["id"] = new_id + Pub().pub(f'openWB/set/io/action/{new_id}/config', device_default) + self.max_id_io_action = new_id + Pub().pub("openWB/set/command/max_id/io_action", self.max_id_io_action) + pub_user_message( + payload, connection_id, + f'Neue IO-Aktion vom Typ \'{" / ".join(payload["data"]["type"])}\' mit ID \'{new_id}\' hinzugefügt.', + MessageType.SUCCESS) + + def removeIoAction(self, connection_id: str, payload: dict) -> None: + if self.max_id_io_action >= payload["data"]["id"]: + ProcessBrokerBranch(f'io/action/{payload["data"]["id"]}/').remove_topics() + pub_user_message(payload, connection_id, f'IO-Aktion mit ID \'{payload["data"]["id"]}\' gelöscht.', + MessageType.SUCCESS) + else: + log.error( + payload, connection_id, + f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_io_action}\'.', + MessageType.ERROR) + + def addIoDevice(self, connection_id: str, payload: dict) -> None: + """ sendet das Topic, zu dem ein neues Io-Device erstellt werden soll. + """ + new_id = self.max_id_io_device + 1 + dev = importlib.import_module(".io_devices."+payload["data"]["type"]+".api", "modules") + descriptor = dev.device_descriptor.configuration_factory() + device_default = dataclass_utils.asdict(descriptor) + device_default["id"] = new_id + Pub().pub(f'openWB/set/system/io/{new_id}/config', device_default) + self.max_id_io_device = new_id + Pub().pub("openWB/set/command/max_id/io_device", self.max_id_io_device) + pub_user_message( + payload, connection_id, + f'Neues IO-Gerät vom Typ \'{payload["data"]["type"]}\' mit ID \'{new_id}\' hinzugefügt.', + MessageType.SUCCESS) + + def removeIoDevice(self, connection_id: str, payload: dict) -> None: + """ löscht ein Io-Device. + """ + if self.max_id_io_device >= payload["data"]["id"]: + ProcessBrokerBranch(f'system/io/{payload["data"]["id"]}/').remove_topics() + pub_user_message(payload, connection_id, f'IO-Gerät mit ID \'{payload["data"]["id"]}\' gelöscht.', + MessageType.SUCCESS) + else: + log.error( + payload, connection_id, + f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_io_device}\'.', + MessageType.ERROR) + def addChargepoint(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Chargepoint erstellt werden soll. """ @@ -177,6 +260,17 @@ def setup_added_chargepoint(): Pub().pub(f'openWB/chargepoint/{new_id}/config', chargepoint_config) Pub().pub(f'openWB/chargepoint/{new_id}/set/manual_lock', False) {Pub().pub(f"openWB/chargepoint/{new_id}/get/"+k, v) for (k, v) in asdict(chargepoint.Get()).items()} + charge_template = SubData.ev_charge_template_data[f"ct{SubData.ev_data['ev0'].data.charge_template}"] + for time_plan in charge_template.data.time_charging.plans: + Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template/time_charging/plans', + dataclass_utils.asdict(time_plan)) + for scheduled_plan in charge_template.data.chargemode.scheduled_charging.plans: + Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template/chargemode/scheduled_charging/plans', + scheduled_plan) + charge_template = dataclass_utils.asdict(charge_template.data) + charge_template["chargemode"]["scheduled_charging"]["plans"].clear() + charge_template["time_charging"]["plans"].clear() + Pub().pub(f'openWB/chargepoint/{new_id}/set/charge_template', charge_template) self.max_id_hierarchy = self.max_id_hierarchy + 1 Pub().pub("openWB/set/command/max_id/hierarchy", self.max_id_hierarchy) if self.max_id_chargepoint_template == -1: @@ -255,10 +349,10 @@ def _check_max_num_of_internal_chargepoints(self, config: Dict) -> Optional[str] return None def removeChargepoint(self, connection_id: str, payload: dict) -> None: - """ löscht ein Chargepoint. + """ löscht ein Ladepunkt. """ if self.max_id_hierarchy < payload["data"]["id"]: - pub_user_message( + log.error( payload, connection_id, f'Die ID \'{payload["data"]["id"]}\' ist größer als die maximal vergebene ' f'ID \'{self.max_id_hierarchy}\'.', MessageType.ERROR) @@ -271,13 +365,26 @@ def removeChargepoint(self, connection_id: str, payload: dict) -> None: def addChargepointTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues Ladepunkt-Profil erstellt werden soll. """ + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + new_chargepoint_template = asdict(data.data.cp_template_data[f'cpt{payload["data"]["copy"]}'].data).copy() + new_chargepoint_template["name"] = f'Kopie von {new_chargepoint_template["name"]}' + else: + new_chargepoint_template = get_chargepoint_template_default() new_id = self.max_id_chargepoint_template + 1 - default = get_chargepoint_template_default() - default["id"] = new_id - Pub().pub(f'openWB/set/chargepoint/template/{new_id}', default) + new_chargepoint_template["id"] = new_id + Pub().pub(f'openWB/set/chargepoint/template/{new_id}', new_chargepoint_template) self.max_id_chargepoint_template = self.max_id_chargepoint_template + 1 Pub().pub("openWB/set/command/max_id/chargepoint_template", self.max_id_chargepoint_template) + # if copying a template, copy autolock plans + if "data" in payload and "copy" in payload["data"]: + for _, plan in data.data.cp_template_data[f'cpt{payload["data"]["copy"]}'].data.autolock.plans.items(): + new_plan = asdict(plan).copy() + new_plan["id"] = self.max_id_autolock_plan + 1 + Pub().pub(f'openWB/set/chargepoint/template/{new_id}/autolock/{new_plan["id"]}', new_plan) + self.max_id_autolock_plan += 1 + Pub().pub("openWB/set/command/max_id/autolock_plan", new_id) pub_user_message( payload, connection_id, f'Neues Ladepunkt-Profil mit ID \'{new_id}\' hinzugefügt.', @@ -303,10 +410,21 @@ def removeChargepointTemplate(self, connection_id: str, payload: dict) -> None: def addAutolockPlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zielladen-Plan erstellt werden soll. """ + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + for plan in data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans: + if plan.id == payload["data"]["copy"]: + new_autolock_plan = copy.deepcopy(plan) + break + new_autolock_plan.name = f'Kopie von {new_autolock_plan.name}' + else: + new_autolock_plan = AutolockPlan() new_id = self.max_id_autolock_plan + 1 - default = get_autolock_plan_default() - Pub().pub(f'openWB/set/chargepoint/template/{payload["data"]["template"]}/autolock/{new_id}', - default) + new_autolock_plan.id = new_id + data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans.append( + new_autolock_plan) + Pub().pub(f'openWB/set/chargepoint/template/{payload["data"]["template"]}', + asdict(data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data)) self.max_id_autolock_plan = new_id Pub().pub("openWB/set/command/max_id/autolock_plan", new_id) pub_user_message( @@ -323,10 +441,13 @@ def removeAutolockPlan(self, connection_id: str, payload: dict) -> None: payload, connection_id, f'Die ID \'{payload["data"]["plan"]}\' ist größer als die ' f'maximal vergebene ID \'{self.max_id_autolock_plan}\'.', MessageType.ERROR) + for plan in data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans: + if plan.id == payload["data"]["plan"]: + data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data.autolock.plans.remove(plan) + break Pub().pub( - f'openWB/chargepoint/template/{payload["data"]["template"]}' - f'/autolock/{payload["data"]["plan"]}', - "") + f'openWB/chargepoint/template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.cp_template_data[f'cpt{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Plan für Sperren nach Uhrzeit mit ID \'{payload["data"]["plan"]}\' vom Profil ' @@ -337,11 +458,26 @@ def addChargeTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues Lade-Profil erstellt werden soll. """ new_id = self.max_id_charge_template + 1 - charge_template_default = get_new_charge_template() - Pub().pub("openWB/set/vehicle/template/charge_template/" + - str(new_id), charge_template_default) self.max_id_charge_template = new_id + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + new_charge_template = copy.deepcopy(data.data.ev_charge_template_data[f'ct{payload["data"]["copy"]}'].data) + new_charge_template.name = f'Kopie von {new_charge_template.name}' + for plan in new_charge_template.chargemode.scheduled_charging.plans: + plan.id = self.max_id_charge_template_scheduled_plan + 1 + self.max_id_charge_template_scheduled_plan += 1 + Pub().pub("openWB/set/command/max_id/charge_template_scheduled_plan", new_id) + for plan in new_charge_template.time_charging.plans: + plan.id = self.max_id_charge_template_time_charging_plan + 1 + self.max_id_charge_template_time_charging_plan += 1 + Pub().pub("openWB/set/command/max_id/charge_template_time_charging_plan", new_id) + new_charge_template = asdict(new_charge_template) + else: + new_charge_template = get_new_charge_template() + new_charge_template["id"] = new_id + Pub().pub("openWB/set/command/max_id/charge_template", new_id) + Pub().pub(f"openWB/set/vehicle/template/charge_template/{new_id}", new_charge_template) pub_user_message(payload, connection_id, f'Neues Lade-Profil mit ID \'{new_id}\' hinzugefügt.', MessageType.SUCCESS) @@ -350,8 +486,8 @@ def removeChargeTemplate(self, connection_id: str, payload: dict) -> None: """ löscht ein Lade-Profil. """ if self.max_id_charge_template < payload["data"]["id"]: - pub_user_message(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.", - MessageType.ERROR) + log.error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.", + MessageType.ERROR) if payload["data"]["id"] > 0: ProcessBrokerBranch(f'vehicle/template/charge_template/{payload["data"]["id"]}/').remove_topics() pub_user_message( @@ -365,13 +501,24 @@ def removeChargeTemplate(self, connection_id: str, payload: dict) -> None: def addChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zielladen-Plan erstellt werden soll. """ + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + for plan in data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans: + if plan.id == payload["data"]["copy"]: + new_charge_template_schedule_plan = copy.deepcopy(plan) + break + new_charge_template_schedule_plan.name = f'Kopie von {new_charge_template_schedule_plan.name}' + else: + new_charge_template_schedule_plan = ScheduledChargingPlan() new_id = self.max_id_charge_template_scheduled_plan + 1 - charge_template_default = ScheduledChargingPlan() - charge_template_default.id = new_id + new_charge_template_schedule_plan.id = new_id + data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans.append( + new_charge_template_schedule_plan) Pub().pub( - f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/chargemode/scheduled_charging/plans/{new_id}', - dataclass_utils.asdict(charge_template_default)) + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) self.max_id_charge_template_scheduled_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_scheduled_plan", new_id) @@ -385,14 +532,20 @@ def removeChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> """ löscht einen Zielladen-Plan. """ if self.max_id_charge_template_scheduled_plan < payload["data"]["plan"]: - pub_user_message( + log.error( payload, connection_id, f'Die ID \'{payload["data"]["plan"]}\' ist größer als die maximal vergebene ' f'ID \'{self.max_id_charge_template_scheduled_plan}\'.', MessageType.ERROR) + for plan in data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans: + if plan.id == payload["data"]["plan"]: + data.data.ev_charge_template_data[ + f'ct{payload["data"]["template"]}'].data.chargemode.scheduled_charging.plans.remove( + plan) + break Pub().pub( - f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/chargemode/scheduled_charging/plans/{payload["data"]["plan"]}', - "") + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Zielladen-Plan mit ID \'{payload["data"]["plan"]}\' von Profil ' @@ -402,13 +555,22 @@ def removeChargeTemplateSchedulePlan(self, connection_id: str, payload: dict) -> def addChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neuer Zeitladen-Plan erstellt werden soll. """ + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + for plan in data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans: + if plan.id == payload["data"]["copy"]: + new_time_charging_plan = copy.deepcopy(plan) + break + new_time_charging_plan.name = f'Kopie von {new_time_charging_plan.name}' + else: + new_time_charging_plan = TimeChargingPlan() new_id = self.max_id_charge_template_time_charging_plan + 1 - time_charging_plan_default = TimeChargingPlan() - time_charging_plan_default.id = new_id + new_time_charging_plan.id = new_id + data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans.append( + new_time_charging_plan) Pub().pub( - f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/time_charging/plans/{new_id}', - dataclass_utils.asdict(time_charging_plan_default)) + f'openWB/set/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) self.max_id_charge_template_time_charging_plan = new_id Pub().pub( "openWB/set/command/max_id/charge_template_time_charging_plan", new_id) @@ -421,12 +583,16 @@ def removeChargeTemplateTimeChargingPlan(self, connection_id: str, payload: dict """ löscht einen Zeitladen-Plan. """ if self.max_id_charge_template_time_charging_plan < payload["data"]["plan"]: - pub_user_message(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.", - MessageType.ERROR) + log.error(payload, connection_id, "Die ID ist größer als die maximal vergebene ID.", + MessageType.ERROR) + for plan in data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans: + if plan.id == payload["data"]["plan"]: + data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data.time_charging.plans.remove( + plan) + break Pub().pub( - f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}' - f'/time_charging/plans/{payload["data"]["plan"]}', - "") + f'openWB/vehicle/template/charge_template/{payload["data"]["template"]}', + dataclass_utils.asdict(data.data.ev_charge_template_data[f'ct{payload["data"]["template"]}'].data)) pub_user_message( payload, connection_id, f'Zeitladen-Plan mit ID \'{payload["data"]["plan"]}\' zu Profil ' @@ -477,8 +643,8 @@ def removeComponent(self, connection_id: str, payload: dict) -> None: """ löscht eine Komponente. """ if self.max_id_hierarchy < payload["data"]["id"]: - pub_user_message(payload, connection_id, - "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) + log.error(payload, connection_id, + "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) branch = f'system/device/{payload["data"]["deviceId"]}/component/{payload["data"]["id"]}/' ProcessBrokerBranch(branch).remove_topics() pub_user_message( @@ -488,9 +654,14 @@ def removeComponent(self, connection_id: str, payload: dict) -> None: def addEvTemplate(self, connection_id: str, payload: dict) -> None: """ sendet das Topic, zu dem ein neues Fahrzeug-Profil erstellt werden soll. """ + # check if "payload" contains "data.copy" + if "data" in payload and "copy" in payload["data"]: + new_ev_template = asdict(data.data.ev_template_data[f"et{payload['data']['copy']}"].data).copy() + new_ev_template["name"] = f'Kopie von {new_ev_template["name"]}' + else: + new_ev_template = dataclass_utils.asdict(EvTemplateData()) new_id = self.max_id_ev_template + 1 - ev_template_default = dataclass_utils.asdict(EvTemplateData()) - Pub().pub(f'openWB/set/vehicle/template/ev_template/{new_id}', ev_template_default) + Pub().pub(f'openWB/set/vehicle/template/ev_template/{new_id}', new_ev_template) self.max_id_ev_template = new_id Pub().pub("openWB/set/command/max_id/ev_template", new_id) pub_user_message( @@ -501,8 +672,8 @@ def removeEvTemplate(self, connection_id: str, payload: dict) -> None: """ löscht ein Fahrzeug-Profil. """ if self.max_id_ev_template < payload["data"]["id"]: - pub_user_message(payload, connection_id, - "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) + log.error(payload, connection_id, + "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) if payload["data"]["id"] > 0: ProcessBrokerBranch(f'vehicle/template/ev_template/{payload["data"]["id"]}/').remove_topics() pub_user_message( @@ -535,8 +706,8 @@ def removeVehicle(self, connection_id: str, payload: dict) -> None: """ löscht ein Vehicle. """ if self.max_id_vehicle < payload["data"]["id"]: - pub_user_message(payload, connection_id, - "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) + log.error(payload, connection_id, + "Die ID ist größer als die maximal vergebene ID.", MessageType.ERROR) if payload["data"]["id"] > 0: Pub().pub(f'openWB/vehicle/{payload["data"]["id"]}', "") ProcessBrokerBranch(f'vehicle/{payload["data"]["id"]}/').remove_topics() @@ -558,16 +729,16 @@ def getChargeLog(self, connection_id: str, payload: dict) -> None: Pub().pub(f'openWB/set/log/{connection_id}/data', chargelog.get_log_data(payload["data"])) def getDailyLog(self, connection_id: str, payload: dict) -> None: - Pub().pub(f'openWB/set/log/daily/{payload["data"]["day"]}', - get_daily_log(payload["data"]["day"])) + Pub().pub(f'openWB/set/log/daily/{payload["data"]["date"]}', + get_daily_log(payload["data"]["date"])) def getMonthlyLog(self, connection_id: str, payload: dict) -> None: - Pub().pub(f'openWB/set/log/monthly/{payload["data"]["month"]}', - get_monthly_log(payload["data"]["month"])) + Pub().pub(f'openWB/set/log/monthly/{payload["data"]["date"]}', + get_monthly_log(payload["data"]["date"])) def getYearlyLog(self, connection_id: str, payload: dict) -> None: - Pub().pub(f'openWB/set/log/yearly/{payload["data"]["year"]}', - get_yearly_log(payload["data"]["year"])) + Pub().pub(f'openWB/set/log/yearly/{payload["data"]["date"]}', + get_yearly_log(payload["data"]["date"])) def initCloud(self, connection_id: str, payload: dict) -> None: parent_file = Path(__file__).resolve().parents[2] @@ -611,9 +782,9 @@ def removeMqttBridge(self, connection_id: str, payload: dict) -> None: pub_user_message(payload, connection_id, f'Bridge mit ID \'{payload["data"]["bridge"]}\' gelöscht.', MessageType.SUCCESS) else: - pub_user_message(payload, connection_id, - f'Die ID \'{payload["data"]["bridge"]}\' ist größer als die maximal vergebene ' - f'ID \'{self.max_id_mqtt_bridge}\'.', MessageType.ERROR) + log.error(payload, connection_id, + f'Die ID \'{payload["data"]["bridge"]}\' ist größer als die maximal vergebene ' + f'ID \'{self.max_id_mqtt_bridge}\'.', MessageType.ERROR) def chargepointReboot(self, connection_id: str, payload: dict) -> None: pub.pub_single("openWB/set/command/primary/todo", @@ -627,6 +798,12 @@ def chargepointShutdown(self, connection_id: str, payload: dict) -> None: hostname=SubData.cp_data[payload["data"]["chargepoint"] ].chargepoint.chargepoint_module.config.configuration.ip_address) + def secondaryChargepointUpdate(self, payload: dict) -> None: + pub.pub_single("openWB/set/command/primary/todo", + {"command": "systemUpdate", "data": {}}, + hostname=SubData.cp_data[payload["data"]["chargepoint"] + ].chargepoint.chargepoint_module.config.configuration.ip_address) + def systemReboot(self, connection_id: str, payload: dict) -> None: pub_user_message(payload, connection_id, "Neustart wird ausgeführt.", MessageType.INFO) parent_file = Path(__file__).resolve().parents[2] @@ -668,6 +845,16 @@ def systemUpdate(self, connection_id: str, payload: dict) -> None: run_command([ str(parent_file / "runs" / "update_self.sh"), SubData.system_data["system"].data["current_branch"]]) + if not SubData.general_data.data.extern and SubData.system_data["system"].data["secondary_auto_update"]: + for cp in SubData.cp_data.values(): + # if chargepoint is external_openwb and not the second CP of duo and version is Release + if ( + cp.chargepoint.chargepoint_module.config.type == 'external_openwb' and + cp.chargepoint.chargepoint_module.config.configuration.duo_num == 0 and + cp.chargepoint.data.get.current_branch == "Release" + ): + time.sleep(2) + self.secondaryChargepointUpdate({"data": {"chargepoint": f"cp{cp.chargepoint.num}"}}) def systemFetchVersions(self, connection_id: str, payload: dict) -> None: log.info("Fetch versions requested") @@ -677,7 +864,7 @@ def systemFetchVersions(self, connection_id: str, payload: dict) -> None: pub_user_message(payload, connection_id, "Versionsliste erfolgreich aktualisiert.", MessageType.SUCCESS) def createBackup(self, connection_id: str, payload: dict) -> None: - pub_user_message(payload, connection_id, "Backup wird erstellt...", MessageType.INFO) + pub_user_message(payload, connection_id, "Sicherung wird erstellt...", MessageType.INFO) parent_file = Path(__file__).resolve().parents[2] result = run_command( [str(parent_file / "runs" / "backup.sh"), @@ -685,16 +872,16 @@ def createBackup(self, connection_id: str, payload: dict) -> None: file_name = result.rstrip('\n') file_link = "/openWB/data/backup/" + file_name pub_user_message(payload, connection_id, - "Backup erfolgreich erstellt.
" + "Sicherung erfolgreich erstellt.
" f'Jetzt herunterladen.', MessageType.SUCCESS) def createCloudBackup(self, connection_id: str, payload: dict) -> None: if SubData.system_data["system"].backup_cloud is not None: - pub_user_message(payload, connection_id, ("Backup wird erstellt. Dieser Vorgang kann je nach Umfang der " + pub_user_message(payload, connection_id, ("Sicherung wird erstellt. Dieser Vorgang kann je nach Umfang der " "Logdaten und Upload-Geschwindigkeit des Cloud-Dienstes einige Zeit in Anspruch nehmen."), MessageType.INFO) SubData.system_data["system"].create_backup_and_send_to_cloud() - pub_user_message(payload, connection_id, "Backup erfolgreich erstellt.
", MessageType.SUCCESS) + pub_user_message(payload, connection_id, "Sicherung erfolgreich erstellt.
", MessageType.SUCCESS) else: pub_user_message(payload, connection_id, "Es ist keine Backup-Cloud konfiguriert.
", MessageType.WARNING) @@ -711,26 +898,26 @@ def restoreBackup(self, connection_id: str, payload: dict) -> None: def requestMSALAuthCode(self, connection_id: str, payload: dict) -> None: ''' fordert einen Authentifizierungscode für MSAL (Microsoft Authentication Library) an um Onedrive Backup zu ermöglichen''' - cloudbackupconfig = SubData.system_data["system"].backup_cloud - if cloudbackupconfig is None: + cloud_backup_config = SubData.system_data["system"].backup_cloud + if cloud_backup_config is None: pub_user_message(payload, connection_id, "Es ist keine Backup-Cloud konfiguriert. Bitte Konfiguration speichern " "und erneut versuchen.
", MessageType.WARNING) return - result = generateMSALAuthCode(cloudbackupconfig.config) + result = generateMSALAuthCode(cloud_backup_config.config) pub_user_message(payload, connection_id, result["message"], result["MessageType"]) # ToDo: move to module commands if implemented def retrieveMSALTokens(self, connection_id: str, payload: dict) -> None: """ holt die Tokens für MSAL (Microsoft Authentication Library) um Onedrive Backup zu ermöglichen """ - cloudbackupconfig = SubData.system_data["system"].backup_cloud - if cloudbackupconfig is None: + cloud_backup_config = SubData.system_data["system"].backup_cloud + if cloud_backup_config is None: pub_user_message(payload, connection_id, "Es ist keine Backup-Cloud konfiguriert. Bitte Konfiguration speichern " "und erneut versuchen.
", MessageType.WARNING) return - result = retrieveMSALTokens(cloudbackupconfig.config) + result = retrieveMSALTokens(cloud_backup_config.config) pub_user_message(payload, connection_id, result["message"], result["MessageType"]) def factoryReset(self, connection_id: str, payload: dict) -> None: @@ -768,23 +955,24 @@ def __enter__(self): return None def __exit__(self, exception_type, exception, exception_traceback) -> bool: - if isinstance(exception, Exception): + if isinstance(exception, subprocess.CalledProcessError): pub_user_message(self.payload, self.connection_id, - f'Es ist ein interner Fehler aufgetreten: {exception}', MessageType.ERROR) - log.error({traceback.format_exc()}) + (f'Fehler-Status: {exception.returncode}
Meldung: ' + f'{exception.stderr if exception.stderr else ""} ' + f'{exception.output if exception.output else ""}'), + MessageType.ERROR) return True - elif isinstance(exception, subprocess.CalledProcessError): - log.debug(exception.stdout) + elif isinstance(exception, Exception): pub_user_message(self.payload, self.connection_id, - f'Fehler-Status: {exception.returncode}
Meldung: {exception.stderr}', - MessageType.ERROR) + f'Es ist ein interner Fehler aufgetreten: {exception}', MessageType.ERROR) + log.error({traceback.format_exc()}) return True else: return False class CompleteCommandContext: - def __init__(self, event_command_completed: threading.Event): + def __init__(self, event_command_completed: Event): self.event_command_completed = event_command_completed def __enter__(self): @@ -806,29 +994,29 @@ def __init__(self, topic_str: str) -> None: def get_payload(self): self.payload: str - InternalBrokerClient("processBrokerBranch", self.on_connect, self.__get_payload).start_finite_loop() + BrokerClient("processBrokerBranch", self.on_connect, self.__get_payload).start_finite_loop() return json.loads(self.payload) def remove_topics(self): """ löscht einen Topic-Zweig auf dem Broker. Payload "" löscht nur ein einzelnes Topic. """ - InternalBrokerClient("processBrokerBranch", self.on_connect, self.__on_message_rm).start_finite_loop() + BrokerClient("processBrokerBranch", self.on_connect, self.__on_message_rm).start_finite_loop() - def get_max_id(self) -> List[str]: + def get_max_id(self) -> Dict[str, str]: try: - self.received_topics = [] - InternalBrokerClient("processBrokerBranch", self.on_connect, self.__on_message_max_id).start_finite_loop() + self.received_topics = {} + BrokerClient("processBrokerBranch", self.on_connect, self.__on_message_max_id).start_finite_loop() return self.received_topics except Exception: log.exception("Fehler im Command-Modul") - return [] + return {} def check_mqtt_bridge_exists(self, name: str) -> bool: try: self.name = name self.mqtt_bridge_exists = False - InternalBrokerClient("processBrokerBranch", self.on_connect, - self.__on_message_mqtt_bridge_exists).start_finite_loop() + BrokerClient("processBrokerBranch", self.on_connect, + self.__on_message_mqtt_bridge_exists).start_finite_loop() return self.mqtt_bridge_exists except Exception: log.exception("Fehler im Command-Modul") @@ -837,7 +1025,7 @@ def check_mqtt_bridge_exists(self, name: str) -> bool: def get_cloud_id(self): try: self.ids = [] - InternalBrokerClient("processBrokerBranch", self.on_connect, self.__on_message_cloud_id).start_finite_loop() + BrokerClient("processBrokerBranch", self.on_connect, self.__on_message_cloud_id).start_finite_loop() return self.ids except Exception: log.exception("Fehler im Command-Modul") @@ -884,7 +1072,7 @@ def __on_message_rm(self, client, userdata, msg): def __on_message_max_id(self, client, userdata, msg): try: - self.received_topics.append(msg.topic) + self.received_topics.update({msg.topic: decode_payload(msg.payload)}) except Exception: log.exception("Fehler in ProcessBrokerBranch") diff --git a/packages/helpermodules/command_test.py b/packages/helpermodules/command_test.py index 22b04d2267..dade5c3324 100644 --- a/packages/helpermodules/command_test.py +++ b/packages/helpermodules/command_test.py @@ -16,7 +16,7 @@ @pytest.fixture def subdata_fixture() -> None: - SubData(*([Mock()]*18)) + SubData(*([Mock()]*16)) SubData.cp_data = {"cp0": Mock(spec=ChargepointStateUpdate, chargepoint=Mock( spec=Chargepoint, chargepoint_module=Mock(spec=ChargepointModulePro)))} diff --git a/packages/helpermodules/create_debug.py b/packages/helpermodules/create_debug.py index 461b46f802..9c7682be2e 100644 --- a/packages/helpermodules/create_debug.py +++ b/packages/helpermodules/create_debug.py @@ -2,7 +2,6 @@ import time import logging from pathlib import Path -import pprint from typing import Any, Optional import requests @@ -10,9 +9,9 @@ from control.chargepoint.chargepoint import Chargepoint import dataclass_utils from helpermodules import subdata -from helpermodules.broker import InternalBrokerClient +from helpermodules.broker import BrokerClient from helpermodules.pub import Pub -from helpermodules.utils.run_command import run_command +from helpermodules.utils.run_command import run_command, run_shell_command from helpermodules.utils.topic_parser import decode_payload from modules.common import req from modules.common.abstract_device import AbstractDevice @@ -20,12 +19,20 @@ log = logging.getLogger(__name__) -def config_and_state(): +def get_common_data(): parsed_data = "" try: - secondary = subdata.SubData.general_data.data.extern + serial_number = subdata.SubData.system_data["system"].data["serial_number"] except Exception: - secondary = False + serial_number = None + try: + mac_address = subdata.SubData.system_data["system"].data["mac_address"] + except Exception: + mac_address = None + try: + ip_address = subdata.SubData.system_data["system"].data["ip_address"] + except Exception: + ip_address = None with ErrorHandlingContext(): parent_file = Path(__file__).resolve().parents[2] @@ -33,45 +40,110 @@ def config_and_state(): version = f.read().strip() with open(f"{parent_file}/web/lastcommit", "r") as f: lastcommit = f.read().strip() - parsed_data += f"# Version\n{version}\n{lastcommit}\n\n" + parsed_data += f"Version: {version} ({lastcommit})\n" with ErrorHandlingContext(): - parsed_data += f"# Cloud/Brücken\n{BrokerContent().get_bridges()}" + if serial_number is None or serial_number == "null": + parsed_data += "openWB_Serial: unknown\n" + else: + parsed_data += f"openWB_Serial: {serial_number}\n" + with ErrorHandlingContext(): + parsed_data += f"IP_Address: {ip_address}\n" + with ErrorHandlingContext(): + parsed_data += f"MAC_Address: {mac_address}\n" + return parsed_data + + +def get_hardware_data(): + parsed_data = "" + temp = run_shell_command(["vcgencmd measure_temp"]).removeprefix("temp=").removesuffix("\n") + throttled = int(run_shell_command("vcgencmd get_throttled").removeprefix("throttled="), 16) + parsed_data += f"Temperature_C: {temp}" + mask_undervoltage = 0b0001 + mask_temp_limit = 0b1000 + mask_undervoltage_reboot = 0x10000 + mask_temp_limit_reboot = 0x80000 + if throttled & mask_temp_limit: + parsed_data += ", aktuell: Soft Temperature Limit" + else: + parsed_data += ", aktuell: okay" + if throttled & mask_temp_limit_reboot: + parsed_data += ", seit Reboot: Soft Temperature Limit\n" + else: + parsed_data += ", seit Reboot: okay\n" + + parsed_data += "RPI_Voltage: " + if throttled & mask_undervoltage: + parsed_data += "aktuell: Undervoltage (< 4.63V)" + else: + parsed_data += "aktuell: okay" + if throttled & mask_undervoltage_reboot: + parsed_data += ", seit Reboot: Undervoltage (< 4.63V)" + else: + parsed_data += ", seit Reboot: okay" + return parsed_data + + +def config_and_state(): + parsed_data = "" + try: + secondary = subdata.SubData.general_data.data.extern + except Exception: + secondary = False + + parsed_data += "## General ##\n" with ErrorHandlingContext(): - chargemode_config = data.data.general_data.data.chargemode_config - parsed_data += "\n# Allgemein\n" + parsed_data += f"openWB_Cloud: {BrokerContent().get_cloud()}" if secondary is False: - parsed_data += (f"Modus: Primary\nHausverbrauch: {data.data.counter_all_data.data.set.home_consumption}W\n" - f"Phasenvorgabe: Sofortladen {chargemode_config.instant_charging.phases_to_use}, Zielladen " - f"{chargemode_config.scheduled_charging.phases_to_use}, Zeitladen: " - f"{chargemode_config.time_charging.phases_to_use}, PV-Laden: " - f"{chargemode_config.pv_charging.phases_to_use}, Einschaltschwelle: " - f"{chargemode_config.pv_charging.switch_on_threshold}W, Ausschaltschwelle: " - f"{chargemode_config.pv_charging.switch_off_threshold}W\n" - f"Regelintervall: {data.data.general_data.data.control_interval}s, ") + parsed_data += ("Mode: Primary\n" + f"Home_Consumption: {data.data.counter_all_data.data.set.home_consumption} W\n" + f"Control_Interval: {data.data.general_data.data.control_interval}s\n") else: - parsed_data += "Modus: Secondary\n" - parsed_data += f"Display aktiviert: {data.data.optional_data.data.int_display.active}\n" - + parsed_data += "Mode: Secondary\n" + parsed_data += f"Display_Active: {data.data.optional_data.data.int_display.active}\n" + if secondary is False: + with ErrorHandlingContext(): + chargemode_config = data.data.general_data.data.chargemode_config + parsed_data += ("\n## General Charge Config/ PV ##\n" + f"Phase_Switch_Delay: {chargemode_config.phase_switch_delay} min\n" + f"Retry_Failed_Phase_Switches: {chargemode_config.retry_failed_phase_switches}\n" + f"Control_Range: {chargemode_config.pv_charging.control_range}W\n" + f"Switch_On_Threshold: {chargemode_config.pv_charging.switch_on_threshold}W\n" + f"Switch_On_Delay: {chargemode_config.pv_charging.switch_on_delay}s\n" + f"Switch_Off_Threshold: {chargemode_config.pv_charging.switch_off_threshold}W\n" + f"Switch_Off_Delay: {chargemode_config.pv_charging.switch_off_delay}s\n" + f"Feed_In_Yield: {chargemode_config.pv_charging.feed_in_yield}W\n" + f"Bat_Mode: {chargemode_config.pv_charging.bat_mode}\n" + f"Min_Bat_SoC: {chargemode_config.pv_charging.min_bat_soc}%\n" + f"Bat_Power_Reserve_Active: {chargemode_config.pv_charging.bat_power_reserve_active}\n" + f"Bat_Power_Reserve: {chargemode_config.pv_charging.bat_power_reserve}W\n" + f"Bat_Power_Discharge_Active: {chargemode_config.pv_charging.bat_power_discharge_active}\n" + f"Bat_Power_Discharge: {chargemode_config.pv_charging.bat_power_discharge}W\n") if secondary is False: with ErrorHandlingContext(): - pretty_hierarchy = pprint.pformat(data.data.counter_all_data.data.get.hierarchy, - indent=4, compact=True, sort_dicts=False, width=100) - parsed_data += f"\n# Hierarchie\n{pretty_hierarchy}\n" + parsed_data += f"\n## Hierarchy ##\n{get_hierarchy(data.data.counter_all_data.data.get.hierarchy)}\n" with ErrorHandlingContext(): if secondary: with ErrorHandlingContext(): - parsed_data += "\n# Ladepunkte\n" + parsed_data += "\n## Charge Points ##\n" for cp in subdata.SubData.cp_data.values(): parsed_data += get_parsed_cp_data(cp.chargepoint) else: - parsed_data += "\n# Geräte und Komponenten\n" + parsed_data += "\n## Devices and Components ##\n" for key, value in data.data.system_data.items(): with ErrorHandlingContext(): if isinstance(value, AbstractDevice): - parsed_data += f"{key}: {dataclass_utils.asdict(value.device_config)}\n" + parsed_data += f"| ### {key} ###\n" + parsed_data += f"| Device_Type: {value.device_config.type}\n" + parsed_data += f"| Device_FN: {value.device_config.name}\n" + parsed_data += ("| Device_Config: " + f"{dataclass_utils.asdict(value.device_config.configuration)}\n") for comp_key, comp_value in value.components.items(): - parsed_data += f"{comp_key}: {dataclass_utils.asdict(comp_value.component_config)}\n" + parsed_data += f"--| #### {comp_key} ####\n" + parsed_data += f"--| Component_Type: {comp_value.component_config.type}\n" + parsed_data += f"--| Component_FN: {comp_value.component_config.name}\n" + parsed_data += ("--| Component_Config: " + f"{dataclass_utils.asdict(comp_value.component_config.configuration)}\n") if "bat" in comp_value.component_config.type: component_data = data.data.bat_data[f"bat{comp_value.component_config.id}"] elif "counter" in comp_value.component_config.type: @@ -79,35 +151,112 @@ def config_and_state(): elif "inverter" in comp_value.component_config.type: component_data = data.data.pv_data[f"pv{comp_value.component_config.id}"] if "bat" in comp_value.component_config.type: - parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, " - f"SoC: {component_data.data.get.soc}%, " - f"Fehlerstatus: {component_data.data.get.fault_str}\n") + parsed_data += (f"--| Bat_Power: {component_data.data.get.power/1000}kW\n" + f"--| Bat_SoC: {component_data.data.get.soc}%\n" + f"--| Bat_Error_Status: {component_data.data.get.fault_str}\n\n") elif "inverter" in comp_value.component_config.type: - parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, " - f"Fehlerstatus: {component_data.data.get.fault_str}\n") + parsed_data += (f"--| Inverter_Power: {component_data.data.get.power/1000}kW\n" + f"--| Max_AC_Out: {component_data.data.config.max_ac_out/1000}kW\n" + f"--| Inverter_Error_Status: {component_data.data.get.fault_str}\n\n") else: counter_all_data = data.data.counter_all_data if counter_all_data.get_evu_counter_str() == f"counter{component_data.num}": - parsed_data += (f"{comp_key}: EVU-Zähler -> max. Leistung " - f"{component_data.data.config.max_total_power}, " - f"max. Ströme {component_data.data.config.max_currents}; ") + parsed_data += ("--| Counter_Type: EVU-Zähler\n" + "--| Counter_Max_Power: " + f"{component_data.data.config.max_total_power}\n" + "--| Counter_Max_Currents: " + f"{component_data.data.config.max_currents}\n") elif counter_all_data.data.config.home_consumption_source_id == component_data.num: - parsed_data += (f"{comp_key}: Hausverbrauchszähler -> max. Leistung " - f"{component_data.data.config.max_total_power}, " - f"max. Ströme {component_data.data.config.max_currents}; ") + parsed_data += ("--| Counter_Type: Hausverbrauchszähler\n" + "--| Counter_Max_Power: " + f"{component_data.data.config.max_total_power}\n" + "--| Counter_Max_Currents: " + f"{component_data.data.config.max_currents}\n") else: - parsed_data += f"{key}: max. Ströme {component_data.data.config.max_currents}" - parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, Ströme: " - f"{component_data.data.get.currents}A, Fehlerstatus: " - f"{component_data.data.get.fault_str}\n") + parsed_data += ("--| Counter_Type: Sonstiger Zähler\nCOUNTER_MAX_CURRENTS: " + f"{component_data.data.config.max_currents}\n") + parsed_data += (f"--| Counter_Power: {component_data.data.get.power/1000}kW\n" + f"--| Counter_Currents: {component_data.data.get.currents}A\n" + f"--| Counter_Error_Status: {component_data.data.get.fault_str}\n\n") with ErrorHandlingContext(): - parsed_data += "\n# Ladepunkte\n" - parsed_data += f"Ladeleistung aller Ladepunkte {data.data.cp_all_data.data.get.power / 1000}kW\n" + parsed_data += "\n## Total Powers ##\n" + evu_id = data.data.counter_all_data.get_id_evu_counter() + try: + evu_powers = filter_log_file('mqtt', 'openWB/counter/' + str(evu_id) + '/get/power,', 5) + except Exception: + evu_powers = "Keine Daten" + parsed_data += f"EVU_Power:\n{evu_powers}\n" + try: + bat_powers = filter_log_file('mqtt', 'openWB/bat/get/power,', 5) + except Exception: + bat_powers = "Keine Daten" + parsed_data += f"Bat_All_Power:\n{bat_powers}\n" + try: + pv_powers = filter_log_file('mqtt', 'openWB/pv/get/power,', 5) + except Exception: + pv_powers = "Keine Daten" + parsed_data += f"PV_All_Power:\n{pv_powers}\n" + try: + cp_powers = filter_log_file('mqtt', 'openWB/chargepoint/get/power,', 5) + except Exception: + cp_powers = "Keine Daten" + parsed_data += f"CP_All_Power:\n{cp_powers}\n" + try: + home_consumption = filter_log_file('mqtt', 'openWB/counter/set/home_consumption', 5) + except Exception: + home_consumption = "Keine Daten" + parsed_data += f"Home_Consumption:\n {home_consumption}\n" + with ErrorHandlingContext(): + parsed_data += "\n## Charge Points ##\n" + parsed_data += f"CP_All_Power: {data.data.cp_all_data.data.get.power / 1000}kW\n\n" for cp in data.data.cp_data.values(): parsed_data += get_parsed_cp_data(cp) return parsed_data +def get_hierarchy(hierarchy, level=0): + # get friendly names of elements + parsed_data = "" + for element in hierarchy: + parsed_data += "-" * (level * 2) + "| " + if element["type"] == "cp": + try: + cp = data.data.cp_data[f"cp{element['id']}"].chargepoint_module.config + parsed_data += f"{element['type']}: {cp.name} (ID: {element['id']})\n" + except Exception: + parsed_data += f"{element['type']} (ID: {element['id']})\n" + else: + try: + for key, value in data.data.system_data.items(): + with ErrorHandlingContext(): + if isinstance(value, AbstractDevice): + for comp_key, comp_value in value.components.items(): + if (f"component{element['id']}" == comp_key): + parsed_data += (f"{element['type']}: {comp_value.component_config.name} " + f"(device_type: {value.device_config.type}, ") + if "counter" in comp_value.component_config.type: + component_data = data.data.counter_data[ + f"counter{comp_value.component_config.id}"] + counter_all_data = data.data.counter_all_data + if counter_all_data.get_evu_counter_str() == f"counter{component_data.num}": + counter_type = ("EVU-Zähler") + elif (counter_all_data.data.config.home_consumption_source_id == + component_data.num): + counter_type = ("Hausverbrauchszähler") + else: + counter_type = "Sonstiger Zähler" + parsed_data += f"counter_type: {counter_type}, " + elif "inverter" in comp_value.component_config.type: + component_data = data.data.pv_data[f"pv{comp_value.component_config.id}"] + parsed_data += f"max_ac_out: {component_data.data.config.max_ac_out/1000}kW, " + parsed_data += f"ID: {element['id']})\n" + except Exception: + parsed_data += f"{element['type']} (ID: {element['id']})\n" + if element["children"]: + parsed_data += get_hierarchy(element["children"], level + 1) + return parsed_data + + def get_parsed_cp_data(cp: Chargepoint) -> str: parsed_data = "" with ErrorHandlingContext(): @@ -115,16 +264,56 @@ def get_parsed_cp_data(cp: Chargepoint) -> str: ip = cp.chargepoint_module.config.configuration.ip_address else: ip = None - parsed_data += (f"LP{cp.num}: Typ: {cp.chargepoint_module.config.type}; IP: " - f"{ip}; Stecker-Status: {cp.data.get.plug_state}, Leistung: " - f"{cp.data.get.power/1000}kW, {cp.data.get.currents}A, {cp.data.get.voltages}V, Lademodus: " - f"{cp.data.control_parameter.chargemode}, Submode: " - f"{cp.data.control_parameter.submode}, Soll-Strom: " - f"{cp.data.set.current}A, EVSE-Strom: {cp.data.get.evse_current}A, " - f"Status: {cp.data.get.state_str}, Fehlerstatus: {cp.data.get.fault_str}\n") + if hasattr(cp.chargepoint_module.config.configuration, "mode"): + mode = f"CP_Mode: {cp.chargepoint_module.config.configuration.mode}\n" + else: + mode = "" + if hasattr(cp.data.get, "frequency"): + frequency = cp.data.get.frequency + else: + frequency = None + + try: + currents = filter_log_file('mqtt', 'openWB/chargepoint/' + + str(cp.chargepoint_module.config.id) + '/get/currents') + voltages = filter_log_file('mqtt', 'openWB/chargepoint/' + + str(cp.chargepoint_module.config.id) + '/get/voltages') + except Exception: + currents = "Keine Daten" + voltages = "Keine Daten" + + parsed_data += (f"### LP{cp.num} ###\n" + f"CP_Type: {cp.chargepoint_module.config.type}\n" + f"CP_FN: {cp.chargepoint_module.config.name}\n" + f"{mode}" + f"CP_Phase_Switch_HW: {cp.data.config.auto_phase_switch_hw}\n" + f"CP_Control_Pilot_HW: {cp.data.config.control_pilot_interruption_hw}\n" + f"CP_IP: {ip}\n" + f"CP_Set_Current: {cp.data.set.current} A\n" + f"Meter_Power: {cp.data.get.power} W\n" + f"Meter_Voltages: {cp.data.get.voltages} V\n" + f"Meter_Currents: {cp.data.get.currents} A\n" + f"Meter_Frequency: {frequency} Hz\n" + f"Meter_Serial: {cp.data.get.serial_number}\n" + f"Meter_Imported: {cp.data.get.imported} kWh\n" + f"EVSE_Max_Current: {cp.data.get.max_evse_current} A\n" + f"EVSE_Current: {cp.data.get.evse_current} A\n" + # EVSE_MODBUS: True / False + # EVSE_ID: 105 (...) + # EVSE_SELFTEST: Passed / Failed + f"EVSE_Plug_State: {cp.data.get.plug_state}\n" + f"Charge_Mode: {cp.data.control_parameter.chargemode}\n" + f"Charge_Submode: {cp.data.control_parameter.submode}\n" + f"Charge_State: {cp.data.get.state_str}\n" + # CP_SW_VERSION: 2.1.7-Patch.2 + # CP_FIRMWARE: 1.2.3 (bei Pro bzw. Satellit) + # CP_SIGNALING_PRO: basic iec61851 iso11518 + f"CP_Error_State: {cp.data.get.fault_str}\n" + f"Additional_Meter_Voltages: \n{voltages}" + f"Additional_Meter_Currents: \n{currents}\n") if cp.chargepoint_module.config.type == "openwb_pro": try: - parsed_data += f"{req.get_http_session().get(f'http://{ip}/connect.php', timeout=5).text}\n" + parsed_data += f"openWB_Pro: {req.get_http_session().get(f'http://{ip}/connect.php', timeout=5).text}\n" except requests.Timeout: parsed_data += "Timeout beim Abrufen der Daten\n" return parsed_data @@ -135,8 +324,26 @@ def get_parsed_cp_data(cp: Chargepoint) -> str: debug_file = ramdisk_dir / 'debug.log' +def filter_log_file(log_name, pattern, num_results=10): + log_files = [f"{ramdisk_dir}/{log_name}.log.{i}" for i in range(5, 0, -1)] + print(log_files) + log_files.append(f"{ramdisk_dir}/{log_name}.log") + print(log_files) + lines = [] + try: + for log_file in log_files: + if os.path.isfile(log_file): + with open(log_file, 'r') as file: + for line in file.readlines(): + if pattern in line: + lines.append(line) + except Exception as e: + log.exception(f"Fehler beim Lesen der Logdateien: {e}") + return ''.join(lines[-num_results:]) + + def merge_log_files(log_name, num_lines): - log_files = [f"{ramdisk_dir}/{log_name}.log.{i}" for i in range(5, 1)] + log_files = [f"{ramdisk_dir}/{log_name}.log.{i}" for i in range(5, 0, -1)] log_files.append(f"{ramdisk_dir}/{log_name}.log") lines = [] @@ -158,6 +365,18 @@ def get_uuids(): log.exception(f"Error reading UUID file: {e}") +def get_boots(num_lines=100): + lines = [] + log_file = openwb_base_dir / 'data/log/boot' + try: + if os.path.isfile(log_file): + with open(log_file, 'r') as file: + lines = file.readlines() + except Exception as e: + log.exception(f"Error reading BOOT file: {e}") + return ''.join(lines[-num_lines:]) + + def create_debug_log(input_data): def write_to_file(file_handler, func, default: Optional[Any] = None): try: @@ -173,38 +392,45 @@ def write_to_file(file_handler, func, default: Optional[Any] = None): header = (f"{input_data['message']}\n{debug_email}\n{input_data['serialNumber']}\n" f"{input_data['installedComponents']}\n{input_data['vehicles']}\n") with open(debug_file, 'w+') as df: + write_to_file(df, lambda: "# section: form data #") write_to_file(df, lambda: header) - write_to_file(df, lambda: f"## section: configuration and state ##\n{config_and_state()}\n") - write_to_file(df, lambda: f'## section: system ##\n{run_command(["uptime"])}{run_command(["free"])}\n') - write_to_file(df, lambda: f"## section: uuids ##\n{get_uuids()}\n") - write_to_file(df, lambda: f'## section: network ##\n{run_command(["ip", "-s", "address"])}\n') - write_to_file(df, lambda: f'## section: storage ##\n{run_command(["df", "-h"])}\n') - write_to_file(df, lambda: f"## section: broker essentials ##\n{broker.get_broker_essentials()}\n") + write_to_file(df, lambda: f'# section: system #\n{get_common_data()}' + f'Kernel: {run_shell_command("uname -s -r -v -m -o")}\n' + f'Uptime:{run_command(["uptime"])}{run_command(["free"])}\n') + write_to_file(df, lambda: f'# section: hardware #\n{get_hardware_data()}') + write_to_file(df, lambda: f'USB_Devices:\n{run_shell_command(["lsusb"])}\n') + write_to_file(df, lambda: f"# section: configuration and state #\n{config_and_state()}") + write_to_file(df, lambda: f"# section: errors #\n{filter_log_file('main', 'ERROR', 30)}\n") + write_to_file(df, lambda: f"# section: uuids #\n{get_uuids()}\n") + write_to_file(df, lambda: f"# section: boots #\n{get_boots(30)}\n") + write_to_file(df, lambda: f'# section: storage #\n{run_command(["df", "-h"])}\n') + write_to_file(df, lambda: f"# section: broker essentials #\n{broker.get_broker_essentials()}\n") write_to_file( - df, lambda: f"## section: retained log ##\n{merge_log_files('main', 500)}") - write_to_file(df, lambda: "## section: info log ##\n") + df, lambda: f"# section: retained log #\n{merge_log_files('main', 500)}") + write_to_file(df, lambda: "# section: info log #\n") Pub().pub('openWB/set/system/debug_level', 20) time.sleep(60) write_to_file(df, lambda: merge_log_files("main", 1000)) - write_to_file(df, lambda: "## section: debug log ##\n") + write_to_file(df, lambda: "# section: debug log #\n") Pub().pub('openWB/set/system/debug_level', 10) time.sleep(60) write_to_file(df, lambda: merge_log_files("main", 2500)) write_to_file( df, - lambda: f'## section: internal chargepoint log ##\n{merge_log_files("internal_chargepoint", 1000)}\n') - write_to_file(df, lambda: f'## section: mqtt log ##\n{merge_log_files("mqtt", 1000)}\n') - write_to_file(df, lambda: f'## section: soc log ##\n{merge_log_files("soc", 1000)}\n') - write_to_file(df, lambda: f'## section: charge log ##\n{merge_log_files("chargelog", 1000)}\n') - write_to_file(df, lambda: f"## section: broker ##\n{broker.get_broker()}") + lambda: f'# section: internal chargepoint log #\n{merge_log_files("internal_chargepoint", 1000)}\n') + write_to_file(df, lambda: f'# section: mqtt log #\n{merge_log_files("mqtt", 1000)}\n') + write_to_file(df, lambda: f'# section: soc log #\n{merge_log_files("soc", 1000)}\n') + write_to_file(df, lambda: f'# section: charge log #\n{merge_log_files("chargelog", 1000)}\n') + write_to_file(df, lambda: f"# section: broker #\n{broker.get_broker()}") + write_to_file(df, lambda: f'# section: network #\n{run_command(["ip", "-s", "address"])}\n') log.info("***** uploading debug log...") with open(debug_file, 'rb') as f: data = f.read() - req.get_http_session().put("https://openwb.de/tools/debug2.php", - data=data, - params={'debugemail': debug_email}, - timeout=10) + req.get_http_session().put("https://openwb.de/tools/debug2.php", + data=data, + params={'debugemail': debug_email}, + timeout=10) log.info("***** cleanup...") os.remove(debug_file) @@ -216,9 +442,10 @@ def write_to_file(file_handler, func, default: Optional[Any] = None): class BrokerContent: def __init__(self) -> None: self.content = "" + self.count = 0 def get_broker(self): - InternalBrokerClient("processBrokerBranch", self.__on_connect_broker, self.__get_content).start_finite_loop() + BrokerClient("processBrokerBranch", self.__on_connect_broker, self.__get_content).start_finite_loop() return self.content def __on_connect_broker(self, client, userdata, flags, rc): @@ -228,8 +455,8 @@ def __get_content(self, client, userdata, msg): self.content += f"{msg.topic} {decode_payload(msg.payload)}\n" def get_broker_essentials(self): - InternalBrokerClient("processBrokerBranch", self.__on_connect_broker_essentials, - self.__get_content).start_finite_loop() + BrokerClient("processBrokerBranch", self.__on_connect_broker_essentials, + self.__get_content).start_finite_loop() return self.content def __on_connect_broker_essentials(self, client, userdata, flags, rc): @@ -247,24 +474,31 @@ def __on_connect_broker_essentials(self, client, userdata, flags, rc): client.subscribe("openWB/bat/#", 2) client.subscribe("openWB/optional/et/provider", 2) - def get_bridges(self): - InternalBrokerClient("processBrokerBranch", self.__on_connect_bridges, self.__get_bridges).start_finite_loop() - return self.content - def __on_connect_bridges(self, client, userdata, flags, rc): client.subscribe("openWB/system/mqtt/#", 2) - def __get_bridges(self, client, userdata, msg): + def get_cloud(self): + BrokerClient("processBrokerBranch", self.__on_connect_bridges, self.__get_cloud).start_finite_loop() + BrokerClient("processBrokerBranch", self.__on_connect_bridges, self.__get_partner).start_finite_loop() + self.content += f"Active_MQTT_Bridges: {self.count}\n" + return self.content + + def __get_cloud(self, client, userdata, msg): if "openWB/system/mqtt/bridge" in msg.topic: payload = decode_payload(msg.payload) - self.content += (f"Name: {payload['name']}, aktiv: {payload['active']}, " - f"openWB-Cloud: {payload['remote']['is_openwb_cloud']}") - if payload['remote'].get("is_openwb_cloud"): - self.content += (f", BN: {payload['remote']['username']}, PW: {payload['remote']['password']}, " - f"Partnerzugang: {payload['access']['partner']}") - self.content += "\n" - elif "openWB/system/mqtt/valid_partner_ids": - self.content += f"Partner-IDs: {decode_payload(msg.payload)}\n" + if payload['active']: + if payload['remote'].get("is_openwb_cloud"): + self.content += f"BN: {payload['remote']['username']}, PW: {payload['remote']['password']}, " + if payload['access']['partner']: + self.content += "Partnerzugang: An\n" + else: + self.content += "Partnerzugang: Aus\n" + else: + self.count += 1 + + def __get_partner(self, client, userdata, msg): + if "openWB/system/mqtt/valid_partner_ids" in msg.topic: + self.content += f"Partner_ID: {decode_payload(msg.payload)}\n" class ErrorHandlingContext: diff --git a/packages/helpermodules/data_migration/data_migration.py b/packages/helpermodules/data_migration/data_migration.py index 369641a846..dc9127a64b 100644 --- a/packages/helpermodules/data_migration/data_migration.py +++ b/packages/helpermodules/data_migration/data_migration.py @@ -15,6 +15,7 @@ import pathlib import shutil import tarfile +from paho.mqtt.client import Client as MqttClient, MQTTMessage from threading import Thread from typing import Callable, Dict, List, Optional, Union @@ -22,17 +23,21 @@ from control.ev import ev from dataclass_utils import dataclass_from_dict import dataclass_utils +from helpermodules.broker import BrokerClient from helpermodules.data_migration.id_mapping import MapId from helpermodules.hardware_configuration import update_hardware_configuration from helpermodules.measurement_logging.process_log import get_totals, string_to_float, string_to_int from helpermodules.measurement_logging.write_log import LegacySmartHomeLogData, get_names from helpermodules.timecheck import convert_timedelta_to_time_string, get_difference from helpermodules.utils import joined_thread_handler +from helpermodules.utils.topic_parser import get_index from helpermodules.pub import Pub from helpermodules.utils.json_file_handler import write_and_check -from modules.ripple_control_receivers.gpio.config import GpioRcr +from modules.io_actions.controllable_consumers.ripple_control_receiver.config import RippleControlReceiverSetup +from modules.io_devices.add_on.config import AddOn import re + log = logging.getLogger("data_migration") @@ -201,7 +206,7 @@ def conv_1_9_datetimes(datetime_str): elif row[7] == "3": chargemode = "stop" elif row[7] == "4": - chargemode = "standby" + chargemode = "eco_charging" elif row[7] == "7": chargemode = "scheduled_charging" else: @@ -439,7 +444,7 @@ def _monthly_log_entry(self, file: str): alte Spaltenbelegung: 12, 19 oder 29 Felder! Wurde in 1.9 nicht vereinheitlicht! Allgemein: - 0: Datum "YYYMMDD" + 0: Datum "YYYYMMDD" EVU: 1-2: Bezug, Einspeisung PV: @@ -569,7 +574,19 @@ def _move_cloud_data(self) -> None: def _move_rse(self) -> None: if bool(self._get_openwb_conf_value("rseenabled", "0")): - Pub().pub("openWB/set/general/ripple_control_receiver/module", dataclass_utils.asdict(GpioRcr())) + action = RippleControlReceiverSetup() + for cp_topic in self.all_received_topics.keys(): + if re.search("openWB/chargepoint/[0-9]+/config", cp_topic) is not None: + action.configuration.cp_ids.append(get_index(cp_topic)) + action.configuration.io_device = 0 + # Wenn mindestens ein Kontakt geschlossen ist, wird die Ladung gesperrt. Wenn beide Kontakt + # offen sind, darf geladen werden. + action.configuration.input_pattern = [{"value": 1, "matrix": {"21": False, "24": False}}, + {"value": 0, "matrix": {"21": False, "24": True}}, + {"value": 0, "matrix": {"21": True, "24": False}}, + {"value": 0, "matrix": {"21": True, "24": True}}] + Pub().pub('openWB/system/io/0/config', dataclass_utils.asdict(AddOn())) + Pub().pub('openWB/io/action/0/config', dataclass_utils.asdict(action)) def _move_max_c_socket(self): try: @@ -621,3 +638,21 @@ def merge_list_of_records(self, key): reduce(self._merge_records_by(key), records) for _, records in groupby(sorted(lst, key=key_prop), key_prop) ] + + +class BrokerCphargepoints: + def get_configured_cp_ids(self) -> List: + self.all_received_topics = {} + BrokerClient("update-config", self.on_connect, self.on_message).start_finite_loop() + cp_ids = [] + for topic, payload in self.all_received_topics.items(): + cp_ids.append(get_index(topic)) + return cp_ids + + def on_connect(self, client: MqttClient, userdata, flags: dict, rc: int): + """ connect to broker and subscribe to set topics + """ + client.subscribe("openWB/chargepoint/+/config", 2) + + def on_message(self, client: MqttClient, userdata, msg: MQTTMessage): + self.all_received_topics.update({msg.topic: msg.payload}) diff --git a/packages/helpermodules/exceptions/os.py b/packages/helpermodules/exceptions/os.py index e39607da10..86e2ffeec0 100644 --- a/packages/helpermodules/exceptions/os.py +++ b/packages/helpermodules/exceptions/os.py @@ -1,9 +1,11 @@ from helpermodules.exceptions.registry import ExceptionRegistry +from requests.exceptions import ReadTimeout +from urllib3.exceptions import ReadTimeoutError def handle_os_error(e: OSError): code = e.errno - if code == 113 or e.args[0] == "timed out": + if code == 113 or e.args[0] == "timed out" or isinstance(e, (ReadTimeout, ReadTimeoutError)): return "Die Verbindung zum Host ist fehlgeschlagen. Überprüfe Adresse und Netzwerk." return "OSError {}: Unbekannter Fehler {}".format(code, e.strerror) diff --git a/packages/helpermodules/exceptions/registry.py b/packages/helpermodules/exceptions/registry.py index ee3cc36d1e..6d892a48a0 100644 --- a/packages/helpermodules/exceptions/registry.py +++ b/packages/helpermodules/exceptions/registry.py @@ -1,5 +1,5 @@ import sys -from typing import Type, Optional, Callable, TypeVar, Generic, List, Union, Any +from typing import List, Type, Optional, Callable, TypeVar, Generic, Union, Any, Tuple from modules.common.fault_state_level import FaultStateLevel @@ -20,9 +20,9 @@ def __init__(self, type: Type[T], handler: Callable[[T], Any]): class ExceptionRegistry: - registry = [] # type: List[RegistryEntry] + registry: List[RegistryEntry] = [] - def translate_exception(self, exception: Exception) -> [str, FaultStateLevel]: + def translate_exception(self, exception: Exception) -> Tuple[str, FaultStateLevel]: entry = self.find_registry_entry(exception) if entry is None: return "{} {}".format(type(exception), exception.args), FaultStateLevel.ERROR diff --git a/packages/helpermodules/exceptions/requests.py b/packages/helpermodules/exceptions/requests.py index 9f764d79e5..684053f3ca 100644 --- a/packages/helpermodules/exceptions/requests.py +++ b/packages/helpermodules/exceptions/requests.py @@ -1,5 +1,5 @@ from requests import HTTPError -from requests.exceptions import ConnectionError +from requests.exceptions import ConnectionError, ReadTimeout from helpermodules.exceptions.registry import ExceptionRegistry @@ -8,6 +8,10 @@ def handle_connection_error(e: ConnectionError): return "Die Verbindung zum Server {} ist fehlgeschlagen. Überprüfe Adresse und Netzwerk.".format(e.request.url) +def handle_read_timeout(e: ReadTimeout): + return "Innerhalb des Timeouts wurde keine Antwort erhalten. Überprüfe Adresse und Netzwerk." + + def handle_http_error(e: HTTPError): code = e.response.status_code if 400 <= code < 500: @@ -22,3 +26,4 @@ def handle_http_error(e: HTTPError): def register_request_exception_handlers(registry: ExceptionRegistry) -> None: registry.add(ConnectionError, handle_connection_error) registry.add(HTTPError, handle_http_error) + registry.add(ReadTimeout, handle_read_timeout) diff --git a/packages/helpermodules/graph.py b/packages/helpermodules/graph.py index f18a9567ec..7808bda897 100644 --- a/packages/helpermodules/graph.py +++ b/packages/helpermodules/graph.py @@ -15,7 +15,7 @@ @dataclass class Config: - duration: int = 120 + duration: int = 240 def config_factory() -> Config: diff --git a/packages/helpermodules/logger.py b/packages/helpermodules/logger.py index 93fafcf7e5..4993b403dc 100644 --- a/packages/helpermodules/logger.py +++ b/packages/helpermodules/logger.py @@ -1,16 +1,22 @@ import functools import logging +import logging.handlers from logging.handlers import RotatingFileHandler from pathlib import Path +import queue import sys import threading import typing_extensions import re +import io +import os +import shutil FORMAT_STR_DETAILED = '%(asctime)s - {%(name)s:%(lineno)s} - {%(levelname)s:%(threadName)s} - %(message)s' FORMAT_STR_SHORT = '%(asctime)s - %(message)s' RAMDISK_PATH = str(Path(__file__).resolve().parents[2]) + '/ramdisk/' PERSISTENT_LOG_PATH = str(Path(__file__).resolve().parents[2]) + '/data/log/' +NUMBER_OF_LOGFILES = 3 KNOWN_SENSITIVE_FIELDS = [ 'password', 'secret', 'token', 'apikey', 'access_token', @@ -63,6 +69,7 @@ class RedactingFilter(logging.Filter): Args: name (str): The name of the filter. """ + def __init__(self, name: str = ''): super().__init__(name) @@ -103,80 +110,249 @@ def filter_pos(name: str, record) -> bool: return False +class InMemoryLogHandler(logging.Handler): + def __init__(self, base_handler=None): + super().__init__() + self.base_handler = base_handler + self.log_stream = io.StringIO() + self.has_warning_or_error = False + + def emit(self, record): + if self.base_handler is None or self.base_handler.filter(record): + msg = self.format(record) + self.log_stream.write(msg + '\n') + if record.levelno >= logging.WARNING: + self.has_warning_or_error = True + + def get_logs(self): + return self.log_stream.getvalue() + + def clear(self): + self.log_stream = io.StringIO() + self.has_warning_or_error = False + + +def clear_in_memory_log_handler(logger_name: str = None) -> None: + global in_memory_log_handlers + if logger_name is None: + # Clear all in-memory log handlers + for handler in in_memory_log_handlers.values(): + handler.clear() + else: + # Clear specified in-memory log handler + if logger_name in in_memory_log_handlers: + in_memory_log_handlers[logger_name].clear() + + +def write_logs_to_file(logger_name: str = None) -> None: + global in_memory_log_handlers + + def rotate_logs(base_path: str, name: str): + # Rotate the log files + for i in range(NUMBER_OF_LOGFILES-1, 0, -1): + src = os.path.join(base_path, f'{name}.previous{i}.log') + dst = os.path.join(base_path, f'{name}.previous{i+1}.log') + if os.path.exists(src): + shutil.move(src, dst) + # Move the current log to previous1 + current_log = os.path.join(base_path, f'{name}.current.log') + if os.path.exists(current_log): + shutil.move(current_log, os.path.join(base_path, f'{name}.previous1.log')) + + def combine_logs(base_path: str, name: str): + latest_log_path = os.path.join(base_path, f'{name}.latest.log') + with open(latest_log_path, 'w') as latest_log: + for i in range(NUMBER_OF_LOGFILES-1, -1, -1): + log_file = os.path.join( + base_path, f'{name}.previous{i}.log') if i > 0 else os.path.join(base_path, f'{name}.current.log') + if os.path.exists(log_file): + with open(log_file, 'r') as f: + latest_log.write(f.read()) + + if logger_name is None: + # Write logs for all in-memory log handlers + for name, handler in in_memory_log_handlers.items(): + logs = handler.get_logs() + if logs: + rotate_logs(RAMDISK_PATH, name) + with open(os.path.join(RAMDISK_PATH, f'{name}.current.log'), 'w') as f: + f.write(logs) + combine_logs(RAMDISK_PATH, name) + + # If any warning or error messages were logged, create a -warning copy + if handler.has_warning_or_error: + with open(os.path.join(RAMDISK_PATH, f'{name}.latest-warning.log'), 'w') as f: + f.write(logs) + + else: + # Write logs for specified in-memory log handler + if logger_name in in_memory_log_handlers: + handler = in_memory_log_handlers[logger_name] + logs = handler.get_logs() + if logs: + rotate_logs(RAMDISK_PATH, logger_name) + with open(os.path.join(RAMDISK_PATH, f'{logger_name}.current.log'), 'w') as f: + f.write(logs) + combine_logs(RAMDISK_PATH, logger_name) + + # If any warning or error messages were logged, create a -warning copy + if handler.has_warning_or_error: + with open(os.path.join(RAMDISK_PATH, f'{logger_name}.latest-warning.log'), 'w') as f: + f.write(logs) + + def setup_logging() -> None: def mb_to_bytes(megabytes: int) -> int: return megabytes * 1000000 - # Mehrere kleine Dateien verwenden, damit nicht zu viel verworfen wird, wenn die Datei voll ist. + + global in_memory_log_handlers + in_memory_log_handlers = {name: InMemoryLogHandler() for name in ["main", "internal_chargepoint"]} + # to do: add smarthome and soc to in_memory_log_handlers, needs updates in individual thread calls + + # Main logger + log_queue = queue.Queue() + queue_handler = logging.handlers.QueueHandler(log_queue) main_file_handler = RotatingFileHandler(RAMDISK_PATH + 'main.log', maxBytes=mb_to_bytes(5.5), backupCount=4) main_file_handler.setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) main_file_handler.addFilter(RedactingFilter()) - logging.basicConfig(level=logging.DEBUG, handlers=[main_file_handler]) + in_memory_log_handlers["main"] = InMemoryLogHandler(main_file_handler) + in_memory_log_handlers["main"].setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) + logging.basicConfig(level=logging.DEBUG, handlers=[queue_handler, in_memory_log_handlers["main"]]) logging.getLogger().handlers[0].addFilter(functools.partial(filter_neg, "soc")) logging.getLogger().handlers[0].addFilter(functools.partial(filter_neg, "Internal Chargepoint")) logging.getLogger().handlers[0].addFilter(functools.partial(filter_neg, "smarthome")) + main_listener = logging.handlers.QueueListener(log_queue, main_file_handler) + main_listener.start() + # Chargelog logger + chargelog_queue = queue.Queue() + chargelog_queue_handler = logging.handlers.QueueHandler(chargelog_queue) chargelog_log = logging.getLogger("chargelog") chargelog_log.propagate = False chargelog_file_handler = RotatingFileHandler( RAMDISK_PATH + 'chargelog.log', maxBytes=mb_to_bytes(2), backupCount=1) chargelog_file_handler.setFormatter(logging.Formatter(FORMAT_STR_SHORT)) chargelog_file_handler.addFilter(RedactingFilter()) - chargelog_log.addHandler(chargelog_file_handler) + chargelog_log.addHandler(chargelog_queue_handler) + chargelog_listener = logging.handlers.QueueListener(chargelog_queue, chargelog_file_handler) + chargelog_listener.start() + # Data migration logger + data_migration_queue = queue.Queue() + data_migration_queue_handler = logging.handlers.QueueHandler(data_migration_queue) data_migration_log = logging.getLogger("data_migration") data_migration_log.propagate = False data_migration_file_handler = RotatingFileHandler( PERSISTENT_LOG_PATH + 'data_migration.log', maxBytes=mb_to_bytes(1), backupCount=1) data_migration_file_handler.setFormatter(logging.Formatter(FORMAT_STR_SHORT)) data_migration_file_handler.addFilter(RedactingFilter()) - data_migration_log.addHandler(data_migration_file_handler) + data_migration_log.addHandler(data_migration_queue_handler) + data_migration_listener = logging.handlers.QueueListener(data_migration_queue, data_migration_file_handler) + data_migration_listener.start() + # MQTT logger + mqtt_queue = queue.Queue() + mqtt_queue_handler = logging.handlers.QueueHandler(mqtt_queue) mqtt_log = logging.getLogger("mqtt") mqtt_log.propagate = False mqtt_file_handler = RotatingFileHandler(RAMDISK_PATH + 'mqtt.log', maxBytes=mb_to_bytes(3), backupCount=1) mqtt_file_handler.setFormatter(logging.Formatter(FORMAT_STR_SHORT)) mqtt_file_handler.addFilter(RedactingFilter()) - mqtt_log.addHandler(mqtt_file_handler) - + mqtt_log.addHandler(mqtt_queue_handler) + mqtt_listener = logging.handlers.QueueListener(mqtt_queue, mqtt_file_handler) + mqtt_listener.start() + + # Steuve control command logger + steuve_control_command_queue = queue.Queue() + steuve_control_command_queue_handler = logging.handlers.QueueHandler(steuve_control_command_queue) + steuve_control_command_log = logging.getLogger("steuve_control_command") + steuve_control_command_log.propagate = False + steuve_control_command_file_handler = RotatingFileHandler( + PERSISTENT_LOG_PATH + 'steuve_control_command.log', maxBytes=mb_to_bytes(80), backupCount=1) + steuve_control_command_file_handler.setFormatter(logging.Formatter(FORMAT_STR_SHORT)) + steuve_control_command_log.addHandler(steuve_control_command_queue_handler) + steuve_control_command_listener = logging.handlers.QueueListener(steuve_control_command_queue, + steuve_control_command_file_handler) + steuve_control_command_listener.start() + + # Smarthome logger + smarthome_queue = queue.Queue() + smarthome_queue_handler = logging.handlers.QueueHandler(smarthome_queue) smarthome_log_handler = RotatingFileHandler(RAMDISK_PATH + 'smarthome.log', maxBytes=mb_to_bytes(1), backupCount=1) smarthome_log_handler.setFormatter(logging.Formatter(FORMAT_STR_SHORT)) smarthome_log_handler.addFilter(functools.partial(filter_pos, "smarthome")) smarthome_log_handler.addFilter(RedactingFilter()) - logging.getLogger().addHandler(smarthome_log_handler) + logging.getLogger().addHandler(smarthome_queue_handler) + smarthome_listener = logging.handlers.QueueListener(smarthome_queue, smarthome_log_handler) + smarthome_listener.start() + # SoC logger + soc_queue = queue.Queue() + soc_queue_handler = logging.handlers.QueueHandler(soc_queue) soc_log_handler = RotatingFileHandler(RAMDISK_PATH + 'soc.log', maxBytes=mb_to_bytes(2), backupCount=1) soc_log_handler.setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) soc_log_handler.addFilter(functools.partial(filter_pos, "soc")) soc_log_handler.addFilter(RedactingFilter()) - logging.getLogger().addHandler(soc_log_handler) - + in_memory_log_handlers["soc"] = InMemoryLogHandler(soc_log_handler) + in_memory_log_handlers["soc"].setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) + logging.getLogger().addHandler(soc_queue_handler) + logging.getLogger().addHandler(in_memory_log_handlers["soc"]) + soc_listener = logging.handlers.QueueListener(soc_queue, soc_log_handler) + soc_listener.start() + + # Internal chargepoint logger + internal_chargepoint_queue = queue.Queue() + internal_chargepoint_queue_handler = logging.handlers.QueueHandler(internal_chargepoint_queue) internal_chargepoint_log_handler = RotatingFileHandler(RAMDISK_PATH + 'internal_chargepoint.log', maxBytes=mb_to_bytes(1), backupCount=1) internal_chargepoint_log_handler.setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) internal_chargepoint_log_handler.addFilter(functools.partial(filter_pos, "Internal Chargepoint")) internal_chargepoint_log_handler.addFilter(RedactingFilter()) - logging.getLogger().addHandler(internal_chargepoint_log_handler) - + in_memory_log_handlers["internal_chargepoint"] = InMemoryLogHandler(internal_chargepoint_log_handler) + in_memory_log_handlers["internal_chargepoint"].setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) + logging.getLogger().addHandler(internal_chargepoint_queue_handler) + logging.getLogger().addHandler(in_memory_log_handlers["internal_chargepoint"]) + internal_chargepoint_listener = logging.handlers.QueueListener(internal_chargepoint_queue, + internal_chargepoint_log_handler) + internal_chargepoint_listener.start() + + # urllib3 logger + urllib3_queue = queue.Queue() + urllib3_queue_handler = logging.handlers.QueueHandler(urllib3_queue) urllib3_log = logging.getLogger("urllib3.connectionpool") urllib3_log.propagate = True urllib3_file_handler = RotatingFileHandler(RAMDISK_PATH + 'soc.log', maxBytes=mb_to_bytes(2), backupCount=1) urllib3_file_handler.setFormatter(logging.Formatter(FORMAT_STR_DETAILED)) urllib3_file_handler.addFilter(RedactingFilter()) urllib3_file_handler.addFilter(functools.partial(filter_pos, "soc")) - urllib3_log.addHandler(urllib3_file_handler) + urllib3_log.addHandler(urllib3_queue_handler) + urllib3_listener = logging.handlers.QueueListener(urllib3_queue, urllib3_file_handler) + urllib3_listener.start() logging.getLogger("pymodbus").setLevel(logging.WARNING) logging.getLogger("uModbus").setLevel(logging.WARNING) logging.getLogger("websockets").setLevel(logging.WARNING) + thread_errors_path = Path(Path(__file__).resolve().parents[2]/"ramdisk"/"thread_errors.log") + with thread_errors_path.open("w") as f: + f.write("") + def threading_excepthook(args): - logging.getLogger(__name__).error("Uncaught exception in threading.excepthook:", exc_info=( - args.exc_type, args.exc_value, args.exc_traceback)) + with open(RAMDISK_PATH+"thread_errors.log", "a") as f: + f.write("Uncaught exception in thread:\n") + f.write(f"Type: {args.exc_type}\n") + f.write(f"Value: {args.exc_value}\n") + import traceback + traceback.print_tb(args.exc_traceback, file=f) threading.excepthook = threading_excepthook def handle_unhandled_exception(exc_type, exc_value, exc_traceback): - logging.getLogger(__name__).error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback)) + with open(RAMDISK_PATH+"thread_errors.log", "a") as f: + f.write("Uncaught exception:\n") + f.write(f"Type: {exc_type}\n") + f.write(f"Value: {exc_value}\n") + f.write(f"Traceback:{exc_traceback}\n") sys.excepthook = handle_unhandled_exception diff --git a/packages/helpermodules/measurement_logging/conftest.py b/packages/helpermodules/measurement_logging/conftest.py index 48200d8b46..17e3209584 100644 --- a/packages/helpermodules/measurement_logging/conftest.py +++ b/packages/helpermodules/measurement_logging/conftest.py @@ -1,7 +1,8 @@ -import threading +from threading import Event import pytest -from control.chargepoint import chargepoint +from control.bat import Bat +from control.chargepoint import chargepoint from control.chargepoint.chargepoint_all import AllChargepoints from control import bat_all, counter, pv_all, pv from control import data @@ -9,8 +10,8 @@ @pytest.fixture(autouse=True) def data_module() -> None: - data.data_init(threading.Event()) - data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": bat_all.Bat(2)}) + data.data_init(Event()) + data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": Bat(2)}) data.data.counter_data.update({"counter0": counter.Counter(0)}) data.data.cp_all_data = AllChargepoints() data.data.cp_data.update({"cp4": chargepoint.Chargepoint( @@ -198,3 +199,116 @@ def daily_log_entry_kw(): 'power_average': 0.12, 'power_exported': 0.0, 'power_imported': 0.12}}} + + +@pytest.fixture() +def daily_log_entry_kw_percentage(): + return { + 'bat': {'all': {'energy_exported': 0.275, + 'energy_imported': 0.0, + 'exported': 1742.135, + 'imported': 2.42, + 'power_average': -3.316, + 'power_exported': 3.316, + 'power_imported': 0.0, + 'soc': 15}, + 'bat2': {'energy_exported': 0.275, + 'energy_imported': 0.0, + 'exported': 1742.135, + 'imported': 2.42, + 'power_average': -3.316, + 'power_exported': 3.316, + 'power_imported': 0.0, + 'soc': 15}}, + 'counter': {'counter0': {'energy_exported': 0.0, + 'energy_imported': 0.746, + 'exported': 2.396, + 'grid': True, + 'imported': 4686.054, + 'power_average': 8.983, + 'power_exported': 0.0, + 'power_imported': 8.983}}, + 'cp': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.96, + 'energy_imported_bat': 0.23, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.624, + 'energy_imported_pv': 0.105, + 'exported': 0, + 'imported': 6028.183, + 'power_average': 11.556, + 'power_exported': 0.0, + 'power_imported': 11.556}, + 'cp3': {'energy_exported': 0.0, + 'energy_imported': 0.576, + 'energy_imported_bat': 0.138, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.375, + 'energy_imported_pv': 0.063, + 'exported': 0, + 'imported': 3620.971, + 'power_average': 6.932, + 'power_exported': 0.0, + 'power_imported': 6.932}, + 'cp4': {'energy_exported': 0.0, + 'energy_imported': 0.192, + 'energy_imported_bat': 0.046, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.125, + 'energy_imported_pv': 0.021, + 'exported': 0, + 'imported': 1198.566, + 'power_average': 2.313, + 'power_exported': 0.0, + 'power_imported': 2.313}, + 'cp5': {'energy_exported': 0.0, + 'energy_imported': 0.192, + 'energy_imported_bat': 0.046, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.125, + 'energy_imported_pv': 0.021, + 'exported': 0, + 'imported': 1208.646, + 'power_average': 2.311, + 'power_exported': 0.0, + 'power_imported': 2.311}}, + 'date': '09:35', + 'energy_source': {'bat': 0.2398, + 'cp': 0.0, + 'grid': 0.6504, + 'pv': 0.1098}, + 'ev': {'ev0': {'soc': 0}}, + 'hc': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.01, + 'energy_imported_bat': 0.002, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.007, + 'energy_imported_pv': 0.001, + 'imported': 100, + 'power_average': 0.12, + 'power_exported': 0.0, + 'power_imported': 0.12}}, + 'pv': {'all': {'energy_exported': 0.126, + 'energy_imported': 0.0, + 'exported': 804, + 'power_average': -1.517, + 'power_exported': 1.517, + 'power_imported': 0.0}, + 'pv1': {'energy_exported': 0.126, + 'energy_imported': 0.0, + 'exported': 804, + 'power_average': -1.517, + 'power_exported': 1.517, + 'power_imported': 0.0}}, + 'sh': {'sh1': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'exported': 0, + 'imported': 0.1, + 'power_average': 0.001, + 'power_exported': 0.0, + 'power_imported': 0.001, + 'temp0': 300, + 'temp1': 300, + 'temp2': 300}}, + 'timestamp': 1690529761, + } diff --git a/packages/helpermodules/measurement_logging/process_log.py b/packages/helpermodules/measurement_logging/process_log.py index df1bc191ee..60feb03e6a 100644 --- a/packages/helpermodules/measurement_logging/process_log.py +++ b/packages/helpermodules/measurement_logging/process_log.py @@ -9,6 +9,7 @@ from helpermodules import timecheck from helpermodules.measurement_logging.write_log import (LegacySmartHomeLogData, LogType, create_entry, get_previous_entry) +from helpermodules.messaging import MessageType, pub_system_message log = logging.getLogger(__name__) @@ -374,9 +375,12 @@ def add_daily_log(day: str) -> None: def _analyse_energy_source(data) -> Dict: if data and len(data["entries"]) > 0: - for i in range(0, len(data["entries"])): - data["entries"][i] = analyse_percentage(data["entries"][i]) - data["totals"] = analyse_percentage_totals(data["entries"], data["totals"]) + try: + for i in range(0, len(data["entries"])): + data["entries"][i] = analyse_percentage(data["entries"][i]) + data["totals"] = analyse_percentage_totals(data["entries"], data["totals"]) + except Exception: + pub_system_message({}, "Fehler beim Berechnen des Strom-Mix", MessageType.ERROR) return data @@ -390,6 +394,13 @@ def get_grid_from(entry) -> Tuple[float, float]: raise KeyError(f"Kein Zähler für das Netz gefunden in Eintrag '{entry['timestamp']}'.") return sum(grid["energy_imported"] for grid in grids), sum(grid["energy_exported"] for grid in grids) + def calc_energy_imported_by_source(energy_imported, energy_source): + value = (Decimal(str(energy_imported)) * + Decimal(str(energy_source))).quantize(Decimal('0.001')) # limit precision + value = f'{value: f}' + value = string_to_float(value) if "." in value else string_to_int(value) + return value + try: bat_imported = entry["bat"]["all"]["energy_imported"] if "all" in entry["bat"].keys() else 0 bat_exported = entry["bat"]["all"]["energy_exported"] if "all" in entry["bat"].keys() else 0 @@ -397,6 +408,19 @@ def get_grid_from(entry) -> Tuple[float, float]: pv = entry["pv"]["all"]["energy_exported"] if "all" in entry["pv"].keys() else 0 grid_imported, grid_exported = get_grid_from(entry) consumption = grid_imported - grid_exported + pv + bat_exported - bat_imported + cp_exported + for type in ("bat", "cp"): + if entry[type]["all"]["energy_imported"] > consumption: + consumption += entry[type]["all"]["energy_imported"] - consumption + grid_imported += entry[type]["all"]["energy_imported"] - grid_imported + log.debug(f"Angepasste Verbrauchswerte für {type} um " + f"{entry[type]['all']['energy_imported'] - consumption} kWh") + for counter in entry["counter"].values(): + if counter["grid"] is False: + if counter["energy_imported"] > consumption: + consumption += counter["energy_imported"] - consumption + grid_imported += counter["energy_imported"] - grid_imported + log.debug(f"Angepasste Verbrauchswerte für {type} um " + f"{entry[type]['all']['energy_imported'] - consumption} kWh") try: if grid_exported > pv: # Ins Netz eingespeiste Leistung kam nicht von der PV-Anlage sondern aus dem Speicher @@ -417,17 +441,16 @@ def get_grid_from(entry) -> Tuple[float, float]: entry["energy_source"] = {"grid": 0, "pv": 0, "bat": 0, "cp": 0} for source in ("grid", "pv", "bat", "cp"): if "all" in entry["hc"].keys(): - value = (Decimal(str(entry["hc"]["all"]["energy_imported"])) * - Decimal(str(entry["energy_source"][source]))).quantize(Decimal('0.001')) # limit precision - value = f'{value: f}' - value = string_to_float(value) if "." in value else string_to_int(value) - entry["hc"]["all"][f"energy_imported_{source}"] = value - if "all" in entry["cp"].keys(): - value = (Decimal(str(entry["cp"]["all"]["energy_imported"])) * - Decimal(str(entry["energy_source"][source]))).quantize(Decimal('0.001')) # limit precision - value = f'{value: f}' - value = string_to_float(value) if "." in value else string_to_int(value) - entry["cp"]["all"][f"energy_imported_{source}"] = value + entry["hc"]["all"][f"energy_imported_{source}"] = calc_energy_imported_by_source( + entry["hc"]["all"]["energy_imported"], entry["energy_source"][source]) + for key in entry["cp"].keys(): + entry["cp"][key][f"energy_imported_{source}"] = calc_energy_imported_by_source( + entry["cp"][key]["energy_imported"], entry["energy_source"][source]) + for counter in entry["counter"].values(): + if counter["grid"] is False: + counter[f"energy_imported_{source}"] = calc_energy_imported_by_source( + counter["energy_imported"], entry["energy_source"][source]) + except Exception: log.exception(f"Fehler beim Berechnen des Strom-Mix von {entry['timestamp']}") finally: @@ -440,13 +463,20 @@ def analyse_percentage_totals(entries, totals): totals[section]["all"] = {} for source in ("grid", "pv", "bat", "cp"): totals["hc"]["all"].update({f"energy_imported_{source}": 0}) - totals["cp"]["all"].update({f"energy_imported_{source}": 0}) for entry in entries: if "hc" in entry.keys() and "all" in entry["hc"].keys(): totals["hc"]["all"][f"energy_imported_{source}"] += entry["hc"]["all"].get( f"energy_imported_{source}", 0)*1000 - if "all" in entry["cp"].keys() and f"energy_imported_{source}" in entry["cp"]["all"].keys(): - totals["cp"]["all"][f"energy_imported_{source}"] += entry["cp"]["all"][f"energy_imported_{source}"]*1000 + for key in entry["cp"].keys(): + if f"energy_imported_{source}" in entry["cp"][key].keys(): + if totals["cp"][key].get(f"energy_imported_{source}") is None: + totals["cp"][key].update({f"energy_imported_{source}": 0}) + totals["cp"][key][f"energy_imported_{source}"] += entry["cp"][key][f"energy_imported_{source}"]*1000 + for key, counter in entry["counter"].items(): + if counter["grid"] is False: + if totals["counter"][key].get(f"energy_imported_{source}") is None: + totals["counter"][key].update({f"energy_imported_{source}": 0}) + totals["counter"][key][f"energy_imported_{source}"] += counter[f"energy_imported_{source}"]*1000 return totals @@ -531,10 +561,14 @@ def get_single_value(source: dict, default: int = 0) -> float: log.exception("Fehler beim Berechnen der Leistung") # next_entry may contain new modules, we add them here try: - for module in next_entry[type].keys(): + for module, module_data in next_entry[type].items(): if module not in entry[type].keys(): log.debug(f"adding module {module} from next entry") - entry[type].update({module: {"energy_imported": 0.0, "energy_exported": 0.0}}) + if calculation in [CalculationType.POWER, CalculationType.ALL]: + module_data.update({"power_average": 0, "power_imported": 0, "power_exported": 0}) + if calculation in [CalculationType.ENERGY, CalculationType.ALL]: + module_data.update({"energy_imported": 0, "energy_exported": 0}) + entry[type].update({module: module_data}) except KeyError: # catch missing "type" pass diff --git a/packages/helpermodules/measurement_logging/process_log_test.py b/packages/helpermodules/measurement_logging/process_log_test.py index 32fdfef405..0bf4e53d52 100644 --- a/packages/helpermodules/measurement_logging/process_log_test.py +++ b/packages/helpermodules/measurement_logging/process_log_test.py @@ -1,4 +1,8 @@ from copy import deepcopy +from unittest.mock import Mock +import pytest + +from helpermodules.measurement_logging import process_log from helpermodules.measurement_logging.process_log import ( analyse_percentage, _calculate_average_power, @@ -6,6 +10,11 @@ get_totals, CalculationType) +from helpermodules.measurement_logging.process_log_testdata import (counter_jumps_forward, + counter_jumps_forward_processed, + regular_daily_log_entry, + regular_daily_log_entry_processed) + def test_get_totals(daily_log_sample, daily_log_totals): # setup and execution @@ -16,9 +25,9 @@ def test_get_totals(daily_log_sample, daily_log_totals): assert totals == daily_log_totals -def test_analyse_percentage(daily_log_entry_kw): +def test_analyse_percentage(daily_log_entry_kw_percentage): # setup - expected = deepcopy(daily_log_entry_kw) + expected = deepcopy(daily_log_entry_kw_percentage) expected.update({"energy_source": {'bat': 0.2398, 'cp': 0.0, 'grid': 0.6504, 'pv': 0.1098}}) expected["cp"]["all"].update({ "energy_imported_bat": 0.23, @@ -32,7 +41,7 @@ def test_analyse_percentage(daily_log_entry_kw): "energy_imported_pv": 0.001}) # execution - entry = analyse_percentage(daily_log_entry_kw) + entry = analyse_percentage(daily_log_entry_kw_percentage) # evaluation assert entry == expected @@ -52,3 +61,19 @@ def test_convert(daily_log_entry_kw, daily_log_sample): # evaluation assert entry == daily_log_entry_kw + + +@pytest.mark.parametrize("data, expected", [ + pytest.param(counter_jumps_forward, counter_jumps_forward_processed, id="counter jumps forward"), + pytest.param(regular_daily_log_entry, regular_daily_log_entry_processed, id="regular daily log entry") +]) +def test_get_daily_log(data, expected, monkeypatch): + # setup + collect_daily_log_data_mock = Mock(return_value=data) + monkeypatch.setattr(process_log, "_collect_daily_log_data", collect_daily_log_data_mock) + + # execution + daily_log_processed = process_log.get_daily_log("20250616") + + # evaluation + assert daily_log_processed == expected diff --git a/packages/helpermodules/measurement_logging/process_log_testdata.py b/packages/helpermodules/measurement_logging/process_log_testdata.py new file mode 100644 index 0000000000..147b6f3765 --- /dev/null +++ b/packages/helpermodules/measurement_logging/process_log_testdata.py @@ -0,0 +1,415 @@ +# Wenn ein Zwischenzähler nicht auslesbar war, soll beim Sprung der Anteil auf das Netz gerechnet werden. +counter_jumps_forward = {'entries': [{'bat': {'all': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}, + 'bat2': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}}, + 'counter': {'counter0': {'exported': 26029.945, + 'grid': True, + 'imported': 2728.572}, + 'counter2': {'exported': 26029.945, + 'grid': False, + 'imported': 0}}, + 'cp': {'all': {'exported': 0, 'imported': 12639.11}, + 'cp3': {'exported': 0, 'imported': 12639.11}, + 'cp4': {'exported': 0, 'imported': 0}, + 'cp5': {'exported': 0, 'imported': 0}}, + 'date': '14:25', + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'imported': 2324.001611140539}}, + 'prices': {'bat': 0.0002, 'grid': 0.0003, 'pv': 0.00015}, + 'pv': {'all': {'exported': 35827}, 'pv1': {'exported': 35827}}, + 'sh': {}, + 'timestamp': 1750767902}, + {'bat': {'all': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}, + 'bat2': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}}, + 'counter': {'counter0': {'exported': 26802.355, + 'grid': True, + 'imported': 2728.572}, + 'counter2': {'exported': 26029.945, + 'grid': True, + 'imported': 2728.572}}, + 'cp': {'all': {'exported': 0, 'imported': 12639.11}, + 'cp3': {'exported': 0, 'imported': 12639.11}, + 'cp4': {'exported': 0, 'imported': 0}, + 'cp5': {'exported': 0, 'imported': 0}}, + 'date': '14:30', + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'imported': 2361.178611303703}}, + 'prices': {'bat': 0.0002, 'grid': 0.0003, 'pv': 0.00015}, + 'pv': {'all': {'exported': 36636}, 'pv1': {'exported': 36636}}, + 'sh': {}, + 'timestamp': 1750768201}], + 'names': {'bat2': 'MQTT-Speicher', + 'counter0': 'MQTT-Zähler', + 'counter2': 'Test-Zähler', + 'cp3': 'MQTT-Ladepunkt', + 'cp4': 'MQTT-Ladepunkt', + 'cp5': 'MQTT-Ladepunkt', + 'ev0': 'Standard-Fahrzeug', + 'pv1': 'MQTT-Wechselrichter'}} + +counter_jumps_forward_processed = {'entries': [{'bat': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'exported': 3195.13, + 'imported': 629.37, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0, + 'soc': 48}, + 'bat2': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'exported': 3195.13, + 'imported': 629.37, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0, + 'soc': 48}}, + 'counter': {'counter0': {'energy_exported': 0.772, + 'energy_imported': 0.0, + 'exported': 26029.945, + 'grid': True, + 'imported': 2728.572, + 'power_average': -9.3, + 'power_exported': 9.3, + 'power_imported': 0}, + 'counter2': {'energy_exported': 0.0, + 'energy_imported': 2.729, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 2.729, + 'energy_imported_pv': 0.0, + 'exported': 26029.945, + 'grid': False, + 'imported': 0, + 'power_average': 32.852, + 'power_exported': 0, + 'power_imported': 32.852}}, + 'cp': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 12639.11, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp3': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 12639.11, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp4': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 0, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp5': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 0, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}}, + 'date': '14:25', + 'energy_source': {'bat': 0.0, 'cp': 0.0, 'grid': 1.0, 'pv': 0.0}, + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.037, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.037, + 'energy_imported_pv': 0.0, + 'imported': 2324.001611140539, + 'power_average': 0.448, + 'power_exported': 0, + 'power_imported': 0.448}}, + 'prices': {'bat': 0.0002, 'grid': 0.0003, 'pv': 0.00015}, + 'pv': {'all': {'energy_exported': 0.809, + 'energy_imported': 0.0, + 'exported': 35827, + 'power_average': -9.74, + 'power_exported': 9.74, + 'power_imported': 0}, + 'pv1': {'energy_exported': 0.809, + 'energy_imported': 0.0, + 'exported': 35827, + 'power_average': -9.74, + 'power_exported': 9.74, + 'power_imported': 0}}, + 'sh': {}, + 'timestamp': 1750767902}], + 'names': {'bat2': 'MQTT-Speicher', + 'counter0': 'MQTT-Zähler', + 'counter2': 'Test-Zähler', + 'cp3': 'MQTT-Ladepunkt', + 'cp4': 'MQTT-Ladepunkt', + 'cp5': 'MQTT-Ladepunkt', + 'ev0': 'Standard-Fahrzeug', + 'pv1': 'MQTT-Wechselrichter'}, + 'totals': {'bat': {'all': {'energy_exported': 0.0, 'energy_imported': 0.0}, + 'bat2': {'energy_exported': 0.0, 'energy_imported': 0.0}}, + 'counter': {'counter0': {'energy_exported': 772.0, + 'energy_imported': 0.0, + 'grid': True}, + 'counter2': {'energy_exported': 0.0, + 'energy_imported': 2729.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 2729.0, + 'energy_imported_pv': 0.0, + 'grid': False}}, + 'cp': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp3': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp4': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp5': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}}, + 'hc': {'all': {'energy_imported': 37.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 37.0, + 'energy_imported_pv': 0.0}}, + 'pv': {'all': {'energy_exported': 809.0}, + 'pv1': {'energy_exported': 809.0}}, + 'sh': {}}} + +regular_daily_log_entry = {'entries': [{'bat': {'all': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}, + 'bat2': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}}, + 'counter': {'counter0': {'exported': 26029.945, + 'grid': True, + 'imported': 2728.572}}, + 'cp': {'all': {'exported': 0, 'imported': 12639.11}, + 'cp3': {'exported': 0, 'imported': 12639.11}, + 'cp4': {'exported': 0, 'imported': 0}, + 'cp5': {'exported': 0, 'imported': 0}}, + 'date': '14:25', + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'imported': 2324.001611140539}}, + 'prices': {'bat': 0.0002, 'grid': 0.0003, 'pv': 0.00015}, + 'pv': {'all': {'exported': 35827}, 'pv1': {'exported': 35827}}, + 'sh': {}, + 'timestamp': 1750767902}, + {'bat': {'all': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}, + 'bat2': {'exported': 3195.13, + 'imported': 629.37, + 'soc': 48}}, + 'counter': {'counter0': {'exported': 26802.355, + 'grid': True, + 'imported': 2728.572}}, + 'cp': {'all': {'exported': 0, 'imported': 12639.11}, + 'cp3': {'exported': 0, 'imported': 12639.11}, + 'cp4': {'exported': 0, 'imported': 0}, + 'cp5': {'exported': 0, 'imported': 0}}, + 'date': '14:30', + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'imported': 2361.178611303703}}, + 'prices': {'bat': 0.0002, 'grid': 0.0003, 'pv': 0.00015}, + 'pv': {'all': {'exported': 36636}, 'pv1': {'exported': 36636}}, + 'sh': {}, + 'timestamp': 1750768201}], + 'names': {'bat2': 'MQTT-Speicher', + 'counter0': 'MQTT-Zähler', + 'counter2': 'Test-Zähler', + 'cp3': 'MQTT-Ladepunkt', + 'cp4': 'MQTT-Ladepunkt', + 'cp5': 'MQTT-Ladepunkt', + 'ev0': 'Standard-Fahrzeug', + 'pv1': 'MQTT-Wechselrichter'}} + +regular_daily_log_entry_processed = {'entries': [{'bat': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'exported': 3195.13, + 'imported': 629.37, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0, + 'soc': 48}, + 'bat2': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'exported': 3195.13, + 'imported': 629.37, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0, + 'soc': 48}}, + 'counter': {'counter0': {'energy_exported': 0.772, + 'energy_imported': 0.0, + 'exported': 26029.945, + 'grid': True, + 'imported': 2728.572, + 'power_average': -9.3, + 'power_exported': 9.3, + 'power_imported': 0}}, + 'cp': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 12639.11, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp3': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 12639.11, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp4': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 0, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}, + 'cp5': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0, + 'exported': 0, + 'imported': 0, + 'power_average': 0.0, + 'power_exported': 0, + 'power_imported': 0.0}}, + 'date': '14:25', + 'energy_source': {'bat': 0.0, + 'cp': 0.0, + 'grid': 0.0, + 'pv': 1.0}, + 'ev': {'ev0': {'soc': None}}, + 'hc': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.037, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.037, + 'imported': 2324.001611140539, + 'power_average': 0.448, + 'power_exported': 0, + 'power_imported': 0.448}}, + 'prices': {'bat': 0.0002, + 'grid': 0.0003, + 'pv': 0.00015}, + 'pv': {'all': {'energy_exported': 0.809, + 'energy_imported': 0.0, + 'exported': 35827, + 'power_average': -9.74, + 'power_exported': 9.74, + 'power_imported': 0}, + 'pv1': {'energy_exported': 0.809, + 'energy_imported': 0.0, + 'exported': 35827, + 'power_average': -9.74, + 'power_exported': 9.74, + 'power_imported': 0}}, + 'sh': {}, + 'timestamp': 1750767902}], + 'names': {'bat2': 'MQTT-Speicher', + 'counter0': 'MQTT-Zähler', + 'counter2': 'Test-Zähler', + 'cp3': 'MQTT-Ladepunkt', + 'cp4': 'MQTT-Ladepunkt', + 'cp5': 'MQTT-Ladepunkt', + 'ev0': 'Standard-Fahrzeug', + 'pv1': 'MQTT-Wechselrichter'}, + 'totals': {'bat': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0}, + 'bat2': {'energy_exported': 0.0, + 'energy_imported': 0.0}}, + 'counter': {'counter0': {'energy_exported': 772.0, + 'energy_imported': 0.0, + 'grid': True}}, + 'cp': {'all': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp3': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp4': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}, + 'cp5': {'energy_exported': 0.0, + 'energy_imported': 0.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 0.0}}, + 'hc': {'all': {'energy_imported': 37.0, + 'energy_imported_bat': 0.0, + 'energy_imported_cp': 0.0, + 'energy_imported_grid': 0.0, + 'energy_imported_pv': 37.0}}, + 'pv': {'all': {'energy_exported': 809.0}, + 'pv1': {'energy_exported': 809.0}}, + 'sh': {}}, + } diff --git a/packages/helpermodules/measurement_logging/write_log.py b/packages/helpermodules/measurement_logging/write_log.py index 92707c9215..ef01486b26 100644 --- a/packages/helpermodules/measurement_logging/write_log.py +++ b/packages/helpermodules/measurement_logging/write_log.py @@ -10,7 +10,7 @@ from typing import Dict, Optional from control import data -from helpermodules.broker import InternalBrokerClient +from helpermodules.broker import BrokerClient from helpermodules import timecheck from helpermodules.utils.json_file_handler import write_and_check from helpermodules.utils.topic_parser import decode_payload, get_index @@ -24,6 +24,11 @@ # { # "timestamp": int, # "date": str, +# "prices": { +# "grid": Preis für Netzbezug, +# "pv": Preis für PV-Strom, +# "bat": Preis für Speicherstrom +# } # "cp": { # "cp1": { # "imported": Zählerstand in Wh, @@ -99,7 +104,7 @@ def __init__(self) -> None: self.sh_dict: Dict = {} self.sh_names: Dict = {} try: - InternalBrokerClient("smart-home-logging", self.on_connect, self.on_message).start_finite_loop() + BrokerClient("smart-home-logging", self.on_connect, self.on_message).start_finite_loop() for topic, payload in self.all_received_topics.items(): if re.search("openWB/LegacySmartHome/config/get/Devices/[1-9]/device_configured", topic) is not None: if decode_payload(payload) == 1: @@ -190,7 +195,26 @@ def create_entry(log_type: LogType, sh_log_data: LegacySmartHomeLogData, previou else: date = timecheck.create_timestamp_YYYYMMDD() current_timestamp = int(timecheck.create_timestamp()) - cp_dict = {} + + try: + prices = data.data.general_data.data.prices + try: + grid_price = data.data.optional_data.et_get_current_price() + except Exception: + grid_price = prices.grid + prices_dict = {"grid": grid_price, + "pv": prices.pv, + "bat": prices.bat} + except Exception: + log.exception("Fehler im Werte-Logging-Modul für Preise") + prices_dict = {} + + try: + cp_dict = {"all": {"imported": data.data.cp_all_data.data.get.imported, + "exported": data.data.cp_all_data.data.get.exported}} + except Exception: + log.exception("Fehler im Werte-Logging-Modul") + cp_dict = {} for cp in data.data.cp_data: try: if "cp" in cp: @@ -198,12 +222,6 @@ def create_entry(log_type: LogType, sh_log_data: LegacySmartHomeLogData, previou "exported": data.data.cp_data[cp].data.get.exported}}) except Exception: log.exception("Fehler im Werte-Logging-Modul für Ladepunkt "+str(cp)) - try: - cp_dict.update( - {"all": {"imported": data.data.cp_all_data.data.get.imported, - "exported": data.data.cp_all_data.data.get.exported}}) - except Exception: - log.exception("Fehler im Werte-Logging-Modul") ev_dict = {} for ev in data.data.ev_data: @@ -227,7 +245,11 @@ def create_entry(log_type: LogType, sh_log_data: LegacySmartHomeLogData, previou except Exception: log.exception("Fehler im Werte-Logging-Modul für Zähler "+str(counter)) - pv_dict = {"all": {"exported": data.data.pv_all_data.data.get.exported}} + try: + pv_dict = {"all": {"exported": data.data.pv_all_data.data.get.exported}} + except Exception: + log.exception("Fehler im Werte-Logging-Modul für PV-Daten") + pv_dict = {} if data.data.pv_all_data.data.config.configured: for pv in data.data.pv_data: try: @@ -236,9 +258,13 @@ def create_entry(log_type: LogType, sh_log_data: LegacySmartHomeLogData, previou except Exception: log.exception("Fehler im Werte-Logging-Modul für Wechselrichter "+str(pv)) - bat_dict = {"all": {"imported": data.data.bat_all_data.data.get.imported, - "exported": data.data.bat_all_data.data.get.exported, - "soc": data.data.bat_all_data.data.get.soc}} + try: + bat_dict = {"all": {"imported": data.data.bat_all_data.data.get.imported, + "exported": data.data.bat_all_data.data.get.exported, + "soc": data.data.bat_all_data.data.get.soc}} + except Exception: + log.exception("Fehler im Werte-Logging-Modul für Batteriespeicher-Daten") + bat_dict = {} if data.data.bat_all_data.data.config.configured: for bat in data.data.bat_data: try: @@ -248,10 +274,15 @@ def create_entry(log_type: LogType, sh_log_data: LegacySmartHomeLogData, previou except Exception: log.exception("Fehler im Werte-Logging-Modul für Speicher "+str(bat)) - hc_dict = {"all": {"imported": data.data.counter_all_data.data.set.imported_home_consumption}} + try: + hc_dict = {"all": {"imported": data.data.counter_all_data.data.set.imported_home_consumption}} + except Exception: + log.exception("Fehler im Werte-Logging-Modul für Hausverbrauch") + hc_dict = {} new_entry = { "timestamp": current_timestamp, "date": date, + "prices": prices_dict, "cp": cp_dict, "ev": ev_dict, "counter": counter_dict, diff --git a/packages/helpermodules/modbusserver.py b/packages/helpermodules/modbusserver.py index 73ac838c03..35a7dcb639 100644 --- a/packages/helpermodules/modbusserver.py +++ b/packages/helpermodules/modbusserver.py @@ -3,7 +3,7 @@ from socketserver import TCPServer from collections import defaultdict import struct -from typing import Optional +from typing import Optional, Union from helpermodules.utils.error_handling import ImportErrorContext with ImportErrorContext(): @@ -31,33 +31,32 @@ log.exception("Fehler im Modbus-Server") -def _form_int32(value, startreg): - secondreg = startreg + 1 +def _form_int32(value: Union[int, float], register: int): try: binary32 = struct.pack('>l', int(value)) high_byte, low_byte = struct.unpack('>hh', binary32) - data_store[startreg] = high_byte - data_store[secondreg] = low_byte + data_store[register] = high_byte + data_store[register + 1] = low_byte except Exception: log.exception("Fehler beim Füllen der Register") - data_store[startreg] = -1 - data_store[secondreg] = -1 + data_store[register] = -1 + data_store[register + 1] = -1 -def _form_int16(value, startreg): +def _form_int16(value: Union[int, float, bool], register: int): try: value = int(value) if (value > 32767 or value < -32768): raise Exception("Number to big") - data_store[startreg] = value + data_store[register] = value except Exception: log.exception("Fehler beim Füllen der Register") - data_store[startreg] = -1 + data_store[register] = -1 -def _form_str(value: Optional[str], startreg): +def _form_str(value: Optional[str], register: int): if value is None or len(value) == 0: - data_store[startreg] = 0 + data_store[register] = 0 else: bytes = value.encode("utf-8") length = len(bytes) @@ -72,50 +71,65 @@ def _form_str(value: Optional[str], startreg): else: stream_two_bytes = struct.pack(">bb", bytes[i], 0) stream_one_word = struct.unpack(">h", stream_two_bytes)[0] - data_store[startreg+register_offset] = stream_one_word + data_store[register + register_offset] = stream_one_word except Exception: - data_store[startreg+register_offset] = -1 + data_store[register + register_offset] = -1 finally: register_offset += 1 -def _get_pos(number, n): - return number // 10**n % 10 - 1 +def _charge_point_index(address: int): + return int(str(address)[-3]) - 1 + + +def _value_index(address: int): + return int(str(address)[-2:]) try: @app.route(slave_ids=[1], function_codes=[3, 4], addresses=list(range(0, 32000))) - def read_data_store(slave_id, function_code, address): + def read_data_store(slave_id: int, function_code: int, address: int): """" Return value of address. """ + # Mapping für einfache Zuordnung + int32_map = { + 0: lambda cp: cp.get.power, + 2: lambda cp: cp.get.imported, + 41: lambda cp: cp.get.exported, + } + int16_map = { + 4: lambda cp: cp.get.voltages[0] * 100, + 5: lambda cp: cp.get.voltages[1] * 100, + 6: lambda cp: cp.get.voltages[2] * 100, + 7: lambda cp: cp.get.currents[0] * 100, + 8: lambda cp: cp.get.currents[1] * 100, + 9: lambda cp: cp.get.currents[2] * 100, + 14: lambda cp: cp.get.plug_state, + 15: lambda cp: cp.get.charge_state, + 16: lambda cp: cp.get.evse_current, + 30: lambda cp: cp.get.powers[0], + 31: lambda cp: cp.get.powers[1], + 32: lambda cp: cp.get.powers[2], + 43: lambda _: 1, + } + str_map = { + 50: lambda _: serial_number, + 60: lambda cp: cp.get.rfid, + } + if address > 10099: Pub().pub("openWB/set/internal_chargepoint/global_data", {"heartbeat": timecheck.create_timestamp(), "parent_ip": None}) - chargepoint = SubData.internal_chargepoint_data[f"cp{_get_pos(address, 2)}"] - askedvalue = int(str(address)[-2:]) - if askedvalue == 00: - _form_int32(chargepoint.get.power, address) - elif askedvalue == 2: - _form_int32(chargepoint.get.imported, address) - elif 4 <= askedvalue <= 6: - _form_int16(chargepoint.get.voltages[askedvalue-4]*100, address) - elif 7 <= askedvalue <= 9: - _form_int16(chargepoint.get.currents[askedvalue-7]*100, address) - elif askedvalue == 14: - _form_int16(chargepoint.get.plug_state, address) - elif askedvalue == 15: - _form_int16(chargepoint.get.charge_state, address) - elif askedvalue == 16: - _form_int16(chargepoint.get.evse_current, address) - elif 30 <= askedvalue <= 32: - _form_int16(chargepoint.get.powers[askedvalue-30], address) - elif askedvalue == 41: - _form_int32(chargepoint.get.exported, address) - elif askedvalue == 43: - _form_int16(1, address) - elif askedvalue == 50: - _form_str(serial_number, address) - elif askedvalue == 60: - _form_str(chargepoint.get.rfid, address) + charge_point = SubData.internal_chargepoint_data[f"cp{_charge_point_index(address)}"] + requested_value = _value_index(address) + + if requested_value in int32_map: + _form_int32(int32_map[requested_value](charge_point), address) + elif requested_value in int16_map: + _form_int16(int16_map[requested_value](charge_point), address) + elif requested_value in str_map: + _form_str(str_map[requested_value](charge_point), address) + else: + log.warning(f"Unbekannte Adresse: {address}") return data_store[address] except Exception: @@ -123,28 +137,31 @@ def read_data_store(slave_id, function_code, address): try: @app.route(slave_ids=[1], function_codes=[6, 16], addresses=list(range(0, 32000))) - def write_data_store(slave_id, function_code, address, value): + def write_data_store(slave_id: int, function_code: int, address: int, value): """" Set value for address. """ if 10170 < address: - cp_topic = f"openWB/set/internal_chargepoint/{_get_pos(address, 2)}/data/" - askedvalue = int(str(address)[-2:]) - if askedvalue == 71: - Pub().pub(f"{cp_topic}set_current", value/100) - elif askedvalue == 80: - Pub().pub(f"{cp_topic}phases_to_use", value) - elif askedvalue == 81: - Pub().pub(f"{cp_topic}trigger_phase_switch", value) - elif askedvalue == 98: - Pub().pub(f"{cp_topic}cp_interruption_duration", value) - elif askedvalue == 99: - Pub().pub("openWB/set/command/modbus_server/todo", {"command": "systemUpdate", "data": {}}) + cp_topic = f"openWB/set/internal_chargepoint/{_charge_point_index(address)}/data/" + requested_value = _value_index(address) + + write_map = { + 71: lambda value: Pub().pub(f"{cp_topic}set_current", value / 100), + 80: lambda value: Pub().pub(f"{cp_topic}phases_to_use", value), + 81: lambda value: Pub().pub(f"{cp_topic}trigger_phase_switch", value), + 98: lambda value: Pub().pub(f"{cp_topic}cp_interruption_duration", value), + 99: lambda _: Pub().pub("openWB/set/command/modbus_server/todo", + {"command": "systemUpdate", "data": {}}) + } + if requested_value in write_map: + write_map[requested_value](value) + else: + log.warning(f"Unbekannte Adresse beim Schreiben: {address}") except Exception: log.exception("Fehler im Modbus-Server") def start_modbus_server(event_modbus_server): try: - # Wenn start_modbus_server aus SubData aufegrufen wird, wenn das Topic gesetzt wird, führt das zu einem + # Wenn start_modbus_server aus SubData aufgerufen wird, wenn das Topic gesetzt wird, führt das zu einem # circular Import. event_modbus_server.wait() event_modbus_server.clear() diff --git a/packages/helpermodules/setdata.py b/packages/helpermodules/setdata.py index dec04bf379..10dc200d4e 100644 --- a/packages/helpermodules/setdata.py +++ b/packages/helpermodules/setdata.py @@ -4,17 +4,16 @@ import copy import dataclasses from pathlib import Path -import threading +from threading import Event from typing import List, Optional, Tuple import re import paho.mqtt.client as mqtt import logging from helpermodules import hardware_configuration, subdata -from helpermodules.broker import InternalBrokerClient +from helpermodules.broker import BrokerClient from helpermodules.pub import Pub, pub_single -from helpermodules.utils.topic_parser import (decode_payload, get_index, get_index_position, get_second_index, - get_second_index_position) +from helpermodules.utils.topic_parser import decode_payload, get_index, get_index_position from helpermodules.update_config import UpdateConfig import dataclass_utils @@ -24,24 +23,18 @@ class SetData: def __init__(self, - event_ev_template: threading.Event, - event_charge_template: threading.Event, - event_cp_config: threading.Event, - event_scheduled_charging_plan: threading.Event, - event_time_charging_plan: threading.Event, - event_soc: threading.Event, - event_subdata_initialized: threading.Event): + event_ev_template: Event, + event_cp_config: Event, + event_soc: Event, + event_subdata_initialized: Event): self.event_ev_template = event_ev_template - self.event_charge_template = event_charge_template self.event_cp_config = event_cp_config - self.event_scheduled_charging_plan = event_scheduled_charging_plan - self.event_time_charging_plan = event_time_charging_plan self.event_soc = event_soc self.event_subdata_initialized = event_subdata_initialized self.heartbeat = False def set_data(self): - self.internal_broker_client = InternalBrokerClient("mqttset", self.on_connect, self.on_message) + self.internal_broker_client = BrokerClient("mqttset", self.on_connect, self.on_message) self.event_subdata_initialized.wait() log.debug("Subdata initialization completed. Starting setdata loop to broker.") self.internal_broker_client.start_infinite_loop() @@ -73,17 +66,26 @@ def on_message(self, client: mqtt.Client, userdata, msg: mqtt.MQTTMessage): if "openWB/set/vehicle/" in msg.topic: if "openWB/set/vehicle/template/ev_template/" in msg.topic: self.event_ev_template.wait(5) + self.process_vehicle_ev_template_topic(msg) elif "openWB/set/vehicle/template/charge_template/" in msg.topic: - self.event_charge_template.wait(5) - self.process_vehicle_topic(msg) + self.process_vehicle_charge_template_topic(msg) + else: + self.process_vehicle_topic(msg) elif "openWB/set/chargepoint/" in msg.topic: - self.process_chargepoint_topic(msg) + if "openWB/set/chargepoint/" in msg.topic and "/set/charge_template" in msg.topic: + self.process_vehicle_charge_template_topic(msg) + else: + self.process_chargepoint_topic(msg) elif "openWB/set/pv/" in msg.topic: self.process_pv_topic(msg) elif "openWB/set/bat/" in msg.topic: self.process_bat_topic(msg) elif "openWB/set/general/" in msg.topic: self.process_general_topic(msg) + elif ("openWB/set/io/" in msg.topic or "openWB/set/internal_io/" in msg.topic): + self.process_io_topic(msg) + elif "openWB/set/mqtt/" in msg.topic: + self.process_mqtt_topic(msg) elif "openWB/set/optional/" in msg.topic: self.process_optional_topic(msg) elif "openWB/set/counter/" in msg.topic: @@ -155,42 +157,7 @@ def _validate_value(self, msg: mqtt.MQTTMessage, data_type, ranges=[], collectio else: # aktuelles json-Objekt liegt in subdata index = get_index(msg.topic) - if "time_charging" in msg.topic and "plans" in msg.topic: - index_second = get_second_index(msg.topic) - event = self.event_time_charging_plan - try: - template = dataclasses.asdict(copy.deepcopy( - subdata.SubData.ev_charge_template_data[ - "ct"+index].data.time_charging.plans[index_second])) - except IndexError: - template = {} - elif "scheduled_charging" in msg.topic and "plans" in msg.topic: - index_second = get_second_index(msg.topic) - event = self.event_scheduled_charging_plan - try: - template = dataclasses.asdict(copy.deepcopy( - subdata.SubData.ev_charge_template_data[ - "ct"+index].data.chargemode.scheduled_charging.plans[index_second])) - except IndexError: - template = {} - elif "charge_template" in msg.topic: - event = self.event_charge_template - if "ct"+str(index) in subdata.SubData.ev_charge_template_data: - template = dataclass_utils.asdict(copy.deepcopy( - subdata.SubData.ev_charge_template_data["ct"+str(index)].data)) - # Wenn eine Einzeleinstellung empfangen wird, muss das gesamte Profil veröffentlicht - # werden (pub_json=True), allerdings ohne Pläne. Diese sind in einem Extra-Topic. - try: - template["chargemode"]["scheduled_charging"].pop("plans") - except KeyError: - log.debug("Key 'plans' nicht gefunden, keine Zielladen-Pläne vorhanden.") - try: - template["time_charging"].pop("plans") - except KeyError: - log.debug("Key 'plans' nicht gefunden, keine Zeitladen-Pläne vorhanden.") - else: - template = {} - elif "ev_template" in msg.topic: + if "ev_template" in msg.topic: event = self.event_ev_template if "et"+str(index) in subdata.SubData.ev_template_data: template = copy.deepcopy( @@ -216,9 +183,6 @@ def _validate_value(self, msg: mqtt.MQTTMessage, data_type, ranges=[], collectio # Wert, der aktualisiert werden soll, erstellen/finden und updaten if event == self.event_cp_config: key_list = msg.topic.split("/")[5:] - elif (event == self.event_scheduled_charging_plan or - event == self.event_time_charging_plan): - key_list = msg.topic.split("/")[-1:] else: key_list = msg.topic.split("/")[6:] self._change_key(template, key_list, value) @@ -226,8 +190,6 @@ def _validate_value(self, msg: mqtt.MQTTMessage, data_type, ranges=[], collectio index_pos = get_index_position(msg.topic) if event == self.event_cp_config: topic = msg.topic[:index_pos]+"/config" - elif event == self.event_scheduled_charging_plan or event == self.event_time_charging_plan: - topic = msg.topic[:get_second_index_position(msg.topic)] elif event == self.event_soc: topic = msg.topic[:index_pos]+"/soc_module/calculated_soc_state" else: @@ -402,8 +364,6 @@ def process_vehicle_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, str) elif "/info" in msg.topic: self._validate_value(msg, "json") - elif "openWB/set/vehicle/template" in msg.topic: - self._subprocess_vehicle_chargemode_topic(msg) elif "openWB/set/vehicle/set/vehicle_update_completed" in msg.topic: self._validate_value(msg, bool) elif "/set/soc_error_counter" in msg.topic: @@ -437,7 +397,7 @@ def process_vehicle_topic(self, msg: mqtt.MQTTMessage): except Exception: log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") - def _subprocess_vehicle_chargemode_topic(self, msg: mqtt.MQTTMessage): + def process_vehicle_charge_template_topic(self, msg: mqtt.MQTTMessage): """ Handler für die Lade-Profil-Topics Parameters ---------- @@ -446,57 +406,15 @@ def _subprocess_vehicle_chargemode_topic(self, msg: mqtt.MQTTMessage): """ try: if "charge_template" in msg.topic: - if "/name" in msg.topic: - self._validate_value(msg, str, pub_json=True) - elif ("/load_default" in msg.topic or - "/prio" in msg.topic): - self._validate_value(msg, bool, pub_json=True) - elif "/chargemode/selected" in msg.topic: - self._validate_value(msg, str, pub_json=True) - elif "/chargemode/instant_charging/current" in msg.topic: - self._validate_value(msg, int, [(6, 32)], pub_json=True) - elif "/chargemode/instant_charging/dc_current" in msg.topic: - self._validate_value(msg, float, [(4, 300)], pub_json=True) - elif "/chargemode/instant_charging/limit/selected" in msg.topic: - self._validate_value(msg, str, pub_json=True) - elif "/chargemode/instant_charging/limit/soc" in msg.topic: - self._validate_value(msg, int, [(0, 100)], pub_json=True) - elif "/chargemode/instant_charging/limit/amount" in msg.topic: - self._validate_value(msg, int, [(1000, float("inf"))], pub_json=True) - elif "/chargemode/pv_charging/feed_in_limit" in msg.topic: - self._validate_value(msg, bool, pub_json=True) - elif "/chargemode/pv_charging/min_current" in msg.topic: - self._validate_value( - msg, int, [(0, 0), (6, 16)], pub_json=True) - elif "/chargemode/pv_charging/dc_min_current" in msg.topic: - self._validate_value(msg, float, [(0, 300)], pub_json=True) - elif "/chargemode/pv_charging/min_soc" in msg.topic: - self._validate_value(msg, int, [(0, 100)], pub_json=True) - elif "/chargemode/pv_charging/min_soc_current" in msg.topic: - self._validate_value(msg, int, [(6, 32)], pub_json=True) - elif "/chargemode/pv_charging/dc_min_soc_current" in msg.topic: - self._validate_value(msg, float, [(4, 300)], pub_json=True) - elif "/chargemode/pv_charging/max_soc" in msg.topic: - self._validate_value(msg, int, [(0, 101)], pub_json=True) - elif "/chargemode/scheduled_charging/plans/" in msg.topic and "/active" in msg.topic: - self._validate_value(msg, bool, pub_json=True) - elif "/chargemode/scheduled_charging/plans" in msg.topic: - self._validate_value(msg, "json") - elif "/chargemode/scheduled_charging" in msg.topic: - self._validate_value(msg, "json", pub_json=True) - elif "/et/active" in msg.topic: - self._validate_value(msg, bool, pub_json=True) - elif "/et/max_price" in msg.topic: - self._validate_value(msg, float, pub_json=True) - elif "/time_charging/active" in msg.topic: - self._validate_value(msg, bool, pub_json=True) - elif "/time_charging/plans/" in msg.topic and "/active" in msg.topic: - self._validate_value(msg, bool, pub_json=True) - elif "/time_charging/plans" in msg.topic: - self._validate_value(msg, "json") - else: - self._validate_value(msg, "json") - elif "ev_template" in msg.topic: + self._validate_value(msg, "json") + else: + self.__unknown_topic(msg) + except Exception: + log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") + + def process_vehicle_ev_template_topic(self, msg: mqtt.MQTTMessage): + try: + if "ev_template" in msg.topic: self._validate_value(msg, "json") else: self.__unknown_topic(msg) @@ -518,7 +436,7 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): "openWB/set/chargepoint/get/power" in msg.topic or "openWB/set/chargepoint/get/daily_imported" in msg.topic or "openWB/set/chargepoint/get/daily_exported" in msg.topic): - self._validate_value(msg, float, [(0, float("inf"))]) + self._validate_value(msg, float) elif re.search("chargepoint/[0-9]+/config/template$", msg.topic) is not None: self._validate_value(msg, int, pub_json=True) elif "template" in msg.topic: @@ -527,14 +445,15 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, "json") elif subdata.SubData.cp_data.get(f"cp{get_index(msg.topic)}"): if ("/set/charging_ev" in msg.topic or - "/set/charging_ev_prev" in msg.topic): + "/set/charging_ev_prev" in msg.topic or + "/set/ev_prev" in msg.topic): self._validate_value(msg, int, [(-1, float("inf"))]) elif ("/set/current" in msg.topic or "/set/current_prev" in msg.topic): if hardware_configuration.get_hardware_configuration_setting("dc_charging"): - self._validate_value(msg, float, [(0, 0), (6, 32), (0, 450)]) + self._validate_value(msg, float, [(float("-inf"), 0), (0, 0), (6, 32), (0, 450)]) else: - self._validate_value(msg, float, [(6, 32), (0, 0)]) + self._validate_value(msg, float, [(float("-inf"), 0), (6, 32), (0, 0)]) elif ("/set/energy_to_charge" in msg.topic or "/set/required_power" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) @@ -544,6 +463,7 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): "/set/perform_control_pilot_interruption" in msg.topic or "/set/perform_phase_switch" in msg.topic or "/set/ocpp_transaction_active" in msg.topic or + "/set/charge_state_prev" in msg.topic or "/set/plug_state_prev" in msg.topic): self._validate_value(msg, bool) elif "/set/autolock_state" in msg.topic: @@ -570,19 +490,20 @@ def process_chargepoint_topic(self, msg: mqtt.MQTTMessage): elif "/control_parameter/failed_phase_switches" in msg.topic: self._validate_value(msg, int, [(0, 4)]) elif ("/control_parameter/submode" in msg.topic or - "/control_parameter/limit" in msg.topic or "/control_parameter/chargemode" in msg.topic): self._validate_value(msg, str) + elif "/control_parameter/limit" in msg.topic: + self._validate_value(msg, "json") elif "/control_parameter/prio" in msg.topic: self._validate_value(msg, bool) elif "/control_parameter/current_plan" in msg.topic: self._validate_value(msg, int) - elif ("/control_parameter/imported_instant_charging" in msg.topic or - "/control_parameter/imported_at_plan_start" in msg.topic or - "/control_parameter/min_current" in msg.topic or + elif ("/control_parameter/min_current" in msg.topic or "/control_parameter/timestamp_switch_on_off" in msg.topic or "/control_parameter/timestamp_charge_start" in msg.topic or - "/control_parameter/timestamp_last_phase_switch" in msg.topic): + "/control_parameter/timestamp_chargemode_changed" in msg.topic or + "/control_parameter/timestamp_last_phase_switch" in msg.topic or + "/control_parameter/timestamp_phase_switch_buffer_start" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) elif "/control_parameter/state" in msg.topic: self._validate_value(msg, int, [(0, 7)]) @@ -607,14 +528,18 @@ def process_chargepoint_get_topics(self, msg): self._validate_value(msg, float, [(40, 60)]) elif ("/get/daily_imported" in msg.topic or "/get/daily_exported" in msg.topic or - "/get/power" in msg.topic or "/get/charging_current" in msg.topic or "/get/charging_power" in msg.topic or "/get/charging_voltage" in msg.topic or + "/get/max_charge_power" in msg.topic or "/get/imported" in msg.topic or "/get/exported" in msg.topic or "/get/soc_timestamp" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) + elif "/get/max_discharge_power" in msg.topic: + self._validate_value(msg, float, [(float("-inf"), 0)]) + elif "/get/power" in msg.topic: + self._validate_value(msg, float) elif "/get/phases_in_use" in msg.topic: self._validate_value(msg, int, [(0, 3)]) elif ("/get/charge_state" in msg.topic or @@ -624,7 +549,11 @@ def process_chargepoint_get_topics(self, msg): self._validate_value(msg, int, [(0, 2)]) elif ("/get/evse_current" in msg.topic or "/get/max_evse_current" in msg.topic): - self._validate_value(msg, float, [(0, 0), (6, 32), (600, 3200)]) + self._validate_value(msg, float, [(float("-inf"), 0), (0, 0), (6, 32), (600, 3200)]) + elif ("/get/version" in msg.topic or + "/get/current_branch" in msg.topic or + "/get/current_commit" in msg.topic): + self._validate_value(msg, str) elif ("/get/error_timestamp" in msg.topic or "/get/rfid_timestamp" in msg.topic): self._validate_value(msg, float) @@ -633,7 +562,8 @@ def process_chargepoint_get_topics(self, msg): "/get/heartbeat" in msg.topic or "/get/rfid" in msg.topic or "/get/vehicle_id" in msg.topic or - "/get/serial_number" in msg.topic): + "/get/serial_number" in msg.topic or + "/get/evse_signaling" in msg.topic): self._validate_value(msg, str) elif ("/get/soc" in msg.topic): self._validate_value(msg, float, [(0, 100)]) @@ -683,8 +613,7 @@ def process_pv_topic(self, msg: mqtt.MQTTMessage): elif "/get/power" in msg.topic: self._validate_value(msg, float) elif "/get/currents" in msg.topic: - self._validate_value( - msg, float, collection=list) + self._validate_value(msg, float, collection=list) else: self.__unknown_topic(msg) else: @@ -702,7 +631,8 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): enthält Topic und Payload """ try: - if ("openWB/set/bat/config/configured" in msg.topic or + if ("openWB/set/bat/config/bat_control_permitted" in msg.topic or + "openWB/set/bat/config/configured" in msg.topic or "openWB/set/bat/get/power_limit_controllable" in msg.topic or "openWB/set/bat/set/regulate_up" in msg.topic): self._validate_value(msg, bool) @@ -710,13 +640,13 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, float) elif "openWB/set/bat/get/soc" in msg.topic: self._validate_value(msg, float, [(0, 100)]) - elif "openWB/set/bat/get/power" in msg.topic: + elif ("openWB/set/bat/get/power" in msg.topic or + "openWB/set/bat/set/power_limit" in msg.topic): self._validate_value(msg, float) elif ("openWB/set/bat/get/imported" in msg.topic or "openWB/set/bat/get/exported" in msg.topic or "openWB/set/bat/get/daily_exported" in msg.topic or - "openWB/set/bat/get/daily_imported" in msg.topic or - "openWB/set/bat/set/power_limit" in msg.topic): + "openWB/set/bat/get/daily_imported" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) elif "openWB/set/bat/get/fault_state" in msg.topic: self._validate_value(msg, int, [(0, 2)]) @@ -733,6 +663,8 @@ def process_bat_topic(self, msg: mqtt.MQTTMessage): "/get/daily_exported" in msg.topic or "/get/daily_imported" in msg.topic): self._validate_value(msg, float, [(0, float("inf"))]) + elif "/get/currents" in msg.topic: + self._validate_value(msg, float, collection=list) elif "/get/soc" in msg.topic: self._validate_value(msg, float, [(0, 100)]) elif "/get/fault_state" in msg.topic: @@ -788,10 +720,6 @@ def process_general_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, int, [(5, 20)]) elif "openWB/set/general/chargemode_config/pv_charging/control_range" in msg.topic: self._validate_value(msg, int, collection=list) - elif (("openWB/set/general/chargemode_config/pv_charging/phases_to_use" in msg.topic or - "openWB/set/general/chargemode_config/scheduled_charging/phases_to_use" in msg.topic or - "openWB/set/general/chargemode_config/scheduled_charging/phases_to_use_pv" in msg.topic)): - self._validate_value(msg, int, [(0, 0), (1, 1), (3, 3)]) elif "openWB/set/general/chargemode_config/pv_charging/min_bat_soc" in msg.topic: self._validate_value(msg, int, [(0, 100)]) elif ("openWB/set/general/chargemode_config/pv_charging/bat_power_discharge" in msg.topic or @@ -822,19 +750,9 @@ def process_general_topic(self, msg: mqtt.MQTTMessage): "openWB/set/general/prices/grid" in msg.topic or "openWB/set/general/prices/pv" in msg.topic): self._validate_value(msg, float, [(0, 99.99)]) - elif ("openWB/set/general/range_unit" in msg.topic or - "openWB/set/general/ripple_control_receiver/override_reference" in msg.topic): - self._validate_value(msg, str) - elif "openWB/set/general/ripple_control_receiver/configured" in msg.topic: - self._validate_value(msg, bool) - elif "openWB/set/general/ripple_control_receiver/get/override_value" in msg.topic: - self._validate_value(msg, float) - elif "openWB/set/general/ripple_control_receiver/get/fault_state" in msg.topic: - self._validate_value(msg, int, [(0, 2)]) - elif "openWB/set/general/ripple_control_receiver/get/fault_str" in msg.topic: + elif "openWB/set/general/range_unit" in msg.topic: self._validate_value(msg, str) - elif ("openWB/set/general/web_theme" in msg.topic or - "openWB/set/general/ripple_control_receiver/module" in msg.topic): + elif "openWB/set/general/web_theme" in msg.topic: self._validate_value(msg, "json") elif ("openWB/set/general/charge_log_data_config" in msg.topic): self._validate_value(msg, "json") @@ -843,6 +761,48 @@ def process_general_topic(self, msg: mqtt.MQTTMessage): except Exception: log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") + def process_io_topic(self, msg: mqtt.MQTTMessage): + """ Handler für die Allgemeinen-Topics + + Parameters + ---------- + + msg: + enthält Topic und Payload + """ + try: + if "config" in msg.topic: + self._validate_value(msg, "json") + elif ("get/digital_input" in msg.topic or + "get/analog_input" in msg.topic or + "get/digital_output" in msg.topic or + "get/analog_output" in msg.topic or + "set/digital_output" in msg.topic or + "set/analog_output" in msg.topic): + self._validate_value(msg, "json") + elif "get/fault_state" in msg.topic: + self._validate_value(msg, int, [(0, 2)]) + elif "get/fault_str" in msg.topic: + self._validate_value(msg, str) + elif "/timestamp" in msg.topic: + self._validate_value(msg, float) + else: + self.__unknown_topic(msg) + except Exception: + log.exception(f"Fehler im setdata-Modul: Topic {msg.topic}, Value: {msg.payload}") + + def process_mqtt_topic(self, msg: mqtt.MQTTMessage): + if "openWB/set/mqtt/chargepoint/" in msg.topic: + self.process_chargepoint_get_topics(msg) + elif "openWB/set/mqtt/counter/" in msg.topic: + self.process_counter_topic(msg) + elif "openWB/set/mqtt/bat/" in msg.topic: + self.process_bat_topic(msg) + elif "openWB/set/mqtt/pv/" in msg.topic: + self.process_pv_topic(msg) + elif "openWB/set/mqtt/vehicle/" in msg.topic: + self.process_vehicle_topic(msg) + def process_optional_topic(self, msg: mqtt.MQTTMessage): """ Handler für die Optionalen-Topics @@ -920,7 +880,7 @@ def process_counter_topic(self, msg: mqtt.MQTTMessage): elif "/module" in msg.topic: self._validate_value(msg, "json") elif "/config/max_currents" in msg.topic: - self._validate_value(msg, int, [(7, 1500)], collection=list) + self._validate_value(msg, int, [(6, 1500)], collection=list) elif ("/config/max_total_power" in msg.topic or "/config/max_power_errorcase" in msg.topic): self._validate_value(msg, int, [(0, float("inf"))]) @@ -1050,6 +1010,8 @@ def process_system_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, "json") elif "openWB/set/system/mqtt/valid_partner_ids" == msg.topic: self._validate_value(msg, str, collection=list) + elif "openWB/set/system/secondary_auto_update" == msg.topic: + self._validate_value(msg, bool) elif "configurable" in msg.topic: self._validate_value(msg, None) elif "device" in msg.topic: @@ -1062,6 +1024,8 @@ def process_system_topic(self, msg: mqtt.MQTTMessage): self.__unknown_topic(msg) elif "/config" in msg.topic: self._validate_value(msg, "json") + elif "/error_timestamp" in msg.topic: + self._validate_value(msg, float, [(0, float("inf"))]) elif "/get/fault_state" in msg.topic: self._validate_value(msg, int, [(0, 2)]) elif "/get/fault_str" in msg.topic: @@ -1070,6 +1034,13 @@ def process_system_topic(self, msg: mqtt.MQTTMessage): self._validate_value(msg, bool) else: self.__unknown_topic(msg) + elif "io" in msg.topic: + if "/config" in msg.topic: + self._validate_value(msg, "json") + elif "/set/manual/analog_output" in msg.topic: + self._validate_value(msg, float) + elif "/set/manual/digital_output" in msg.topic: + self._validate_value(msg, bool) else: # hier kommen auch noch alte Topics ohne json-Format an. # log.error("Unbekanntes set-Topic: "+str(msg.topic)+", "+ diff --git a/packages/helpermodules/skip_while_unchanged.py b/packages/helpermodules/skip_while_unchanged.py index 08cfad851c..54eec3b52b 100644 --- a/packages/helpermodules/skip_while_unchanged.py +++ b/packages/helpermodules/skip_while_unchanged.py @@ -1,4 +1,4 @@ -import threading +from threading import Lock from typing import Callable, TypeVar T = TypeVar("T") @@ -8,7 +8,7 @@ def skip_while_unchanged(source: Callable, initial=None): """Before each call check if value given by `source` has changed. If it has not, ignore the call""" def wrap(function: T) -> T: previous = [initial] - lock = threading.Lock() + lock = Lock() def wrapper(*args, **kwargs): with lock: diff --git a/packages/helpermodules/subdata.py b/packages/helpermodules/subdata.py index f834771322..f07b62b436 100644 --- a/packages/helpermodules/subdata.py +++ b/packages/helpermodules/subdata.py @@ -3,13 +3,13 @@ import importlib import logging from pathlib import Path -import threading +from threading import Event from typing import Dict, Union import re import subprocess import paho.mqtt.client as mqtt -from control import bat_all, bat, counter, counter_all, general, optional, pv, pv_all +from control import bat_all, bat, counter, counter_all, general, io_device, optional, pv, pv_all from control.chargepoint import chargepoint from control.chargepoint.chargepoint_all import AllChargepoints from control.chargepoint.chargepoint_data import Log @@ -18,18 +18,18 @@ from control.ev.charge_template import ChargeTemplate, ChargeTemplateData from control.ev import ev from control.ev.ev_template import EvTemplate, EvTemplateData +from control.limiting_value import LoadmanagementLimit from control.optional_data import Ocpp from helpermodules import graph, system -from helpermodules.abstract_plans import AutolockPlan, ScheduledChargingPlan, TimeChargingPlan -from helpermodules.broker import InternalBrokerClient +from helpermodules.broker import BrokerClient from helpermodules.messaging import MessageType, pub_system_message +from helpermodules.utils import ProcessingCounter from helpermodules.utils.run_command import run_command from helpermodules.utils.topic_parser import decode_payload, get_index, get_second_index from helpermodules.pub import Pub from dataclass_utils import dataclass_from_dict from modules.common.abstract_vehicle import CalculatedSocState, GeneralVehicleConfig from modules.common.configurable_backup_cloud import ConfigurableBackupCloud -from modules.common.configurable_ripple_control_receiver import ConfigurableRcr from modules.common.configurable_tariff import ConfigurableElectricityTariff from modules.common.simcount.simcounter_state import SimCounterState from modules.internal_chargepoint_handler.internal_chargepoint_handler_config import ( @@ -64,31 +64,30 @@ class SubData: "cp1": InternalChargepoint(), "global_data": GlobalHandlerData(), "rfid_data": RfidData()} + io_actions = io_device.IoActions() + io_states: Dict[str, io_device.IoStates] = {} optional_data = optional.Optional() system_data = {"system": system.System()} graph_data = graph.Graph() def __init__(self, - event_ev_template: threading.Event, - event_charge_template: threading.Event, - event_cp_config: threading.Event, - event_module_update_completed: threading.Event, - event_copy_data: threading.Event, - event_global_data_initialized: threading.Event, - event_command_completed: threading.Event, - event_subdata_initialized: threading.Event, - event_vehicle_update_completed: threading.Event, - event_scheduled_charging_plan: threading.Event, - event_time_charging_plan: threading.Event, - event_start_internal_chargepoint: threading.Event, - event_stop_internal_chargepoint: threading.Event, - event_update_config_completed: threading.Event, - event_update_soc: threading.Event, - event_soc: threading.Event, - event_jobs_running: threading.Event, - event_modbus_server: threading.Event,): + event_ev_template: Event, + event_cp_config: Event, + event_module_update_completed: Event, + event_copy_data: Event, + event_global_data_initialized: Event, + event_command_completed: Event, + event_subdata_initialized: Event, + event_vehicle_update_completed: Event, + event_start_internal_chargepoint: Event, + event_stop_internal_chargepoint: Event, + event_update_config_completed: Event, + event_update_soc: Event, + event_soc: Event, + event_jobs_running: Event, + event_modbus_server: Event, + event_restart_gpio: Event,): self.event_ev_template = event_ev_template - self.event_charge_template = event_charge_template self.event_cp_config = event_cp_config self.event_module_update_completed = event_module_update_completed self.event_copy_data = event_copy_data @@ -96,8 +95,6 @@ def __init__(self, self.event_command_completed = event_command_completed self.event_subdata_initialized = event_subdata_initialized self.event_vehicle_update_completed = event_vehicle_update_completed - self.event_scheduled_charging_plan = event_scheduled_charging_plan - self.event_time_charging_plan = event_time_charging_plan self.event_start_internal_chargepoint = event_start_internal_chargepoint self.event_stop_internal_chargepoint = event_stop_internal_chargepoint self.event_update_config_completed = event_update_config_completed @@ -105,10 +102,15 @@ def __init__(self, self.event_soc = event_soc self.event_jobs_running = event_jobs_running self.event_modbus_server = event_modbus_server + self.event_restart_gpio = event_restart_gpio self.heartbeat = False + # Immer wenn ein Subscribe hinzugefügt wird, wird der Zähler hinzugefügt und subdata_initialized gepublished. + # Wenn subdata_initialized empfangen wird, wird der Zäheler runtergezählt. Erst wenn alle subdata_initialized + # empfangen wurden, wurden auch die vorher subskribierten Topics empfangen und der Algorithmus kann starten. + self.processing_counter = ProcessingCounter(self.event_subdata_initialized) def sub_topics(self): - self.internal_broker_client = InternalBrokerClient("mqttsub", self.on_connect, self.on_message) + self.internal_broker_client = BrokerClient("mqttsub", self.on_connect, self.on_message) self.internal_broker_client.start_infinite_loop() def disconnect(self) -> None: @@ -129,6 +131,8 @@ def on_connect(self, client: mqtt.Client, userdata, flags: dict, rc: int): ("openWB/bat/#", 2), ("openWB/general/#", 2), ("openWB/graph/#", 2), + ("openWB/internal_io/#", 2), + ("openWB/io/#", 2), ("openWB/optional/#", 2), ("openWB/counter/#", 2), ("openWB/command/command_completed", 2), @@ -142,8 +146,10 @@ def on_connect(self, client: mqtt.Client, userdata, flags: dict, rc: int): ("openWB/system/backup_cloud/#", 2), ("openWB/system/device/module_update_completed", 2), ("openWB/system/device/+/config", 2), + ("openWB/system/io/#", 2), ("openWB/LegacySmartHome/Status/wattnichtHaus", 2), ]) + self.processing_counter.add_task() Pub().pub("openWB/system/subdata_initialized", True) def on_message(self, client: mqtt.Client, userdata, msg: mqtt.MQTTMessage): @@ -171,6 +177,10 @@ def on_message(self, client: mqtt.Client, userdata, msg: mqtt.MQTTMessage): self.process_general_topic(self.general_data, msg) elif "openWB/graph/" in msg.topic: self.process_graph_topic(self.graph_data, msg) + elif "openWB/io/action" in msg.topic: + self.process_io_topic(self.io_actions, msg) + elif "openWB/io/states" in msg.topic or "openWB/internal_io/states" in msg.topic: + self.process_io_topic(self.io_states, msg) elif "openWB/internal_chargepoint/" in msg.topic: self.process_internal_chargepoint_topic(client, self.internal_chargepoint_data, msg) elif "openWB/optional/" in msg.topic: @@ -240,6 +250,9 @@ def set_json_payload_class(self, class_obj: Dict, msg: mqtt.MQTTMessage): log.error("Elemente können nur aus Dictionaries entfernt werden, nicht aus Klassen.") else: raise Exception(f"Key konnte nicht aus Topic {msg.topic} ermittelt werden.") + except AttributeError: + pass + # manche Topics werden nicht in einem Attribut gespeichert except Exception: log.exception("Fehler im subdata-Modul") @@ -298,13 +311,27 @@ def process_vehicle_topic(self, client: mqtt.Client, var: Dict[str, ev.Ev], msg: var["ev"+index].soc_module = mod.create_vehicle(config, index) client.subscribe(f"openWB/vehicle/{index}/soc_module/calculated_soc_state", 2) client.subscribe(f"openWB/vehicle/{index}/soc_module/general_config", 2) + self.processing_counter.add_task() + Pub().pub("openWB/system/subdata_initialized", True) self.event_soc.set() else: + # temporäres ChargeTemplate aktualisieren, wenn dem Fahrzeug ein anderes Ladeprofil zugeordnet + # wird self.set_json_payload_class(var["ev"+index].data, msg) + if re.search("/vehicle/[0-9]+/charge_template$", msg.topic) is not None: + charge_template_id = int(decode_payload(msg.payload)) + if var["ev"+index].data.charge_template != charge_template_id: + ev_id = get_index(msg.topic) + for cp in self.cp_data.values(): + if ((cp.chargepoint.data.set.charging_ev != -1 and + cp.chargepoint.data.set.charging_ev == ev_id) or + cp.chargepoint.data.config.ev == ev_id): + cp.chargepoint.update_charge_template( + self.ev_charge_template_data[f"ct{charge_template_id}"]) except Exception: log.exception("Fehler im subdata-Modul") - def process_vehicle_charge_template_topic(self, var: Dict[str, ev.ChargeTemplate], msg: mqtt.MQTTMessage): + def process_vehicle_charge_template_topic(self, var: Dict[str, ChargeTemplate], msg: mqtt.MQTTMessage): """ Handler für die EV-Topics Parameter @@ -316,47 +343,27 @@ def process_vehicle_charge_template_topic(self, var: Dict[str, ev.ChargeTemplate """ try: index = get_index(msg.topic) - if decode_payload(msg.payload) == "" and re.search("/vehicle/template/charge_template/[0-9]+$", - msg.topic) is not None: - if "ct"+index in var: - var.pop("ct"+index) - else: + if re.search("/vehicle/template/charge_template/[0-9]+$", msg.topic) is not None: + if decode_payload(msg.payload) == "": + if "ct"+index in var: + var.pop("ct"+index) if "ct"+index not in var: - var["ct"+index] = ev.ChargeTemplate(int(index)) - if re.search("/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/plans/[0-9]+$", - msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var["ct"+index].data.chargemode.scheduled_charging.plans.pop(index_second) - except KeyError: - log.error("Es konnte kein Zielladen-Plan mit der ID " + - str(index_second)+" in dem Lade-Profil "+str(index)+" gefunden werden.") - else: - var["ct"+index].data.chargemode.scheduled_charging.plans[ - index_second] = dataclass_from_dict(ScheduledChargingPlan, decode_payload(msg.payload)) - self.event_scheduled_charging_plan.set() - elif re.search("/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", - msg.topic) is not None: - index_second = get_second_index(msg.topic) - if decode_payload(msg.payload) == "": - try: - var["ct"+index].data.time_charging.plans.pop(index_second) - except KeyError: - log.error("Es konnte kein Zeitladen-Plan mit der ID " + - str(index_second)+" in dem Lade-Profil "+str(index)+" gefunden werden.") - else: - var["ct"+index].data.time_charging.plans[ - index_second] = dataclass_from_dict(TimeChargingPlan, decode_payload(msg.payload)) - self.event_time_charging_plan.set() - else: - # Pläne unverändert übernehmen - scheduled_charging_plans = var["ct" + index].data.chargemode.scheduled_charging.plans - time_charging_plans = var["ct" + index].data.time_charging.plans - var["ct" + index].data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) - var["ct"+index].data.time_charging.plans = time_charging_plans - var["ct"+index].data.chargemode.scheduled_charging.plans = scheduled_charging_plans - self.event_charge_template.set() + var["ct"+index] = ChargeTemplate() + var["ct"+index].data = dataclass_from_dict(ChargeTemplateData, decode_payload(msg.payload)) + # Temporäres ChargeTemplate aktualisieren, wenn persistentes geändert wird + for vehicle in self.ev_data.values(): + if vehicle.data.charge_template == int(index): + for cp in self.cp_data.values(): + if ((cp.chargepoint.data.set.charging_ev != -1 and + cp.chargepoint.data.set.charging_ev == vehicle.num) or + cp.chargepoint.data.config.ev == vehicle.num): + # UI sendet immer alle Topics, auch nicht geänderte. Damit die temporären Topics nicht + # mehrfach gepbulished werden, muss das publishen der temporären Topics 1:1 erfolgen. + if re.search("/vehicle/template/charge_template/[0-9]+$", msg.topic) is not None: + if decode_payload(msg.payload) == "": + Pub().pub(f"openWB/chargepoint/{cp.chargepoint.num}/set/charge_template", "") + else: + cp.chargepoint.update_charge_template(var["ct"+index]) except Exception: log.exception("Fehler im subdata-Modul") @@ -378,7 +385,7 @@ def process_vehicle_ev_template_topic(self, var: Dict[str, EvTemplate], msg: mqt var.pop("et"+index) else: if "et"+index not in var: - var["et"+index] = EvTemplate(et_num=int(index)) + var["et"+index] = EvTemplate() var["et" + index].data = dataclass_from_dict(EvTemplateData, decode_payload(msg.payload)) self.event_ev_template.set() except Exception: @@ -398,11 +405,12 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg if re.search("/chargepoint/[0-9]+/", msg.topic) is not None: index = get_index(msg.topic) if decode_payload(msg.payload) == "": - log.debug("Stop des Handlers für den internen Ladepunkt.") - self.event_stop_internal_chargepoint.set() - if "cp"+index in var: - var.pop("cp"+index) - self.set_internal_chargepoint_configured() + if re.search("/chargepoint/[0-9]+/config", msg.topic) is not None: + log.debug("Stop des Handlers für den internen Ladepunkt.") + self.event_stop_internal_chargepoint.set() + if "cp"+index in var: + var.pop("cp"+index) + self.set_internal_chargepoint_configured() else: if "cp"+index not in var: var["cp"+index] = ChargepointStateUpdate( @@ -417,6 +425,10 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg if re.search("/chargepoint/[0-9]+/set/log$", msg.topic) is not None: var["cp"+index].chargepoint.data.set.log = dataclass_from_dict( Log, decode_payload(msg.payload)) + elif "charge_template" in msg.topic: + var["cp"+index].chargepoint.data.set.charge_template = ChargeTemplate() + var["cp"+index].chargepoint.data.set.charge_template.data = dataclass_from_dict( + ChargeTemplateData, decode_payload(msg.payload)) else: self.set_json_payload_class(var["cp"+index].chargepoint.data.set, msg) elif re.search("/chargepoint/[0-9]+/get/", msg.topic) is not None: @@ -429,7 +441,8 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg Pub().pub(f'openWB/set/vehicle/{var["cp"+index].chargepoint.data.set.charging_ev}' '/get/force_soc_update', True) self.set_json_payload_class(var["cp"+index].chargepoint.data.get, msg) - elif re.search("/chargepoint/[0-9]+/get/error_timestamp$", msg.topic) is not None: + elif (re.search("/chargepoint/[0-9]+/get/error_timestamp$", msg.topic) is not None and + hasattr(var[f"cp{index}"].chargepoint.chargepoint_module, "client_error_context")): var["cp" + index].chargepoint.chargepoint_module.client_error_context.error_timestamp = ( decode_payload(msg.payload) @@ -444,7 +457,12 @@ def process_chargepoint_topic(self, var: Dict[str, chargepoint.Chargepoint], msg elif re.search("/chargepoint/[0-9]+/config$", msg.topic) is not None: self.process_chargepoint_config_topic(var, msg) elif re.search("/chargepoint/[0-9]+/control_parameter/", msg.topic) is not None: - self.set_json_payload_class(var["cp"+index].chargepoint.data.control_parameter, msg) + if re.search("/chargepoint/[0-9]+/control_parameter/limit", msg.topic) is not None: + payload = decode_payload(msg.payload) + var["cp"+index].chargepoint.data.control_parameter.limit = dataclass_from_dict( + LoadmanagementLimit, payload) + else: + self.set_json_payload_class(var["cp"+index].chargepoint.data.control_parameter, msg) elif re.search("/chargepoint/get/", msg.topic) is not None: self.set_json_payload_class(self.cp_all_data.data.get, msg) except Exception: @@ -480,22 +498,12 @@ def process_chargepoint_template_topic(self, var: Dict[str, chargepoint.CpTempla try: index = get_index(msg.topic) payload = decode_payload(msg.payload) - if re.search("/chargepoint/template/[0-9]+/autolock/", msg.topic) is not None: - index_second = get_second_index(msg.topic) - if payload == "": - var["cpt"+index].data.autolock.plans.pop(index_second) - else: - var["cpt"+index].data.autolock.plans[ - index_second] = dataclass_from_dict(AutolockPlan, payload) + if payload == "": + var.pop("cpt"+index) else: - if payload == "": - var.pop("cpt"+index) - else: - if "cpt"+index not in var: - var["cpt"+index] = CpTemplate() - autolock_plans = var["cpt"+index].data.autolock.plans - var["cpt"+index].data = dataclass_from_dict(CpTemplateData, payload) - var["cpt"+index].data.autolock.plans = autolock_plans + if "cpt"+index not in var: + var["cpt"+index] = CpTemplate() + var["cpt"+index].data = dataclass_from_dict(CpTemplateData, payload) except Exception: log.exception("Fehler im subdata-Modul") @@ -579,35 +587,11 @@ def process_general_topic(self, var: general.General, msg: mqtt.MQTTMessage): """ try: if re.search("/general/", msg.topic) is not None: - if re.search("/general/ripple_control_receiver/module", msg.topic) is not None: - config_dict = decode_payload(msg.payload) - if config_dict["type"] is None: - var.data.ripple_control_receiver.module = None - var.ripple_control_receiver = None - else: - mod = importlib.import_module(".ripple_control_receivers." + - config_dict["type"]+".ripple_control_receiver", "modules") - config = dataclass_from_dict(mod.device_descriptor.configuration_factory, config_dict) - var.data.ripple_control_receiver.module = config_dict - var.ripple_control_receiver = ConfigurableRcr( - config=config, component_initializer=mod.create_ripple_control_receiver) - elif re.search("/general/ripple_control_receiver/get/", msg.topic) is not None: - self.set_json_payload_class(var.data.ripple_control_receiver.get, msg) - elif re.search("/general/ripple_control_receiver/", msg.topic) is not None: - return - elif re.search("/general/prices/", msg.topic) is not None: + if re.search("/general/prices/", msg.topic) is not None: self.set_json_payload_class(var.data.prices, msg) elif re.search("/general/chargemode_config/", msg.topic) is not None: if re.search("/general/chargemode_config/pv_charging/", msg.topic) is not None: self.set_json_payload_class(var.data.chargemode_config.pv_charging, msg) - elif re.search("/general/chargemode_config/instant_charging/", msg.topic) is not None: - self.set_json_payload_class(var.data.chargemode_config.instant_charging, msg) - elif re.search("/general/chargemode_config/scheduled_charging/", msg.topic) is not None: - self.set_json_payload_class(var.data.chargemode_config.scheduled_charging, msg) - elif re.search("/general/chargemode_config/time_charging/", msg.topic) is not None: - self.set_json_payload_class(var.data.chargemode_config.time_charging, msg) - elif re.search("/general/chargemode_config/standby/", msg.topic) is not None: - self.set_json_payload_class(var.data.chargemode_config.standby, msg) else: self.set_json_payload_class(var.data.chargemode_config, msg) elif "openWB/general/extern" == msg.topic: @@ -640,6 +624,63 @@ def process_general_topic(self, var: general.General, msg: mqtt.MQTTMessage): except Exception: log.exception("Fehler im subdata-Modul") + def process_io_topic(self, var: Dict[str, Union[io_device.IoActions, io_device.IoStates]], msg: mqtt.MQTTMessage): + """ Handler für die IO-Topics + + Parameter + ---------- + var : Dictionary + enthält aktuelle Daten + msg : + enthält Topic und Payload + """ + try: + if (re.search("/io/states/", msg.topic) is not None or + re.search("/internal_io/states/", msg.topic) is not None): + if re.search("/io/states/[0-9]+/", msg.topic) is not None: + index = get_index(msg.topic) + key = "io_states"+index + else: + index = "internal" + key = "internal_io_states" + + payload = decode_payload(msg.payload) + if payload == "": + if key in var: + var.pop(key) + else: + if key not in var: + var[key] = io_device.IoStates(index) + if (re.search("/io/states/[0-9]+/get", msg.topic) is not None or + re.search("/internal_io/states/get", msg.topic) is not None): + # Sonst werden Dicts als Payload verwendet, aber es wird alles in ein eigenes Attribut gespeichert + # Typ ist hier auch kein typing.Dict, sondern ein generisches Dict[str, bool] + setattr(var[key].data.get, msg.topic.split("/")[-1], payload) + elif (re.search("/io/states/[0-9]+/set", msg.topic) is not None or + re.search("/internal_io/states/set", msg.topic) is not None): + # Sonst werden Dicts als Payload verwendet, aber es wird alles in ein eigenes Attribut gespeichert + # Typ ist hier auch kein typing.Dict, sondern ein generisches Dict[str, bool] + setattr(var[key].data.set, msg.topic.split("/")[-1], payload) + else: + self.set_json_payload_class(var[key].data, msg) + elif "io/action" in msg.topic: + if re.search("/io/action/[0-9]+/config", msg.topic) is not None: + index = get_index(msg.topic) + payload = decode_payload(msg.payload) + if payload == "": + if f"io_action{index}" in var.actions: + var.actions.pop(f"io_action{index}") + else: + mod = importlib.import_module( + f".io_actions.{payload['group']}.{payload['type']}.api", "modules") + config = dataclass_from_dict(mod.device_descriptor.configuration_factory, payload) + var.actions[f"io_action{index}"] = mod.create_action(config) + elif re.search("/io/action/[0-9]+/timestamp", msg.topic) is not None: + index = get_index(msg.topic) + self.set_json_payload_class(var.actions[f"io_action{index}"], msg) + except Exception: + log.exception("Fehler im subdata-Modul") + def process_optional_topic(self, var: optional.Optional, msg: mqtt.MQTTMessage): """ Handler für die Optionalen-Topics @@ -770,12 +811,18 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes var["device"+index] = (dev.Device if hasattr(dev, "Device") else dev.create_device)(config) # Durch das erneute Subscribe werden die Komponenten mit dem aktualisierten TCP-Client angelegt. client.subscribe(f"openWB/system/device/{index}/component/+/config", 2) + client.subscribe(f"openWB/system/device/{index}/error_timestamp", 2) + self.processing_counter.add_task() + Pub().pub("openWB/system/subdata_initialized", True) elif re.search("^.+/device/[0-9]+/component/[0-9]+/simulation$", msg.topic) is not None: index = get_index(msg.topic) index_second = get_second_index(msg.topic) var["device"+index].components["component"+index_second].sim_counter.data = dataclass_from_dict( SimCounterState, decode_payload(msg.payload)) + elif re.search("^.+/device/[0-9]+/error_timestamp$", msg.topic) is not None: + index = get_index(msg.topic) + var["device"+index].client_error_context.error_timestamp = decode_payload(msg.payload) elif re.search("^.+/device/[0-9]+/component/[0-9]+/config$", msg.topic) is not None: index = get_index(msg.topic) index_second = get_second_index(msg.topic) @@ -803,6 +850,8 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes config = dataclass_from_dict(component.component_descriptor.configuration_factory, component_config) var["device"+index].add_component(config) client.subscribe(f"openWB/system/device/{index}/component/{index_second}/simulation", 2) + self.processing_counter.add_task() + Pub().pub("openWB/system/subdata_initialized", True) elif "mqtt" and "bridge" in msg.topic: # do not reconfigure mqtt bridges if topic is received on startup if self.event_subdata_initialized.is_set(): @@ -844,9 +893,39 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes self.set_json_payload(var["system"].data["backup_cloud"], msg) elif ("openWB/system/dataprotection_acknowledged" == msg.topic and decode_payload(msg.payload) is False): - Pub().pub("openWB/set/command/removeCloudBridge/todo", { - "command": "removeCloudBridge" - }) + if self.event_subdata_initialized.is_set(): + Pub().pub("openWB/set/command/removeCloudBridge/todo", + {"command": "removeCloudBridge"}) + else: + log.debug("skipping data protection message on startup") + elif re.search("^.+/io/[0-9]+/config$", msg.topic) is not None: + index = get_index(msg.topic) + if decode_payload(msg.payload) == "": + if "io"+index in var: + if var[f"io{index}"].config.configuration.host == "localhost": + var.pop("iolocal") + var.pop("io"+index) + else: + log.error("Es konnte kein IO-Device mit der ID " + + str(index)+" gefunden werden.") + else: + io_config = decode_payload(msg.payload) + if io_config["type"] == "add_on" and io_config["configuration"]["host"] == "localhost": + self.event_restart_gpio.set() + dev = importlib.import_module(f".internal_chargepoint_handler.{io_config['type']}.api", + "modules") + config = dataclass_from_dict(dev.device_descriptor.configuration_factory, io_config) + var["iolocal"] = dev.create_io(config) + dev = importlib.import_module(f".io_devices.{io_config['type']}.api", + "modules") + config = dataclass_from_dict(dev.device_descriptor.configuration_factory, io_config) + var["io"+index] = dev.create_io(config) + elif re.search("^.+/io/[0-9]+/set/manual/analog_output", msg.topic) is not None: + index = get_index(msg.topic) + self.set_json_payload(var["io"+index].set_manual["analog_output"], msg) + elif re.search("^.+/io/[0-9]+/set/manual/digital_output", msg.topic) is not None: + index = get_index(msg.topic) + self.set_json_payload(var["io"+index].set_manual["digital_output"], msg) else: if "module_update_completed" in msg.topic: self.event_module_update_completed.set() @@ -857,7 +936,7 @@ def process_system_topic(self, client: mqtt.Client, var: dict, msg: mqtt.MQTTMes elif "openWB/system/subdata_initialized" == msg.topic: if decode_payload(msg.payload) != "": Pub().pub("openWB/system/subdata_initialized", "") - self.event_subdata_initialized.set() + self.processing_counter.task_done() elif "openWB/system/update_config_completed" == msg.topic: if decode_payload(msg.payload) != "": Pub().pub("openWB/system/update_config_completed", "") diff --git a/packages/helpermodules/system.py b/packages/helpermodules/system.py index eb6fe059f0..7b1dd3c727 100644 --- a/packages/helpermodules/system.py +++ b/packages/helpermodules/system.py @@ -3,7 +3,7 @@ import logging import os import subprocess -import threading +from threading import Thread import time from pathlib import Path from typing import Optional @@ -85,7 +85,7 @@ def create(): self.create_backup_and_send_to_cloud() except Exception as e: log.exception(f"Error in cloud backup: {e}") - thread_handler(threading.Thread(target=create, args=(), name="cloud backup")) + thread_handler(Thread(target=create, args=(), name="cloud backup")) def create_backup_and_send_to_cloud(self): if self.backup_cloud is not None: diff --git a/packages/helpermodules/timecheck.py b/packages/helpermodules/timecheck.py index 02b116dec1..8b42a97d8e 100644 --- a/packages/helpermodules/timecheck.py +++ b/packages/helpermodules/timecheck.py @@ -1,9 +1,8 @@ """prüft, ob Zeitfenster aktuell sind """ -import copy import logging import datetime -from typing import Dict, List, Optional, Tuple, TypeVar, Union +from typing import List, Optional, Tuple, TypeVar, Union from helpermodules.utils.error_handling import ImportErrorContext with ImportErrorContext(): @@ -47,12 +46,12 @@ def is_now_in_locking_time(now: datetime.datetime, T = TypeVar("T", AutolockPlan, TimeChargingPlan) -def check_plans_timeframe(plans: Dict[int, T]) -> Optional[T]: +def check_plans_timeframe(plans: List[T]) -> Optional[T]: """ gibt den ersten aktiven Plan zurück. None, falls kein Plan aktiv ist. """ state = False try: - for plan in plans.values(): + for plan in plans: if plan.active: state = check_timeframe(plan) if state: @@ -113,81 +112,63 @@ def is_timeframe_valid(now: datetime.datetime, begin: datetime.datetime, end: da return state -def check_duration(plan: ScheduledChargingPlan, duration: float, buffer: int) -> Tuple[Optional[float], bool]: +def check_end_time(plan: ScheduledChargingPlan, + chargemode_switch_timestamp: Optional[float]) -> Optional[float]: """ prüft, ob der in angegebene Zeitpunkt abzüglich der Dauer jetzt ist. - Um etwas Puffer zu haben, werden bei Überschreiten des Zeitpunkts die nachfolgenden 20 Min auch noch als Ladezeit - zurückgegeben. Return ------ neg: Zeitpunkt vorbei pos: verbleibende Sekunden """ - + def missed_date_still_active(remaining_time: float) -> bool: + return (chargemode_switch_timestamp and + remaining_time.total_seconds() < 0 and + end.timestamp() < chargemode_switch_timestamp) now = datetime.datetime.today() end = datetime.datetime.strptime(plan.time, '%H:%M') remaining_time = None if plan.frequency.selected == "once": endDate = datetime.datetime.strptime(plan.frequency.once, "%Y-%m-%d") end = end.replace(endDate.year, endDate.month, endDate.day) - remaining_time = _get_remaining_time(now, duration, end) + remaining_time = end - now elif plan.frequency.selected == "daily": end = end.replace(now.year, now.month, now.day) - remaining_time_today = _get_remaining_time(now, duration, end) - remaining_time, end = check_following_days(now, duration, end, remaining_time_today, buffer) + remaining_time = end - now + if missed_date_still_active(remaining_time): + # Wenn auf Zielladen umgeschaltet wurde und der Termin noch nicht vorbei war, noch auf diesen Termin laden. + end = end + datetime.timedelta(days=1) + remaining_time = end - now elif plan.frequency.selected == "weekly": - end = end.replace(now.year, now.month, now.day) - if plan.frequency.weekly[now.weekday()]: - remaining_time = _get_remaining_time(now, duration, end) - # prüfen, ob für den nächsten Tag ein Termin ansteht und heute schon begonnen werden muss if not any(plan.frequency.weekly): raise ValueError("Es muss mindestens ein Tag ausgewählt werden.") - num_of_following_days = _get_next_charging_day(copy.deepcopy(plan.frequency.weekly), now.weekday()) - remaining_time, end = check_following_days(now, duration, end, remaining_time, buffer, num_of_following_days) + end = end.replace(now.year, now.month, now.day) + end += datetime.timedelta(days=_get_next_charging_day(plan.frequency.weekly, now.weekday())) + remaining_time = end - now + if missed_date_still_active(remaining_time): + end = end.replace(now.year, now.month, now.day) + end += datetime.timedelta(days=_get_next_charging_day(plan.frequency.weekly, now.weekday()+1)+1) + remaining_time = end - now else: raise TypeError(f'Unbekannte Häufigkeit {plan.frequency.selected}') - return remaining_time, _missed_date_today(now, end, buffer) - - -def _get_next_charging_day(weekly_temp: List[bool], weekday: int) -> int: - weekly_temp[weekday] = False - try: - return (weekly_temp[weekday:] + weekly_temp[:weekday]).index(True) - except ValueError: - # Es wird nur am Wochentag von weekday geladen. - return 7 - - -def _missed_date_today(now: datetime.datetime, - end: datetime.datetime, - buffer: float): - return end < now + datetime.timedelta(seconds=buffer) - - -def check_following_days(now: datetime.datetime, - duration: float, - end: datetime.datetime, - remaining_time_today: Optional[float], - buffer: float, - num_of_following_days: int = 1) -> Tuple[Optional[float], datetime.datetime]: - # Zeitpunkt heute darf noch nicht verstrichen sein - if remaining_time_today and not _missed_date_today(now, end, buffer): - return remaining_time_today, end - end = end+datetime.timedelta(days=num_of_following_days) - remaining_time = _get_remaining_time(now, duration, end) - return remaining_time, end - - -def _get_remaining_time(now: datetime.datetime, duration: float, end: datetime.datetime) -> float: - """ Return - ------ - neg: Zeitpunkt vorbei - pos: verbleibende Sekunden - """ - delta = datetime.timedelta(seconds=duration) - start_time = end-delta - log.debug(f"delta {delta} start_time {start_time} end {end} now {now}") - return (start_time-now).total_seconds() + if chargemode_switch_timestamp and end.timestamp() < chargemode_switch_timestamp: + # Als auf Zielladen umgeschaltet wurde, war der Termin schon vorbei + return None + else: + return remaining_time.total_seconds() + + +def _get_next_charging_day(weekly: List[bool], weekday: int) -> int: + count = 0 + for i in range(weekday, len(weekly)): + if weekly[i] is True: + return count + count += 1 + for i in range(0, weekday): + if weekly[i] is True: + return count + count += 1 + return count def is_list_valid(hour_list: List[int]) -> bool: diff --git a/packages/helpermodules/timecheck_test.py b/packages/helpermodules/timecheck_test.py index 0f602383ae..d45f0b8227 100644 --- a/packages/helpermodules/timecheck_test.py +++ b/packages/helpermodules/timecheck_test.py @@ -3,9 +3,9 @@ from unittest.mock import MagicMock, Mock import pytest -from control.ev.charge_template import ChargeTemplate from helpermodules import timecheck -from helpermodules.abstract_plans import AutolockPlan, Frequency, ScheduledChargingPlan, TimeChargingPlan +from helpermodules.abstract_plans import (AutolockPlan, FrequencyDate, FrequencyPeriod, ScheduledChargingPlan, + TimeChargingPlan) class Params: @@ -20,54 +20,42 @@ def __init__(self, name: str, self.second_time = second_time -@pytest.mark.parametrize("begin_hour, begin_min, end_hour, end_min,expected", - [pytest.param(0, 0, 5, 5, 9300, id="too early"), - pytest.param(8, 18, 10, 35, -780, id="start"), - pytest.param(18, 8, 17, 24, -11640, id="missed date") - ]) -def test_get_remaining_time(begin_hour: int, begin_min: int, end_hour: int, end_min: int, expected: int): - # setup - end = datetime.datetime(2022, 9, 26, end_hour, end_min) - begin = datetime.datetime(2022, 9, 26, begin_hour, begin_min) - - # execution - diff = timecheck._get_remaining_time(begin, 9000, end) - - # evaluation - assert expected == diff - - -@pytest.mark.parametrize("time, selected, date, expected", - [pytest.param("9:00", "once", "2022-05-16", (-7852.0, False), id="once"), - pytest.param("8:00", "once", "2022-05-16", (-11452.0, True), id="once missed date"), - pytest.param("12:00", "daily", [], (2948.0, False), id="daily today"), - pytest.param("2:00", "daily", [], (53348.0, False), id="daily missed today, use next day"), - pytest.param("8:05", "weekly", [True, False, False, False, - False, False, False], (593648.0, False), id="weekly missed today"), +@pytest.mark.parametrize("time, selected, date, expected_remaining_time", + [pytest.param("9:00", "once", "2022-05-16", 1148, id="once"), + pytest.param("7:55", "once", "2022-05-16", None, id="missed date, plugged before"), + pytest.param("8:05", "once", "2022-05-16", - + 2152, id="once missed date, plugged after"), + pytest.param("12:00", "daily", [], 11948, id="daily today"), + pytest.param("2:00", "daily", [], 62348, id="daily missed today, use next day"), + pytest.param("7:55", "weekly", [True, False, False, False, + False, False, False], 602048, id="weekly missed today"), pytest.param("2:00", "weekly", [False, False, True, False, False, False, False], - (139748.0, False), - id="weekly missed today's date, no date on next day"), + 148748, id="weekly missed today's date, no date on next day"), pytest.param("2:00", "weekly", [True, True, False, False, False, False, False], - (53348.0, False), - id="weekly missed today's date, date on next day"), + 62348, id="weekly missed today's date, date on next day"), ] ) -def test_check_duration(time: str, selected: str, date: List, expected: float): +def test_check_end_time(time: str, + selected: str, + date: List, + expected_remaining_time: float): # setup - plan = Mock(spec=ScheduledChargingPlan, time=time, frequency=Mock(spec=Frequency, selected=selected,)) + plan = Mock(spec=ScheduledChargingPlan, time=time, frequency=Mock(spec=FrequencyDate, selected=selected,)) if date: setattr(plan.frequency, selected, date) # execution - remaining_time, missed_date_today = timecheck.check_duration(plan, 9000, ChargeTemplate.BUFFER) + remaining_time = timecheck.check_end_time( + plan, + chargemode_switch_timestamp=datetime.datetime.strptime("5/16/2022 8:00", "%m/%d/%Y %H:%M").timestamp()) # evaluation - assert (remaining_time, missed_date_today) == expected + assert remaining_time == expected_remaining_time @pytest.mark.parametrize("weekday, weekly, expected_days", - [pytest.param(0, [True, True, False, False, False, False, False], 1), - pytest.param(0, [True, False, False, False, False, False, False], 7), + [pytest.param(0, [True, True, False, False, False, False, False], 0), + pytest.param(1, [True, False, False, False, False, False, False], 6), pytest.param(3, [True, False, False, False, False, False, False], 4), ] ) @@ -79,62 +67,45 @@ def test_get_next_charging_day(weekday: int, weekly: List[bool], expected_days: assert days == expected_days -@pytest.mark.parametrize("now, end, expected_missed", - [pytest.param(datetime.datetime(2022, 9, 29, 8, 40), - datetime.datetime(2022, 9, 29, 9, 5), False), - pytest.param(datetime.datetime(2022, 9, 29, 8, 40), - datetime.datetime(2022, 9, 29, 8, 39), False), - pytest.param(datetime.datetime(2022, 9, 29, 8, 40), - datetime.datetime(2022, 9, 29, 8, 19), True) - ] - ) -def test_missed_date_today(now: datetime.datetime, end: datetime.datetime, expected_missed: bool): - # setup and execution - missed = timecheck._missed_date_today(now, end, -1200) - - # evaluation - assert missed == expected_missed - - @pytest.mark.parametrize( "plan, now, expected_state", [pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 9:50", False, id="once, 10-12 at 9.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 10:50", True, id="once, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="once", once=["2022-05-16", "2022-05-16"])), + frequency=FrequencyPeriod(selected="once", once=["2022-05-16", "2022-05-16"])), "2022-05-16 13:50", False, id="once, 10-12 at 13.50"), - pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-16 11:50", True, id="daily, 10-12 at 11.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-16 22:50", True, id="daily, 22-02 at 22.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-17 1:50", True, id="daily, 22-02 at 1.50"), - pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=Frequency(selected="daily")), + pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], frequency=FrequencyPeriod(selected="daily")), "2022-05-17 2:50", False, id="daily, 22-02 at 2.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 10:50", True, id="weekly, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["10:00", "12:00"], - frequency=Frequency(selected="weekly", weekly=[0, 1, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[0, 1, 0, 0, 0, 0, 0])), "2022-05-16 10:50", False, id="weekly, 10-12 at 10.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 22:50", True, id="weekly, 22-02 at 22.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-17 1:50", True, id="weekly, 22-02 at 1.50"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-16 1:50", False, id="weekly, 22-02 at 1.50, weekday before"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-18 1:50", False, id="weekly, 22-02 at 1.50, weekday after"), pytest.param(TimeChargingPlan(active=True, time=["22:00", "2:00"], - frequency=Frequency(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), + frequency=FrequencyPeriod(selected="weekly", weekly=[1, 0, 0, 0, 0, 0, 0])), "2022-05-17 2:50", False, id="weekly, 22-02 at 2.50"), ] ) diff --git a/packages/helpermodules/update_config.py b/packages/helpermodules/update_config.py index 1ef2073582..b51e6dbf9c 100644 --- a/packages/helpermodules/update_config.py +++ b/packages/helpermodules/update_config.py @@ -1,6 +1,8 @@ +import copy from dataclasses import asdict import datetime import glob +import importlib import json import logging from pathlib import Path @@ -9,12 +11,15 @@ from typing import List, Optional from paho.mqtt.client import Client as MqttClient, MQTTMessage +from control.limiting_value import LoadmanagementLimit import dataclass_utils from control.chargepoint.chargepoint_template import get_chargepoint_template_default from helpermodules import timecheck from helpermodules import hardware_configuration -from helpermodules.broker import InternalBrokerClient +from helpermodules import pub +from helpermodules.broker import BrokerClient +from helpermodules.abstract_plans import Limit from helpermodules.constants import NO_ERROR from helpermodules.hardware_configuration import ( get_hardware_configuration_setting, @@ -32,7 +37,7 @@ from control.bat_all import BatConsiderationMode from control.chargepoint.charging_type import ChargingType from control.counter import get_counter_default_config -from control.ev.charge_template import get_charge_template_default +from control.ev.charge_template import EcoCharging, get_charge_template_default from control.ev import ev from control.ev.ev_template import EvTemplateData from control.general import ChargemodeConfig, Prices @@ -41,7 +46,7 @@ from modules.common.component_type import ComponentType from modules.devices.sungrow.sungrow.version import Version from modules.display_themes.cards.config import CardsDisplayTheme -from modules.ripple_control_receivers.gpio.config import GpioRcr +from modules.io_actions.controllable_consumers.ripple_control_receiver.config import RippleControlReceiverSetup from modules.web_themes.standard_legacy.config import StandardLegacyWebTheme from modules.devices.good_we.good_we.version import GoodWeVersion @@ -51,8 +56,11 @@ class UpdateConfig: - DATASTORE_VERSION = 75 + + DATASTORE_VERSION = 93 + valid_topic = [ + "^openWB/bat/config/bat_control_permitted$", "^openWB/bat/config/configured$", "^openWB/bat/config/power_limit_mode$", "^openWB/bat/set/charging_power_left$", @@ -83,23 +91,30 @@ class UpdateConfig: "^openWB/chargepoint/get/daily_exported$", "^openWB/chargepoint/get/daily_imported$", "^openWB/chargepoint/template/[0-9]+$", - "^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", + "^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", # OBSOLET seit 2.1.8 "^openWB/chargepoint/[0-9]+/config$", - "^openWB/chargepoint/[0-9]+/control_parameter/submode$", "^openWB/chargepoint/[0-9]+/control_parameter/chargemode$", "^openWB/chargepoint/[0-9]+/control_parameter/current_plan$", + "^openWB/chargepoint/[0-9]+/control_parameter/failed_phase_switches$", "^openWB/chargepoint/[0-9]+/control_parameter/imported_at_plan_start$", "^openWB/chargepoint/[0-9]+/control_parameter/imported_instant_charging$", "^openWB/chargepoint/[0-9]+/control_parameter/limit$", + "^openWB/chargepoint/[0-9]+/control_parameter/min_current$", + "^openWB/chargepoint/[0-9]+/control_parameter/phases$", "^openWB/chargepoint/[0-9]+/control_parameter/prio$", "^openWB/chargepoint/[0-9]+/control_parameter/required_current$", + "^openWB/chargepoint/[0-9]+/control_parameter/required_currents$", + "^openWB/chargepoint/[0-9]+/control_parameter/state$", + "^openWB/chargepoint/[0-9]+/control_parameter/submode$", + "^openWB/chargepoint/[0-9]+/control_parameter/timestamp_charge_start$", + "^openWB/chargepoint/[0-9]+/control_parameter/timestamp_chargemode_changed$", "^openWB/chargepoint/[0-9]+/control_parameter/timestamp_last_phase_switch$", "^openWB/chargepoint/[0-9]+/control_parameter/timestamp_switch_on_off$", - "^openWB/chargepoint/[0-9]+/control_parameter/used_amount_instant_charging$", - "^openWB/chargepoint/[0-9]+/control_parameter/phases$", - "^openWB/chargepoint/[0-9]+/control_parameter/state$", "^openWB/chargepoint/[0-9]+/get/charge_state$", "^openWB/chargepoint/[0-9]+/get/currents$", + "^openWB/chargepoint/[0-9]+/get/current_branch$", + "^openWB/chargepoint/[0-9]+/get/current_commit$", + "^openWB/chargepoint/[0-9]+/get/error_timestamp$", "^openWB/chargepoint/[0-9]+/get/evse_current$", "^openWB/chargepoint/[0-9]+/get/fault_state$", "^openWB/chargepoint/[0-9]+/get/fault_str$", @@ -115,6 +130,7 @@ class UpdateConfig: "^openWB/chargepoint/[0-9]+/get/powers$", "^openWB/chargepoint/[0-9]+/get/power_factors$", "^openWB/chargepoint/[0-9]+/get/vehicle_id$", + "^openWB/chargepoint/[0-9]+/get/version$", "^openWB/chargepoint/[0-9]+/get/voltages$", "^openWB/chargepoint/[0-9]+/get/serial_number$", "^openWB/chargepoint/[0-9]+/get/soc$", @@ -127,9 +143,11 @@ class UpdateConfig: "^openWB/chargepoint/[0-9]+/get/rfid$", "^openWB/chargepoint/[0-9]+/get/rfid_timestamp$", "^openWB/chargepoint/[0-9]+/set/charging_ev$", + "^openWB/chargepoint/[0-9]+/set/charge_template$", "^openWB/chargepoint/[0-9]+/set/current$", "^openWB/chargepoint/[0-9]+/set/energy_to_charge$", "^openWB/chargepoint/[0-9]+/set/manual_lock$", + "^openWB/chargepoint/[0-9]+/set/charge_state_prev$", "^openWB/chargepoint/[0-9]+/set/plug_state_prev$", "^openWB/chargepoint/[0-9]+/set/plug_time$", "^openWB/chargepoint/[0-9]+/set/rfid$", @@ -147,6 +165,8 @@ class UpdateConfig: "^openWB/command/max_id/device$", "^openWB/command/max_id/ev_template$", "^openWB/command/max_id/hierarchy$", + "^openWB/command/max_id/io_action$", + "^openWB/command/max_id/io_device$", "^openWB/command/max_id/mqtt_bridge$", "^openWB/command/max_id/vehicle$", "^openWB/command/[A-Za-z0-9_]+/error$", @@ -200,12 +220,6 @@ class UpdateConfig: "^openWB/general/notifications/stop_charging$", "^openWB/general/notifications/plug$", "^openWB/general/notifications/smart_home$", - "^openWB/general/ripple_control_receiver/configured$", - "^openWB/general/ripple_control_receiver/module$", - "^openWB/general/ripple_control_receiver/get/fault_state$", - "^openWB/general/ripple_control_receiver/get/fault_str$", - "^openWB/general/ripple_control_receiver/get/override_value$", - "^openWB/general/ripple_control_receiver/override_reference$", "^openWB/general/chargemode_config/unbalanced_load_limit$", "^openWB/general/chargemode_config/unbalanced_load$", "^openWB/general/chargemode_config/pv_charging/bat_mode$", @@ -216,17 +230,12 @@ class UpdateConfig: "^openWB/general/chargemode_config/pv_charging/switch_off_delay$", "^openWB/general/chargemode_config/phase_switch_delay$", "^openWB/general/chargemode_config/pv_charging/control_range$", - "^openWB/general/chargemode_config/pv_charging/phases_to_use$", "^openWB/general/chargemode_config/pv_charging/min_bat_soc$", "^openWB/general/chargemode_config/pv_charging/bat_power_discharge$", "^openWB/general/chargemode_config/pv_charging/bat_power_discharge_active$", "^openWB/general/chargemode_config/pv_charging/bat_power_reserve$", "^openWB/general/chargemode_config/pv_charging/bat_power_reserve_active$", "^openWB/general/chargemode_config/retry_failed_phase_switches$", - "^openWB/general/chargemode_config/scheduled_charging/phases_to_use$", - "^openWB/general/chargemode_config/scheduled_charging/phases_to_use_pv$", - "^openWB/general/chargemode_config/instant_charging/phases_to_use$", - "^openWB/general/chargemode_config/time_charging/phases_to_use$", # obsolet, Daten hieraus müssen nach prices/ überführt werden "^openWB/general/price_kwh$", "^openWB/general/prices/bat$", @@ -244,6 +253,60 @@ class UpdateConfig: "^openWB/internal_chargepoint/[0-1]/data/set_current$", "^openWB/internal_chargepoint/[0-1]/data/phases_to_use$", "^openWB/internal_chargepoint/[0-1]/data/parent_cp$", + "^openWB/internal_chargepoint/[0-1]/get/charge_state$", + "^openWB/internal_chargepoint/[0-1]/get/currents$", + "^openWB/internal_chargepoint/[0-1]/get/current_branch$", + "^openWB/internal_chargepoint/[0-1]/get/current_commit$", + "^openWB/internal_chargepoint/[0-1]/get/error_timestamp$", + "^openWB/internal_chargepoint/[0-1]/get/evse_current$", + "^openWB/internal_chargepoint/[0-1]/get/fault_state$", + "^openWB/internal_chargepoint/[0-1]/get/fault_str$", + "^openWB/internal_chargepoint/[0-1]/get/frequency$", + "^openWB/internal_chargepoint/[0-1]/get/max_evse_current$", + "^openWB/internal_chargepoint/[0-1]/get/plug_state$", + "^openWB/internal_chargepoint/[0-1]/get/phases_in_use$", + "^openWB/internal_chargepoint/[0-1]/get/exported$", + "^openWB/internal_chargepoint/[0-1]/get/imported$", + "^openWB/internal_chargepoint/[0-1]/get/power$", + "^openWB/internal_chargepoint/[0-1]/get/powers$", + "^openWB/internal_chargepoint/[0-1]/get/power_factors$", + "^openWB/internal_chargepoint/[0-1]/get/vehicle_id$", + "^openWB/internal_chargepoint/[0-1]/get/version$", + "^openWB/internal_chargepoint/[0-1]/get/voltages$", + "^openWB/internal_chargepoint/[0-1]/get/serial_number$", + "^openWB/internal_chargepoint/[0-1]/get/soc$", + "^openWB/internal_chargepoint/[0-1]/get/soc_timestamp$", + "^openWB/internal_chargepoint/[0-1]/get/simulation$", + "^openWB/internal_chargepoint/[0-1]/get/state_str$", + "^openWB/internal_chargepoint/[0-1]/get/rfid$", + "^openWB/internal_chargepoint/[0-1]/get/rfid_timestamp$", + + "^openWB/io/states/[0-9]+/get/digital_input$", + "^openWB/io/states/[0-9]+/get/analog_input$", + "^openWB/io/states/[0-9]+/set/digital_output$", + "^openWB/io/states/[0-9]+/set/analog_output$", + "^openWB/io/action/[0-9]+/config$", + "^openWB/io/action/[0-9]+/timestamp$", + + "^openWB/mqtt/bat/[0-9]+/get/power$", + "^openWB/mqtt/bat/[0-9]+/get/soc$", + "^openWB/mqtt/bat/[0-9]+/get/imported$", + "^openWB/mqtt/bat/[0-9]+/get/exported$", + "^openWB/mqtt/counter/[0-9]+/get/currents$", + "^openWB/mqtt/counter/[0-9]+/get/imported$", + "^openWB/mqtt/counter/[0-9]+/get/exported$", + "^openWB/mqtt/counter/[0-9]+/get/power$", + "^openWB/mqtt/counter/[0-9]+/get/frequency$", + "^openWB/mqtt/counter/[0-9]+/get/power_factors$", + "^openWB/mqtt/counter/[0-9]+/get/powers$", + "^openWB/mqtt/counter/[0-9]+/get/voltages$", + "^openWB/mqtt/inverter/[0-9]+/get/currents$", + "^openWB/mqtt/inverter/[0-9]+/get/power$", + "^openWB/mqtt/inverter/[0-9]+/get/exported$", + "^openWB/mqtt/inverter/[0-9]+/get/dc_power$", + "^openWB/mqtt/vehicle/[0-9]+/get/range$", + "^openWB/mqtt/vehicle/[0-9]+/get/soc$", + "^openWB/mqtt/vehicle/[0-9]+/get/soc_timestamp$", "^openWB/set/log/request", "^openWB/set/log/data", @@ -287,7 +350,8 @@ class UpdateConfig: "^openWB/vehicle/set/vehicle_update_completed$", "^openWB/vehicle/template/ev_template/[0-9]+$", - "^openWB/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", + "^openWB/vehicle/template/charge_template/[0-9]+/time_charging/plans/[0-9]+$", # OBSOLET seit 2.1.8 + # OBSOLET seit 2.1.8 "^openWB/vehicle/template/charge_template/[0-9]+/chargemode/scheduled_charging/plans/[0-9]+$", "^openWB/vehicle/template/charge_template/[0-9]+", "^openWB/vehicle/[0-9]+/charge_template$", @@ -409,8 +473,9 @@ class UpdateConfig: "^openWB/system/configurable/devices_components$", "^openWB/system/configurable/electricity_tariffs$", "^openWB/system/configurable/display_themes$", + "^openWB/system/configurable/io_actions$", + "^openWB/system/configurable/io_devices$", "^openWB/system/configurable/monitoring$", - "^openWB/system/configurable/ripple_control_receivers$", "^openWB/system/configurable/soc_modules$", "^openWB/system/configurable/web_themes$", "^openWB/system/current_branch", @@ -429,17 +494,20 @@ class UpdateConfig: "^openWB/system/device/[0-9]+/component/[0-9]+/simulation/timestamp_present$", "^openWB/system/device/[0-9]+/config$", "^openWB/system/device/module_update_completed$", + "^openWB/system/io/[0-9]+/config$", "^openWB/system/ip_address$", "^openWB/system/lastlivevaluesJson$", "^openWB/system/mqtt/bridge/[0-9]+$", "^openWB/system/mqtt/valid_partner_ids$", "^openWB/system/release_train$", + "^openWB/system/secondary_auto_update$", "^openWB/system/time$", "^openWB/system/update_in_progress$", "^openWB/system/usage_terms_acknowledged$", "^openWB/system/version$", ] default_topic = ( + ("openWB/bat/config/bat_control_permitted", False), ("openWB/bat/config/configured", False), ("openWB/bat/config/power_limit_mode", "no_limit"), ("openWB/bat/get/fault_state", 0), @@ -452,17 +520,16 @@ class UpdateConfig: ("openWB/counter/config/home_consumption_source_id", counter_all.Config().home_consumption_source_id), ("openWB/vehicle/0/name", "Standard-Fahrzeug"), ("openWB/vehicle/0/info", {"manufacturer": None, "model": None}), - ("openWB/vehicle/0/charge_template", ev.Ev(0).charge_template.ct_num), + ("openWB/vehicle/0/charge_template", ev.Ev(0).charge_template.data.id), ("openWB/vehicle/0/soc_module/config", NO_MODULE), ("openWB/vehicle/0/soc_module/general_config", dataclass_utils.asdict(GeneralVehicleConfig())), - ("openWB/vehicle/0/ev_template", ev.Ev(0).ev_template.et_num), + ("openWB/vehicle/0/ev_template", ev.Ev(0).ev_template.data.id), ("openWB/vehicle/0/tag_id", ev.Ev(0).data.tag_id), ("openWB/vehicle/0/get/soc", ev.Ev(0).data.get.soc), ("openWB/vehicle/template/ev_template/0", asdict(EvTemplateData(name="Standard-Fahrzeug-Profil", min_current=10))), ("openWB/vehicle/template/charge_template/0", get_charge_template_default()), ("openWB/general/charge_log_data_config", get_default_charge_log_columns()), - ("openWB/general/chargemode_config/instant_charging/phases_to_use", 3), ("openWB/general/chargemode_config/pv_charging/bat_mode", BatConsiderationMode.EV_MODE.value), ("openWB/general/chargemode_config/pv_charging/bat_power_discharge", 1000), ("openWB/general/chargemode_config/pv_charging/bat_power_discharge_active", True), @@ -476,12 +543,8 @@ class UpdateConfig: ("openWB/general/chargemode_config/pv_charging/switch_on_threshold", 1500), ("openWB/general/chargemode_config/pv_charging/feed_in_yield", 0), ("openWB/general/chargemode_config/phase_switch_delay", 7), - ("openWB/general/chargemode_config/pv_charging/phases_to_use", 0), ("openWB/general/chargemode_config/retry_failed_phase_switches", ChargemodeConfig().retry_failed_phase_switches), - ("openWB/general/chargemode_config/scheduled_charging/phases_to_use", 0), - ("openWB/general/chargemode_config/scheduled_charging/phases_to_use_pv", 0), - ("openWB/general/chargemode_config/time_charging/phases_to_use", 1), ("openWB/general/chargemode_config/unbalanced_load", False), ("openWB/general/chargemode_config/unbalanced_load_limit", 18), ("openWB/general/control_interval", 10), @@ -501,7 +564,6 @@ class UpdateConfig: ("openWB/general/prices/grid", Prices().grid), ("openWB/general/prices/pv", Prices().pv), ("openWB/general/range_unit", "km"), - ("openWB/general/ripple_control_receiver/module", NO_MODULE), ("openWB/general/web_theme", dataclass_utils.asdict(StandardLegacyWebTheme())), ("openWB/graph/config/duration", 120), ("openWB/internal_chargepoint/0/data/parent_cp", None), @@ -531,6 +593,7 @@ class UpdateConfig: ("openWB/system/ip_address", "unknown"), ("openWB/system/mqtt/valid_partner_ids", []), ("openWB/system/release_train", "master"), + ("openWB/system/secondary_auto_update", True), ("openWB/system/serial_number", get_serial_number()), ) invalid_topic = ( @@ -554,7 +617,7 @@ def __init__(self) -> None: def update(self): log.debug("Broker-Konfiguration aktualisieren") - InternalBrokerClient("update-config", self.on_connect, self.on_message).start_finite_loop() + BrokerClient("update-config", self.on_connect, self.on_message).start_finite_loop() try: # erst breaking changes auflösen, sonst sind alte Topics schon gelöscht self.__solve_breaking_changes() @@ -853,7 +916,7 @@ def upgrade(topic: str, payload) -> Optional[dict]: if re.search("openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None: payload = decode_payload(payload) if "keep_charge_active_duration" not in payload: - payload["keep_charge_active_duration"] = ev.EvTemplateData().keep_charge_active_duration + payload["keep_charge_active_duration"] = EvTemplateData().keep_charge_active_duration return {topic: payload} self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 8) @@ -1322,11 +1385,11 @@ def convert_file(file): convert_file(file) self.__update_topic("openWB/system/datastore_version", 36) - def upgrade_datastore_36(self) -> None: - if hardware_configuration.get_hardware_configuration_setting("ripple_control_receiver_configured", False): - Pub().pub("openWB/set/general/ripple_control_receiver/module", dataclass_utils.asdict(GpioRcr())) - hardware_configuration.remove_setting_hardware_configuration("ripple_control_receiver_configured") - self.__update_topic("openWB/system/datastore_version", 37) + # def upgrade_datastore_36(self) -> None: + # if hardware_configuration.get_hardware_configuration_setting("ripple_control_receiver_configured", False): + # Pub().pub("openWB/set/general/ripple_control_receiver/module", dataclass_utils.asdict(GpioRcr())) + # hardware_configuration.remove_setting_hardware_configuration("ripple_control_receiver_configured") + # self.__update_topic("openWB/system/datastore_version", 37) def upgrade_datastore_37(self) -> None: def collect_names(topic: str, payload) -> None: @@ -1957,3 +2020,408 @@ def upgrade(topic: str, payload) -> None: Pub().pub(topic, payload) self._loop_all_received_topics(upgrade) self.__update_topic("openWB/system/datastore_version", 75) + + def upgrade_datastore_75(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if "openWB/general/ripple_control_receiver/module" == topic: + payload = decode_payload(payload) + if payload["type"] is not None: + if payload["type"] == "gpio": + dev = importlib.import_module(".io_devices.add_on.api", "modules") + else: + dev = importlib.import_module(".io_devices."+payload["type"]+".api", "modules") + io_device = dev.device_descriptor.configuration_factory() + + action = RippleControlReceiverSetup() + for cp_topic in self.all_received_topics.keys(): + if re.search("openWB/chargepoint/[0-9]+/config", cp_topic) is not None: + action.configuration.devices.append([f"cp{int(get_index(cp_topic))}"]) + action.configuration.io_device = 0 + + if payload["type"] == "dimm_kit": + io_device.configuration.host = payload["configuration"]["ip_address"] + io_device.configuration.port = payload["configuration"]["port"] + io_device.configuration.modbus_id = payload["configuration"]["modbus_id"] + # Wenn mindestens ein Kontakt offen ist, wird die Ladung gesperrt. Wenn beide Kontakte + # geschlossen sind, darf geladen werden. + action.configuration.input_pattern = [ + {"value": 0, "input_matrix": {"DI1": False, "DI2": False}}, + {"value": 0, "input_matrix": {"DI1": False, "DI2": True}}, + {"value": 0, "input_matrix": {"DI1": True, "DI2": False}}, + {"value": 1, "input_matrix": {"DI1": True, "DI2": True}}] + elif payload["type"] == "gpio": + io_device.configuration.host = "localhost" + # Wenn mindestens ein Kontakt geschlossen ist, wird die Ladung gesperrt. Wenn beide Kontakt + # offen sind, darf geladen werden. + action.configuration.input_pattern = [ + {"value": 1, "input_matrix": {"RSE1": False, "RSE2": False}}, + {"value": 0, "input_matrix": {"RSE1": False, "RSE2": True}}, + {"value": 0, "input_matrix": {"RSE1": True, "RSE2": False}}, + {"value": 0, "input_matrix": {"RSE1": True, "RSE2": True}}] + + return {'openWB/system/io/0/config': dataclass_utils.asdict(io_device), + 'openWB/io/action/0/config': dataclass_utils.asdict(action)} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 76) + + def upgrade_datastore_76(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/chargepoint/[0-9]+/control_parameter/limit", topic) is not None: + return {topic: dataclass_utils.asdict(LoadmanagementLimit(None, None))} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 77) + + def upgrade_datastore_77(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + # add "official" flag to selected backup cloud + if re.search("openWB/system/backup_cloud/config", topic) is not None: + configuration_payload = decode_payload(payload) + if configuration_payload.get("type") == "nextcloud": + configuration_payload.update({"official": True}) + return {topic: configuration_payload} + # add "official" flag to selected electricity tariff provider + if re.search("openWB/optional/et/provider", topic) is not None: + configuration_payload = decode_payload(payload) + official_providers = ["awattar", "energycharts", "rabot", "tibber", "voltego"] + if configuration_payload.get("type") in official_providers: + configuration_payload.update({"official": True}) + return {topic: configuration_payload} + # add "official" flag to selected monitoring module + if re.search("openWB/optional/monitoring/config", topic) is not None: + configuration_payload = decode_payload(payload) + if configuration_payload.get("type") == "zabbix": + configuration_payload.update({"official": True}) + return {topic: configuration_payload} + # add "official" flag to selected vehicle modules + if re.search("openWB/vehicle/[0-9]+/soc_module/config", topic) is not None: + configuration_payload = decode_payload(payload) + official_vehicle_modules = ["http", "json", "manual", "mqtt", "tronity"] + if configuration_payload.get("type") in official_vehicle_modules: + configuration_payload.update({"official": True}) + return {topic: configuration_payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 78) + + def upgrade_datastore_78(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + for topic, payload in self.all_received_topics.items(): + if (re.search("openWB/system/device/[0-9]+", topic) is not None or + re.search("openWB/chargepoint/[0-9]+/config", topic) is not None): + payload = decode_payload(payload) + if payload.get("type") == "mqtt": + pub_system_message( + {}, "Die Topics für MQTT-Komponenten und MQTT-Ladepunkte wurden angepasst. Bitte " + "aktualisiere die Topics in Deinen angebundenen (Smarthome-)Systemen.", MessageType.WARNING) + # Nachricht nur einmal senden + break + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 79) + + def upgrade_datastore_79(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + # Simcount-Topic löschen, damit ein neuer Simcount mit dem akutellen Zählerstand des virtuellen Zählers + # gestartet wird. Akutell ist nur der feste Verbrauch im Simcount. + if re.search("openWB/system/device/[0-9]+/config", topic) is not None: + device_config = decode_payload(payload) + if device_config.get("type") == "virtual": + for component_topic, component_payload in self.all_received_topics.items(): + if re.search(f"openWB/system/device/{device_config['id']}/component/[0-9]+/config", + component_topic): + component_config = decode_payload(component_payload) + if "counter" == component_config["type"]: + Pub().pub((f"openWB/system/device/{device_config['id']}/component/" + f"{component_config['id']}/simulation"), "") + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 80) + + # moved and corrected to 87 + + def upgrade_datastore_81(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/chargepoint/[0-9]+/config", topic) is not None: + topics = {} + payload = decode_payload(payload) + index = get_index(topic) + charge_template_id = decode_payload( + self.all_received_topics[f'openWB/vehicle/{payload["ev"]}/charge_template']) + for template_topic, template_payload in self.all_received_topics.items(): + if f'openWB/vehicle/template/charge_template/{charge_template_id}' in template_topic: + topics.update( + {template_topic.replace(f'openWB/vehicle/template/charge_template/{charge_template_id}', + f"openWB/chargepoint/{index}/set/charge_template"): + decode_payload(template_payload)}) + return topics + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 82) + + def upgrade_datastore_82(self) -> None: + def upgrade(topic: str, payload) -> None: + def get_new_phases_to_use(topic) -> int: + return min(max_phases_ev, decode_payload(self.all_received_topics[topic])) + topics = {} + if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None: + index = int(get_index(topic)) + # Die Phasenauswahl im Fahrzeugprofil entfällt mit der Phaseneinstellung im Ladeprofil anstelle der + # globalen Phasen. + max_phases_ev = 3 + for ev_topic, ev_payload in self.all_received_topics.items(): + if re.search("openWB/vehicle/[0-9]+/charge_template$", ev_topic) is not None: + assigned_charge_template = decode_payload(ev_payload) + if assigned_charge_template == index: + ev_index = get_index(ev_topic) + ev_template_id = decode_payload( + self.all_received_topics[f"openWB/vehicle/{ev_index}/ev_template"]) + ev_phases = decode_payload( + self.all_received_topics[ + f"openWB/vehicle/template/ev_template/{ev_template_id}"])["max_phases"] + if ev_phases == 1: + max_phases_ev = ev_phases + + payload = decode_payload(payload) + charge_template = copy.deepcopy(payload) + charge_template["chargemode"]["eco_charging"] = dataclass_utils.asdict(EcoCharging()) + if payload["chargemode"]["selected"] == "standby": + charge_template["chargemode"]["selected"] = "stop" + if payload["et"]["active"] is True: + charge_template["chargemode"]["eco_charging"]["max_price"] = payload["et"]["max_price"] + charge_template["chargemode"]["eco_charging"]["limit"] = copy.deepcopy( + payload["chargemode"]["instant_charging"]["limit"]) + if payload["chargemode"]["selected"] == "instant_charging": + charge_template["chargemode"]["selected"] = "eco_charging" + charge_template["chargemode"]["eco_charging"]["phases_to_use"] = get_new_phases_to_use( + "openWB/general/chargemode_config/instant_charging/phases_to_use") + charge_template["chargemode"]["instant_charging"]["phases_to_use"] = get_new_phases_to_use( + "openWB/general/chargemode_config/instant_charging/phases_to_use") + charge_template["chargemode"]["pv_charging"]["phases_to_use"] = get_new_phases_to_use( + "openWB/general/chargemode_config/pv_charging/phases_to_use") + charge_template["chargemode"]["pv_charging"]["phases_to_use_min_soc"] = min(max_phases_ev, 3) + charge_template["chargemode"]["pv_charging"]["limit"] = dataclass_utils.asdict(Limit()) + if payload["chargemode"]["pv_charging"]["max_soc"] == 101: + charge_template["chargemode"]["pv_charging"]["limit"]["selected"] = "none" + else: + charge_template["chargemode"]["pv_charging"]["limit"]["selected"] = "soc" + charge_template["chargemode"]["pv_charging"]["limit"]["soc"] = payload[ + "chargemode"]["pv_charging"]["max_soc"] + charge_template["chargemode"]["pv_charging"].pop("max_soc") + topics.update({topic: charge_template}) + + for scheduled_plan_topic, scheduled_plan_payload in self.all_received_topics.items(): + if re.search(f"openWB/vehicle/template/charge_template/{index}" + "/chargemode/scheduled_charging/plans/[0-9]+$", scheduled_plan_topic) is not None: + scheduled_plan = copy.deepcopy(decode_payload(scheduled_plan_payload)) + scheduled_plan["phases_to_use"] = get_new_phases_to_use( + "openWB/general/chargemode_config/scheduled_charging/phases_to_use") + scheduled_plan["phases_to_use_pv"] = get_new_phases_to_use( + "openWB/general/chargemode_config/scheduled_charging/phases_to_use_pv") + scheduled_plan["et_active"] = payload["et"]["active"] + topics.update({scheduled_plan_topic: scheduled_plan}) + for time_plan_topic, time_play_payload in self.all_received_topics.items(): + if re.search(f"openWB/vehicle/template/charge_template/{index}" + "/time_charging/plans/[0-9]+$", time_plan_topic) is not None: + time_plan = copy.deepcopy(decode_payload(time_play_payload)) + time_plan["phases_to_use"] = get_new_phases_to_use( + "openWB/general/chargemode_config/time_charging/phases_to_use") + topics.update({time_plan_topic: time_plan}) + + charge_template.pop("et") + return topics + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 83) + + def upgrade_datastore_83(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/system/device/[0-9]+", topic) is not None: + payload = decode_payload(payload) + index = get_index(topic) + if payload.get("type") == "solaredge": + for component_topic, component_payload in self.all_received_topics.items(): + if re.search(f"openWB/system/device/{index}/component/[0-9]+/config", + component_topic) is not None: + config_payload = decode_payload(component_payload) + if (config_payload["type"] == "bat" and + config_payload["configuration"].get("battery_index") is None): + config_payload["configuration"].update({ + "battery_index": 1, + }) + return {component_topic: config_payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 84) + + def upgrade_datastore_84(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("^openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None: + index = get_index(topic) + template = decode_payload(payload) + if template.get("id") is None: + template["id"] = int(index) + if template["chargemode"]["scheduled_charging"].get("plans") is None: + template["chargemode"]["scheduled_charging"]["plans"] = [] + if template["time_charging"].get("plans") is None: + template["time_charging"]["plans"] = [] + for template_topic, template_payload in self.all_received_topics.items(): + if re.search(f"openWB/vehicle/template/charge_template/{index}/chargemode/scheduled_charging/plans/" + "[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + if isinstance(plan["frequency"]["once"], List): + plan["frequency"]["once"] = plan["frequency"]["once"][0] + template["chargemode"]["scheduled_charging"]["plans"].append(plan) + elif re.search(f"openWB/vehicle/template/charge_template/{index}/time_charging/plans/" + "[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + template["time_charging"]["plans"].append(plan) + return {topic: template} + elif re.search("^openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None: + template = decode_payload(payload) + if template.get("id") is None: + template["id"] = int(get_index(topic)) + return {topic: template} + elif re.search("openWB/chargepoint/template/[0-9]+$", topic) is not None: + index = get_index(topic) + template = decode_payload(payload) + if template["autolock"].get("plans") is None: + template["autolock"]["plans"] = [] + if template.get("id") is None: + template["id"] = int(index) + for template_topic, template_payload in self.all_received_topics.items(): + if re.search("^openWB/chargepoint/template/[0-9]+/autolock/[0-9]+$", template_topic) is not None: + plan = decode_payload(template_payload) + if plan.get("id") is None: + plan["id"] = int(get_second_index(template_topic)) + template["autolock"]["plans"].append(plan) + return {topic: template} + + def cp_upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/chargepoint/[0-9]+/config", topic) is not None: + payload = decode_payload(payload) + index = get_index(topic) + if payload["template"] is not None: + charge_template = decode_payload( + self.all_received_topics[f'openWB/vehicle/template/charge_template/{payload["template"]}']) + return {f'openWB/chargepoint/{index}/set/charge_template': charge_template} + self._loop_all_received_topics(upgrade) + self._loop_all_received_topics(cp_upgrade) + self.__update_topic("openWB/system/datastore_version", 85) + + def upgrade_datastore_85(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/system/device/[0-9]+", topic) is not None: + payload = decode_payload(payload) + index = get_index(topic) + if payload.get("type") == "good_we": + for component_topic, component_payload in self.all_received_topics.items(): + if re.search(f"openWB/system/device/{index}/component/[0-9]+/config", + component_topic) is not None: + config_payload = decode_payload(component_payload) + if (config_payload["type"] == "bat" and + config_payload["configuration"].get("battery_index") is None): + config_payload["configuration"].update({ + "battery_index": 1, + }) + return {component_topic: config_payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 86) + + def upgrade_datastore_86(self) -> None: + if "openWB/bat/config/bat_control_permitted" not in self.all_received_topics.keys(): + self.__update_topic("openWB/bat/config/bat_control_permitted", False) + if decode_payload(self.all_received_topics["openWB/bat/get/power_limit_controllable"]) is True: + pub_system_message({}, "Bitte akzeptiere zunächst die " + "rechtlichen Hinweise " + "für die Speichersteuerung. Die Speichersteuerung war bisher bereits verfügbar, ist" + " jedoch bis zum Akzeptieren standardmäßig deaktiviert.", MessageType.WARNING) + self.__update_topic("openWB/system/datastore_version", 87) + + def upgrade_datastore_87(self) -> None: + def upgrade(topic: str, payload) -> None: + if (re.search("openWB/vehicle/template/charge_template/[0-9]+", topic) is not None or + re.search("openWB/vehicle/template/ev_template/[0-9]+", topic) is not None): + payload = decode_payload(payload) + index = int(get_index(topic)) + payload.update({"id": index}) + Pub().pub(topic, payload) + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 88) + + def upgrade_datastore_88(self) -> None: + pub_system_message({}, "Änderungen, die du auf der Hauptseite vornimmst, gelten nur vorübergehend, bis das " + "Fahrzeug abgesteckt wird. \nDie dauerhaften Einstellungen aus dem Einstellungsmenü werden " + "danach automatisch wieder aktiviert.", MessageType.INFO) + pub_system_message({}, "Es gibt ein neues Theme: das Koala-Theme! Smarthpone-optimiert und mit " + "Energiefluss-Diagramm & Karten-Ansicht der Ladepunkte", MessageType.INFO) + self.__update_topic("openWB/system/datastore_version", 89) + + def upgrade_datastore_89(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("^openWB/io/action/[0-9]+/config$", topic) is not None: + payload = decode_payload(payload) + # modify key "input_matrix" to "matrix" for all patterns + if "input_pattern" not in payload["configuration"] and "output_pattern" not in payload["configuration"]: + # No input/output patterns found in IO action configuration + return None + log.debug(f"Updating IO action configuration: {topic}: {payload}") + if "input_pattern" in payload["configuration"]: + for pattern in payload["configuration"]["input_pattern"]: + if "input_matrix" in pattern: + pattern["matrix"] = pattern.pop("input_matrix") + if "output_pattern" in payload["configuration"]: + for pattern in payload["configuration"]["output_pattern"]: + if "input_matrix" in pattern: + pattern["matrix"] = pattern.pop("input_matrix") + log.debug(f"Updated IO action configuration: {topic}: {payload}") + return {topic: payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 90) + + def upgrade_datastore_90(self) -> None: + def upgrade(topic: str, payload) -> None: + if re.search("openWB/chargepoint/[0-9]+/config", topic) is not None: + config = decode_payload(payload) + if config["type"] == "external_openwb": + log.info(f"Update an LP {config['name']} angestoßen.") + ip_address = config["configuration"]["ip_address"] + branch = decode_payload(self.all_received_topics[ + "openWB/system/current_branch"]) + if branch == "master": + tag = "*HEAD*" + else: + tag = re.search(r"\[([^\]]+)\]", + decode_payload(self.all_received_topics["openWB/system/current_branch_commit"]) + ).group(1) + pub.pub_single("openWB/set/command/primary/todo", + json.dumps({"command": "systemUpdate", + "data": {"branch": branch, + "tag": tag, }}), + ip_address, + no_json=True) + time.sleep(2) + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 91) + + def upgrade_datastore_91(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/vehicle/template/ev_template/[0-9]+$", topic) is not None: + payload = decode_payload(payload) + if "bidi" not in payload: + payload.update({"bidi": False}) + return {topic: payload} + elif re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None: + payload = decode_payload(payload) + for plan in payload["chargemode"]["scheduled_charging"]["plans"]: + if "bidi" not in plan: + plan.update({"bidi": False, "bidi_power": 10000}) + return {topic: payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 92) + + def upgrade_datastore_92(self) -> None: + def upgrade(topic: str, payload) -> Optional[dict]: + if re.search("openWB/vehicle/template/charge_template/[0-9]+$", topic) is not None: + payload = decode_payload(payload) + for plan in payload["chargemode"]["scheduled_charging"]["plans"]: + if "bidi" in plan: + bidi_charging_enabled = plan["bidi"] + plan.pop("bidi") + plan.update({"bidi_charging_enabled": bidi_charging_enabled}) + return {topic: payload} + self._loop_all_received_topics(upgrade) + self.__update_topic("openWB/system/datastore_version", 93) diff --git a/packages/helpermodules/utils/__init__.py b/packages/helpermodules/utils/__init__.py index c87b48fb9a..15a1ee8103 100644 --- a/packages/helpermodules/utils/__init__.py +++ b/packages/helpermodules/utils/__init__.py @@ -1,2 +1,3 @@ -from helpermodules.utils._exit_after import exit_after +from helpermodules.utils._get_default import get_default from helpermodules.utils._thread_handler import joined_thread_handler, thread_handler +from helpermodules.utils.processing_counter import ProcessingCounter diff --git a/packages/helpermodules/utils/_exit_after.py b/packages/helpermodules/utils/_exit_after.py deleted file mode 100644 index ba77976165..0000000000 --- a/packages/helpermodules/utils/_exit_after.py +++ /dev/null @@ -1,26 +0,0 @@ -import _thread as thread -import threading -import sys - - -def quit_function(fn_name): - sys.stderr.flush() # Python 3 stderr is likely buffered. - thread.interrupt_main() # raises KeyboardInterrupt - - -def exit_after(s): - ''' https://stackoverflow.com/questions/492519/timeout-on-a-function-call - use as decorator to exit process if - function takes longer than s seconds - ''' - def outer(fn): - def inner(*args, **kwargs): - timer = threading.Timer(s, quit_function, args=[fn.__name__]) - timer.start() - try: - result = fn(*args, **kwargs) - finally: - timer.cancel() - return result - return inner - return outer diff --git a/packages/helpermodules/utils/_get_default.py b/packages/helpermodules/utils/_get_default.py new file mode 100644 index 0000000000..01e11f02f7 --- /dev/null +++ b/packages/helpermodules/utils/_get_default.py @@ -0,0 +1,6 @@ +import inspect + + +def get_default(cls, param): + sig = inspect.signature(cls.__init__) + return sig.parameters[param].default diff --git a/packages/helpermodules/utils/_thread_handler.py b/packages/helpermodules/utils/_thread_handler.py index 9536731959..cea839dd20 100644 --- a/packages/helpermodules/utils/_thread_handler.py +++ b/packages/helpermodules/utils/_thread_handler.py @@ -1,11 +1,12 @@ import logging -import threading +from threading import Thread, enumerate +import time from typing import List, Optional log = logging.getLogger(__name__) -def joined_thread_handler(threads: List[threading.Thread], timeout: Optional[int]) -> List[str]: +def joined_thread_handler(threads: List[Thread], timeout: Optional[int]) -> List[str]: def split_chunks(to_split, n): for i in range(0, len(to_split), n): yield to_split[i:i + n] @@ -14,28 +15,37 @@ def split_chunks(to_split, n): threads_splitted = list(split_chunks(threads, 50)) for threads_chunk in threads_splitted: + threads_to_keep = [] + for thread in threads_chunk: if is_thread_alive(thread.name): log.error(f"{thread.name} ist bereits aktiv und wird nicht erneut gestartet.") not_finished_threads.append(thread.name) - threads_chunk.remove(thread) - break + else: + threads_to_keep.append(thread) # Start them all - for thread in threads_chunk: + for thread in threads_to_keep: thread.start() - # Wait for all to complete - for thread in threads_chunk: - thread.join(timeout=timeout) - - for thread in threads_chunk: + if threads_to_keep: + if timeout is not None: + start_time = time.monotonic() + while time.monotonic() - start_time < timeout: + if not [t for t in threads_to_keep if t.is_alive()]: + break + time.sleep(0.05) + else: + for thread in threads_to_keep: + thread.join() + + for thread in threads_to_keep: if thread.is_alive(): log.error(f"{thread.name} konnte nicht innerhalb des Timeouts abgearbeitet werden.") not_finished_threads.append(thread.name) return not_finished_threads -def thread_handler(thread: threading.Thread) -> bool: +def thread_handler(thread: Thread) -> bool: if is_thread_alive(thread.name): log.error(f"Thread {thread.name} ist bereits aktiv und wird nicht erneut gestartet.") return False @@ -45,4 +55,4 @@ def thread_handler(thread: threading.Thread) -> bool: def is_thread_alive(thread_name: str) -> bool: - return any(running_thread.name == thread_name for running_thread in threading.enumerate()) + return any(running_thread.name == thread_name for running_thread in enumerate()) diff --git a/packages/helpermodules/utils/error_handling.py b/packages/helpermodules/utils/error_handling.py index 0545c503f2..56e2a29bad 100644 --- a/packages/helpermodules/utils/error_handling.py +++ b/packages/helpermodules/utils/error_handling.py @@ -29,7 +29,10 @@ def __exit__(self, exception_type, exception, exception_traceback) -> bool: self.error_timestamp = timecheck.create_timestamp() Pub().pub(self.topic, self.error_timestamp) log.error(exception) - if self.hide_exception is False or timecheck.check_timestamp(self.error_timestamp, self.timeout) is False: + if (self.hide_exception is False or + timecheck.check_timestamp(self.error_timestamp, self.timeout + 10) is False): + # Fehlermeldung als abgelaufen markieren, bevor die Exception gesetzt wird, mit der Exception werden + # keine Werte mehr gepublished. return False return True @@ -60,7 +63,7 @@ def __exit__(self, exception_type, exception, exception_traceback) -> bool: "Neustart wird versucht, fehlende Software-Pakete zu installieren.") except IndexError: msg = "Importfehler: " + str(exception) - # pub_system_message() publlished an openWB/set/, dass wird beim Starten gelöscht + # pub_system_message() veröffentlicht an openWB/set/, dass wird beim Starten gelöscht log.exception(msg) now = time.time() message_payload = { diff --git a/packages/helpermodules/utils/processing_counter.py b/packages/helpermodules/utils/processing_counter.py new file mode 100644 index 0000000000..f8d4aead99 --- /dev/null +++ b/packages/helpermodules/utils/processing_counter.py @@ -0,0 +1,26 @@ +from threading import Event, Lock + + +class ProcessingCounter: + def __init__(self, done_event: Event): + self.lock = Lock() + self.counter = 0 + self.done_event = done_event + + def add_task(self): + with self.lock: + self.counter += 1 + self.done_event.clear() + + def task_done(self): + with self.lock: + self.counter -= 1 + if self.counter <= 0: + self.done_event.set() + + def wait_for_completion(self, timeout=None): + return self.done_event.wait(timeout) + + def is_done(self): + with self.lock: + return self.counter == 0 diff --git a/packages/helpermodules/utils/run_command.py b/packages/helpermodules/utils/run_command.py index 4bb4acdf7a..cf8d963670 100644 --- a/packages/helpermodules/utils/run_command.py +++ b/packages/helpermodules/utils/run_command.py @@ -4,6 +4,19 @@ log = logging.getLogger(__name__) +def run_shell_command(command, process_exception: bool = False): + try: + result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, text=True) + output, _ = result.communicate() + return output + except subprocess.CalledProcessError as e: + if process_exception: + log.debug(e.stdout) + log.exception(e.stderr) + else: + raise e + + def run_command(command, process_exception: bool = False): # if return is non-zero a CalledProcessError is raised try: @@ -17,7 +30,9 @@ def run_command(command, process_exception: bool = False): return result.stdout except subprocess.CalledProcessError as e: if process_exception: - log.debug(e.stdout) - log.exception(e.stderr) + if e.output is not None: + log.exception(e.output) + if e.stderr is not None: + log.exception(e.stderr) else: raise e diff --git a/packages/main.py b/packages/main.py index afa82ce914..25fd3ca184 100755 --- a/packages/main.py +++ b/packages/main.py @@ -1,10 +1,14 @@ #!/usr/bin/env python3 """Starten der benötigten Prozesse """ -# flake8: noqa: F402 +# flake8: noqa: E402 import logging from helpermodules import logger -from helpermodules.utils import thread_handler +from helpermodules.utils import run_command, thread_handler +import threading +import sys +import functools + # als erstes logging initialisieren, damit auch ImportError geloggt werden logger.setup_logging() log = logging.getLogger() @@ -13,9 +17,8 @@ from random import randrange import schedule import time -import threading +from threading import Event, Thread, enumerate import traceback -from threading import Thread from control.chargelog.chargelog import calculate_charge_cost from control import data, prepare, process @@ -26,9 +29,9 @@ from helpermodules.measurement_logging.write_log import LogType, save_log from helpermodules.modbusserver import start_modbus_server from helpermodules.pub import Pub -from helpermodules.utils import exit_after from modules import configuration, loadvars, update_soc from modules.internal_chargepoint_handler.internal_chargepoint_handler import GeneralInternalChargepointHandler +from modules.internal_chargepoint_handler.gpio import InternalGpioHandler from modules.internal_chargepoint_handler.rfid import RfidReader from modules.utils import wait_for_module_update_completed from smarthome.smarthome import readmq, smarthome_handler @@ -38,12 +41,97 @@ class HandlerAlgorithm: def __init__(self): self.interval_counter = 1 self.current_day = None + self.handler_locks = {} + self.handler_timestamps = {} + + def __acquire_lock(self, handler_name, error_threshold=60): + """Versucht, den Lock für den angegebenen Handler zu erwerben. + Erstellt Lock und Timestamp-Eintrag bei Bedarf dynamisch. + Gibt True zurück, wenn der Lock erfolgreich erworben wurde, sonst False. + """ + if handler_name not in self.handler_locks: + self.handler_locks[handler_name] = threading.Lock() + if handler_name not in self.handler_timestamps: + self.handler_timestamps[handler_name] = 0 + + lock = self.handler_locks[handler_name] + now = time.time() + if lock.acquire(blocking=False): + self.handler_timestamps[handler_name] = now + log.debug(f"Lock für {handler_name} erworben.") + return True + # Wenn der Lock älter als 'error_threshold' Sekunden ist, wird ein Error geloggt. + log_handler = log.error if now - self.handler_timestamps[handler_name] > error_threshold else log.debug + log_handler( + f"{handler_name} läuft bereits, neuer Aufruf wird übersprungen. Letzter Start: " + f"{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.handler_timestamps[handler_name]))} " + f"(vor {now - self.handler_timestamps[handler_name]} Sekunden).") + return False + + def __release_lock(self, handler_name): + """Gibt den Lock für den angegebenen Handler frei.""" + lock = self.handler_locks.get(handler_name) + if lock: + lock.release() + log.debug(f"Lock für {handler_name} freigegeben nach {time.time() - self.handler_timestamps[handler_name]} Sekunden.") + self.handler_timestamps.pop(handler_name, None) + else: + log.warning(f"Lock für {handler_name} nicht gefunden.") + def __with_handler_lock(error_threshold=60): + def decorator(func): + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + handler_name = func.__name__ + if self.__acquire_lock(handler_name, error_threshold): + try: + return func(self, *args, **kwargs) + finally: + self.__release_lock(handler_name) + else: + return + return wrapper + return decorator + + def monitor_handler_locks(self, max_runtime=300): + emergency_exit = False + for handler_name, lock in self.handler_locks.items(): + if lock.locked(): + # Überprüfen, wie lange der Lock bereits aktiv ist + duration = time.time() - self.handler_timestamps[handler_name] + log.warning(f"Handler {handler_name} ist seit {duration} Sekunden gesperrt.") + # Stack Trace des Threads, der den Lock hält + for tid, frame in sys._current_frames().items(): + if tid == lock._get_ident(): + stack_trace = traceback.format_stack(frame) + log.warning(f"Stack Trace für {handler_name}:") + for line in stack_trace: + log.warning(line.strip()) + if duration > max_runtime: + log.error(f"Handler {handler_name} ist seit mehr als {max_runtime} Sekunden gesperrt.") + emergency_exit = True + if emergency_exit: + log.error(f"Deadlock erkannt, Prozess wird beendet!") + log.debug(f"Threads: {enumerate()}") + for thread in enumerate(): + logging.debug(f"Thread Name: {thread.name}") + if hasattr(thread, "ident"): + thread_id = thread.ident + for tid, frame in sys._current_frames().items(): + if tid == thread_id: + logging.debug(f" File: {frame.f_code.co_filename}, Line: {frame.f_lineno}, Function: {frame.f_code.co_name}") + stack_trace = traceback.format_stack(frame) + logging.debug(" Stack Trace:") + for line in stack_trace: + logging.debug(line.strip()) + sys.exit(1) + + # decorator can not be used here as it would block logging before handler_with_control_interval() + # @__with_handler_lock(error_threshold=30) def handler10Sec(self): """ führt den Algorithmus durch. """ try: - @exit_after(data.data.general_data.data.control_interval) def handler_with_control_interval(): if (data.data.general_data.data.control_interval / 10) == self.interval_counter: data.data.copy_data() @@ -66,16 +154,25 @@ def handler_with_control_interval(): self.interval_counter = 1 else: self.interval_counter = self.interval_counter + 1 + + # In-Memory Log-Handler zurücksetzen + logger.clear_in_memory_log_handler("main") + log.info("# ***Start*** ") - log.debug(f"Threads: {threading.enumerate()}") + # log.debug(run_command.run_shell_command("top -b -n 1 | head -n 20")) + # log.debug(f'Drosselung: {run_command.run_shell_command("if which vcgencmd >/dev/null; then vcgencmd get_throttled; else echo not found; fi")}') Pub().pub("openWB/set/system/time", timecheck.create_timestamp()) - handler_with_control_interval() - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) + if not self.__acquire_lock("handler10Sec", error_threshold=30): + return + try: + handler_with_control_interval() + logger.write_logs_to_file("main") + finally: + self.__release_lock("handler10Sec") except Exception: log.exception("Fehler im Main-Modul") - @exit_after(10) + @__with_handler_lock(error_threshold=60) def handler5MinAlgorithm(self): """ Handler, der alle 5 Minuten aufgerufen wird und die Heartbeats der Threads überprüft und die Aufgaben ausführt, die nur alle 5 Minuten ausgeführt werden müssen. @@ -86,15 +183,12 @@ def handler5MinAlgorithm(self): update_daily_yields(totals) update_pv_monthly_yearly_yields() data.data.general_data.grid_protection() - data.data.optional_data.et_get_prices() data.data.optional_data.ocpp_transfer_meter_values() data.data.counter_all_data.validate_hierarchy() - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") - @exit_after(10) + @__with_handler_lock(error_threshold=60) def handler5Min(self): """ Handler, der alle 5 Minuten aufgerufen wird und die Heartbeats der Threads überprüft und die Aufgaben ausführt, die nur alle 5 Minuten ausgeführt werden müssen. @@ -124,37 +218,35 @@ def handler5Min(self): general_internal_chargepoint_handler.internal_chargepoint_handler.heartbeat = False with ChangedValuesContext(loadvars_.event_module_update_completed): sub.system_data["system"].update_ip_address() - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") - @exit_after(10) + @__with_handler_lock(error_threshold=60) def handler_midnight(self): try: save_log(LogType.MONTHLY) - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) + thread_errors_path = Path(Path(__file__).resolve().parents[1]/"ramdisk"/"thread_errors.log") + with thread_errors_path.open("w") as f: + f.write("") except Exception: log.exception("Fehler im Main-Modul") - @exit_after(10) + @__with_handler_lock(error_threshold=60) def handler_random_nightly(self): try: data.data.system_data["system"].thread_backup_and_send_to_cloud() - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) except Exception: log.exception("Fehler im Main-Modul") - @exit_after(10) + @__with_handler_lock(error_threshold=60) def handler_hour(self): + """ Handler, der jede Stunde aufgerufen wird und die Aufgaben ausführt, die nur jede Stunde ausgeführt werden müssen. + """ try: with ChangedValuesContext(loadvars_.event_module_update_completed): for cp in data.data.cp_data.values(): calculate_charge_cost(cp) - except KeyboardInterrupt: - log.critical("Ausführung durch exit_after gestoppt: "+traceback.format_exc()) + data.data.optional_data.et_get_prices() except Exception: log.exception("Fehler im Main-Modul") @@ -170,6 +262,8 @@ def schedule_jobs(): schedule.every().day.at(f"0{randrange(0, 5)}:{randrange(0, 59):02d}:{randrange(0, 59):02d}").do( handler.handler_random_nightly) [schedule.every().minute.at(f":{i:02d}").do(handler.handler10Sec).tag("algorithm") for i in range(0, 60, 10)] + # 30 Sekunden Handler, der die Locks überwacht, Deadlocks erkennt, loggt und ggf. den Prozess beendet + schedule.every(30).seconds.do(handler.monitor_handler_locks, max_runtime=600) try: @@ -190,45 +284,40 @@ def schedule_jobs(): prep = prepare.Prepare() general_internal_chargepoint_handler = GeneralInternalChargepointHandler() rfid = RfidReader() - event_ev_template = threading.Event() + event_ev_template = Event() event_ev_template.set() - event_charge_template = threading.Event() - event_charge_template.set() - event_cp_config = threading.Event() + event_cp_config = Event() event_cp_config.set() - event_scheduled_charging_plan = threading.Event() - event_scheduled_charging_plan.set() - event_time_charging_plan = threading.Event() - event_time_charging_plan.set() - event_soc = threading.Event() + event_soc = Event() event_soc.set() - event_copy_data = threading.Event() # set: Kopieren abgeschlossen, reset: es wird kopiert + event_copy_data = Event() # set: Kopieren abgeschlossen, reset: es wird kopiert event_copy_data.set() - event_global_data_initialized = threading.Event() - event_command_completed = threading.Event() + event_global_data_initialized = Event() + event_command_completed = Event() event_command_completed.set() - event_subdata_initialized = threading.Event() - event_update_config_completed = threading.Event() - event_modbus_server = threading.Event() - event_jobs_running = threading.Event() + event_subdata_initialized = Event() + event_update_config_completed = Event() + event_modbus_server = Event() + event_jobs_running = Event() event_jobs_running.set() - event_update_soc = threading.Event() + event_update_soc = Event() + event_restart_gpio = Event() + gpio = InternalGpioHandler(event_restart_gpio) prep = prepare.Prepare() soc = update_soc.UpdateSoc(event_update_soc) - set = setdata.SetData(event_ev_template, event_charge_template, - event_cp_config, event_scheduled_charging_plan, event_time_charging_plan, event_soc, + set = setdata.SetData(event_ev_template, + event_cp_config, event_soc, event_subdata_initialized) - sub = subdata.SubData(event_ev_template, event_charge_template, + sub = subdata.SubData(event_ev_template, event_cp_config, loadvars_.event_module_update_completed, event_copy_data, event_global_data_initialized, event_command_completed, event_subdata_initialized, soc.event_vehicle_update_completed, - event_scheduled_charging_plan, event_time_charging_plan, general_internal_chargepoint_handler.event_start, general_internal_chargepoint_handler.event_stop, event_update_config_completed, event_update_soc, event_soc, - event_jobs_running, event_modbus_server) + event_jobs_running, event_modbus_server, event_restart_gpio) comm = command.Command(event_command_completed) t_sub = Thread(target=sub.sub_topics, args=(), name="Subdata") t_set = Thread(target=set.set_data, args=(), name="Setdata") @@ -237,17 +326,21 @@ def schedule_jobs(): t_internal_chargepoint = Thread(target=general_internal_chargepoint_handler.handler, args=(), name="Internal Chargepoint") if rfid.keyboards_detected: - t_rfid = Thread(target=rfid.run, args=(), name="Internal Chargepoint") + t_rfid = Thread(target=rfid.run, args=(), name="Internal RFID") t_rfid.start() + t_gpio = Thread(target=gpio.loop, args=(), name="Internal GPIO") + t_gpio.start() + t_sub.start() t_set.start() t_comm.start() t_soc.start() t_internal_chargepoint.start() - threading.Thread(target=start_modbus_server, args=(event_modbus_server,), name="Modbus Control Server").start() + Thread(target=start_modbus_server, args=(event_modbus_server,), name="Modbus Control Server").start() # Warten, damit subdata Zeit hat, alle Topics auf dem Broker zu empfangen. event_update_config_completed.wait(300) + event_subdata_initialized.wait(300) Pub().pub("openWB/set/system/boot_done", True) Path(Path(__file__).resolve().parents[1]/"ramdisk"/"bootdone").touch() schedule_jobs() diff --git a/packages/modbus_control_tester.py b/packages/modbus_control_tester.py index 232621e625..f11ccbba5e 100755 --- a/packages/modbus_control_tester.py +++ b/packages/modbus_control_tester.py @@ -28,7 +28,7 @@ class ReadMode(Enum): slave_id = 1 read_mode = ReadMode.READ_INPUT_REG read_client = modbus.ModbusTcpClient_(host, port=port) -Register = namedtuple("Register", "reg, action, length, type, name, expected") +Register = namedtuple("Register", ("reg", "action", "length", "type", "name", "expected")) REGISTERS = ( Register(10100, Actions.READ_NUMBER, 1, ModbusDataType.INT_32, name="Actual Power", expected=(0, 0)), Register(10102, Actions.READ_NUMBER, 1, ModbusDataType.INT_32, @@ -135,7 +135,7 @@ def evaluate_reg(reg): def read_all_registers(): heartbeat_read() for reg in REGISTERS: - if reg.reg == 10160: + if reg.reg % 100 == 60: while True: try: if evaluate_reg(reg): diff --git a/packages/modules/backup_clouds/nextcloud/backup_cloud.py b/packages/modules/backup_clouds/nextcloud/backup_cloud.py index cee8d87290..c059190f2f 100644 --- a/packages/modules/backup_clouds/nextcloud/backup_cloud.py +++ b/packages/modules/backup_clouds/nextcloud/backup_cloud.py @@ -26,7 +26,7 @@ def upload_backup(config: NextcloudBackupCloudConfiguration, backup_filename: st headers={'X-Requested-With': 'XMLHttpRequest', }, data=backup_file, auth=(user, '' if config.password is None else config.password), - timeout=30 + timeout=60 ) diff --git a/packages/modules/backup_clouds/nextcloud/config.py b/packages/modules/backup_clouds/nextcloud/config.py index c4ec33173f..6f19a8bd13 100644 --- a/packages/modules/backup_clouds/nextcloud/config.py +++ b/packages/modules/backup_clouds/nextcloud/config.py @@ -12,7 +12,9 @@ class NextcloudBackupCloud: def __init__(self, name: str = "NextCloud", type: str = "nextcloud", + official: bool = True, configuration: NextcloudBackupCloudConfiguration = None) -> None: self.name = name self.type = type + self.official = official self.configuration = configuration or NextcloudBackupCloudConfiguration() diff --git a/packages/modules/backup_clouds/nfs/config.py b/packages/modules/backup_clouds/nfs/config.py index d69703ba0b..47eef80c27 100644 --- a/packages/modules/backup_clouds/nfs/config.py +++ b/packages/modules/backup_clouds/nfs/config.py @@ -10,7 +10,9 @@ class NfsBackupCloud: def __init__(self, name: str = "Nfs", type: str = "nfs", + official: bool = False, configuration: NfsBackupCloudConfiguration = None) -> None: self.name = name self.type = type + self.official = official self.configuration = configuration or NfsBackupCloudConfiguration() diff --git a/packages/modules/backup_clouds/onedrive/config.py b/packages/modules/backup_clouds/onedrive/config.py index c221cafe34..c456b6946b 100644 --- a/packages/modules/backup_clouds/onedrive/config.py +++ b/packages/modules/backup_clouds/onedrive/config.py @@ -24,7 +24,9 @@ class OneDriveBackupCloud: def __init__(self, name: str = "OneDrive", type: str = "onedrive", + official: bool = False, configuration: OneDriveBackupCloudConfiguration = None) -> None: self.name = name self.type = type + self.official = official self.configuration = configuration or OneDriveBackupCloudConfiguration() diff --git a/packages/modules/backup_clouds/samba/backup_cloud.py b/packages/modules/backup_clouds/samba/backup_cloud.py index 044f1c3fbd..398c5d01f6 100644 --- a/packages/modules/backup_clouds/samba/backup_cloud.py +++ b/packages/modules/backup_clouds/samba/backup_cloud.py @@ -34,8 +34,8 @@ def upload_backup(config: SambaBackupCloudConfiguration, backup_filename: str, b host_is_reachable = is_port_open(config.smb_server, 139) if found_invalid_chars: - log.warn("Folgenden ungültige Zeichen im Pfad gefunden: {}".format(found_invalid_chars.group())) - log.warn("Sicherung nicht erfolgreich.") + log.warning("Folgenden ungültige Zeichen im Pfad gefunden: {}".format(found_invalid_chars.group())) + log.warning("Sicherung nicht erfolgreich.") send_file = False else: send_file = True @@ -51,9 +51,9 @@ def upload_backup(config: SambaBackupCloudConfiguration, backup_filename: str, b log.error("Möglicherweise ist die Freigabe oder ein Unterordner nicht vorhanden.") conn.close() elif send_file: - log.warn("SMB Verbindungsaufbau fehlgeschlagen.") + log.warning("SMB Verbindungsaufbau fehlgeschlagen.") elif not host_is_reachable: - log.warn("Host {} und/oder Port 139 nicht zu erreichen.".format(config.smb_server)) + log.warning("Host {} und/oder Port 139 nicht zu erreichen.".format(config.smb_server)) def create_backup_cloud(config: SambaBackupCloud): diff --git a/packages/modules/backup_clouds/samba/config.py b/packages/modules/backup_clouds/samba/config.py index 41b7c1f98b..f8f5e483fc 100644 --- a/packages/modules/backup_clouds/samba/config.py +++ b/packages/modules/backup_clouds/samba/config.py @@ -19,7 +19,9 @@ class SambaBackupCloud: def __init__(self, name: str = "Samba", type: str = "samba", + official: bool = False, configuration: SambaBackupCloudConfiguration = None) -> None: self.name = name self.type = type + self.official = official self.configuration = configuration or SambaBackupCloudConfiguration() diff --git a/packages/modules/chargepoints/external_openwb/chargepoint_module.py b/packages/modules/chargepoints/external_openwb/chargepoint_module.py index e6258d0cdd..7116b8a630 100644 --- a/packages/modules/chargepoints/external_openwb/chargepoint_module.py +++ b/packages/modules/chargepoints/external_openwb/chargepoint_module.py @@ -1,13 +1,21 @@ +import logging import time from control import data from helpermodules import pub, timecheck +from helpermodules.broker import BrokerClient +from helpermodules.utils import get_default from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext +from helpermodules.utils.topic_parser import decode_payload from modules.chargepoints.external_openwb.config import OpenWBSeries from modules.common.abstract_chargepoint import AbstractChargepoint from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_state import ChargepointState from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store._chargepoint import get_chargepoint_value_store + +log = logging.getLogger(__name__) class ChargepointModule(AbstractChargepoint): @@ -18,6 +26,7 @@ def __init__(self, config: OpenWBSeries) -> None: "Ladepunkt", "chargepoint")) self.client_error_context = ErrorTimerContext( f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) + self.store = get_chargepoint_value_store(self.config.id) def set_current(self, current: float) -> None: if self.client_error_context.error_counter_exceeded(): @@ -36,7 +45,9 @@ def set_current(self, current: float) -> None: hostname=self.config.configuration.ip_address) def get_values(self) -> None: - with SingleComponentUpdateContext(self.fault_state, update_always=False): + def parse_received_topics(value: str): + return received_topics.get(f"{topic_prefix}{value}", get_default(ChargepointState, value)) + with SingleComponentUpdateContext(self.fault_state): with self.client_error_context: ip_address = self.config.configuration.ip_address num = self.config.id @@ -56,6 +67,59 @@ def get_values(self) -> None: else: pub.pub_single("openWB/set/internal_chargepoint/0/data/parent_cp", str(num), hostname=ip_address) pub.pub_single("openWB/set/isss/parentCPlp1", str(num), hostname=ip_address) + + def on_connect(client, userdata, flags, rc): + client.subscribe(f"openWB/internal_chargepoint/{self.config.configuration.duo_num}/get/#") + + def on_message(client, userdata, message): + received_topics.update({message.topic: decode_payload(message.payload)}) + + received_topics = {} + BrokerClient(f"subscribeSeriesChargepoint{self.config.id}", + on_connect, + on_message, + host=self.config.configuration.ip_address, + port=1883).start_finite_loop() + + if received_topics: + log.debug(f"Empfange MQTT Daten für Ladepunkt {self.config.id}: {received_topics}") + topic_prefix = f"openWB/internal_chargepoint/{self.config.configuration.duo_num}/get/" + try: + chargepoint_state = ChargepointState( + power=received_topics[f"{topic_prefix}power"], + phases_in_use=received_topics[f"{topic_prefix}phases_in_use"], + imported=received_topics[f"{topic_prefix}imported"], + exported=received_topics[f"{topic_prefix}exported"], + serial_number=parse_received_topics("serial_number"), + powers=parse_received_topics("powers"), + voltages=parse_received_topics("voltages"), + currents=received_topics[f"{topic_prefix}currents"], + power_factors=parse_received_topics("power_factors"), + plug_state=received_topics[f"{topic_prefix}plug_state"], + charge_state=received_topics[f"{topic_prefix}charge_state"], + rfid=parse_received_topics("rfid"), + rfid_timestamp=parse_received_topics("rfid_timestamp"), + frequency=parse_received_topics("frequency"), + soc=parse_received_topics("soc"), + soc_timestamp=parse_received_topics("soc_timestamp"), + vehicle_id=parse_received_topics("vehicle_id"), + evse_current=parse_received_topics("evse_current"), + max_evse_current=parse_received_topics("max_evse_current"), + version=parse_received_topics("version"), + current_branch=parse_received_topics("current_branch"), + current_commit=parse_received_topics("current_commit") + ) + self.store.set(chargepoint_state) + if received_topics[f"{topic_prefix}fault_state"] == 2: + self.fault_state.error(received_topics[f"{topic_prefix}fault_str"]) + elif received_topics[f"{topic_prefix}fault_state"] == 1: + self.fault_state.warning(received_topics[f"{topic_prefix}fault_str"]) + except KeyError: + raise KeyError("Es wurden nicht alle notwendigen Daten empfangen.") + else: + self.fault_state.warning(f"Keine MQTT-Daten für Ladepunkt {self.config.name} empfangen. Noch keine " + "Daten nach dem Start oder Ladepunkt nicht erreichbar.") + self.client_error_context.reset_error_counter() def switch_phases(self, phases_to_use: int, duration: int) -> None: diff --git a/packages/modules/chargepoints/mqtt/chargepoint_module.py b/packages/modules/chargepoints/mqtt/chargepoint_module.py index e340ff7b40..8bbd7f0deb 100644 --- a/packages/modules/chargepoints/mqtt/chargepoint_module.py +++ b/packages/modules/chargepoints/mqtt/chargepoint_module.py @@ -1,11 +1,17 @@ import logging -from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext +from helpermodules.broker import BrokerClient +from helpermodules.pub import Pub +from helpermodules.utils._get_default import get_default +from helpermodules.utils.topic_parser import decode_payload from modules.chargepoints.mqtt.config import Mqtt from modules.common.abstract_chargepoint import AbstractChargepoint from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_state import ChargepointState from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store._chargepoint import get_chargepoint_value_store + log = logging.getLogger(__name__) @@ -13,21 +19,87 @@ class ChargepointModule(AbstractChargepoint): def __init__(self, config: Mqtt) -> None: self.config = config - self.fault_state = FaultState(ComponentInfo( - self.config.id, - "Ladepunkt", "chargepoint")) - self.client_error_context = ErrorTimerContext( - f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) + self.store = get_chargepoint_value_store(self.config.id) + self.fault_state = FaultState(ComponentInfo(self.config.id, "Ladepunkt", "chargepoint")) + + def on_connect(client, userdata, flags, rc): + client.subscribe(f"openWB/mqtt/chargepoint/{self.config.id}/#") + + def on_message(client, userdata, message): + received_topics.update({message.topic: decode_payload(message.payload)}) + + received_topics = {} + BrokerClient(f"subscribeMqttChargepointInit{self.config.id}", + on_connect, on_message).start_finite_loop() + phases_to_use = received_topics.get(f"openWB/mqtt/chargepoint/{self.config.id}/set/phases_to_use") + + if phases_to_use == 0: + phases_in_use = received_topics.get(f"openWB/mqtt/chargepoint/{self.config.id}/get/phases_in_use") + Pub().pub( + f"openWB/mqtt/chargepoint/{self.config.id}/set/phases_to_use", + phases_in_use if phases_in_use is not None else 3 + ) + else: + Pub().pub(f"openWB/mqtt/chargepoint/{self.config.id}/set/phases_to_use", 3) def set_current(self, current: float) -> None: - log.debug("MQTT-Ladepunkte abonnieren die Soll-Stromstärke direkt vom Broker.") + Pub().pub(f"openWB/mqtt/chargepoint/{self.config.id}/set/current", current) def get_values(self) -> None: + def parse_received_topics(value: str): + return received_topics.get(f"{topic_prefix}{value}", get_default(ChargepointState, value)) with SingleComponentUpdateContext(self.fault_state): - log.debug("MQTT-Ladepunkte müssen nicht ausgelesen werden.") + def on_connect(client, userdata, flags, rc): + client.subscribe(f"openWB/mqtt/chargepoint/{self.config.id}/get/#") + + def on_message(client, userdata, message): + received_topics.update({message.topic: decode_payload(message.payload)}) + + received_topics = {} + BrokerClient(f"subscribeMqttChargepoint{self.config.id}", + on_connect, on_message).start_finite_loop() + + if received_topics: + log.debug(f"Empfange MQTT Daten für Ladepunkt {self.config.id}: {received_topics}") + topic_prefix = f"openWB/mqtt/chargepoint/{self.config.id}/get/" + try: + chargepoint_state = ChargepointState( + power=received_topics[f"{topic_prefix}power"], + phases_in_use=received_topics[f"{topic_prefix}phases_in_use"], + imported=received_topics[f"{topic_prefix}imported"], + exported=received_topics[f"{topic_prefix}exported"], + serial_number=parse_received_topics("serial_number"), + powers=parse_received_topics("powers"), + voltages=parse_received_topics("voltages"), + currents=received_topics[f"{topic_prefix}currents"], + power_factors=parse_received_topics("power_factors"), + plug_state=received_topics[f"{topic_prefix}plug_state"], + charge_state=received_topics[f"{topic_prefix}charge_state"], + rfid=parse_received_topics("rfid"), + rfid_timestamp=parse_received_topics("rfid_timestamp"), + frequency=parse_received_topics("frequency"), + soc=parse_received_topics("soc"), + soc_timestamp=parse_received_topics("soc_timestamp"), + vehicle_id=parse_received_topics("vehicle_id"), + evse_current=parse_received_topics("evse_current"), + max_evse_current=parse_received_topics("max_evse_current"), + version=parse_received_topics("version"), + current_branch=parse_received_topics("current_branch"), + current_commit=parse_received_topics("current_commit"), + max_charge_power=parse_received_topics("max_charge_power"), + max_discharge_power=parse_received_topics("max_discharge_power"), + evse_signaling=parse_received_topics("evse_signaling"), + ) + self.store.set(chargepoint_state) + except KeyError: + raise KeyError("Es wurden nicht alle notwendigen Daten empfangen.") + else: + self.fault_state.warning(f"Keine MQTT-Daten für Ladepunkt {self.config.name} empfangen oder es werden " + "veraltete, abwärtskompatible Topics verwendet. Bitte die Doku in den " + "Einstellungen beachten.") def switch_phases(self, phases_to_use: int, duration: int) -> None: - log.warning("Phasenumschaltung für MQTT-Ladepunkte nicht unterstützt.") + Pub().pub(f"openWB/mqtt/chargepoint/{self.config.id}/set/phases_to_use", phases_to_use) chargepoint_descriptor = DeviceDescriptor(configuration_factory=Mqtt) diff --git a/packages/modules/chargepoints/openwb_pro/chargepoint_module.py b/packages/modules/chargepoints/openwb_pro/chargepoint_module.py index f6c71843da..9a7d0767d2 100644 --- a/packages/modules/chargepoints/openwb_pro/chargepoint_module.py +++ b/packages/modules/chargepoints/openwb_pro/chargepoint_module.py @@ -10,12 +10,20 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.hardware_check import check_meter_values from modules.common.store import get_chargepoint_value_store -from modules.common.component_state import ChargepointState +from modules.common.component_state import ChargepointState, CounterState from modules.common import req +from modules.internal_chargepoint_handler.internal_chargepoint_handler_config import InternalChargepoint log = logging.getLogger(__name__) +class EvseSignaling: + HLC = "HLC" + ISO15118 = "ISO15118" + FAKE_HIGHLEVEL = "fake_highlevel" + PWM = "PWM" + + class ChargepointModule(AbstractChargepoint): WRONG_CHARGE_STATE = "Lade-Status ist nicht aktiv, aber Strom fließt." WRONG_PLUG_STATE = "Ladepunkt ist nicht angesteckt, aber es wird geladen." @@ -29,23 +37,21 @@ def __init__(self, config: OpenWBPro) -> None: self.__session = req.get_http_session() self.client_error_context = ErrorTimerContext( f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) - self.old_chargepoint_state = ChargepointState() with SingleComponentUpdateContext(self.fault_state, update_always=False): - with self.client_error_context: - self.__session.post( - 'http://' + self.config.configuration.ip_address + '/connect.php', - data={'heartbeatenabled': '1'}) + self.__session.post( + 'http://' + self.config.configuration.ip_address + '/connect.php', + data={'heartbeatenabled': '1'}) - def set_internal_context_handlers(self, parent_cp, parent_hostname): + def set_internal_context_handlers(self, hierarchy_id: int, internal_cp: InternalChargepoint): self.fault_state = FaultState(ComponentInfo( - parent_cp, + self.config.id, "Ladepunkt "+str(self.config.id), - "chargepoint", - parent_id=parent_cp, - parent_hostname=parent_hostname)) + "internal_chargepoint", + hierarchy_id=hierarchy_id)) self.client_error_context = ErrorTimerContext( f"openWB/set/internal_chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) + self.client_error_context.error_timestamp = internal_cp.get.error_timestamp def set_current(self, current: float) -> None: if self.client_error_context.error_counter_exceeded(): @@ -58,11 +64,12 @@ def set_current(self, current: float) -> None: def get_values(self) -> None: with SingleComponentUpdateContext(self.fault_state): chargepoint_state = self.request_values() - self.store.set(chargepoint_state) + if chargepoint_state is not None: + # bei Fehler, aber Fehlezähler noch nicht abgelaufen, keine Werte mehr publishen. + self.store.set(chargepoint_state) def request_values(self) -> ChargepointState: with self.client_error_context: - chargepoint_state = self.old_chargepoint_state ip_address = self.config.configuration.ip_address json_rsp = self.__session.get('http://'+ip_address+'/connect.php').json() @@ -77,13 +84,16 @@ def request_values(self) -> ChargepointState: phases_in_use=json_rsp["phases_in_use"], vehicle_id=json_rsp["vehicle_id"], evse_current=json_rsp["offered_current"], - serial_number=json_rsp["serial"] + serial_number=json_rsp["serial"], + evse_signaling=json_rsp["evse_signaling"], ) if json_rsp.get("voltages"): - meter_msg = check_meter_values(json_rsp["voltages"]) - if meter_msg: - self.fault_state.warning(meter_msg) + check_meter_values(CounterState(voltages=json_rsp["voltages"], + currents=json_rsp["currents"], + powers=json_rsp["powers"], + power=json_rsp["power_all"]), + self.fault_state) chargepoint_state.voltages = json_rsp["voltages"] if json_rsp.get("soc_value"): chargepoint_state.soc = json_rsp["soc_value"] @@ -95,17 +105,23 @@ def request_values(self) -> ChargepointState: chargepoint_state.rfid = json_rsp["rfid_tag"] if json_rsp.get("rfid_timestamp"): chargepoint_state.rfid_timestamp = json_rsp["rfid_timestamp"] + if json_rsp.get("max_discharge_power"): + chargepoint_state.max_discharge_power = json_rsp["max_discharge_power"] + if json_rsp.get("max_charge_power"): + chargepoint_state.max_charge_power = json_rsp["max_charge_power"] self.validate_values(chargepoint_state) - self.old_chargepoint_state = chargepoint_state self.client_error_context.reset_error_counter() + return chargepoint_state if self.client_error_context.error_counter_exceeded(): chargepoint_state = ChargepointState() chargepoint_state.plug_state = False chargepoint_state.charge_state = False - chargepoint_state.imported = self.old_chargepoint_state.imported - chargepoint_state.exported = self.old_chargepoint_state.exported - return chargepoint_state + chargepoint_state.imported = None # bei None werden keine Werte gepublished + chargepoint_state.exported = None + return chargepoint_state + else: + return None def validate_values(self, chargepoint_state: ChargepointState) -> None: if chargepoint_state.charge_state is False and max(chargepoint_state.currents) > 1: diff --git a/packages/modules/chargepoints/openwb_pro/chargepoint_module_test.py b/packages/modules/chargepoints/openwb_pro/chargepoint_module_test.py index 583a2ea1d9..78c64b73ea 100644 --- a/packages/modules/chargepoints/openwb_pro/chargepoint_module_test.py +++ b/packages/modules/chargepoints/openwb_pro/chargepoint_module_test.py @@ -25,6 +25,7 @@ def sample_chargepoint_state(): rfid_timestamp=1700839714, vehicle_id="98:ED:5C:B4:EE:8D", evse_current=6, + evse_signaling="fake highlevel + basic iec61851", serial_number="823950" ) @@ -65,6 +66,7 @@ def sample_chargepoint_extended(): rfid=None, frequency=50.2, evse_current=6, + evse_signaling="unclear\n", serial_number="493826" ) @@ -116,10 +118,13 @@ def test_openwb_pro(sample_state: Dict, expected_state: Dict, monkeypatch, reque @pytest.mark.parametrize("chargepoint_state, expected_exception, expected_message", [ (ChargepointState(charge_state=False, currents=[0, 2, 0], plug_state=True, - power=0), ValueError, chargepoint_module.ChargepointModule.WRONG_CHARGE_STATE), + power=0, imported=None, exported=None, phases_in_use=0), + ValueError, chargepoint_module.ChargepointModule.WRONG_CHARGE_STATE), (ChargepointState(charge_state=True, currents=[0, 0, 0], plug_state=False, - power=30), ValueError, chargepoint_module.ChargepointModule.WRONG_PLUG_STATE), - (ChargepointState(charge_state=True, currents=[0, 2, 0], plug_state=True, power=30), None, None) + power=30, imported=None, exported=None, phases_in_use=0), + ValueError, chargepoint_module.ChargepointModule.WRONG_PLUG_STATE), + (ChargepointState(charge_state=True, currents=[0, 2, 0], plug_state=True, + power=30, imported=None, exported=None, phases_in_use=0), None, None) ]) def test_validate_values(chargepoint_state, expected_exception, expected_message): cp = chargepoint_module.ChargepointModule(OpenWBPro(configuration=OpenWBProConfiguration(ip_address=SAMPLE_IP))) @@ -148,10 +153,10 @@ def sample_wrong_charge_state_chargepoint_state(): return sample_wrong_charge_state_chargepoint_state -def sample_chargepoint_state_resetted(): - sample_chargepoint_state_resetted = sample_wrong_charge_state_chargepoint_state() - sample_chargepoint_state_resetted.plug_state = False - return sample_chargepoint_state_resetted +def sample_chargepoint_state_is_reset(): + sample_chargepoint_state_is_reset = sample_wrong_charge_state_chargepoint_state() + sample_chargepoint_state_is_reset.plug_state = False + return sample_chargepoint_state_is_reset @pytest.mark.parametrize( @@ -161,12 +166,11 @@ def sample_chargepoint_state_resetted(): pytest.param(sample(), 1652683242, None, None, sample_chargepoint_state(), id="Timestamp gesetzt, kein Fehler aufgetreten"), pytest.param(sample_wrong_charge_state(), None, None, 1652683252, - sample_wrong_charge_state_chargepoint_state(), id="kein Timestamp gesetzt, Fehler aufgetreten"), - pytest.param(sample_wrong_charge_state(), 1652683242, None, 1652683242, - sample_wrong_charge_state_chargepoint_state(), + None, id="kein Timestamp gesetzt, Fehler aufgetreten"), + pytest.param(sample_wrong_charge_state(), 1652683242, None, 1652683242, None, id="Timestamp gesetzt, Fehler aufgetreten, Timestamp nicht abgelaufen"), - pytest.param(sample_wrong_charge_state(), 1652683182, ValueError, 1652683182, - sample_chargepoint_state_resetted(), + pytest.param(sample_wrong_charge_state(), 1652683112, ValueError, 1652683112, + sample_chargepoint_state_is_reset(), id="Timestamp gesetzt, Fehler aufgetreten, Timestamp abgelaufen"), ]) def test_error_timestamp(sample_state, @@ -185,7 +189,6 @@ def test_error_timestamp(sample_state, cp = chargepoint_module.ChargepointModule(OpenWBPro(configuration=OpenWBProConfiguration(ip_address=SAMPLE_IP))) cp.client_error_context.error_timestamp = error_timestamp - cp.old_chargepoint_state = sample_chargepoint_state # evaluation if expected_exception is not None: diff --git a/packages/modules/chargepoints/openwb_pro/config.py b/packages/modules/chargepoints/openwb_pro/config.py index cab0724e61..e534ee193e 100644 --- a/packages/modules/chargepoints/openwb_pro/config.py +++ b/packages/modules/chargepoints/openwb_pro/config.py @@ -4,7 +4,9 @@ class OpenWBProConfiguration: - def __init__(self, ip_address: Optional[str] = None, duo_num: int = 0): + def __init__(self, + ip_address: Optional[str] = None, + duo_num: int = 0) -> None: self.ip_address = ip_address self.duo_num = duo_num diff --git a/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py b/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py index 20734da31f..0e24638ac0 100644 --- a/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py +++ b/packages/modules/chargepoints/openwb_series2_satellit/chargepoint_module.py @@ -1,10 +1,12 @@ #!/usr/bin/python3 import logging +from pathlib import Path import time from typing import Optional from control import data from helpermodules.utils.error_handling import CP_ERROR, ErrorTimerContext +from helpermodules.utils.run_command import run_command from modules.chargepoints.openwb_series2_satellit.config import OpenWBseries2Satellit from modules.common import modbus from modules.common.abstract_chargepoint import AbstractChargepoint @@ -40,7 +42,6 @@ def __init__(self, config: OpenWBseries2Satellit) -> None: f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) self._create_client() self._validate_version() - self.max_evse_current = self._client.evse_client.get_max_current() def delay_second_cp(self, delay: float): if self.config.configuration.duo_num == 0: @@ -74,34 +75,37 @@ def _validate_version(self): def get_values(self) -> None: with SingleComponentUpdateContext(self.fault_state): if self.version is not None: - with self.client_error_context: - try: - self.delay_second_cp(self.CP1_DELAY) - with self._client.client: - self._client.check_hardware(self.fault_state) - if self.version is False: - self._validate_version() - currents = self._client.meter_client.get_currents() - phases_in_use = sum(1 for current in currents if current > 3) - plug_state, charge_state, _ = self._client.evse_client.get_plug_charge_state() + try: + self.delay_second_cp(self.CP1_DELAY) + with self._client.client, self.client_error_context: + evse_state, counter_state = self._client.request_and_check_hardware(self.fault_state) + if self.version is False: + self._validate_version() - chargepoint_state = ChargepointState( - power=self._client.meter_client.get_power()[1], - currents=currents, - imported=self._client.meter_client.get_imported(), - exported=0, - voltages=self._client.meter_client.get_voltages(), - plug_state=plug_state, - charge_state=charge_state, - phases_in_use=phases_in_use, - serial_number=self._client.meter_client.get_serial_number(), - max_evse_current=self.max_evse_current - ) + currents = counter_state.currents + phases_in_use = sum(1 for current in currents if current > 3) + + chargepoint_state = ChargepointState( + power=counter_state.power, + currents=currents, + imported=counter_state.imported, + exported=0, + voltages=counter_state.voltages, + plug_state=evse_state.plug_state, + charge_state=evse_state.charge_state, + phases_in_use=phases_in_use, + serial_number=counter_state.serial_number, + max_evse_current=evse_state.max_current + ) self.store.set(chargepoint_state) self.client_error_context.reset_error_counter() - except AttributeError: - self._create_client() - self._validate_version() + except Exception: + if self.client_error_context.error_counter_exceeded(): + run_command(f"{Path(__file__).resolve().parents[3]}/modules/chargepoints/" + "openwb_series2_satellit/restart_protoss_satellite") + except AttributeError: + self._create_client() + self._validate_version() else: self._create_client() self._validate_version() @@ -115,7 +119,6 @@ def set_current(self, current: float) -> None: try: self.delay_second_cp(self.CP1_DELAY) with self._client.client: - self._client.check_hardware(self.fault_state) if self.version: self._client.evse_client.set_current(int(current)) else: @@ -130,7 +133,6 @@ def switch_phases(self, phases_to_use: int, duration: int) -> None: with self.client_error_context: try: with self._client.client: - self._client.check_hardware(self.fault_state) if phases_to_use == 1: self._client.client.delegate.write_register( 0x0001, 256, unit=self.ID_PHASE_SWITCH_UNIT) diff --git a/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite b/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite new file mode 100755 index 0000000000..99dbb6250c Binary files /dev/null and b/packages/modules/chargepoints/openwb_series2_satellit/restart_protoss_satellite differ diff --git a/packages/modules/chargepoints/smartwb/chargepoint_module.py b/packages/modules/chargepoints/smartwb/chargepoint_module.py index cbf4b3b19d..ac6f7b7b76 100644 --- a/packages/modules/chargepoints/smartwb/chargepoint_module.py +++ b/packages/modules/chargepoints/smartwb/chargepoint_module.py @@ -20,7 +20,6 @@ def __init__(self, config: SmartWB) -> None: "Ladepunkt", "chargepoint")) self.client_error_context = ErrorTimerContext( f"openWB/set/chargepoint/{self.config.id}/get/error_timestamp", CP_ERROR, hide_exception=True) - self.phases_in_use = 1 self.session = req.get_http_session() def set_current(self, current: float) -> None: @@ -55,12 +54,9 @@ def get_values(self) -> None: currents = [json_rsp["currentP1"], json_rsp["currentP2"], json_rsp["currentP3"]] - if currents[2] > 3: - self.phases_in_use = 3 - elif currents[1] > 3: - self.phases_in_use = 2 - elif currents[0] > 3: - self.phases_in_use = 1 + phases_in_use = sum(1 for current in currents if current > 3) + if phases_in_use == 0: + phases_in_use = None if json_rsp.get("voltageP1"): voltages = [json_rsp["voltageP1"], json_rsp["voltageP2"], json_rsp["voltageP3"]] @@ -84,9 +80,10 @@ def get_values(self) -> None: power=json_rsp["actualPower"] * 1000, currents=currents, imported=json_rsp["meterReading"] * 1000, + exported=0, plug_state=plug_state, charge_state=charge_state, - phases_in_use=self.phases_in_use, + phases_in_use=phases_in_use, voltages=voltages, rfid=tag, serial_number=mac, diff --git a/packages/modules/chargepoints/smartwb/smartwb_test.py b/packages/modules/chargepoints/smartwb/smartwb_test.py index 32890476a3..396f36b8ae 100644 --- a/packages/modules/chargepoints/smartwb/smartwb_test.py +++ b/packages/modules/chargepoints/smartwb/smartwb_test.py @@ -25,6 +25,7 @@ class TestSmartWb: power=5790, currents=[8.54, 8.54, 8.54], imported=54350.0, + exported=0, plug_state=True, charge_state=False, phases_in_use=3, @@ -58,10 +59,10 @@ class TestSmartWb: currents=[9.78, 0, 0], voltages=[228.28, 231.85, 232.07], imported=54350.0, + exported=0, plug_state=True, charge_state=True, rfid="0a1b2c3d", - rfid_timestamp=1652683252, serial_number="94:B9:7E:69:F0:D1", max_evse_current=16 ) @@ -97,11 +98,11 @@ class TestSmartWb: currents=[0, 0, 0], voltages=[228.28, 231.85, 232.07], imported=54350.0, + exported=0, plug_state=True, charge_state=True, rfid="0a1b2c3d", - rfid_timestamp=1652683252, - phases_in_use=1, + phases_in_use=None, serial_number="94:B9:7E:69:F0:D1", max_evse_current=16 ) diff --git a/packages/modules/common/abstract_chargepoint.py b/packages/modules/common/abstract_chargepoint.py index 140cdabc9e..540fe75cb7 100644 --- a/packages/modules/common/abstract_chargepoint.py +++ b/packages/modules/common/abstract_chargepoint.py @@ -6,7 +6,7 @@ class AbstractChargepoint: @abstractmethod - def __init__(self, id: int, connection_module: dict, power_module: dict) -> None: + def __init__(self, config) -> None: pass @abstractmethod diff --git a/packages/modules/common/abstract_device.py b/packages/modules/common/abstract_device.py index b0c235b9ed..15fa1a1ac5 100644 --- a/packages/modules/common/abstract_device.py +++ b/packages/modules/common/abstract_device.py @@ -18,7 +18,12 @@ def update(self) -> None: class AbstractBat: @abstractmethod - def __init__(self, *kwargs) -> None: + def __init__(self, component_config, **kwargs) -> None: + self.component_config = component_config + self.kwargs = kwargs + + @abstractmethod + def initializer(self): pass @abstractmethod @@ -30,10 +35,18 @@ def set_power_limit(self, power_limit: Optional[int]) -> None: # power limit None heißt, auf maximale Speicherleistung setzen = Speicher-Begrenzung aufheben pass + def power_limit_controllable(self) -> bool: + return False + class AbstractCounter: @abstractmethod - def __init__(self, *kwargs) -> None: + def __init__(self, component_config, **kwargs) -> None: + self.component_config = component_config + self.kwargs = kwargs + + @abstractmethod + def initializer(self): pass @abstractmethod @@ -43,13 +56,23 @@ def update(self, *kwargs) -> None: class AbstractInverter: @abstractmethod - def __init__(self, *kwargs) -> None: + def __init__(self, component_config, **kwargs) -> None: + self.component_config = component_config + self.kwargs = kwargs + + @abstractmethod + def initializer(self): pass @abstractmethod def update(self, *kwargs) -> None: pass + @abstractmethod + def set_power_limit(self, power_limit: float) -> None: + # power_limit in Werten zwischen 0 und 1 + pass + class DeviceDescriptor: def __init__(self, configuration_factory: Type): diff --git a/packages/modules/common/abstract_io.py b/packages/modules/common/abstract_io.py new file mode 100644 index 0000000000..0af498b96b --- /dev/null +++ b/packages/modules/common/abstract_io.py @@ -0,0 +1,25 @@ +from abc import abstractmethod +from typing import Dict, Optional + + +class AbstractIoDevice: + @abstractmethod + def __init__(self, io_config: dict) -> None: + pass + + @abstractmethod + def read(self) -> None: + pass + + @abstractmethod + def write(self, analog_output: Optional[Dict[str, int]], digital_output: Optional[Dict[str, bool]]) -> None: + pass + + +class AbstractIoAction: + def __init__(self): + self.timestamp = None + + @abstractmethod + def setup(self) -> None: + pass diff --git a/packages/modules/common/b23.py b/packages/modules/common/b23.py index c3ee955055..affebbd9d0 100644 --- a/packages/modules/common/b23.py +++ b/packages/modules/common/b23.py @@ -4,6 +4,9 @@ from modules.common import modbus from modules.common.abstract_counter import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.fault_state import FaultState +from modules.common.hardware_check import check_meter_values from modules.common.modbus import ModbusDataType @@ -24,9 +27,10 @@ class B23(AbstractCounter): "INT_64": 0x7FFFFFFFFFFFFFFF, } - def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: self.client = client self.id = modbus_id + self.fault_state = fault_state def check_nan(self, raw_value: int, value: any, data_type: ModbusDataType) -> Optional[Union[float, int]]: """Checks if the value is a NaN and returns None if it is. @@ -101,3 +105,19 @@ def get_voltages(self) -> List[float]: values = [self.check_nan(val, val / 10, data_type) for val in self.client.read_holding_registers(0x5B00, [data_type]*3, unit=self.id)] return values + + def get_counter_state(self) -> CounterState: + powers, power = self.get_power() + counter_state = CounterState( + imported=self.get_imported(), + exported=self.get_exported(), + power=power, + voltages=self.get_voltages(), + currents=self.get_currents(), + powers=powers, + power_factors=self.get_power_factors(), + frequency=self.get_frequency(), + serial_number=self.get_serial_number() + ) + check_meter_values(counter_state, self.fault_state) + return counter_state diff --git a/packages/modules/common/component_context.py b/packages/modules/common/component_context.py index 534bc25416..e7b2bb280e 100644 --- a/packages/modules/common/component_context.py +++ b/packages/modules/common/component_context.py @@ -1,5 +1,5 @@ import logging -import threading +from threading import local from typing import Optional, List, Union, Any, Dict from helpermodules.constants import NO_ERROR @@ -17,7 +17,10 @@ class SingleComponentUpdateContext: component.update() """ - def __init__(self, fault_state: FaultState, update_always: bool = True, reraise: bool = False): + def __init__(self, + fault_state: FaultState, + update_always: bool = True, + reraise: bool = False): self.__fault_state = fault_state self.update_always = update_always self.reraise = reraise @@ -30,7 +33,7 @@ def __enter__(self): def __exit__(self, exception_type, exception, exception_traceback) -> bool: MultiComponentUpdateContext.override_subcomponent_state(self.__fault_state, exception, self.update_always) - if self.reraise is False: + if self.reraise is False or exception is None: return True else: return False @@ -44,12 +47,13 @@ class MultiComponentUpdateContext: for component in self.components: component.update() """ - __thread_local = threading.local() + __thread_local = local() - def __init__(self, device_components: Union[Dict[Any, Any], List[Any]]): + def __init__(self, device_components: Union[Dict[Any, Any], List[Any]], error_handler: Optional[callable] = None): self.__device_components = \ device_components.values() if isinstance(device_components, dict) else device_components self.__ignored_components = [] # type: List[ComponentInfo] + self.error_handler = error_handler def __enter__(self): if hasattr(self.__thread_local, "active_context"): @@ -66,9 +70,12 @@ def __exit__(self, exception_type, exception, exception_traceback) -> bool: for component in self.__device_components: fault_state = component.fault_state if fault_state not in self.__ignored_components: - fault_state.from_exception(exception) + if exception: + fault_state.from_exception(exception) fault_state.store_error() delattr(MultiComponentUpdateContext.__thread_local, "active_context") + if isinstance(exception, Exception) and self.error_handler is not None: + self.error_handler() return True def ignore_subcomponent_state(self, component: ComponentInfo): @@ -86,7 +93,7 @@ def override_subcomponent_state(fault_state: FaultState, exception, update_alway if exception: fault_state.from_exception(exception) - elif update_always is False: + elif update_always is False and fault_state.fault_state == 0: # Fehlerstatus nicht überschreiben return fault_state.store_error() diff --git a/packages/modules/common/component_state.py b/packages/modules/common/component_state.py index 3a56202ea0..ee856d4e22 100644 --- a/packages/modules/common/component_state.py +++ b/packages/modules/common/component_state.py @@ -56,6 +56,7 @@ def __init__( exported: float = 0, power: float = 0, soc: float = 0, + currents: Optional[List[float]] = None, ): """Args: imported: total imported energy in Wh @@ -67,6 +68,12 @@ def __init__( self.exported = exported self.power = power self.soc = soc + if _check_none(currents): + currents = [0.0]*3 + else: + if not ((sum(currents) < 0 and power < 0) or (sum(currents) > 0 and power > 0)): + log.debug("currents sign wrong "+str(currents)) + self.currents = currents @auto_str @@ -81,6 +88,7 @@ def __init__( powers: Optional[List[Optional[float]]] = None, power_factors: Optional[List[Optional[float]]] = None, frequency: float = 50, + serial_number: str = "", ): """Args: imported: total imported energy in Wh @@ -100,6 +108,7 @@ def __init__( self.exported = exported self.power = power self.frequency = frequency + self.serial_number = serial_number @auto_str @@ -108,11 +117,13 @@ def __init__( self, exported: float, power: float, + imported: float = 0, # simulated import counter to properly calculate PV energy when bat is charged from AC currents: Optional[List[Optional[float]]] = None, dc_power: Optional[float] = None ): """Args: exported: total energy in Wh + imported: total energy in Wh power: actual power in W currents: actual currents for 3 phases in A dc_power: dc power in W @@ -125,6 +136,7 @@ def __init__( self.currents = currents self.power = power self.exported = exported + self.imported = imported self.dc_power = dc_power @@ -150,20 +162,23 @@ def __init__(self, soc: float, range: Optional[float] = None, soc_timestamp: Opt @auto_str class ChargepointState: def __init__(self, - phases_in_use: int = 0, - imported: float = 0, - exported: float = 0, - power: float = 0, + phases_in_use: int, + imported: float, + exported: float, + power: float, + currents: List[float], + charge_state: bool, + plug_state: bool, serial_number: str = "", charging_current: Optional[float] = 0, charging_voltage: Optional[float] = 0, charging_power: Optional[float] = 0, + evse_signaling: Optional[str] = None, + max_charge_power: Optional[float] = None, + max_discharge_power: Optional[float] = None, powers: Optional[List[Optional[float]]] = None, voltages: Optional[List[Optional[float]]] = None, - currents: Optional[List[Optional[float]]] = None, power_factors: Optional[List[Optional[float]]] = None, - charge_state: bool = False, - plug_state: bool = False, rfid: Optional[str] = None, rfid_timestamp: Optional[float] = None, frequency: float = 50, @@ -171,7 +186,10 @@ def __init__(self, soc_timestamp: Optional[int] = None, evse_current: Optional[float] = None, vehicle_id: Optional[str] = None, - max_evse_current: Optional[int] = None): + max_evse_current: Optional[int] = None, + current_branch: Optional[str] = None, + current_commit: Optional[str] = None, + version: Optional[str] = None): self.currents, self.powers, self.voltages = _calculate_powers_and_currents(currents, powers, voltages) self.frequency = frequency self.imported = imported @@ -182,10 +200,7 @@ def __init__(self, self.charge_state = charge_state self.plug_state = plug_state self.rfid = rfid - if self.rfid and rfid_timestamp is None: - self.rfid_timestamp = timecheck.create_timestamp() - else: - self.rfid_timestamp = rfid_timestamp + self.rfid_timestamp = rfid_timestamp if _check_none(power_factors): power_factors = [0.0]*3 self.charging_current = charging_current @@ -197,6 +212,12 @@ def __init__(self, self.evse_current = evse_current self.max_evse_current = max_evse_current self.vehicle_id = vehicle_id + self.current_branch = current_branch + self.current_commit = current_commit + self.version = version + self.evse_signaling = evse_signaling + self.max_charge_power = max_charge_power + self.max_discharge_power = max_discharge_power @auto_str @@ -207,6 +228,22 @@ def __init__(self, @auto_str -class RcrState: - def __init__(self, override_value: float) -> None: - self.override_value = override_value +class IoState: + """JSON erlaubt nur Zeichenketten als Schlüssel für Objekte""" + + def __init__(self, analog_input: Dict[str, float] = None, + digital_input: Dict[str, bool] = None, + analog_output: Dict[str, float] = None, + digital_output: Dict[str, bool] = None) -> None: + self.analog_input = analog_input + self.digital_input = digital_input + self.analog_output = analog_output + self.digital_output = digital_output + + +class EvseState: + def __init__(self, plug_state: bool, charge_state: bool, set_current: int, max_current: int) -> None: + self.plug_state = plug_state + self.charge_state = charge_state + self.set_current = set_current + self.max_current = max_current diff --git a/packages/modules/common/component_type.py b/packages/modules/common/component_type.py index 896f1f5737..d7a3b4b4bc 100644 --- a/packages/modules/common/component_type.py +++ b/packages/modules/common/component_type.py @@ -9,7 +9,7 @@ class ComponentType(Enum): COUNTER = "counter" ELECTRICITY_TARIFF = "electricity_tariff" INVERTER = "inverter" - RIPPLE_CONTROL_RECEIVER = "ripple_control_receiver" + IO = "io" def special_to_general_type_mapping(component_type: str) -> ComponentType: @@ -34,6 +34,8 @@ def type_to_topic_mapping(component_type: str) -> str: return "pv" elif ComponentType.ELECTRICITY_TARIFF.value in component_type: return "optional/et" + elif ComponentType.IO.value in component_type: + return "io/states" else: return component_type diff --git a/packages/modules/common/configurable_backup_cloud.py b/packages/modules/common/configurable_backup_cloud.py index ce126d5d31..f1ea316965 100644 --- a/packages/modules/common/configurable_backup_cloud.py +++ b/packages/modules/common/configurable_backup_cloud.py @@ -15,6 +15,9 @@ def __init__(self, self.config = config self.fault_state = FaultState(ComponentInfo(None, self.config.name, ComponentType.BACKUP_CLOUD.value)) + # nach Init auf NO_ERROR setzen, damit der Fehlerstatus beim Modulwechsel gelöscht wird + self.fault_state.no_error() + self.fault_state.store_error() with SingleComponentUpdateContext(self.fault_state): self._component_updater = component_initializer(config) diff --git a/packages/modules/common/configurable_device.py b/packages/modules/common/configurable_device.py index cfa5963f96..6de11f6413 100644 --- a/packages/modules/common/configurable_device.py +++ b/packages/modules/common/configurable_device.py @@ -1,10 +1,13 @@ import inspect +import logging from typing import TypeVar, Generic, Dict, Any, Callable, Iterable, List from dataclass_utils import dataclass_from_dict +from helpermodules import timecheck +from helpermodules.pub import Pub from modules.common.abstract_device import AbstractDevice from modules.common.component_context import SingleComponentUpdateContext, MultiComponentUpdateContext -from modules.common.fault_state import FaultState +from modules.common.fault_state import ComponentInfo, FaultState T_DEVICE_CONFIG = TypeVar("T_DEVICE_CONFIG") T_COMPONENT = TypeVar("T_COMPONENT") @@ -13,26 +16,35 @@ ComponentUpdater = Callable[[Iterable[T_COMPONENT]], None] ComponentFactory = Callable[[T_COMPONENT_CONFIG], T_COMPONENT] +log = logging.getLogger(__name__) + class IndependentComponentUpdater(Generic[T_COMPONENT]): def __init__(self, updater: Callable[[T_COMPONENT], None]): self.__updater = updater - def __call__(self, components: Iterable[T_COMPONENT]) -> None: + def __call__(self, components: Iterable[T_COMPONENT], error_handler: Callable) -> None: + # error_handler nur einmal ausführen, da er für das ganze Gerät gilt + run_error_handler = False for component in components: - with SingleComponentUpdateContext(component.fault_state): - self.__updater(component) + try: + with SingleComponentUpdateContext(component.fault_state, reraise=True): + self.__updater(component) + except Exception: + run_error_handler = True + if run_error_handler: + error_handler() class MultiComponentUpdater: def __init__(self, updater: Callable[[List[T_COMPONENT]], None]): self.__updater = updater - def __call__(self, components: Iterable[T_COMPONENT]) -> None: + def __call__(self, components: Iterable[T_COMPONENT], error_handler: Callable) -> None: components_list = list(components) - with MultiComponentUpdateContext(components_list): + with MultiComponentUpdateContext(components_list, error_handler): if not components: - raise FaultState.warning("Keine Komponenten konfiguriert") + raise Exception("Keine Komponenten konfiguriert oder Initialisierung fehlgeschlagen") self.__updater(components_list) @@ -62,14 +74,50 @@ class ConfigurableDevice(Generic[T_COMPONENT, T_DEVICE_CONFIG, T_COMPONENT_CONFI def __init__(self, device_config: T_DEVICE_CONFIG, component_factory: ComponentFactory[Any, T_COMPONENT], - component_updater: ComponentUpdater[T_COMPONENT]) -> None: + component_updater: ComponentUpdater[T_COMPONENT], + initializer: Callable = lambda: None, + error_handler: Callable = lambda: None) -> None: + self.__initializer = initializer + self.__error_handler = error_handler self.__component_factory = component_factory self.__component_updater = component_updater self.device_config = device_config self.components: Dict[str, T_COMPONENT] = {} + self.error_timestamp = None + try: + self.__initializer() + except Exception: + log.exception(f"Initialisierung von Gerät {self.device_config.name} fehlgeschlagen") + + def error_handler(self) -> None: + error_timestamp_topic = f"openWB/set/system/device/{self.device_config.id}/error_timestamp" + if self.error_timestamp is None: + self.error_timestamp = timecheck.create_timestamp() + Pub().pub(error_timestamp_topic, self.error_timestamp) + log.debug( + f"Fehler bei Gerät {self.device_config.name} aufgetreten, Fehlerzeitstempel: {self.error_timestamp}") + if timecheck.check_timestamp(self.error_timestamp, 60) is False: + try: + self.__error_handler() + except Exception: + log.exception(f"Fehlerbehandlung für Gerät {self.device_config.name} fehlgeschlagen") + else: + log.debug(f"Fehlerbehandlung für Gerät {self.device_config.name} wurde durchgeführt.") + + self.error_timestamp = None + Pub().pub(error_timestamp_topic, self.error_timestamp) def add_component(self, component_config: T_COMPONENT_CONFIG) -> None: - self.components["component" + str(component_config.id)] = self.__component_factory(component_config) + with SingleComponentUpdateContext(FaultState(ComponentInfo.from_component_config(component_config))): + component = self.__component_factory(component_config) + component.initialized = False + self.components["component" + str(component_config.id)] = component + component.initialize() + component.initialized = True def update(self): - self.__component_updater(self.components.values()) + initialized_components = [] + for component in self.components.values(): + if hasattr(component, "initialized") and component.initialized: + initialized_components.append(component) + self.__component_updater(initialized_components, self.error_handler) diff --git a/packages/modules/common/configurable_io.py b/packages/modules/common/configurable_io.py new file mode 100644 index 0000000000..981bdfeb7d --- /dev/null +++ b/packages/modules/common/configurable_io.py @@ -0,0 +1,59 @@ +import logging +from typing import Dict, Optional, TypeVar, Generic, Callable, Union + +from helpermodules.pub import Pub +from modules.common import store +from modules.common.abstract_io import AbstractIoDevice +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_state import IoState +from modules.common.component_type import ComponentType +from modules.common.fault_state import ComponentInfo, FaultState + + +T_IO_CONFIG = TypeVar("T_IO_CONFIG") +log = logging.getLogger(__name__) + + +class ConfigurableIo(Generic[T_IO_CONFIG], AbstractIoDevice): + def __init__(self, + config: T_IO_CONFIG, + component_reader: Callable[[], IoState], + component_writer: Callable[[Dict[int, Union[float, int]]], Optional[IoState]], + initializer: Callable = lambda: None) -> None: + self.config = config + self.fault_state = FaultState(ComponentInfo(self.config.id, self.config.name, + ComponentType.IO.value)) + self.store = store.get_io_value_store(self.config.id) + self.set_manual: Dict = {"analog_output": {}, "digital_output": {}} + with SingleComponentUpdateContext(self.fault_state): + self.component_reader = component_reader + self.component_writer = component_writer + initializer() + + def read(self): + if hasattr(self, "component_reader"): + # Wenn beim Initialisieren etwas schief gelaufen ist, ursprüngliche Fehlermeldung beibehalten + with SingleComponentUpdateContext(self.fault_state): + io_state = self.component_reader() + self.store.set(io_state) + + def update_manual_output(self, manual: Dict[str, bool], output: Dict[str, bool], string: str, topic_suffix: str): + if len(manual) > 0: + log.debug(f"Manuell gesetzte {string} Ausgänge: {manual}") + for manual_out_pin, manual_out_value in manual.items(): + output[manual_out_pin] = manual_out_value + # nur die in diesem Zyklus gesetzten manuellen Ausgänge setzen, für nächsten Zyklus zurücksetzen + Pub().pub(f"openWB/set/io/{self.config.id}/set/manual/{topic_suffix}/{manual_out_pin}", "") + + def write(self, analog_output, digital_output): + if hasattr(self, "component_writer"): + # Wenn beim Initialisieren etwas schief gelaufen ist, ursprüngliche Fehlermeldung beibehalten + with SingleComponentUpdateContext(self.fault_state): + self.update_manual_output(self.set_manual["analog_output"], analog_output, "analoge", "analog_output") + self.update_manual_output(self.set_manual["digital_output"], + digital_output, "digitale", "digital_output") + if ((analog_output and self.store.delegate.state.analog_output != analog_output) or + (digital_output and self.store.delegate.state.digital_output != digital_output)): + io_state = self.component_writer(analog_output, digital_output) + if io_state is not None: + self.store.set(io_state) diff --git a/packages/modules/common/configurable_ripple_control_receiver.py b/packages/modules/common/configurable_ripple_control_receiver.py deleted file mode 100644 index e15475604b..0000000000 --- a/packages/modules/common/configurable_ripple_control_receiver.py +++ /dev/null @@ -1,27 +0,0 @@ -from typing import TypeVar, Generic, Callable - -from modules.common import store -from modules.common.component_context import SingleComponentUpdateContext -from modules.common.component_type import ComponentType -from modules.common.fault_state import ComponentInfo, FaultState - - -T_RCR_CONFIG = TypeVar("T_RCR_CONFIG") - - -class ConfigurableRcr(Generic[T_RCR_CONFIG]): - def __init__(self, - config: T_RCR_CONFIG, - component_initializer: Callable[[], float]) -> None: - self.config = config - self.fault_state = FaultState(ComponentInfo(None, self.config.name, - ComponentType.RIPPLE_CONTROL_RECEIVER.value)) - with SingleComponentUpdateContext(self.fault_state): - self._component_updater = component_initializer(config) - self.store = store.get_ripple_control_receiver_value_store() - - def update(self): - if hasattr(self, "_component_updater"): - # Wenn beim Initialisieren etwas schief gelaufen ist, ursprüngliche Fehlermeldung beibehalten - with SingleComponentUpdateContext(self.fault_state): - self.store.set(self._component_updater()) diff --git a/packages/modules/common/configurable_tariff.py b/packages/modules/common/configurable_tariff.py index f7091fda91..afb933eacd 100644 --- a/packages/modules/common/configurable_tariff.py +++ b/packages/modules/common/configurable_tariff.py @@ -3,6 +3,7 @@ from modules.common import store from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_state import TariffState from modules.common.component_type import ComponentType from modules.common.fault_state import ComponentInfo, FaultState @@ -17,6 +18,9 @@ def __init__(self, self.config = config self.store = store.get_electricity_tariff_value_store() self.fault_state = FaultState(ComponentInfo(None, self.config.name, ComponentType.ELECTRICITY_TARIFF.value)) + # nach Init auf NO_ERROR setzen, damit der Fehlerstatus beim Modulwechsel gelöscht wird + self.fault_state.no_error() + self.fault_state.store_error() with SingleComponentUpdateContext(self.fault_state): self._component_updater = component_initializer(config) @@ -24,14 +28,19 @@ def update(self): if hasattr(self, "_component_updater"): # Wenn beim Initialisieren etwas schief gelaufen ist, ursprüngliche Fehlermeldung beibehalten with SingleComponentUpdateContext(self.fault_state): - tariff_state = self._component_updater() - current_hour = str(int(create_unix_timestamp_current_full_hour())) + tariff_state = self._remove_outdated_prices(self._component_updater()) self.store.set(tariff_state) self.store.update() - for timestamp in tariff_state.prices.keys(): - if timestamp < current_hour: - self.fault_state.warning('Die Preisliste startet nicht mit der aktuellen Stunde.') if len(tariff_state.prices) < 24: self.fault_state.no_error( f'Die Preisliste hat nicht 24, sondern {len(tariff_state.prices)} Einträge. ' 'Die Strompreise werden vom Anbieter erst um 14:00 für den Folgetag aktualisiert.') + + def _remove_outdated_prices(self, tariff_state: TariffState) -> TariffState: + current_hour = str(int(create_unix_timestamp_current_full_hour())) + for timestamp in list(tariff_state.prices.keys()): + if timestamp < current_hour: + self.fault_state.warning( + 'Die Preisliste startet nicht mit der aktuellen Stunde. Abgelaufene Einträge wurden entfernt.') + tariff_state.prices.pop(timestamp) + return tariff_state diff --git a/packages/modules/common/configurable_tariff_test.py b/packages/modules/common/configurable_tariff_test.py new file mode 100644 index 0000000000..c2b0f7e3c8 --- /dev/null +++ b/packages/modules/common/configurable_tariff_test.py @@ -0,0 +1,34 @@ + +from unittest.mock import Mock +import pytest + +from modules.common.component_state import TariffState +from modules.common.configurable_tariff import ConfigurableElectricityTariff +from modules.electricity_tariffs.awattar.config import AwattarTariff + + +@pytest.mark.parametrize( + "tariff_state, expected", + [ + pytest.param(TariffState(prices={"1652680800": -5.87e-06, + "1652684400": 5.467e-05, + "1652688000": 10.72e-05}), + TariffState(prices={"1652680800": -5.87e-06, + "1652684400": 5.467e-05, + "1652688000": 10.72e-05}), id="keine veralteten Einträge"), + pytest.param(TariffState(prices={"1652677200": -5.87e-06, + "1652680800": 5.467e-05, + "1652684400": 10.72e-05}), + TariffState(prices={"1652680800": 5.467e-05, + "1652684400": 10.72e-05}), id="Lösche ersten Eintrag"), + ], +) +def test_remove_outdated_prices(tariff_state: TariffState, expected: TariffState, monkeypatch): + # setup + tariff = ConfigurableElectricityTariff(AwattarTariff(), Mock()) + + # test + result = tariff._remove_outdated_prices(tariff_state) + + # assert + assert result.prices == expected.prices diff --git a/packages/modules/common/configurable_vehicle.py b/packages/modules/common/configurable_vehicle.py index a9ecba4f2c..f829b7e40f 100644 --- a/packages/modules/common/configurable_vehicle.py +++ b/packages/modules/common/configurable_vehicle.py @@ -34,10 +34,12 @@ def __init__(self, vehicle: int, calc_while_charging: bool = False, general_config: Optional[GeneralVehicleConfig] = None, - calculated_soc_state: Optional[CalculatedSocState] = None) -> None: + calculated_soc_state: Optional[CalculatedSocState] = None, + initializer: Callable = lambda: None) -> None: self.__component_updater = component_updater self.vehicle_config = vehicle_config self.calculated_soc_state = calculated_soc_state + self.__initializer = initializer if calculated_soc_state is None: self.calculated_soc_state = CalculatedSocState() else: @@ -51,19 +53,30 @@ def __init__(self, self.vehicle = vehicle self.store = store.get_car_value_store(self.vehicle) self.fault_state = FaultState(ComponentInfo(self.vehicle, self.vehicle_config.name, "vehicle")) + # nach Init auf NO_ERROR setzen, damit der Fehlerstatus beim Modulwechsel gelöscht wird + self.fault_state.no_error() + self.fault_state.store_error() + + try: + self.__initializer() + except Exception: + log.exception(f"Initialisierung von Fahrzeug {self.vehicle_config.name} fehlgeschlagen") def update(self, vehicle_update_data: VehicleUpdateData): log.debug(f"Vehicle Instance {type(self.vehicle_config)}") log.debug(f"Calculated SoC-State {self.calculated_soc_state}") log.debug(f"Vehicle Update Data {vehicle_update_data}") log.debug(f"General Config {self.general_config}") - with SingleComponentUpdateContext(self.fault_state): + with SingleComponentUpdateContext(self.fault_state, self.__initializer): source = self._get_carstate_source(vehicle_update_data) if source == SocSource.NO_UPDATE: log.debug("No soc update necessary.") return car_state = self._get_carstate_by_source(vehicle_update_data, source) + if isinstance(self.vehicle_config, MqttSocSetup) and car_state is None: + log.debug("Mqtt uses legacy topics.") + return log.debug(f"Requested start soc from {source.value}: {car_state.soc}%") if (source != SocSource.CALCULATION or @@ -73,8 +86,12 @@ def update(self, vehicle_update_data: VehicleUpdateData): self.calculated_soc_state.soc_start = car_state.soc Pub().pub(f"openWB/set/vehicle/{self.vehicle}/soc_module/calculated_soc_state", asdict(self.calculated_soc_state)) - if vehicle_update_data.soc_timestamp is None or vehicle_update_data.soc_timestamp < car_state.soc_timestamp: + if (vehicle_update_data.soc_timestamp is None or + vehicle_update_data.soc_timestamp <= car_state.soc_timestamp + 60): # Nur wenn der SoC neuer ist als der bisherige, diesen setzen. + # Manche Fahrzeuge liefern in Ladepausen zwar einen SoC, aber manchmal einen alten. + # Die Pro liefert manchmal den SoC nicht, bis nach dem Anstecken das SoC-Update getriggert wird. + # Wenn Sie dann doch noch den alten SoC liefert, darf dieser nicht verworfen werden. self.store.set(car_state) elif vehicle_update_data.soc_timestamp > 1e10: # car_state ist in ms geschrieben, dieser kann überschrieben werden @@ -83,15 +100,13 @@ def update(self, vehicle_update_data: VehicleUpdateData): log.debug("Not updating SoC, because timestamp is older.") def _get_carstate_source(self, vehicle_update_data: VehicleUpdateData) -> SocSource: - if isinstance(self.vehicle_config, MqttSocSetup): - return SocSource.NO_UPDATE # Kein SoC vom LP vorhanden oder erwünscht if (vehicle_update_data.soc_from_cp is None or self.general_config.use_soc_from_cp is False or # oder aktueller manueller SoC vorhanden (ausgelesenen SoC während der Ladung korrigieren) - self.calculated_soc_state.manual_soc): + self.calculated_soc_state.manual_soc is not None): if isinstance(self.vehicle_config, ManualSoc): # Wenn ein manueller SoC gesetzt wurde, diesen als neuen Start merken. - if self.calculated_soc_state.manual_soc or self.calculated_soc_state.imported_start is None: + if self.calculated_soc_state.manual_soc is not None or self.calculated_soc_state.imported_start is None: return SocSource.MANUAL else: if vehicle_update_data.plug_state: @@ -126,7 +141,10 @@ def _get_carstate_by_source(self, vehicle_update_data: VehicleUpdateData, source return CarState(soc=vehicle_update_data.soc_from_cp, soc_timestamp=vehicle_update_data.timestamp_soc_from_cp) elif source == SocSource.MANUAL: - soc = self.calculated_soc_state.manual_soc or self.calculated_soc_state.soc_start + if self.calculated_soc_state.manual_soc is not None: + soc = self.calculated_soc_state.manual_soc + else: + soc = self.calculated_soc_state.soc_start self.calculated_soc_state.manual_soc = None return CarState(soc) diff --git a/packages/modules/common/configurable_vehicle_test.py b/packages/modules/common/configurable_vehicle_test.py index 98e71dff49..f31bcf6603 100644 --- a/packages/modules/common/configurable_vehicle_test.py +++ b/packages/modules/common/configurable_vehicle_test.py @@ -120,7 +120,7 @@ def conf_vehicle_mqtt(): pytest.param(conf_vehicle_api_while_charging(), False, VehicleUpdateData(plug_state=True, charge_state=True), CalculatedSocState(), SocSource.CALCULATION, id="API mit Berechnung, Ladung"), pytest.param(conf_vehicle_mqtt(), False, VehicleUpdateData(plug_state=True), - CalculatedSocState(), SocSource.NO_UPDATE, id="Kein Update, da Werte per MQTT"), + CalculatedSocState(), SocSource.API, id="MQTT-Werte werden vom Broker abgerufen"), ]) def test_get_carstate_source(conf_vehicle: ConfigurableVehicle, use_soc_from_cp, diff --git a/packages/modules/common/evse.py b/packages/modules/common/evse.py index 670b779056..b50779cebb 100644 --- a/packages/modules/common/evse.py +++ b/packages/modules/common/evse.py @@ -6,12 +6,13 @@ from helpermodules.logger import ModifyLoglevelContext from modules.common import modbus +from modules.common.component_state import EvseState from modules.common.modbus import ModbusDataType log = logging.getLogger(__name__) -class EvseState(IntEnum): +class EvseStatusCode(IntEnum): READY = (1, False, False) EV_PRESENT = (2, True, False) CHARGING = (3, True, True) @@ -32,6 +33,19 @@ class Evse: def __init__(self, modbus_id: int, client: modbus.ModbusSerialClient_) -> None: self.client = client self.id = modbus_id + with client: + time.sleep(0.1) + self.version = self.client.read_holding_registers(1005, ModbusDataType.UINT_16, unit=self.id) + time.sleep(0.1) + self.max_current = self.client.read_holding_registers(2007, ModbusDataType.UINT_16, unit=self.id) + with ModifyLoglevelContext(log, logging.DEBUG): + log.debug(f"Firmware-Version der EVSE: {self.version}") + if self.version < 17: + self._precise_current = False + else: + if self.is_precise_current_active() is False: + self.activate_precise_current() + self._precise_current = self.is_precise_current_active() def get_plug_charge_state(self) -> Tuple[bool, bool, float]: time.sleep(0.1) @@ -41,8 +55,8 @@ def get_plug_charge_state(self) -> Tuple[bool, bool, float]: set_current = int(set_current) log.debug("Gesetzte Stromstärke EVSE: "+str(set_current) + ", Status: "+str(state_number)+", Modbus-ID: "+str(self.id)) - state = EvseState(state_number) - if state == EvseState.FAILURE: + state = EvseStatusCode(state_number) + if state == EvseStatusCode.FAILURE: raise ValueError("Unbekannter Zustand der EVSE: State " + str(state)+", Soll-Stromstärke: "+str(set_current)) plugged = state.plugged @@ -52,9 +66,15 @@ def get_plug_charge_state(self) -> Tuple[bool, bool, float]: return plugged, charging, set_current def get_firmware_version(self) -> int: - time.sleep(0.1) - version = self.client.read_holding_registers(1005, ModbusDataType.UINT_16, unit=self.id) - return version + return self.version + + def get_evse_state(self) -> EvseState: + plugged, charging, set_current = self.get_plug_charge_state() + state = EvseState(plug_state=plugged, + charge_state=charging, + set_current=set_current, + max_current=self.max_current) + return state def is_precise_current_active(self) -> bool: time.sleep(0.1) @@ -92,8 +112,3 @@ def deactivate_precise_current(self) -> None: def set_current(self, current: int) -> None: time.sleep(0.1) self.client.write_registers(1000, current, unit=self.id) - - def get_max_current(self) -> int: - time.sleep(0.1) - current = self.client.read_holding_registers(2007, ModbusDataType.UINT_16, unit=self.id) - return current diff --git a/packages/modules/common/fault_state.py b/packages/modules/common/fault_state.py index 7c1dd19254..7c4756174a 100644 --- a/packages/modules/common/fault_state.py +++ b/packages/modules/common/fault_state.py @@ -17,24 +17,20 @@ def __init__(self, name: str, type: str, hostname: str = "localhost", - parent_id: Optional[int] = None, - parent_hostname: Optional[str] = None) -> None: + hierarchy_id: Optional[int] = None) -> None: self.id = id self.name = name self.type = type self.hostname = hostname - self.parent_id = parent_id - self.parent_hostname = parent_hostname + self.hierarchy_id = hierarchy_id @staticmethod def from_component_config(component_config: ComponentSetup, - hostname: str = "localhost", - parent_hostname: Optional[str] = None): + hostname: str = "localhost"): return ComponentInfo(component_config.id, component_config.name, component_config.type, - hostname, - parent_hostname) + hostname) class FaultState(Exception): @@ -53,18 +49,15 @@ def store_error(self) -> None: topic = component_type.type_to_topic_mapping(self.component_info.type) if self.component_info.type == component_type.ComponentType.ELECTRICITY_TARIFF.value: topic_prefix = f"openWB/set/{topic}" - elif self.component_info.type == component_type.ComponentType.RIPPLE_CONTROL_RECEIVER.value: - topic_prefix = f"openWB/set/general/{topic}" else: topic_prefix = f"openWB/set/{topic}/{self.component_info.id}" pub.Pub().pub(f"{topic_prefix}/get/fault_str", self.fault_str) pub.Pub().pub(f"{topic_prefix}/get/fault_state", self.fault_state.value) - if (self.component_info.parent_hostname and - self.component_info.parent_hostname != self.component_info.hostname): - pub.pub_single(f"openWB/set/{topic}/{self.component_info.parent_id}/get/fault_str", - self.fault_str, hostname=self.component_info.parent_hostname) - pub.pub_single(f"openWB/set/{topic}/{self.component_info.parent_id}/get/fault_state", - self.fault_state.value, hostname=self.component_info.parent_hostname) + if self.component_info.type == "internal_chargepoint": + pub.pub_single(f"openWB/set/chargepoint/{self.component_info.hierarchy_id}/get/fault_str", + self.fault_str) + pub.pub_single(f"openWB/set/chargepoint/{self.component_info.hierarchy_id}/get/fault_state", + self.fault_state.value) except Exception: log.exception("Fehler im Modul fault_state") @@ -84,10 +77,7 @@ def no_error(self, message: Optional[str] = None) -> None: self.fault_state = FaultStateLevel.NO_ERROR def from_exception(self, exception: Optional[Exception] = None) -> None: - if exception is None: - self.fault_str = NO_ERROR - self.fault_state = FaultStateLevel.NO_ERROR - elif isinstance(exception, FaultState): + if isinstance(exception, FaultState): self.fault_str = exception.fault_str self.fault_state = exception.fault_state else: diff --git a/packages/modules/common/hardware_check.py b/packages/modules/common/hardware_check.py index fcb5cb508c..420c1f2958 100644 --- a/packages/modules/common/hardware_check.py +++ b/packages/modules/common/hardware_check.py @@ -1,6 +1,7 @@ import pymodbus -from typing import Any, List, Optional, Protocol, Tuple, Union +from typing import Any, Optional, Protocol, Tuple, Union +from modules.common.component_state import CounterState, EvseState from modules.common.evse import Evse from modules.common.fault_state import FaultState from modules.common.modbus import ModbusSerialClient_, ModbusTcpClient_ @@ -15,25 +16,48 @@ LAN_ADAPTER_BROKEN = (f"{RS485_ADAPTER_BROKEN.format('der LAN-Konverter abgestürzt,')} " "Bitte den openWB series2 satellit stromlos machen.") METER_PROBLEM = "Der Zähler konnte nicht ausgelesen werden. Vermutlich ist der Zähler falsch konfiguriert oder defekt." -METER_BROKEN = "Die Spannungen des Zählers konnten nicht korrekt ausgelesen werden: {}V Der Zähler ist defekt." +METER_BROKEN_VOLTAGES = "Die Spannungen des Zählers konnten nicht korrekt ausgelesen werden: {}V Der Zähler ist defekt." +METER_VOLTAGE = "Die Spannung des Zählers ist zu {}. Bitte prüfen Sie die Spannungsversorgung. Spannung: {}V." +METER_VOLTAGE_TOO_HIGH = METER_VOLTAGE.format("hoch", "{}") +METER_VOLTAGE_TOO_LOW = METER_VOLTAGE.format("niedrig", "{}") METER_NO_SERIAL_NUMBER = ("Die Seriennummer des Zählers für das Ladelog kann nicht ausgelesen werden. Wenn Sie die " "Seriennummer für Abrechnungszwecke benötigen, wenden Sie sich bitte an unseren Support. Die " "Funktionalität wird dadurch nicht beeinträchtigt!") EVSE_BROKEN = ("Auslesen der EVSE nicht möglich. Vermutlich ist die EVSE defekt oder hat eine unbekannte Modbus-ID. " "(Fehlermeldung nur relevant, wenn diese auf der Startseite oder im Status angezeigt wird.)") +METER_IMPLAUSIBLE_VALUE = ("Der Zähler hat einen unplausiblen Wert zurückgegeben: Leistungen {}W, Ströme {}A, " + "Spannungen {}V.") -def check_meter_values(voltages: List[float]) -> Optional[str]: +def check_meter_values(counter_state: CounterState, fault_state: Optional[FaultState] = None) -> None: + meter_msg = _check_meter_values(counter_state) + if fault_state and meter_msg: + fault_state.warning(meter_msg) + + +def _check_meter_values(counter_state: CounterState) -> Optional[str]: + VOLTAGE_HIGH_THRESHOLD = 260 + VOLTAGE_LOW_THRESHOLD = 200 + VOLTAGE_DETECTED_THRESHOLD = 50 # Phasenaufall detektieren + def valid_voltage(voltage) -> bool: - return 200 < voltage < 260 - if ((valid_voltage(voltages[0]) and voltages[1] == 0 and voltages[2] == 0) or + return VOLTAGE_LOW_THRESHOLD < voltage < VOLTAGE_HIGH_THRESHOLD + voltages = counter_state.voltages + # wenn ein Wert in voltages großer VOLTAGE_HIGH_THRESHOLD ist, gebe eine Fehlermeldung zurück + if any(v > VOLTAGE_HIGH_THRESHOLD and v > VOLTAGE_DETECTED_THRESHOLD for v in voltages): + return METER_VOLTAGE_TOO_HIGH.format(voltages) + elif any(v < VOLTAGE_LOW_THRESHOLD and v > VOLTAGE_DETECTED_THRESHOLD for v in voltages): + return METER_VOLTAGE_TOO_LOW.format(voltages) + if not ((valid_voltage(voltages[0]) and voltages[1] == 0 and voltages[2] == 0) or # Zoe lädt einphasig an einphasiger Wallbox und erzeugt Spannung auf L2 (ca 126V) (valid_voltage(voltages[0]) and 115 < voltages[1] < 135 and voltages[2] == 0) or (valid_voltage(voltages[0]) and valid_voltage(voltages[1]) and voltages[2] == 0) or (valid_voltage(voltages[0]) and valid_voltage(voltages[1]) and valid_voltage((voltages[2])))): - return None - else: - return METER_BROKEN.format(voltages) + return METER_BROKEN_VOLTAGES.format(voltages) + if ((sum(counter_state.currents) < 0.5 and counter_state.power > 230) or + (sum(counter_state.currents) > 1 and counter_state.power < 100)): + return METER_IMPLAUSIBLE_VALUE.format(counter_state.powers, counter_state.currents, counter_state.voltages) + return None class ClientHandlerProtocol(Protocol): @@ -49,6 +73,12 @@ def evse_client(self) -> Evse: ... def meter_client(self) -> Any: ... @property def read_error(self) -> int: ... + @property + def handle_exception(self, exception: Exception) -> bool: ... + @property + def request_and_check_hardware(self, fault_state: FaultState) -> Tuple[EvseState, CounterState]: ... + @property + def check_meter(self) -> Tuple[bool, Optional[str], CounterState]: ... class SeriesHardwareCheckMixin: @@ -63,16 +93,20 @@ def handle_exception(self: ClientHandlerProtocol, exception: Exception): else: return False - def check_hardware(self: ClientHandlerProtocol, fault_state: FaultState): - + def request_and_check_hardware(self: ClientHandlerProtocol, + fault_state: FaultState) -> Tuple[EvseState, CounterState]: try: - if self.evse_client.get_firmware_version() > EVSE_MIN_FIRMWARE: - evse_check_passed = True - else: - evse_check_passed = False + with self.client: + evse_state = self.evse_client.get_evse_state() + evse_check_passed = True except Exception as e: evse_check_passed = self.handle_exception(e) - meter_check_passed, meter_error_msg = self.check_meter() + meter_check_passed, meter_error_msg, counter_state = self.check_meter() + if meter_check_passed is False and evse_check_passed is False: + if isinstance(self.client, ModbusTcpClient_): + raise Exception(LAN_ADAPTER_BROKEN) + else: + raise Exception(USB_ADAPTER_BROKEN) if meter_check_passed is False: if evse_check_passed is False: if isinstance(self.client, ModbusTcpClient_): @@ -90,12 +124,14 @@ def check_hardware(self: ClientHandlerProtocol, fault_state: FaultState): raise Exception(EVSE_BROKEN + " " + meter_error_msg + OPEN_TICKET) else: raise Exception(EVSE_BROKEN + OPEN_TICKET) + return evse_state, counter_state - def check_meter(self: ClientHandlerProtocol) -> Tuple[bool, Optional[str]]: + def check_meter(self: ClientHandlerProtocol) -> Tuple[bool, Optional[str], CounterState]: try: - serial_number = self.meter_client.get_serial_number() - if serial_number == "0" or serial_number is None: - return True, METER_NO_SERIAL_NUMBER - return True, check_meter_values(self.meter_client.get_voltages()) + with self.client: + counter_state = self.meter_client.get_counter_state() + if counter_state.serial_number == "0" or counter_state.serial_number is None: + return True, METER_NO_SERIAL_NUMBER, counter_state + return True, _check_meter_values(counter_state), counter_state except Exception: - return False, METER_PROBLEM + return False, METER_PROBLEM, None diff --git a/packages/modules/common/hardware_check_test.py b/packages/modules/common/hardware_check_test.py index b88dac6464..cde1b270dc 100644 --- a/packages/modules/common/hardware_check_test.py +++ b/packages/modules/common/hardware_check_test.py @@ -1,39 +1,41 @@ import re from typing import List, Optional, Tuple, Union -from unittest.mock import MagicMock, Mock, patch +from unittest.mock import Mock, patch import pytest from modules.common import sdm +from modules.common import hardware_check +from modules.common.component_state import CounterState, EvseState from modules.common.evse import Evse from modules.common.hardware_check import ( - EVSE_BROKEN, LAN_ADAPTER_BROKEN, METER_BROKEN, METER_NO_SERIAL_NUMBER, METER_PROBLEM, OPEN_TICKET, - USB_ADAPTER_BROKEN, SeriesHardwareCheckMixin, check_meter_values) -from modules.common.modbus import NO_CONNECTION, ModbusClient, ModbusSerialClient_, ModbusTcpClient_ + EVSE_BROKEN, LAN_ADAPTER_BROKEN, METER_BROKEN_VOLTAGES, METER_IMPLAUSIBLE_VALUE, METER_NO_SERIAL_NUMBER, + METER_PROBLEM, METER_VOLTAGE_TOO_HIGH, METER_VOLTAGE_TOO_LOW, OPEN_TICKET, USB_ADAPTER_BROKEN, + SeriesHardwareCheckMixin, _check_meter_values) +from modules.common.modbus import NO_CONNECTION, ModbusSerialClient_, ModbusTcpClient_ from modules.conftest import SAMPLE_IP, SAMPLE_PORT from modules.internal_chargepoint_handler.clients import ClientHandler @pytest.mark.parametrize( - ("evse_side_effect, evse_return_value, meter_side_effect, meter_return_value, handle_exception_side_effect," + ("evse_side_effect, meter_side_effect, meter_return_value, handle_exception_side_effect," "handle_exception_return_value, client_spec, expected_error_msg"), - [pytest.param(Exception("Modbus"), None, None, [230]*3, None, False, ModbusSerialClient_, EVSE_BROKEN, + [pytest.param(Exception("Modbus"), None, [230]*3, None, False, ModbusSerialClient_, EVSE_BROKEN, id="EVSE defekt"), - pytest.param(Exception("Modbus"), None, None, [230, 0, 230], None, False, ModbusSerialClient_, - EVSE_BROKEN + " " + METER_BROKEN.format([230, 0, 230]) + OPEN_TICKET, + pytest.param(Exception("Modbus"), None, [230, 0, 230], None, False, ModbusSerialClient_, + EVSE_BROKEN + " " + METER_BROKEN_VOLTAGES.format([230, 0, 230]) + OPEN_TICKET, id="EVSE defekt und Zähler eine Phase defekt"), - pytest.param(None, 18, Exception("Modbus"), None, None, None, + pytest.param(None, Exception("Modbus"), None, None, None, ModbusSerialClient_, METER_PROBLEM, id="Zähler falsch konfiguriert"), - pytest.param(Exception("Modbus"), None, Exception("Modbus"), None, None, False, ModbusSerialClient_, + pytest.param(Exception("Modbus"), Exception("Modbus"), None, None, False, ModbusSerialClient_, USB_ADAPTER_BROKEN, id="USB-Adapter defekt"), - pytest.param(Exception("Modbus"), None, Exception("Modbus"), None, None, False, ModbusTcpClient_, + pytest.param(Exception("Modbus"), Exception("Modbus"), None, None, False, ModbusTcpClient_, LAN_ADAPTER_BROKEN, id="LAN-Adapter defekt"), - pytest.param(Exception("Modbus"), None, Exception("Modbus"), None, + pytest.param(Exception("Modbus"), Exception("Modbus"), None, Exception(NO_CONNECTION.format(SAMPLE_IP, SAMPLE_PORT)), None, ModbusTcpClient_, NO_CONNECTION.format(SAMPLE_IP, SAMPLE_PORT), id="LAN-Adapter nicht erreichbar"), ] ) def test_hardware_check_fails(evse_side_effect, - evse_return_value, meter_side_effect, meter_return_value, handle_exception_side_effect, @@ -42,87 +44,127 @@ def test_hardware_check_fails(evse_side_effect, expected_error_msg, monkeypatch): # setup - mock_evse_client = Mock(spec=Evse, get_firmware_version=Mock( - side_effect=evse_side_effect, return_value=evse_return_value)) - mock_evse_factory = Mock(spec=Evse, return_value=mock_evse_client) - monkeypatch.setattr(ClientHandler, "_evse_factory", mock_evse_factory) + mock_evse_client = Mock(spec=Evse, version=18, get_evse_state=Mock(side_effect=[evse_side_effect])) + monkeypatch.setattr(ClientHandler, "_evse_factory", Mock(return_value=mock_evse_client)) - mock_meter_client = Mock(spec=sdm.Sdm630_72, get_voltages=Mock( - side_effect=meter_side_effect, return_value=meter_return_value)) - mock_find_meter_client = Mock(spec=sdm.Sdm630_72, return_value=mock_meter_client) - monkeypatch.setattr(ClientHandler, "find_meter_client", mock_find_meter_client) + counter_state_mock = Mock(spec=CounterState, + voltages=meter_return_value, + currents=[0, 0, 0], + powers=[0, 0, 0], + power=0, + serial_number="1234") + mock_meter_client = Mock(spec=sdm.Sdm630_72, get_counter_state=Mock( + side_effect=meter_side_effect, return_value=counter_state_mock)) + monkeypatch.setattr(ClientHandler, "find_meter_client", Mock(return_value=mock_meter_client)) - handle_exception_mock = Mock(side_effect=handle_exception_side_effect, return_value=handle_exception_return_value) - monkeypatch.setattr(SeriesHardwareCheckMixin, "handle_exception", handle_exception_mock) + monkeypatch.setattr(SeriesHardwareCheckMixin, "handle_exception", Mock( + side_effect=handle_exception_side_effect, return_value=handle_exception_return_value)) - mock_modbus_client = MagicMock(spec=client_spec, address=SAMPLE_IP, port=SAMPLE_PORT) - mock_modbus_client.__enter__.return_value = mock_modbus_client + client = Mock(spec=client_spec, __enter__=Mock(return_value=None), __exit__=Mock(return_value=None)) # execution and evaluation with pytest.raises(Exception, match=re.escape(expected_error_msg)): - ClientHandler(0, mock_modbus_client, [1], Mock()) + ClientHandler(0, client, [1], Mock()) def test_hardware_check_succeeds(monkeypatch): # setup - mock_evse_client = Mock(spec=Evse, get_firmware_version=Mock(return_value=18)) - mock_evse_factory = Mock(spec=Evse, return_value=mock_evse_client) - monkeypatch.setattr(ClientHandler, "_evse_factory", mock_evse_factory) + mock_evse_client = Mock(spec=Evse, get_evse_state=Mock(return_value=Mock(spec=EvseState)), version=17) + mock_evse_facotry = Mock(return_value=mock_evse_client) + monkeypatch.setattr(ClientHandler, "_evse_factory", mock_evse_facotry) - mock_meter_client = Mock(spec=sdm.Sdm630_72, get_voltages=Mock(return_value=[230]*3)) + counter_state_mock = Mock(spec=CounterState, voltages=[ + 230]*3, currents=[0, 0, 0], powers=[0, 0, 0], power=0, serial_number="1234") + mock_meter_client = Mock(spec=sdm.Sdm630_72, get_counter_state=Mock(return_value=counter_state_mock)) mock_find_meter_client = Mock(spec=sdm.Sdm630_72, return_value=mock_meter_client) monkeypatch.setattr(ClientHandler, "find_meter_client", mock_find_meter_client) - mock_modbus_client = MagicMock(spec=ModbusClient) - mock_modbus_client.__enter__.return_value = mock_modbus_client + enter_mock = Mock(return_value=None) + exit_mock = Mock(return_value=True) + client = Mock(spec=ModbusTcpClient_, __enter__=enter_mock, __exit__=exit_mock) # execution and evaluation # keine Exception - ClientHandler(0, mock_modbus_client, [1], Mock()) + ClientHandler(0, client, [1], Mock()) @pytest.mark.parametrize( - "voltages, expected_msg", - [pytest.param([230, 0, 0], None, id="einphasig oder zweiphasig L2 defekt (nicht erkennbar)"), - pytest.param([0, 0, 0], METER_BROKEN, id="einphasig, L1 defekt"), - pytest.param([230, 230, 0], None, id="zweiphasig oder dreiphasig, L3 defekt (nicht erkennbar)"), - pytest.param([0, 230, 0], METER_BROKEN, id="zweiphasig, L1 defekt"), - pytest.param([230, 230, 230], None, id="dreiphasig"), - pytest.param([0, 230, 230], METER_BROKEN, id="dreiphasig, L1 defekt"), - pytest.param([230, 0, 230], METER_BROKEN, id="dreiphasig, L2 defekt"), + "voltages, power, expected_msg", + [pytest.param([230, 0, 0], 0, None, id="einphasig oder zweiphasig L2 defekt (nicht erkennbar)"), + pytest.param([0, 0, 0], 0, METER_BROKEN_VOLTAGES.format([0]*3), id="einphasig, L1 defekt"), + pytest.param([230, 230, 0], 0, None, id="zweiphasig oder dreiphasig, L3 defekt (nicht erkennbar)"), + pytest.param([0, 230, 0], 0, METER_BROKEN_VOLTAGES.format([0, 230, 0]), id="zweiphasig, L1 defekt"), + pytest.param([230, 230, 230], 0, None, id="dreiphasig"), + pytest.param([0, 230, 230], 0, METER_BROKEN_VOLTAGES.format([0, 230, 230]), id="dreiphasig, L1 defekt"), + pytest.param([230, 0, 230], 0, METER_BROKEN_VOLTAGES.format([230, 0, 230]), id="dreiphasig, L2 defekt"), + pytest.param([230]*3, 100, METER_PROBLEM, id="Phantom-Leistung"), + pytest.param([261, 230, 230], 0, METER_VOLTAGE_TOO_HIGH.format([261, 230, 230]), id="Spannung zu hoch"), + pytest.param([230, 230, 199], 0, METER_VOLTAGE_TOO_LOW.format([230, 230, 199]), id="Spannung zu niedrig"), ] ) -def test_check_meter_values(voltages, expected_msg, monkeypatch): - # setup & execution - msg = check_meter_values(voltages) +def test_check_meter_values_voltages(voltages, power, expected_msg, monkeypatch): + # setup + counter_state = Mock(voltages=voltages, currents=[0, 0, 0], powers=[0, 0, 0], power=power) + # execution + msg = _check_meter_values(counter_state) # assert assert msg == expected_msg if expected_msg is None else expected_msg.format(voltages) +@pytest.mark.parametrize( + "currents, power, expected_msg", + [pytest.param([0.4, 0, 0], -80, None, id="Kriechströme"), + pytest.param([1, 0, 0], 0, METER_IMPLAUSIBLE_VALUE.format([0]*3, [1, 0, 0], [230]*3), + id="zu hoher positiver Strom"), + pytest.param([0, -1, 0], 0, METER_IMPLAUSIBLE_VALUE.format([0]*3, [0, -1, 0], [230]*3), + id="zu hoher negativer Strom"), + pytest.param([0.1, 0, 0], 120, METER_IMPLAUSIBLE_VALUE.format([40]*3, [0.1, 5, 0], [230]*3), + id="zu niedriger Strom bei Leistung"), + ] +) +def test_check_meter_values_powers(currents, power, expected_msg, monkeypatch): + # setup + counter_state = Mock(spec=CounterState, voltages=[230]*3, currents=currents, powers=[power/3]*3, power=power) + # execution + msg = _check_meter_values(counter_state) + + # assert + assert msg == expected_msg if expected_msg is None else expected_msg.format(counter_state.powers, + counter_state.currents, + counter_state.voltages) + + @patch('modules.common.hardware_check.ClientHandlerProtocol') -@pytest.mark.parametrize("serial_number_return, voltages_return, expected", - [("0", [230]*3, (True, METER_NO_SERIAL_NUMBER)), - (12345, [230]*3, (True, None)), - (Exception(), [230]*3, (False, METER_PROBLEM))]) +@pytest.mark.parametrize("serial_number, voltages, expected", + [("0", [230]*3, (True, METER_NO_SERIAL_NUMBER, CounterState)), + (12345, [230]*3, (True, None, CounterState)), + (Exception(), [230]*3, (False, METER_PROBLEM, None))]) def test_check_meter( MockClientHandlerProtocol: Mock, - serial_number_return: Union[int, Exception], - voltages_return: List[int], + serial_number: Union[int, Exception], + voltages: List[int], expected: Tuple[bool, Optional[str]], + monkeypatch, ): # Arrange - mock_meter_client = Mock() - if isinstance(serial_number_return, Exception): - mock_meter_client.get_serial_number.side_effect = serial_number_return + + if isinstance(serial_number, Exception): + counter_state_mock = Mock(spec=CounterState, side_effect=serial_number) else: - mock_meter_client.get_serial_number.return_value = serial_number_return - mock_meter_client.get_voltages.return_value = voltages_return + counter_state_mock = Mock(spec=CounterState, serial_number=serial_number, + voltages=voltages, currents=[0, 0, 0], powers=[0, 0, 0], power=0) + mock_meter_client = Mock() + mock_meter_client.get_counter_state.return_value = counter_state_mock MockClientHandlerProtocol.meter_client = mock_meter_client mixin = SeriesHardwareCheckMixin + mock_check_meter_values = Mock(return_value=expected[1]) + monkeypatch.setattr(hardware_check, "check_meter_values", mock_check_meter_values) # Act result = mixin.check_meter(MockClientHandlerProtocol) # Assert - assert result == expected + assert result[0] == expected[0] + assert result[1] == expected[1] + assert isinstance(result[2], expected[2] if expected[2] is not None else type(result[2])) diff --git a/packages/modules/common/io_setup.py b/packages/modules/common/io_setup.py new file mode 100644 index 0000000000..5ae5792e12 --- /dev/null +++ b/packages/modules/common/io_setup.py @@ -0,0 +1,23 @@ + +from typing import Dict, Generic, Optional, TypeVar + +from dataclass_utils.factories import empty_dict_factory + + +T = TypeVar("T") + + +class IoDeviceSetup(Generic[T]): + def __init__(self, + name: str, + type: str, + id: int, + configuration: T, + input: Optional[Dict[str, Dict[int, float]]] = None, + output: Optional[Dict[str, Dict[int, float]]] = None) -> None: + self.name = name + self.type = type + self.id = id + self.configuration = configuration + self.input = input if input is not None else empty_dict_factory() + self.output = output if output is not None else empty_dict_factory() diff --git a/packages/modules/common/lovato.py b/packages/modules/common/lovato.py index 504f37687d..cb8a0b596e 100644 --- a/packages/modules/common/lovato.py +++ b/packages/modules/common/lovato.py @@ -3,13 +3,17 @@ from modules.common import modbus from typing import List, Tuple from modules.common.abstract_counter import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.fault_state import FaultState +from modules.common.hardware_check import check_meter_values from modules.common.modbus import ModbusDataType class Lovato(AbstractCounter): - def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: self.client = client self.id = modbus_id + self.fault_state = fault_state def get_voltages(self) -> List[float]: return [val / 100 for val in self.client.read_input_registers( @@ -36,3 +40,16 @@ def get_frequency(self) -> float: def get_currents(self) -> List[float]: return [val / 10000 for val in self.client.read_input_registers( 0x0007, [ModbusDataType.INT_32]*3, unit=self.id)] + + def get_counter_state(self) -> CounterState: + powers, power = self.get_power() + counter_state = CounterState( + power=power, + voltages=self.get_voltages(), + currents=self.get_currents(), + powers=powers, + power_factors=self.get_power_factors(), + frequency=self.get_frequency() + ) + check_meter_values(counter_state, self.fault_state) + return counter_state diff --git a/packages/modules/common/modbus.py b/packages/modules/common/modbus.py index f81f28b41e..49fbaa4ac9 100644 --- a/packages/modules/common/modbus.py +++ b/packages/modules/common/modbus.py @@ -14,6 +14,7 @@ from pymodbus.client.sync import ModbusTcpClient, ModbusSerialClient from pymodbus.constants import Endian from pymodbus.payload import BinaryPayloadDecoder +from pymodbus.transaction import ModbusSocketFramer from urllib3.util import parse_url log = logging.getLogger(__name__) @@ -188,18 +189,22 @@ def read_coils(self, address: int, count: int, **kwargs): def write_registers(self, address: int, value: Any, **kwargs): self._delegate.write_registers(address, value, **kwargs) + def write_single_coil(self, address: int, value: Any, **kwargs): + self._delegate.write_coil(address, value, **kwargs) + class ModbusTcpClient_(ModbusClient): def __init__(self, address: str, port: int = 502, sleep_after_connect: Optional[int] = 0, + framer: type[ModbusSocketFramer] = ModbusSocketFramer, **kwargs): parsed_url = parse_url(address) host = parsed_url.host if parsed_url.port is not None: port = parsed_url.port - super().__init__(ModbusTcpClient(host, port, **kwargs), address, port, sleep_after_connect) + super().__init__(ModbusTcpClient(host, port, framer, **kwargs), address, port, sleep_after_connect) class ModbusSerialClient_(ModbusClient): diff --git a/packages/modules/common/mpm3pm.py b/packages/modules/common/mpm3pm.py index 6cc99fbbe0..4791398dc7 100644 --- a/packages/modules/common/mpm3pm.py +++ b/packages/modules/common/mpm3pm.py @@ -3,13 +3,17 @@ from modules.common import modbus from modules.common.abstract_counter import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.fault_state import FaultState +from modules.common.hardware_check import check_meter_values from modules.common.modbus import ModbusDataType class Mpm3pm(AbstractCounter): - def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: self.client = client self.id = modbus_id + self.fault_state = fault_state def get_voltages(self) -> List[float]: return [val / 10 for val in self.client.read_input_registers( @@ -47,3 +51,19 @@ def get_currents(self) -> List[float]: def get_serial_number(self) -> str: return str(self.client.read_input_registers(0x33, ModbusDataType.UINT_32, unit=self.id)) + + def get_counter_state(self) -> CounterState: + powers, power = self.get_power() + counter_state = CounterState( + voltages=self.get_voltages(), + currents=self.get_currents(), + powers=powers, + power=power, + power_factors=self.get_power_factors(), + imported=self.get_imported(), + exported=self.get_exported(), + frequency=self.get_frequency(), + serial_number=self.get_serial_number() + ) + check_meter_values(counter_state, self.fault_state) + return counter_state diff --git a/packages/modules/common/req.py b/packages/modules/common/req.py index f05ad5492e..3fe978deff 100644 --- a/packages/modules/common/req.py +++ b/packages/modules/common/req.py @@ -1,16 +1,37 @@ - import copy import logging from requests import Session +import urllib3 +from functools import wraps +import warnings log = logging.getLogger(__name__) +def disable_insecure_request_warning(func): + @wraps(func) + def wrapper(*args, **kwargs): + if kwargs.get('verify') is False: + # store the original filters to restore them after the request + original_filters = warnings.filters[:] + # disable the warning for this request + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + try: + return func(*args, **kwargs) + finally: + # restore the original filters + warnings.filters = original_filters + else: + return func(*args, **kwargs) + return wrapper + + class CustomSession(Session): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.default_timeout = 5 + @disable_insecure_request_warning def request(self, method, url, *args, **kwargs): kwargs.setdefault('timeout', self.default_timeout) return super().request(method, url, *args, **kwargs) diff --git a/packages/modules/common/restart_protoss_admin b/packages/modules/common/restart_protoss_admin new file mode 100755 index 0000000000..5acf807caa Binary files /dev/null and b/packages/modules/common/restart_protoss_admin differ diff --git a/packages/modules/common/sdm.py b/packages/modules/common/sdm.py index 5f3ffd053e..b77eec9eac 100644 --- a/packages/modules/common/sdm.py +++ b/packages/modules/common/sdm.py @@ -1,8 +1,12 @@ #!/usr/bin/env python3 +import time from typing import List, Tuple from modules.common import modbus from modules.common.abstract_counter import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.fault_state import FaultState +from modules.common.hardware_check import check_meter_values from modules.common.modbus import ModbusDataType @@ -10,49 +14,115 @@ class Sdm(AbstractCounter): def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: self.client = client self.id = modbus_id + self.last_query = self._get_time_ms() + self.WAIT_MS_BETWEEN_QUERIES = 100 + with client: + self.serial_number = str(self.client.read_holding_registers(0xFC00, ModbusDataType.UINT_32, unit=self.id)) def get_imported(self) -> float: + self._ensure_min_time_between_queries() return self.client.read_input_registers(0x0048, ModbusDataType.FLOAT_32, unit=self.id) * 1000 def get_exported(self) -> float: + self._ensure_min_time_between_queries() return self.client.read_input_registers(0x004a, ModbusDataType.FLOAT_32, unit=self.id) * 1000 def get_frequency(self) -> float: + self._ensure_min_time_between_queries() frequency = self.client.read_input_registers(0x46, ModbusDataType.FLOAT_32, unit=self.id) if frequency > 100: frequency = frequency / 10 return frequency + # These meters require some minimum time between subsequent Modbus reads. Some Eastron papers recommend 100 ms. + # Sometimes the time between calls to the get_* methods are much shorter so we forcibly wait for the remaining time. + def _ensure_min_time_between_queries(self) -> None: + current_time = self._get_time_ms() + elapsed_time = current_time - self.last_query + if elapsed_time < self.WAIT_MS_BETWEEN_QUERIES: + time.sleep((self.WAIT_MS_BETWEEN_QUERIES - elapsed_time) / 1e3) + self.last_query = current_time + + def _get_time_ms(self) -> float: + return time.time_ns() / 1e6 + def get_serial_number(self) -> str: - return str(self.client.read_holding_registers(0xFC00, ModbusDataType.UINT_32, unit=self.id)) + return self.serial_number class Sdm630_72(Sdm): - def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: super().__init__(modbus_id, client) + self.fault_state = fault_state def get_currents(self) -> List[float]: + self._ensure_min_time_between_queries() return self.client.read_input_registers(0x06, [ModbusDataType.FLOAT_32]*3, unit=self.id) def get_power_factors(self) -> List[float]: + self._ensure_min_time_between_queries() return self.client.read_input_registers(0x1E, [ModbusDataType.FLOAT_32]*3, unit=self.id) def get_power(self) -> Tuple[List[float], float]: + self._ensure_min_time_between_queries() powers = self.client.read_input_registers(0x0C, [ModbusDataType.FLOAT_32]*3, unit=self.id) power = sum(powers) return powers, power def get_voltages(self) -> List[float]: + self._ensure_min_time_between_queries() return self.client.read_input_registers(0x00, [ModbusDataType.FLOAT_32]*3, unit=self.id) + def get_counter_state(self) -> CounterState: + powers, power = self.get_power() + counter_state = CounterState( + imported=self.get_imported(), + exported=self.get_exported(), + power=power, + voltages=self.get_voltages(), + currents=self.get_currents(), + powers=powers, + power_factors=self.get_power_factors(), + frequency=self.get_frequency(), + serial_number=self.get_serial_number() + ) + check_meter_values(counter_state, self.fault_state) + return counter_state + class Sdm120(Sdm): - def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_) -> None: + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: super().__init__(modbus_id, client) + self.fault_state = fault_state def get_power(self) -> Tuple[List[float], float]: + self._ensure_min_time_between_queries() power = self.client.read_input_registers(0x0C, ModbusDataType.FLOAT_32, unit=self.id) return [power, 0, 0], power def get_currents(self) -> List[float]: - return [self.client.read_input_registers(0x06, ModbusDataType.FLOAT_32, unit=self.id), 0, 0] + self._ensure_min_time_between_queries() + return [self.client.read_input_registers(0x06, ModbusDataType.FLOAT_32, unit=self.id), 0.0, 0.0] + + def get_voltages(self) -> List[float]: + self._ensure_min_time_between_queries() + voltage = self.client.read_input_registers(0x00, ModbusDataType.FLOAT_32, unit=self.id) + return [voltage, 0.0, 0.0] + + def get_power_factors(self) -> List[float]: + self._ensure_min_time_between_queries() + return [self.client.read_input_registers(0x1E, ModbusDataType.FLOAT_32, unit=self.id), 0.0, 0.0] + + def get_counter_state(self) -> CounterState: + powers, power = self.get_power() + counter_state = CounterState( + imported=self.get_imported(), + exported=self.get_exported(), + power=power, + currents=self.get_currents(), + powers=powers, + frequency=self.get_frequency(), + serial_number=self.get_serial_number() + ) + check_meter_values(counter_state, self.fault_state) + return counter_state diff --git a/packages/modules/common/simcount/_simcounter_store.py b/packages/modules/common/simcount/_simcounter_store.py index 2c0cb14158..9f5700c287 100644 --- a/packages/modules/common/simcount/_simcounter_store.py +++ b/packages/modules/common/simcount/_simcounter_store.py @@ -1,18 +1,13 @@ import logging from abc import abstractmethod -from enum import Enum -from queue import Queue, Empty from typing import Optional -from paho.mqtt.client import Client as MqttClient, MQTTMessage from control import data -from helpermodules import pub, compatibility +from helpermodules import pub from helpermodules.utils.topic_parser import get_index, get_second_index from modules.common.component_type import type_to_topic_mapping from modules.common.simcount.simcounter_state import SimCounterState -from modules.common.store import ramdisk_write, ramdisk_read_float -from modules.common.store.ramdisk.io import RamdiskReadError POSTFIX_EXPORT = "watt0neg" POSTFIX_IMPORT = "watt0pos" @@ -20,98 +15,6 @@ log = logging.getLogger(__name__) -def get_serial(): - try: - with open('/proc/cpuinfo', 'r') as f: - for line in f: - if line[0:6] == 'Serial': - return line[10:26] - except Exception: - log.exception("Could not read serial from cpuinfo") - - return "0000000000000000" - - -def read_mqtt_topic(topic: str) -> Optional[str]: - """Reads and returns the first message received for the specified topic. - - Returns None if no value is received before timeout""" - - def on_message(_client, _userdata, message: MQTTMessage): - queue.put(message.payload.decode("utf-8")) - - def on_connect(*_args): - client.subscribe(topic) - - queue = Queue(1) # type: Queue[str] - client = MqttClient("openWB-simcounter-" + get_serial()) - try: - client.on_connect = on_connect - client.on_message = on_message - client.connect("localhost", 1883) - client.loop_start() - return queue.get(block=True, timeout=.5) - except Empty: - return None - finally: - client.disconnect() - - -class SimCountPrefix(Enum): - PV = ("pv", None, "pvkwh") - PV2 = ("pv2", None, "pvkwh2") - BEZUG = ("evu", "bezugkwh", "einspeisungkwh") - SPEICHER = ("housebattery", "speicherikwh", "speicherekwh") - - def __init__(self, topic: str, import_file: Optional[str], export_file: str): - self.topic = topic - self.import_file = import_file - self.export_file = export_file - - def read_import(self) -> float: - return self.__read_file(self.import_file) - - def read_export(self) -> float: - return self.__read_file(self.export_file) - - @staticmethod - def __read_file(file: str) -> float: - if file is None: - return 0.0 - try: - result = ramdisk_read_float(file) - log.info("Found counter reading <%g Wh> in file <%s>", result, file) - return result - except FileNotFoundError: - return 0.0 - - -def restore_value(prefix_str: str, postfix: str) -> float: - prefix = SimCountPrefix[prefix_str.upper()] - # topic = "openWB/" + prefix.topic + ("/WHImported_temp" if postfix == POSTFIX_IMPORT else "/WHExport_temp") - if prefix.topic == "pv2": - topic = "openWB/pv" + ("/WH2Imported_temp" if postfix == POSTFIX_IMPORT else "/WH2Export_temp") - else: - topic = "openWB/" + prefix.topic + ("/WHImported_temp" if postfix == POSTFIX_IMPORT else "/WHExport_temp") - mqtt_value = read_mqtt_topic(topic) - log.info("read from broker: %s=%s", topic, mqtt_value) - if mqtt_value is None: - result = None - else: - try: - # MQTT-Value is in Watt-seconds for historic reasons -> / 3600 - result = float(mqtt_value) / 3600 - except ValueError: - log.warning("Value <%s> from topic <%s> is not a valid number", mqtt_value, topic) - result = None - - if result is None: - result = (prefix.read_import if postfix == POSTFIX_IMPORT else prefix.read_export)() - - ramdisk_write(prefix_str + postfix, result) - return result - - class SimCounterStore: @abstractmethod def load(self, prefix: str, topic: str) -> Optional[SimCounterState]: @@ -126,52 +29,6 @@ def initialize(self, prefix: str, topic: str, power: float, timestamp: float) -> pass -class SimCounterStoreRamdisk(SimCounterStore): - def initialize(self, prefix: str, topic: str, power: float, timestamp: float) -> SimCounterState: - result = SimCounterState( - timestamp, power, restore_value(prefix, POSTFIX_IMPORT), restore_value(prefix, POSTFIX_EXPORT) - ) - self.save(prefix, topic, result) - return result - - def load(self, prefix: str, topic: str) -> Optional[SimCounterState]: - try: - timestamp = ramdisk_read_float(prefix + "sec0") - except FileNotFoundError: - return None - - def read_or_restore(postfix: str) -> float: - try: - # ramdisk value is in watt-seconds for historic reasons -> / 3600 - return ramdisk_read_float(prefix + postfix) / 3600 - except (FileNotFoundError, RamdiskReadError) as e: - log.warning("Read from ramdisk failed: %s. Attempting restore from broker", e) - return restore_value(prefix, postfix) - - return SimCounterState( - timestamp=timestamp, - power=ramdisk_read_float(prefix + "wh0"), - imported=read_or_restore(POSTFIX_IMPORT), - # abs() weil runs/simcount.py speichert das Zwischenergebnis des Exports negativ ab: - exported=abs(read_or_restore(POSTFIX_EXPORT)), - ) - - def save(self, prefix: str, topic: str, state: SimCounterState): - topic = SimCountPrefix[prefix.upper()].topic - ramdisk_write(prefix + "sec0", state.timestamp) - ramdisk_write(prefix + "wh0", state.power) - - # For historic reasons, the SimCount stored state uses Watt-seconds instead of Watt-hours -> * 3600: - ramdisk_write(prefix + POSTFIX_IMPORT, state.imported * 3600) - ramdisk_write(prefix + POSTFIX_EXPORT, state.exported * 3600) - if topic == "pv2": - pub.pub_single("openWB/pv/WH2Imported_temp", state.imported * 3600, no_json=True) - pub.pub_single("openWB/pv/WH2Export_temp", state.exported * 3600, no_json=True) - else: - pub.pub_single("openWB/" + topic + "/WHImported_temp", state.imported * 3600, no_json=True) - pub.pub_single("openWB/" + topic + "/WHExport_temp", state.exported * 3600, no_json=True) - - class SimCounterStoreBroker(SimCounterStore): def initialize(self, prefix: str, topic: str, power: float, timestamp: float) -> SimCounterState: state = SimCounterState(timestamp, power, imported=restore_last_energy( @@ -209,4 +66,4 @@ def restore_last_energy(topic: str, value: str): def get_sim_counter_store() -> SimCounterStore: - return SimCounterStoreRamdisk() if compatibility.is_ramdisk_in_use() else SimCounterStoreBroker() + return SimCounterStoreBroker() diff --git a/packages/modules/common/store/__init__.py b/packages/modules/common/store/__init__.py index 79530f7414..451f2ce2c1 100644 --- a/packages/modules/common/store/__init__.py +++ b/packages/modules/common/store/__init__.py @@ -5,7 +5,7 @@ from modules.common.store._chargepoint_internal import get_internal_chargepoint_value_store from modules.common.store._counter import get_counter_value_store from modules.common.store._inverter import get_inverter_value_store -from modules.common.store._ripple_control_receiver import get_ripple_control_receiver_value_store +from modules.common.store._io import get_io_value_store from modules.common.store._tariff import get_electricity_tariff_value_store from modules.common.store.ramdisk.io import ramdisk_write, ramdisk_read, ramdisk_read_float, ramdisk_read_int, \ RAMDISK_PATH diff --git a/packages/modules/common/store/_battery.py b/packages/modules/common/store/_battery.py index f1a4fc8075..da4bf72d57 100644 --- a/packages/modules/common/store/_battery.py +++ b/packages/modules/common/store/_battery.py @@ -30,15 +30,28 @@ def set(self, bat_state: BatState): def update(self): try: + pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/currents", self.state.currents, 2) pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/power", self.state.power, 2) pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/soc", self.state.soc, 0) - pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/imported", self.state.imported, 2) - pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/exported", self.state.exported, 2) + if self.state.imported is not None and self.state.exported is not None: + pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/imported", self.state.imported, 2) + pub_to_broker("openWB/set/bat/"+str(self.num)+"/get/exported", self.state.exported, 2) except Exception as e: raise FaultState.from_exception(e) +class PurgeBatteryState: + def __init__(self, delegate: LoggingValueStore) -> None: + self.delegate = delegate + + def set(self, state: BatState) -> None: + self.delegate.set(state) + + def update(self) -> None: + self.delegate.update() + + def get_bat_value_store(component_num: int) -> ValueStore[BatState]: - return LoggingValueStore( + return PurgeBatteryState(LoggingValueStore( (BatteryValueStoreRamdisk if compatibility.is_ramdisk_in_use() else BatteryValueStoreBroker)(component_num) - ) + )) diff --git a/packages/modules/common/store/_chargepoint.py b/packages/modules/common/store/_chargepoint.py index 608408c0fb..8743646f41 100644 --- a/packages/modules/common/store/_chargepoint.py +++ b/packages/modules/common/store/_chargepoint.py @@ -36,22 +36,33 @@ def update(self): pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/voltages", self.state.voltages, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/currents", self.state.currents, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/power_factors", self.state.power_factors, 2) - pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/imported", self.state.imported, 2) - pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/exported", self.state.exported, 2) + if self.state.imported is not None: + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/imported", self.state.imported, 2) + if self.state.exported is not None: + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/exported", self.state.exported, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/power", self.state.power, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/powers", self.state.powers, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/frequency", self.state.frequency, 2) - pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/phases_in_use", self.state.phases_in_use, 2) + if self.state.phases_in_use: + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/phases_in_use", self.state.phases_in_use, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/charge_state", self.state.charge_state, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/plug_state", self.state.plug_state, 2) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/rfid", self.state.rfid) - pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/rfid_timestamp", self.state.rfid_timestamp) + if self.state.rfid_timestamp is not None: + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/rfid_timestamp", self.state.rfid_timestamp) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/serial_number", self.state.serial_number) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/soc", self.state.soc) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/soc_timestamp", self.state.soc_timestamp) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/evse_current", self.state.evse_current) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/vehicle_id", self.state.vehicle_id) pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/max_evse_current", self.state.max_evse_current) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/max_charge_power", self.state.max_charge_power) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/max_discharge_power", + self.state.max_discharge_power) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/version", self.state.version) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/current_branch", self.state.current_branch) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/current_commit", self.state.current_commit) + pub_to_broker("openWB/set/chargepoint/" + str(self.num) + "/get/evse_signaling", self.state.evse_signaling) def get_chargepoint_value_store(id: int) -> ValueStore[ChargepointState]: diff --git a/packages/modules/common/store/_chargepoint_internal.py b/packages/modules/common/store/_chargepoint_internal.py index e1ab78736d..d16e821742 100644 --- a/packages/modules/common/store/_chargepoint_internal.py +++ b/packages/modules/common/store/_chargepoint_internal.py @@ -12,27 +12,32 @@ def set(self, state: ChargepointState) -> None: self.state = state def update(self): - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/voltages", self.state.voltages, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/currents", self.state.currents, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/frequency", self.state.frequency, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/power_factors", self.state.power_factors, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/imported", self.state.imported, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/exported", self.state.exported, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/power", self.state.power, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/powers", self.state.powers, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/phases_in_use", self.state.phases_in_use, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/charge_state", self.state.charge_state, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/plug_state", self.state.plug_state, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + "/get/rfid", self.state.rfid) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/serial_number", self.state.serial_number) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/evse_current", self.state.evse_current, 2) - pub_to_broker("openWB/set/internal_chargepoint/" + str(self.num) + - "/get/max_evse_current", self.state.max_evse_current, 2) + topic_prefix = f"openWB/set/internal_chargepoint/{self.num}/get" + pub_to_broker(f"{topic_prefix}/voltages", self.state.voltages, 2) + pub_to_broker(f"{topic_prefix}/currents", self.state.currents, 2) + pub_to_broker(f"{topic_prefix}/frequency", self.state.frequency, 2) + pub_to_broker(f"{topic_prefix}/power_factors", self.state.power_factors, 2) + pub_to_broker(f"{topic_prefix}/imported", self.state.imported, 2) + pub_to_broker(f"{topic_prefix}/exported", self.state.exported, 2) + pub_to_broker(f"{topic_prefix}/power", self.state.power, 2) + pub_to_broker(f"{topic_prefix}/powers", self.state.powers, 2) + pub_to_broker(f"{topic_prefix}/phases_in_use", self.state.phases_in_use, 2) + pub_to_broker(f"{topic_prefix}/charge_state", self.state.charge_state, 2) + pub_to_broker(f"{topic_prefix}/plug_state", self.state.plug_state, 2) + pub_to_broker(f"{topic_prefix}/rfid", self.state.rfid) + pub_to_broker(f"{topic_prefix}/serial_number", self.state.serial_number) + pub_to_broker(f"{topic_prefix}/evse_current", self.state.evse_current, 2) + pub_to_broker(f"{topic_prefix}/max_evse_current", self.state.max_evse_current, 2) + pub_to_broker(f"{topic_prefix}/version", self.state.version) + pub_to_broker(f"{topic_prefix}/current_branch", self.state.current_branch) + pub_to_broker(f"{topic_prefix}/current_commit", self.state.current_commit) + if self.state.soc is not None: + pub_to_broker(f"{topic_prefix}/get/soc", self.state.soc) + if self.state.soc_timestamp is not None: + pub_to_broker(f"{topic_prefix}/soc_timestamp", self.state.soc_timestamp) + if self.state.rfid_timestamp is not None: + pub_to_broker(f"{topic_prefix}/vehicle_id", self.state.vehicle_id) + pub_to_broker(f"{topic_prefix}/rfid_timestamp", self.state.rfid_timestamp) def get_internal_chargepoint_value_store(id: int) -> ValueStore[ChargepointState]: diff --git a/packages/modules/common/store/_counter.py b/packages/modules/common/store/_counter.py index 06254e4dac..8ddff768a1 100644 --- a/packages/modules/common/store/_counter.py +++ b/packages/modules/common/store/_counter.py @@ -1,4 +1,6 @@ +import logging from operator import add +from typing import Optional from control import data from helpermodules import compatibility @@ -6,10 +8,14 @@ from modules.common.component_state import CounterState from modules.common.component_type import ComponentType from modules.common.fault_state import FaultState +from modules.common.simcount._simcounter import SimCounter from modules.common.store import ValueStore from modules.common.store._api import LoggingValueStore from modules.common.store._broker import pub_to_broker from modules.common.store.ramdisk import files +from modules.common.utils.component_parser import get_component_obj_by_id + +log = logging.getLogger(__name__) class CounterValueStoreRamdisk(ValueStore[CounterState]): @@ -49,9 +55,13 @@ def update(self): class PurgeCounterState: - def __init__(self, delegate: LoggingValueStore, add_child_values: bool = False) -> None: + def __init__(self, + delegate: LoggingValueStore, + add_child_values: bool = False, + simcounter: Optional[SimCounter] = None) -> None: self.delegate = delegate self.add_child_values = add_child_values + self.sim_counter = simcounter def set(self, state: CounterState) -> None: self.delegate.set(state) @@ -65,69 +75,60 @@ def calc_virtual(self, state: CounterState) -> CounterState: if self.add_child_values: self.currents = state.currents if state.currents else [0.0]*3 self.power = state.power - self.imported = state.imported - self.exported = state.exported self.incomplete_currents = False def add_current_power(element): - if element.data.get.currents is not None: - if sum(element.data.get.currents) == 0 and element.data.get.power != 0: + if hasattr(element, "currents") and element.currents is not None: + if sum(element.currents) == 0 and element.power != 0: self.currents = [0, 0, 0] self.incomplete_currents = True else: - self.currents = list(map(add, self.currents, element.data.get.currents)) + self.currents = list(map(add, self.currents, element.currents)) else: self.currents = [0, 0, 0] self.incomplete_currents = True - self.power += element.data.get.power - - def add_imported_exported(element): - self.imported += element.data.get.imported - self.exported += element.data.get.exported - - def add_exported(element): - self.exported += element.data.get.exported + self.power += element.power counter_all = data.data.counter_all_data elements = counter_all.get_elements_for_downstream_calculation(self.delegate.delegate.num) for element in elements: - if element["type"] == ComponentType.CHARGEPOINT.value: - chargepoint = data.data.cp_data[f"cp{element['id']}"] - try: - self.currents = list(map(add, - self.currents, - convert_cp_currents_to_evu_currents( - chargepoint.data.config.phase_1, - chargepoint.data.get.currents))) - except KeyError: - raise KeyError("Für den virtuellen Zähler muss der Anschluss der Phasen von Ladepunkt" - f" {chargepoint.data.config.name} an die Phasen des EVU Zählers " - "angegeben werden.") - self.power += chargepoint.data.get.power - self.imported += chargepoint.data.get.imported - elif element["type"] == ComponentType.BAT.value: - add_current_power(data.data.bat_data[f"bat{element['id']}"]) - add_imported_exported(data.data.bat_data[f"bat{element['id']}"]) - elif element["type"] == ComponentType.COUNTER.value: - add_current_power(data.data.counter_data[f"counter{element['id']}"]) - add_imported_exported(data.data.counter_data[f"counter{element['id']}"]) - elif element["type"] == ComponentType.INVERTER.value: - add_current_power(data.data.pv_data[f"pv{element['id']}"]) - add_exported(data.data.pv_data[f"pv{element['id']}"]) + try: + if element["type"] == ComponentType.CHARGEPOINT.value: + chargepoint = data.data.cp_data[f"cp{element['id']}"] + chargepoint_state = chargepoint.chargepoint_module.store.delegate.state + try: + self.currents = list(map(add, + self.currents, + convert_cp_currents_to_evu_currents( + chargepoint.data.config.phase_1, + chargepoint_state.currents))) + except KeyError: + raise KeyError("Für den virtuellen Zähler muss der Anschluss der Phasen von Ladepunkt" + f" {chargepoint.data.config.name} an die Phasen des EVU Zählers " + "angegeben werden.") + self.power += chargepoint_state.power + else: + component = get_component_obj_by_id(element['id']) + add_current_power(component.store.delegate.delegate.state) + except Exception: + log.exception(f"Fehler beim Hinzufügen der Werte für Element {element}") + imported, exported = self.sim_counter.sim_count(self.power) if self.incomplete_currents: self.currents = None return CounterState(currents=self.currents, power=self.power, - exported=self.exported, - imported=self.imported) + exported=exported, + imported=imported) else: return state -def get_counter_value_store(component_num: int, add_child_values: bool = False) -> PurgeCounterState: +def get_counter_value_store(component_num: int, + add_child_values: bool = False, + simcounter: Optional[SimCounter] = None) -> PurgeCounterState: if compatibility.is_ramdisk_in_use(): delegate = CounterValueStoreRamdisk() else: delegate = CounterValueStoreBroker(component_num) - return PurgeCounterState(LoggingValueStore(delegate), add_child_values) + return PurgeCounterState(LoggingValueStore(delegate), add_child_values, simcounter) diff --git a/packages/modules/common/store/_counter_test.py b/packages/modules/common/store/_counter_test.py index 13772d020e..b9e20cd649 100644 --- a/packages/modules/common/store/_counter_test.py +++ b/packages/modules/common/store/_counter_test.py @@ -7,15 +7,20 @@ from control import data -from control.bat import Bat, BatData -from control.bat import Get as BatGet from control.chargepoint.chargepoint import Chargepoint from control.counter import Counter, CounterData, Get from control.counter_all import CounterAll -from control.pv import Pv, PvData -from control.pv import Get as PvGet -from modules.common.component_state import CounterState +from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule +from modules.common.component_state import BatState, ChargepointState, CounterState, InverterState +from modules.common.simcount._simcounter import SimCounter +from modules.common.store import _counter +from modules.common.store._api import LoggingValueStore +from modules.common.store._battery import BatteryValueStoreBroker, PurgeBatteryState from modules.common.store._counter import PurgeCounterState +from modules.common.store._inverter import InverterValueStoreBroker, PurgeInverterState +from modules.devices.generic.mqtt.bat import MqttBat +from modules.devices.generic.mqtt.counter import MqttCounter +from modules.devices.generic.mqtt.inverter import MqttInverter @pytest.fixture(autouse=True) @@ -28,21 +33,28 @@ def mock_data() -> None: def add_chargepoint(id: int): data.data.cp_data[f"cp{id}"] = Mock(spec=Chargepoint, id=id, - chargepoint_module=Mock(), data=Mock( config=Mock(phase_1=1), get=Mock(power=13359, currents=[19.36, 19.36, 19.36], imported=0, - exported=0))) + exported=0)), + chargepoint_module=Mock( + spec=ChargepointModule, + store=Mock(spec=LoggingValueStore, + delegate=Mock(spec=LoggingValueStore, + state=ChargepointState(power=13359, + currents=[ + 19.36, 19.36, 19.36], + imported=0, + exported=0, + phases_in_use=3, + plug_state=True, + charge_state=True))))) def mock_data_standard(): add_chargepoint(3) - data.data.bat_data["inverter1"] = Mock(spec=Pv, data=Mock( - spec=PvData, get=Mock(spec=PvGet, power=5786, exported=200))) - data.data.bat_data["bat2"] = Mock(spec=Bat, data=Mock( - spec=BatData, get=Mock(spec=BatGet, power=223, exported=200, imported=100))) data.data.counter_all_data.data.get.hierarchy = [{"id": 0, "type": "counter", "children": [{"id": 3, "type": "cp", "children": []}]}, {"id": 1, "type": "inverter", "children": []}, @@ -63,19 +75,63 @@ def mock_data_nested(): {"id": 3, "type": "cp", "children": []}]}]}] -Params = NamedTuple("Params", [("name", str), ("mock_data", Callable), ("expected_state", CounterState)]) +mock_comp_obj_inv_bat = [ + Mock(spec=MqttBat, + store=Mock(spec=PurgeBatteryState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=BatteryValueStoreBroker, + state=BatState(power=223, + exported=200, + imported=100))))), + Mock(spec=MqttInverter, + store=Mock(spec=PurgeInverterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=InverterValueStoreBroker, + state=InverterState(power=5786, + exported=2000)))))] + +mock_comp_obj_counter_inv_bat = [Mock(spec=MqttCounter, + store=Mock(spec=_counter.PurgeCounterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=_counter.CounterValueStoreBroker, + state=CounterState(power=13359, + exported=0, + imported=0, + currents=[19.36]*3))))), + Mock(spec=MqttBat, + store=Mock(spec=PurgeBatteryState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=BatteryValueStoreBroker, + state=BatState(power=223, + exported=200, + imported=100))))), + Mock(spec=MqttInverter, + store=Mock(spec=PurgeInverterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=InverterValueStoreBroker, + state=InverterState(power=5786, + exported=2000))))) + ] + +Params = NamedTuple("Params", [("name", str), ("mock_comp", Mock), + ("mock_data", Callable), ("expected_state", CounterState)]) cases = [ - Params("standard", mock_data_standard, CounterState(power=8358, currents=[26.61]*3, exported=200, imported=100)), - Params("nested virtual", mock_data_nested, CounterState( + Params("standard", mock_comp_obj_inv_bat, mock_data_standard, CounterState( + power=8358, currents=[26.61]*3, exported=200, imported=100)), + Params("nested virtual", mock_comp_obj_counter_inv_bat, mock_data_nested, CounterState( power=21717, currents=[45.97]*3, exported=200, imported=100)) ] @pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) -def test_calc_virtual(params): +def test_calc_virtual(params: Params, monkeypatch): # setup params.mock_data() - purge = PurgeCounterState(delegate=Mock(delegate=Mock(num=0)), add_child_values=True) + purge = PurgeCounterState(delegate=Mock(delegate=Mock(num=0)), + add_child_values=True, + simcounter=SimCounter(0, 0, prefix="bezug")) + mock_comp_obj = Mock(side_effect=params.mock_comp) + monkeypatch.setattr(_counter, "get_component_obj_by_id", mock_comp_obj) # execution state = purge.calc_virtual(CounterState(power=-5001, currents=[7.25]*3, exported=200, imported=100)) diff --git a/packages/modules/common/store/_inverter.py b/packages/modules/common/store/_inverter.py index abbae2dfda..30380bb37b 100644 --- a/packages/modules/common/store/_inverter.py +++ b/packages/modules/common/store/_inverter.py @@ -52,14 +52,23 @@ def set(self, state: InverterState) -> None: self.delegate.set(state) def update(self) -> None: - state = self.fix_hybrid_values(self.delegate.delegate.state) + state = self.filter_peaks(self.delegate.delegate.state) + state = self.fix_hybrid_values(state) self.delegate.set(state) self.delegate.update() + def filter_peaks(self, state: InverterState) -> InverterState: + inverter = data.data.pv_data[f"pv{self.delegate.delegate.num}"] + max_ac_out = inverter.data.config.max_ac_out + if max_ac_out > 0 and state.power > max_ac_out: + state.power = max_ac_out + return state + def fix_hybrid_values(self, state: InverterState) -> InverterState: children = data.data.counter_all_data.get_entry_of_element(self.delegate.delegate.num)["children"] power = state.power exported = state.exported + imported = state.imported if len(children): hybrid = [] for c in children: @@ -70,12 +79,16 @@ def fix_hybrid_values(self, state: InverterState) -> InverterState: for bat in hybrid: bat_get = data.data.bat_data[bat].data.get power -= bat_get.power - exported += bat_get.imported - bat_get.exported + + exported += bat_get.imported - bat_get.exported - imported + if state.dc_power is not None: # Manche Systeme werden auch aus dem Netz geladen, um einen Mindest-SoC zu halten. if state.dc_power == 0: power = 0 - return InverterState(power=power, exported=exported) + state.power = power + state.exported = exported + return state def get_inverter_value_store(component_num: int) -> PurgeInverterState: diff --git a/packages/modules/common/store/_inverter_test.py b/packages/modules/common/store/_inverter_test.py index 02687ee280..46b2c58092 100644 --- a/packages/modules/common/store/_inverter_test.py +++ b/packages/modules/common/store/_inverter_test.py @@ -37,7 +37,7 @@ def mock_data() -> None: ] -@ pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) +@pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) def test_fix_hybrid_values(params): # setup data.data.counter_all_data.data.get.hierarchy = params.hierarchy diff --git a/packages/modules/common/store/_io.py b/packages/modules/common/store/_io.py new file mode 100644 index 0000000000..f083c4d46f --- /dev/null +++ b/packages/modules/common/store/_io.py @@ -0,0 +1,46 @@ +from control import data +from modules.common.component_state import IoState +from modules.common.fault_state import FaultState +from modules.common.store import ValueStore +from modules.common.store._api import LoggingValueStore +from modules.common.store._broker import pub_to_broker + + +class IoValueStoreBroker(ValueStore[IoState]): + def __init__(self, num: int) -> None: + self.num = num + self.state = IoState() + + def set(self, state: IoState) -> None: + self.state = state + + def update(self): + try: + if self.state.digital_input: + pub_to_broker(f"openWB/set/io/states/{self.num}/get/digital_input_prev", + data.data.io_states[f"io_states{self.num}"].data.get.digital_input) + pub_to_broker(f"openWB/set/io/states/{self.num}/get/digital_input", self.state.digital_input) + if self.state.analog_input: + pub_to_broker(f"openWB/set/io/states/{self.num}/get/analog_input_prev", + data.data.io_states[f"io_states{self.num}"].data.get.analog_input) + pub_to_broker(f"openWB/set/io/states/{self.num}/get/analog_input", self.state.analog_input) + if self.state.digital_output: + pub_to_broker(f"openWB/set/io/states/{self.num}/get/digital_output_prev", + data.data.io_states[f"io_states{self.num}"].data.get.digital_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/get/digital_output", self.state.digital_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/set/digital_output_prev", + data.data.io_states[f"io_states{self.num}"].data.set.digital_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/set/digital_output", self.state.digital_output) + if self.state.analog_output: + pub_to_broker(f"openWB/set/io/states/{self.num}/get/analog_output_prev", + data.data.io_states[f"io_states{self.num}"].data.get.analog_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/get/analog_output", self.state.analog_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/set/analog_output_prev", + data.data.io_states[f"io_states{self.num}"].data.set.analog_output) + pub_to_broker(f"openWB/set/io/states/{self.num}/set/analog_output", self.state.analog_output) + except Exception as e: + raise FaultState.from_exception(e) + + +def get_io_value_store(num: int) -> ValueStore[IoState]: + return LoggingValueStore(IoValueStoreBroker(num)) diff --git a/packages/modules/common/store/_io_internal.py b/packages/modules/common/store/_io_internal.py new file mode 100644 index 0000000000..b3f191f76b --- /dev/null +++ b/packages/modules/common/store/_io_internal.py @@ -0,0 +1,30 @@ +from modules.common.component_state import IoState +from modules.common.fault_state import FaultState +from modules.common.store import ValueStore +from modules.common.store._api import LoggingValueStore +from modules.common.store._broker import pub_to_broker + + +class InternalIoValueStoreBroker(ValueStore[IoState]): + def __init__(self) -> None: + pass + + def set(self, state: IoState) -> None: + self.state = state + + def update(self): + try: + if self.state.digital_input: + pub_to_broker("openWB/set/internal_io/states/get/digital_input", self.state.digital_input) + if self.state.analog_input: + pub_to_broker("openWB/set/internal_io/states/get/analog_input", self.state.analog_input) + if self.state.digital_output: + pub_to_broker("openWB/set/internal_io/states/get/digital_output", self.state.digital_output) + if self.state.analog_output: + pub_to_broker("openWB/set/internal_io/states/get/analog_output", self.state.analog_output) + except Exception as e: + raise FaultState.from_exception(e) + + +def get_internal_io_value_store() -> ValueStore[IoState]: + return LoggingValueStore(InternalIoValueStoreBroker()) diff --git a/packages/modules/common/store/_ripple_control_receiver.py b/packages/modules/common/store/_ripple_control_receiver.py deleted file mode 100644 index c92ddec83f..0000000000 --- a/packages/modules/common/store/_ripple_control_receiver.py +++ /dev/null @@ -1,19 +0,0 @@ -from modules.common.component_state import RcrState -from modules.common.store import ValueStore -from modules.common.store._api import LoggingValueStore -from modules.common.store._broker import pub_to_broker - - -class RippleControlReceiverValueStore(ValueStore[RcrState]): - def __init__(self): - pass - - def set(self, state: RcrState) -> None: - self.state = state - - def update(self): - pub_to_broker("openWB/set/general/ripple_control_receiver/get/override_value", self.state.override_value) - - -def get_ripple_control_receiver_value_store() -> ValueStore[RcrState]: - return LoggingValueStore(RippleControlReceiverValueStore()) diff --git a/packages/modules/common/tasmota.py b/packages/modules/common/tasmota.py deleted file mode 100644 index 42c99f1bbd..0000000000 --- a/packages/modules/common/tasmota.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -import logging - -from modules.common.component_state import CounterState -from modules.common import req - -log = logging.getLogger(__name__) - - -class Tasmota: - def __init__(self, - device_id: int, - ip_address: str, - phase: int) -> None: - self.__device_id = device_id - self.__ip_address = ip_address - if phase: - self.__phase = phase - else: - self.__phase = 1 - - def get_CounterState(self) -> CounterState: - url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" - response = req.get_http_session().get(url, timeout=5).json() - - voltages = [0.0, 0.0, 0.0] - powers = [0.0, 0.0, 0.0] - currents = [0.0, 0.0, 0.0] - power_factors = [0.0, 0.0, 0.0] - - voltages[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Voltage']) - powers[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Power']) - power = sum(powers) - currents[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Current']) - power_factors[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Factor']) - imported = float(response['StatusSNS']['ENERGY']['Total']*1000) - exported = 0.0 - - counter_state = CounterState( - imported=imported, - exported=exported, - power=power, - voltages=voltages, - currents=currents, - powers=powers, - power_factors=power_factors - ) - log.debug("tasmota.get_CounterState:\nurl=" + url + - "\nresponse=" + str(response) + - "\nCounterState=" + str(counter_state)) - return counter_state - - def set_PowerOn(self) -> str: - url = "http://" + self.__ip_address + "/cm?cmnd=Power%20on" - response = req.get_http_session().get(url, timeout=3).json() - return response['POWER'] - - def setPowerOff(self) -> str: - url = "http://" + self.__ip_address + "/cm?cmnd=Power%20off" - response = req.get_http_session().get(url, timeout=3).json() - return response['POWER'] diff --git a/packages/modules/common/utils/component_parser.py b/packages/modules/common/utils/component_parser.py index b235042261..5dcbb226ac 100644 --- a/packages/modules/common/utils/component_parser.py +++ b/packages/modules/common/utils/component_parser.py @@ -3,6 +3,7 @@ from control import data from modules.common.abstract_device import AbstractDevice +from modules.common.abstract_io import AbstractIoDevice from modules.common.component_type import type_to_topic_mapping log = logging.getLogger(__name__) @@ -17,7 +18,16 @@ def get_component_name_by_id(id: int): raise ValueError(f"Element {id} konnte keinem Gerät zugeordnet werden.") -def get_component_obj_by_id(id: int, not_finished_threads: List[str]) -> Optional[Any]: +def get_io_name_by_id(id: int): + for item in data.data.system_data.values(): + if isinstance(item, AbstractIoDevice): + if item.config.id == id: + return item.config.name + else: + raise ValueError(f"Element {id} konnte keinem Gerät zugeordnet werden.") + + +def get_finished_component_obj_by_id(id: int, not_finished_threads: List[str]) -> Optional[Any]: for item in data.data.system_data.values(): if isinstance(item, AbstractDevice): for t in not_finished_threads: @@ -38,3 +48,14 @@ def get_component_obj_by_id(id: int, not_finished_threads: List[str]) -> Optiona else: log.error(f"Element {id} konnte keinem Gerät zugeordnet werden.") return None + + +def get_component_obj_by_id(id: int) -> Optional[Any]: + for item in data.data.system_data.values(): + if isinstance(item, AbstractDevice): + for comp in item.components.values(): + if comp.component_config.id == id: + return comp + else: + log.error(f"Element {id} konnte keinem Gerät zugeordnet werden.") + return None diff --git a/packages/modules/configuration.py b/packages/modules/configuration.py index 625bfe3488..ba1aa23150 100644 --- a/packages/modules/configuration.py +++ b/packages/modules/configuration.py @@ -5,6 +5,7 @@ import dataclass_utils from helpermodules.pub import Pub +from modules.io_actions.groups import READABLE_GROUP_NAME, ActionGroup log = logging.getLogger(__name__) @@ -18,7 +19,8 @@ def pub_configurable(): _pub_configurable_soc_modules() _pub_configurable_devices_components() _pub_configurable_chargepoints() - _pub_configurable_ripple_control_receivers() + _pub_configurable_io_devices() + _pub_configurable_io_actions() _pub_configurable_monitoring() @@ -294,7 +296,7 @@ def create_chargepoints_list(path_list): path_list = Path(_get_packages_path()/"modules"/"chargepoints").glob('**/chargepoint_module.py') cp_list = create_chargepoints_list(path_list) - # Nach Umbennnung der "Externen openWB" zu "Secondary openWB" soll der Eintrag weiterhin an zweiter Stelle + # Nach Umbenennung der "Externen openWB" zu "Secondary openWB" soll der Eintrag weiterhin an zweiter Stelle # stehen cp_list.remove({'value': 'external_openwb', 'text': 'Secondary openWB'}) cp_list.insert(1, {'value': 'external_openwb', 'text': 'Secondary openWB'}) @@ -307,37 +309,53 @@ def create_chargepoints_list(path_list): log.exception("Fehler im configuration-Modul") -def _pub_configurable_ripple_control_receivers() -> None: +def _pub_configurable_io_devices() -> None: try: - ripple_control_receivers = [] - path_list = Path(_get_packages_path()/"modules"/"ripple_control_receivers").glob('**/config.py') + io_devices = [] + path_list = Path(_get_packages_path()/"modules"/"io_devices").glob('**/config.py') for path in path_list: try: if path.name.endswith("_test.py"): # Tests überspringen continue dev_defaults = importlib.import_module( - f".ripple_control_receivers.{path.parts[-2]}.ripple_control_receiver", - "modules").device_descriptor.configuration_factory() - ripple_control_receivers.append({ + f".io_devices.{path.parts[-2]}.api", "modules").device_descriptor.configuration_factory() + io_devices.append({ "value": dev_defaults.type, "text": dev_defaults.name, "defaults": dataclass_utils.asdict(dev_defaults) }) except Exception: log.exception("Fehler im configuration-Modul") - ripple_control_receivers = sorted(ripple_control_receivers, key=lambda d: d['text'].upper()) - # "leeren" Eintrag an erster Stelle einfügen - ripple_control_receivers.insert(0, - { - "value": None, - "text": "- kein RSE Modul -", - "defaults": { - "type": None, - "configuration": {} - } - }) - Pub().pub("openWB/set/system/configurable/ripple_control_receivers", ripple_control_receivers) + io_devices = sorted(io_devices, key=lambda d: d['text'].upper()) + Pub().pub("openWB/set/system/configurable/io_devices", io_devices) + except Exception: + log.exception("Fehler im configuration-Modul") + + +def _pub_configurable_io_actions() -> None: + try: + action_groups = {} + for group in ActionGroup: + action_groups[group.value] = {"group_name": READABLE_GROUP_NAME[group], "actions": []} + path_list = Path(_get_packages_path()/"modules"/"io_actions"/group.value).glob('**/api.py') + for path in path_list: + try: + if path.name.endswith("_test.py"): + # Tests überspringen + continue + action_defaults = importlib.import_module(f".io_actions.{group.value}.{path.parts[-2]}.api", + "modules").device_descriptor.configuration_factory() + action_groups[group.value]["actions"].append({ + "value": action_defaults.type, + "text": action_defaults.name, + "defaults": dataclass_utils.asdict(action_defaults) + }) + except Exception: + log.exception(f"Fehler im configuration-Modul: groups: {path}") + action_groups[group.value]["actions"] = sorted( + action_groups[group.value]["actions"], key=lambda d: d['text'].upper()) + Pub().pub("openWB/set/system/configurable/io_actions", action_groups) except Exception: log.exception("Fehler im configuration-Modul") diff --git a/packages/modules/conftest.py b/packages/modules/conftest.py index f83ea44a66..c5e6f1f617 100644 --- a/packages/modules/conftest.py +++ b/packages/modules/conftest.py @@ -13,10 +13,9 @@ sys.modules['pkce'] = type(sys)('pkce') sys.modules['msal'] = type(sys)('msal') sys.modules['smb'] = type(sys)('smb') -sys.modules['skodaconnect'] = type(sys)('skodaconnect') -sys.modules['skodaconnect.Connection'] = type(sys)('skodaconnect.Connection') sys.modules['socketserver'] = type(sys)('socketserver') sys.modules['grpc'] = type(sys)('grpc') +sys.modules['pycarwings3'] = type(sys)('pycarwings3') # sys.modules['telnetlib3'] = type(sys)('telnetlib3') @@ -34,6 +33,11 @@ module.BinaryPayloadDecoder = Mock() sys.modules['pymodbus.payload'] = module +module = type(sys)('pymodbus.transaction') +module.ModbusSocketFramer = Mock() +module.ModbusRtuFramer = Mock() +sys.modules['pymodbus.transaction'] = module + module = type(sys)('socketserver') module.TCPServer = Mock() sys.modules['socketserver'] = module diff --git a/packages/modules/devices/algodue/__init__.py b/packages/modules/devices/algodue/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/algodue/algodue/__init__.py b/packages/modules/devices/algodue/algodue/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/algodue/algodue/bat.py b/packages/modules/devices/algodue/algodue/bat.py new file mode 100644 index 0000000000..48fecab95f --- /dev/null +++ b/packages/modules/devices/algodue/algodue/bat.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.devices.algodue.algodue.config import AlgodueBatSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store + + +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class AlgodueBat(AbstractBat): + def __init__(self, component_config: AlgodueBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + currents = self.__tcp_client.read_input_registers( + 0x100E, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + powers = self.__tcp_client.read_input_registers(0x1020, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + power = sum(powers) + + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + currents=currents, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=AlgodueBatSetup) diff --git a/packages/modules/devices/algodue/algodue/config.py b/packages/modules/devices/algodue/algodue/config.py new file mode 100644 index 0000000000..d4717dcce0 --- /dev/null +++ b/packages/modules/devices/algodue/algodue/config.py @@ -0,0 +1,73 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor +from helpermodules.auto_str import auto_str + + +class AlgodueConfiguration: + def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: int = 502): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + + +class Algodue: + def __init__(self, + name: str = "Algodue", + type: str = "algodue", + id: int = 0, + configuration: AlgodueConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or AlgodueConfiguration() + + +@auto_str +class AlgodueCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class AlgodueCounterSetup(ComponentSetup[AlgodueCounterConfiguration]): + def __init__(self, + name: str = "Algodue Zähler", + type: str = "counter", + id: int = 0, + configuration: AlgodueCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or AlgodueCounterConfiguration()) + + +@auto_str +class AlgodueInverterConfiguration: + def __init__(self): + pass + + +@auto_str +class AlgodueInverterSetup(ComponentSetup[AlgodueInverterConfiguration]): + def __init__(self, + name: str = "Algodue Wechselrichterzähler", + type: str = "inverter", + id: int = 0, + configuration: AlgodueInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or AlgodueInverterConfiguration()) + + +@auto_str +class AlgodueBatConfiguration: + def __init__(self): + pass + + +@auto_str +class AlgodueBatSetup(ComponentSetup[AlgodueBatConfiguration]): + def __init__(self, + name: str = "Algodue Speicherzähler", + type: str = "bat", + id: int = 0, + configuration: AlgodueBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or AlgodueBatConfiguration()) diff --git a/packages/modules/devices/algodue/algodue/counter.py b/packages/modules/devices/algodue/algodue/counter.py new file mode 100644 index 0000000000..162176b8fc --- /dev/null +++ b/packages/modules/devices/algodue/algodue/counter.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.devices.algodue.algodue.config import AlgodueCounterSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store + + +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class AlgodueCounter(AbstractCounter): + def __init__(self, component_config: AlgodueCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + frequency = self.__tcp_client.read_input_registers(0x1038, ModbusDataType.FLOAT_32, unit=self.__modbus_id) + currents = self.__tcp_client.read_input_registers( + 0x100E, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + powers = self.__tcp_client.read_input_registers(0x1020, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + power = sum(powers) + voltages = self.__tcp_client.read_input_registers( + 0x1000, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + power_factors = self.__tcp_client.read_input_registers( + 0x1018, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + voltages=voltages, + currents=currents, + powers=powers, + imported=imported, + exported=exported, + power=power, + frequency=frequency, + power_factors=power_factors + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=AlgodueCounterSetup) + +# serial_chars = self.client.read_holding_registers(0x500, [ModbusDataType.UINT_8]*10, unit=self.id) +# model_id = self.client.read_holding_registers(0x505, ModbusDataType.UINT_16, unit=self.id) +# model_string = "unknown" +# if model_id == 0x03: +# model_string = "6 A, 3 phases, 4 wires" +# elif model_id == 0x08: +# model_string = "80 A, 3 phases, 4 wires" +# elif model_id == 0x0c: +# model_string = "80 A, 1 phase, 2 wires" +# elif model_id == 0x10: +# model_string = "40 A, 1 phase, 2 wires" +# elif model_id == 0x12: +# model_string = "63 A, 3 phases, 4 wires" + +# type_id = self.client.read_holding_registers(0x506, ModbusDataType.UINT_16, unit=self.id) +# type_string = "unknown" +# if type_id == 0x00: +# type_string = "NO MID, RESET" +# elif type_id == 0x01: +# type_string = "MID" +# elif type_id == 0x02: +# type_string = "NO MID" +# elif type_id == 0x03: +# type_string = "NO MID, Wiring selection" +# elif type_id == 0x05: +# type_string = "MID no varh" +# elif type_id == 0x09: +# type_string = "MID Wiring selection" +# elif type_id == 0x0a: +# type_string = "MID no varh, Wiring selection" +# elif type_id == 0x0b: +# type_string = "NO MID, RESET, Wiring selection" +# meterinfo = "Algodue UEM " + model_string + ", " + type_string diff --git a/packages/modules/devices/algodue/algodue/device.py b/packages/modules/devices/algodue/algodue/device.py new file mode 100644 index 0000000000..08821584c3 --- /dev/null +++ b/packages/modules/devices/algodue/algodue/device.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.devices.algodue.algodue import counter, inverter, bat +from modules.devices.algodue.algodue.config import Algodue, AlgodueCounterSetup, AlgodueInverterSetup, AlgodueBatSetup +from modules.common import modbus +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext + +log = logging.getLogger(__name__) + + +def create_device(device_config: Algodue): + client = None + + def create_counter_component(component_config: AlgodueCounterSetup): + nonlocal client + return counter.AlgodueCounter(component_config=component_config, device_id=device_config.id, + tcp_client=client, modbus_id=device_config.configuration.modbus_id) + + def create_inverter_component(component_config: AlgodueInverterSetup): + nonlocal client + return inverter.AlgodueInverter(component_config=component_config, device_id=device_config.id, + tcp_client=client, modbus_id=device_config.configuration.modbus_id) + + def create_bat_component(component_config: AlgodueBatSetup): + nonlocal client + return bat.AlgodueBat(component_config=component_config, device_id=device_config.id, + tcp_client=client, modbus_id=device_config.configuration.modbus_id) + + def update_components( + components: Iterable[Union[counter.AlgodueCounter, inverter.AlgodueInverter, bat.AlgodueBat]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Algodue) diff --git a/packages/modules/devices/algodue/algodue/inverter.py b/packages/modules/devices/algodue/algodue/inverter.py new file mode 100644 index 0000000000..63ad9adca2 --- /dev/null +++ b/packages/modules/devices/algodue/algodue/inverter.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.devices.algodue.algodue.config import AlgodueInverterSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store + + +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class AlgodueInverter(AbstractInverter): + def __init__(self, component_config: AlgodueInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + currents = self.__tcp_client.read_input_registers( + 0x100E, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + powers = self.__tcp_client.read_input_registers(0x1020, [ModbusDataType.FLOAT_32]*3, unit=self.__modbus_id) + power = sum(powers) + + _, exported = self.sim_counter.sim_count(power) + + inverter_state = InverterState( + power=power, + currents=currents, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=AlgodueInverterSetup) diff --git a/packages/modules/devices/algodue/vendor.py b/packages/modules/devices/algodue/vendor.py new file mode 100644 index 0000000000..d4970b9763 --- /dev/null +++ b/packages/modules/devices/algodue/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Algodue" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/alpha_ess/alpha_ess/bat.py b/packages/modules/devices/alpha_ess/alpha_ess/bat.py index 4dcc0ca9e5..3d332d0f85 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/bat.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/bat.py @@ -1,33 +1,33 @@ -#!/usr/bin/env python3 import logging import time -from typing import Dict, Union - -from dataclass_utils import dataclass_from_dict -from modules.devices.alpha_ess.alpha_ess.config import AlphaEssBatSetup, AlphaEssConfiguration -from modules.common import modbus +from typing import TypedDict, Any from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store +from modules.devices.alpha_ess.alpha_ess.config import AlphaEssBatSetup log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + tcp_client: ModbusTcpClient_ + modbus_id: int + + class AlphaEssBat(AbstractBat): - def __init__(self, device_id: int, - component_config: Union[Dict, AlphaEssBatSetup], - tcp_client: modbus.ModbusTcpClient_, - device_config: AlphaEssConfiguration, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(AlphaEssBatSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + def __init__(self, component_config: AlphaEssBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/alpha_ess/alpha_ess/counter.py b/packages/modules/devices/alpha_ess/alpha_ess/counter.py index 9682ab8333..6bac7a2a4a 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/counter.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import time -from typing import Callable, Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.devices.alpha_ess.alpha_ess.config import AlphaEssConfiguration, AlphaEssCounterSetup from modules.common import modbus from modules.common.abstract_device import AbstractCounter @@ -13,55 +12,41 @@ from modules.common.store import get_counter_value_store +class KwargsDict(TypedDict): + tcp_client: modbus.ModbusTcpClient_ + device_config: AlphaEssConfiguration + modbus_id: int + + class AlphaEssCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, AlphaEssCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - device_config: AlphaEssConfiguration, - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(AlphaEssCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__device_config = device_config - self.__modbus_id = modbus_id + def __init__(self, component_config: AlphaEssCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__device_config: AlphaEssConfiguration = self.kwargs['device_config'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self): time.sleep(0.1) - factory_method = self.__get_values_factory() - counter_state = factory_method(self.__modbus_id) - self.store.set(counter_state) - - def __get_values_factory(self) -> Callable[[int], CounterState]: if self.__device_config.source == 0 and self.__device_config.version == 0: - return self.__get_values_before_v123 + power, exported, imported = self.__tcp_client.read_holding_registers( + 0x6, [modbus.ModbusDataType.INT_32] * 3, unit=self.__modbus_id) + exported *= 10 + imported *= 10 + currents = [val / 230 for val in self.__tcp_client.read_holding_registers( + 0x0000, [ModbusDataType.INT_32]*3, unit=self.__modbus_id)] else: - return self.__get_values_since_v123 - - def __get_values_before_v123(self, unit: int) -> CounterState: - power, exported, imported = self.__tcp_client.read_holding_registers( - 0x6, [modbus.ModbusDataType.INT_32] * 3, unit=unit) - exported *= 10 - imported *= 10 - currents = [val / 230 for val in self.__tcp_client.read_holding_registers( - 0x0000, [ModbusDataType.INT_32]*3, unit=unit)] - - counter_state = CounterState( - currents=currents, - imported=imported, - exported=exported, - power=power - ) - return counter_state - - def __get_values_since_v123(self, unit: int) -> CounterState: - power = self.__tcp_client.read_holding_registers(0x0021, ModbusDataType.INT_32, unit=unit) - exported, imported = [ - val * 10 for val in self.__tcp_client.read_holding_registers( - 0x0010, [ModbusDataType.INT_32] * 2, unit=unit)] - currents = [val / 1000 for val in self.__tcp_client.read_holding_registers( - 0x0017, [ModbusDataType.INT_16]*3, unit=unit)] + power = self.__tcp_client.read_holding_registers(0x0021, ModbusDataType.INT_32, unit=self.__modbus_id) + exported, imported = [ + val * 10 for val in self.__tcp_client.read_holding_registers( + 0x0010, [ModbusDataType.INT_32] * 2, unit=self.__modbus_id + )] + currents = [val / 1000 for val in self.__tcp_client.read_holding_registers( + 0x0017, [ModbusDataType.INT_16]*3, unit=self.__modbus_id)] counter_state = CounterState( currents=currents, @@ -69,8 +54,7 @@ def __get_values_since_v123(self, unit: int) -> CounterState: exported=exported, power=power ) - return counter_state + self.store.set(counter_state) -component_descriptor = ComponentDescriptor( - configuration_factory=AlphaEssCounterSetup) +component_descriptor = ComponentDescriptor(configuration_factory=AlphaEssCounterSetup) diff --git a/packages/modules/devices/alpha_ess/alpha_ess/device.py b/packages/modules/devices/alpha_ess/alpha_ess/device.py index 65d887dd7c..693a2edc12 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/device.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/device.py @@ -1,13 +1,15 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.alpha_ess.alpha_ess.config import ( AlphaEss, AlphaEssBatSetup, AlphaEssCounterSetup, AlphaEssInverterSetup) from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext from modules.devices.alpha_ess.alpha_ess import bat from modules.devices.alpha_ess.alpha_ess import counter from modules.devices.alpha_ess.alpha_ess import inverter @@ -19,43 +21,52 @@ def create_device(device_config: AlphaEss): + client = None + def create_bat_component(component_config: AlphaEssBatSetup): - return bat.AlphaEssBat(device_config.id, - component_config, - client, - device_config.configuration, - device_config.configuration.modbus_id) + nonlocal client + return bat.AlphaEssBat(component_config, + device_id=device_config.id, + tcp_client=client, + modbus_id=device_config.configuration.modbus_id) def create_counter_component(component_config: AlphaEssCounterSetup): - return counter.AlphaEssCounter(device_config.id, - component_config, - client, - device_config.configuration, - device_config.configuration.modbus_id) + nonlocal client + return counter.AlphaEssCounter(component_config, + tcp_client=client, + device_config=device_config.configuration, + modbus_id=device_config.configuration.modbus_id) def create_inverter_component(component_config: AlphaEssInverterSetup): - return inverter.AlphaEssInverter(device_config.id, - component_config, - client, - device_config.configuration, - device_config.configuration.modbus_id) + nonlocal client + return inverter.AlphaEssInverter(component_config=component_config, + device_id=device_config.id, + tcp_client=client, + device_config=device_config.configuration, + modbus_id=device_config.configuration.modbus_id) def update_components(components: Iterable[Union[alpha_ess_component_classes]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client if device_config.configuration.source == 0: client = modbus.ModbusTcpClient_("192.168.193.125", 8899) else: client = modbus.ModbusTcpClient_( device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/alpha_ess/alpha_ess/inverter.py b/packages/modules/devices/alpha_ess/alpha_ess/inverter.py index 624fe2dd15..1724266f52 100644 --- a/packages/modules/devices/alpha_ess/alpha_ess/inverter.py +++ b/packages/modules/devices/alpha_ess/alpha_ess/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.devices.alpha_ess.alpha_ess.config import AlphaEssConfiguration, AlphaEssInverterSetup from modules.common import modbus from modules.common.abstract_device import AbstractInverter @@ -13,17 +12,23 @@ from modules.common.store import get_inverter_value_store +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + device_config: AlphaEssConfiguration + modbus_id: int + + class AlphaEssInverter(AbstractInverter): - def __init__(self, device_id: int, - component_config: Union[Dict, AlphaEssInverterSetup], - tcp_client: modbus.ModbusTcpClient_, - device_config: AlphaEssConfiguration, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(AlphaEssInverterSetup, component_config) - self.__tcp_client = tcp_client - self.__device_config = device_config - self.__modbus_id = modbus_id + def __init__(self, component_config: AlphaEssInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__device_config: AlphaEssConfiguration = self.kwargs['device_config'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/ampere/ampere/bat.py b/packages/modules/devices/ampere/ampere/bat.py index 297efd9178..0b376e0fba 100644 --- a/packages/modules/devices/ampere/ampere/bat.py +++ b/packages/modules/devices/ampere/ampere/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union -from dataclass_utils import dataclass_from_dict +from typing import Any, TypedDict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -12,21 +11,30 @@ from modules.devices.ampere.ampere.config import AmpereBatSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class AmpereBat(AbstractBat): def __init__(self, - device_id: int, - component_config: Union[Dict, AmpereBatSetup], - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(AmpereBatSetup, component_config) - self.modbus_id = modbus_id - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + component_config: AmpereBatSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.client = self.kwargs['client'] - def update(self, client: ModbusTcpClient_) -> None: - power = client.read_input_registers(535, ModbusDataType.INT_16, unit=self.modbus_id) - soc = client.read_input_registers(1339, ModbusDataType.UINT_16, unit=self.modbus_id) + def update(self) -> None: + power = self.client.read_input_registers(535, ModbusDataType.INT_16, unit=self.modbus_id) * -1 + soc = self.client.read_input_registers(1339, ModbusDataType.UINT_16, unit=self.modbus_id) imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( diff --git a/packages/modules/devices/ampere/ampere/counter.py b/packages/modules/devices/ampere/ampere/counter.py index 0f8b64603d..b2d456197a 100644 --- a/packages/modules/devices/ampere/ampere/counter.py +++ b/packages/modules/devices/ampere/ampere/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -12,26 +11,35 @@ from modules.devices.ampere.ampere.config import AmpereCounterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class AmpereCounter(AbstractCounter): def __init__(self, - device_id: int, - component_config: Union[Dict, AmpereCounterSetup], - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(AmpereCounterSetup, component_config) - self.modbus_id = modbus_id + component_config: AmpereCounterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): - powers = client.read_input_registers(1349, [ModbusDataType.INT_16]*3, unit=self.modbus_id) - power = client.read_input_registers(1348, ModbusDataType.INT_16, unit=self.modbus_id) + def update(self): + powers = self.client.read_input_registers(1349, [ModbusDataType.INT_16]*3, unit=self.modbus_id) + power = self.client.read_input_registers(1348, ModbusDataType.INT_16, unit=self.modbus_id) imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( - currents=powers, + powers=powers, imported=imported, exported=exported, power=power diff --git a/packages/modules/devices/ampere/ampere/device.py b/packages/modules/devices/ampere/ampere/device.py index ee0e7b942e..42310bc708 100644 --- a/packages/modules/devices/ampere/ampere/device.py +++ b/packages/modules/devices/ampere/ampere/device.py @@ -15,28 +15,42 @@ def create_device(device_config: Ampere): + client = None + def create_bat_component(component_config: AmpereBatSetup): - return AmpereBat(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return AmpereBat(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_counter_component(component_config: AmpereCounterSetup): - return AmpereCounter(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return AmpereCounter(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_inverter_component(component_config: AmpereInverterSetup): - return AmpereInverter(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return AmpereInverter(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def update_components(components: Iterable[Union[AmpereBat, AmpereCounter, AmpereInverter]]): with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(client) + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - try: - client = ModbusTcpClient_(device_config.configuration.ip_address, - device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/ampere/ampere/inverter.py b/packages/modules/devices/ampere/ampere/inverter.py index a7dc6efb42..67f2d673e6 100644 --- a/packages/modules/devices/ampere/ampere/inverter.py +++ b/packages/modules/devices/ampere/ampere/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,21 +11,30 @@ from modules.devices.ampere.ampere.config import AmpereInverterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class AmpereInverter(AbstractInverter): def __init__(self, - device_id: int, - component_config: Union[Dict, AmpereInverterSetup], - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(AmpereInverterSetup, component_config) - self.modbus_id = modbus_id + component_config: AmpereInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - pv1_power = client.read_holding_registers(519, ModbusDataType.INT_16, unit=self.modbus_id) * -1 - pv2_power = client.read_holding_registers(522, ModbusDataType.INT_16, unit=self.modbus_id) * -1 + def update(self) -> None: + pv1_power = self.client.read_holding_registers(519, ModbusDataType.INT_16, unit=self.modbus_id) * -1 + pv2_power = self.client.read_holding_registers(522, ModbusDataType.INT_16, unit=self.modbus_id) * -1 power = pv1_power + pv2_power diff --git a/packages/modules/devices/avm/__init__ .py b/packages/modules/devices/avm/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/avm/avm/__init__.py b/packages/modules/devices/avm/avm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/avm/avm/config.py b/packages/modules/devices/avm/avm/config.py new file mode 100644 index 0000000000..72cbda823e --- /dev/null +++ b/packages/modules/devices/avm/avm/config.py @@ -0,0 +1,50 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class AvmConfiguration: + def __init__(self, + ip_address: Optional[str] = None, + username: Optional[str] = None, + password: Optional[str] = None, + session_id: Optional[str] = None, + session_mtime: Optional[str] = None) -> None: + self.ip_address = ip_address + self.username = username + self.password = password + self.session_id = session_id # don't show in UI + self.session_mtime = session_mtime # don't show in UI + + +@auto_str +class Avm: + def __init__(self, + name: str = "AVM Fritz!Box", + type: str = "avm", + id: int = 0, + configuration: AvmConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or AvmConfiguration() + + +@auto_str +class AvmCounterConfiguration: + def __init__(self, name: Optional[str] = None): + self.name = name + + +@auto_str +class AvmCounterSetup(ComponentSetup[AvmCounterConfiguration]): + def __init__(self, + name: str = "Avm Zähler", + type: str = "counter", + id: int = 0, + configuration: AvmCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or AvmCounterConfiguration()) diff --git a/packages/modules/devices/avm/avm/counter.py b/packages/modules/devices/avm/avm/counter.py new file mode 100644 index 0000000000..fb73a393c6 --- /dev/null +++ b/packages/modules/devices/avm/avm/counter.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +from xml.etree.ElementTree import Element + +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_counter_value_store +from modules.devices.avm.avm.config import AvmCounterSetup + + +class AvmCounter(AbstractCounter): + def __init__(self, component_config: AvmCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self, deviceListElementTree: Element): + for device in deviceListElementTree: + name = device.find("name").text + if name == self.component_config.configuration.name: + presentText = device.find("present").text + if presentText != '1': + continue + + powermeterBlock = device.find("powermeter") + if powermeterBlock is not None: + # AVM returns mW, convert to W here + power = float(powermeterBlock.find("power").text)/1000 + # AVM returns mV, convert to V here + voltageInfo = powermeterBlock.find("voltage") + if voltageInfo is not None: + voltages = [float(voltageInfo.text)/1000, 0, 0] + # AVM returns Wh + imported = powermeterBlock.find("energy").text + + counter_state = CounterState( + imported=imported, + exported=0, + power=power, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=AvmCounterSetup) diff --git a/packages/modules/devices/avm/avm/device.py b/packages/modules/devices/avm/avm/device.py new file mode 100644 index 0000000000..942c39e5da --- /dev/null +++ b/packages/modules/devices/avm/avm/device.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python3 +import hashlib +import logging +import time +from typing import Iterable +import xml.etree.ElementTree as ET + +from dataclass_utils._dataclass_asdict import asdict +from helpermodules.pub import Pub +from modules.common import req +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.devices.avm.avm.config import Avm, AvmCounterSetup +from modules.devices.avm.avm.counter import AvmCounter + +log = logging.getLogger(__name__) + +INVALID_SESSIONID = "0000000000000000" + + +def create_device(device_config: Avm): + def create_counter_component(component_config: AvmCounterSetup): + return AvmCounter(component_config) + + def update_components(components: Iterable[AvmCounter]): + if (device_config.configuration.session_id is None or + device_config.configuration.session_mtime is None or + time.time() - device_config.configuration.session_mtime > 300): + device_config.configuration.session_mtime = time.time() + device_config.configuration.session_id = get_session_id() + Pub().pub(f"openWB/set/system/device/{device_config.id}/config", asdict(device_config)) + + response = req.get_http_session().get( + f"http://{device_config.configuration.ip_address}/webservices/homeautoswitch.lua?sid=" + f"{device_config.configuration.session_id}&switchcmd=getdevicelistinfos") + deviceListElementTree = ET.fromstring(response.text.strip()) + + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update(deviceListElementTree) + + def get_session_id(): + # checking existing sessionID + response = req.get_http_session().post(f"http://{device_config.configuration.ip_address}/login_sid.lua") + challengeResponse = ET.fromstring(response.content) + session_id = challengeResponse.find('SID').text + if session_id != INVALID_SESSIONID: + return + blockTimeXML = challengeResponse.find('BlockTime') + if blockTimeXML is not None and int(blockTimeXML.text) > 0: + raise Exception("Durch Anmeldefehler in der Vergangenheit ist der Zugang zur FRITZ!Box " + f"noch für {blockTimeXML.text} Sekunden gesperrt.") + + # last sessionID was invalid, performing new challenge-response authentication + challenge = challengeResponse.find('Challenge').text + m = hashlib.md5() + m.update((f"{challenge}-{device_config.configuration.password}").encode('utf-16le')) + hashedPassword = m.hexdigest() + + data = { + 'username': device_config.configuration.username, + 'response': challenge + "-" + hashedPassword + } + try: + response = req.get_http_session().post( + f"http://{device_config.configuration.ip_address}/login_sid.lua", data=data, timeout=5) + session_info = ET.fromstring(response.content) + session_id = session_info.find('SID').text + except Exception: + session_id = None + raise Exception("Anmeldung fehlgeschlagen, bitte Benutzername und Passwort überprüfen. Anmeldung für " + f"die nächsten {session_info.find('BlockTime').text} Sekunden durch FRITZ!Box-Webinterface " + "gesperrt.") + return session_id + + return ConfigurableDevice( + device_config=device_config, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Avm) diff --git a/packages/modules/devices/avm/vendor.py b/packages/modules/devices/avm/vendor.py new file mode 100644 index 0000000000..de4025faae --- /dev/null +++ b/packages/modules/devices/avm/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "AVM Fritz!Box" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py index f906db80b3..42c2da0931 100644 --- a/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -11,27 +10,34 @@ from modules.devices.azzurro_zcs.azzurro_zcs.config import ZCSBatSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class ZCSBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, ZCSBatSetup], - modbus_id: int) -> None: - self.__modbus_id = modbus_id - self.component_config = dataclass_from_dict(ZCSBatSetup, component_config) + def __init__(self, component_config: ZCSBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - # 0x020D Battery charge-discharge power Int16 -10-10 kW accury 0,01 kW pos charge, neg discharge + def update(self) -> None: + # 0x020D Battery charge-discharge power Int16 -10-10 kW accuracy 0,01 kW pos charge, neg discharge # 0x020E Battery voltage Cell UInt16 0-100 V accuracy 0,1 V # 0x020F Battery charge-discharge current Int -100-100 A accuracy 0,01A - power = client.read_input_registers(0x020D, ModbusDataType.INT_16, unit=self.__modbus_id) + power = self.client.read_input_registers(0x020D, ModbusDataType.INT_16, unit=self.__modbus_id) # 0x0210 SoC UInt16 0-100 % - soc = client.read_input_registers(0x0210, ModbusDataType.UINT_16, unit=self.__modbus_id) + soc = self.client.read_input_registers(0x0210, ModbusDataType.UINT_16, unit=self.__modbus_id) # 0x0227 Total energy charging battery low UInt16 in kWh LSB - imported = client.read_input_registers( + imported = self.client.read_input_registers( 0x0227, ModbusDataType.UINT_16, unit=self.__modbus_id) * 100 # 0x0229 Total energy discharging battery low UInt16 in kWh LSB - exported = client.read_input_registers( + exported = self.client.read_input_registers( 0x0229, ModbusDataType.UINT_16, unit=self.__modbus_id) * 100 bat_state = BatState( diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs/counter.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs/counter.py index 3840cdf853..0bb0ecc349 100644 --- a/packages/modules/devices/azzurro_zcs/azzurro_zcs/counter.py +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -12,42 +11,40 @@ from modules.devices.azzurro_zcs.azzurro_zcs.config import ZCSCounterSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class ZCSCounter(AbstractCounter): def __init__(self, - component_config: Union[Dict, ZCSCounterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(ZCSCounterSetup, component_config) - self.__modbus_id = modbus_id + component_config: ZCSCounterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): + def update(self) -> None: # 0x0212 Grid Power Int16 -10-10 kW Unit 0,01kW Feed in/out power # 0x0214 Input/Output power Int16 -10-10kW 0,01kW Energy storage power inverter - power = client.read_input_registers(0x0212, ModbusDataType.INT_16, wordorder=Endian.Little, - unit=self.__modbus_id) * -1 + power = self.client.read_input_registers(0x0212, ModbusDataType.INT_16, wordorder=Endian.Little, + unit=self.__modbus_id) * -1 # 0x020C Grid frequency UInt 0-100 Hz Unit 0,01 Hz - frequency = client.read_input_registers( + frequency = self.client.read_input_registers( 0x020C, ModbusDataType.UINT_16, unit=self.__modbus_id) / 100 - try: - # 0x0206 A phase voltage UInt 0-1000V unit 0,1V - # 0x0207 A phase current Int 0-100A Unit 0,01A, rms - # 0x0230 R-Phase voltage UInt Unit 0,1V - # 0x0231 R-Phase current UInt Unit 0,01A - powers = (client.read_input_registers( - 0x0206, ModbusDataType.UINT_16, unit=self.__modbus_id) / 10) * \ - (client.read_input_registers(0x0207, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) - except Exception: - powers = None - exported = [value * 10 - for value in client.read_input_registers( + for value in self.client.read_input_registers( # 0x021E Total energy injected into the grid UInt16 Unit 1kWh high # 0x021F Total energy injected into the grid UInt16 Unit 1kWh low 0x021E, [ModbusDataType.UINT_16] * 10, wordorder=Endian.Little, unit=self.__modbus_id)] imported = [value * 10 - for value in client.read_input_registers( + for value in self.client.read_input_registers( # 0x0220 Total energy taken from the grid UInt16 Unit 1kWh high # 0x0221 Total energy taken from the grid UInt16 Unit 1kWh low 0x0220, [ModbusDataType.UINT_16] * 10, @@ -57,7 +54,6 @@ def update(self, client: ModbusTcpClient_): imported=imported, exported=exported, power=power, - powers=powers, frequency=frequency, ) self.store.set(counter_state) diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs/device.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs/device.py index b175b75ce8..126d40f774 100644 --- a/packages/modules/devices/azzurro_zcs/azzurro_zcs/device.py +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs/device.py @@ -15,27 +15,38 @@ def create_device(device_config: ZCS): + client = None + def create_bat_component(component_config: ZCSBatSetup): - return ZCSBat(component_config, device_config.configuration.modbus_id) + nonlocal client + return ZCSBat(component_config=component_config, modbus_id=device_config.configuration.modbus_id, client=client) def create_counter_component(component_config: ZCSCounterSetup): - return ZCSCounter(component_config, device_config.configuration.modbus_id) + nonlocal client + return ZCSCounter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_inverter_component(component_config: ZCSInverterSetup): - return ZCSInverter(component_config, device_config.configuration.modbus_id) + nonlocal client + return ZCSInverter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + client=client) def update_components(components: Iterable[Union[ZCSBat, ZCSCounter, ZCSInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/azzurro_zcs/azzurro_zcs/inverter.py b/packages/modules/devices/azzurro_zcs/azzurro_zcs/inverter.py index b37117c616..966084e70f 100644 --- a/packages/modules/devices/azzurro_zcs/azzurro_zcs/inverter.py +++ b/packages/modules/devices/azzurro_zcs/azzurro_zcs/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,32 +11,39 @@ from modules.devices.azzurro_zcs.azzurro_zcs.config import ZCSInverterSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class ZCSInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, ZCSInverterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(ZCSInverterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: ZCSInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: # 0x0252 PV1 Power UInt16 0-100kW Unit 0,01kW # 0x0250 PV1 Voltage UInt16 0-1000V Unit 0,1V # 0x0251 PV1 current Int16 0-100A Unit 0,01A - power_string1 = (client.read_input_registers( + power_string1 = (self.client.read_input_registers( 0x0250, ModbusDataType.UINT_16, unit=self.__modbus_id) / 10) * \ - (client.read_input_registers(0x0251, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) + (self.client.read_input_registers(0x0251, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) # 0x0255 PV2 Power UInt16 0-100 kW Unit 0,01kW # 0x0253 PV2 Voltage UInt16 0-1000V Unit 0,1V # 0x0254 PV2 current Int16 0-100A Unit 0,01A - power_string2 = (client.read_input_registers( + power_string2 = (self.client.read_input_registers( 0x0253, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) * \ - (client.read_input_registers(0x0254, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) + (self.client.read_input_registers(0x0254, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) power = (power_string1 + power_string2) * -1 # 0x0215 PV Power generation UInt16 0 -10 kW Unit 0,01kW - exported = client.read_input_registers(0x0215, ModbusDataType.UINT_16, wordorder=Endian.Little, - unit=self.__modbus_id) * 100 + exported = self.client.read_input_registers(0x0215, ModbusDataType.UINT_16, wordorder=Endian.Little, + unit=self.__modbus_id) * 100 inverter_state = InverterState( power=power, diff --git a/packages/modules/devices/batterx/batterx/bat.py b/packages/modules/devices/batterx/batterx/bat.py index 7828ea72cc..2a5b6949a8 100644 --- a/packages/modules/devices/batterx/batterx/bat.py +++ b/packages/modules/devices/batterx/batterx/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.devices.batterx.batterx.config import BatterXBatSetup from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -11,10 +10,17 @@ from modules.common.store import get_bat_value_store +class KwargsDict(TypedDict): + device_id: int + + class BatterXBat(AbstractBat): - def __init__(self, device_id: int, component_config: Union[Dict, BatterXBatSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(BatterXBatSetup, component_config) + def __init__(self, component_config: BatterXBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/batterx/batterx/counter.py b/packages/modules/devices/batterx/batterx/counter.py index a38c177a2a..8d3fcae70b 100644 --- a/packages/modules/devices/batterx/batterx/counter.py +++ b/packages/modules/devices/batterx/batterx/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, List, Union +from typing import Any, Dict, List, TypedDict -from dataclass_utils import dataclass_from_dict from modules.devices.batterx.batterx.config import BatterXCounterSetup from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -14,10 +13,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class BatterXCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, BatterXCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(BatterXCounterSetup, component_config) + def __init__(self, component_config: BatterXCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/batterx/batterx/device.py b/packages/modules/devices/batterx/batterx/device.py index 69bf308394..d015deb74b 100644 --- a/packages/modules/devices/batterx/batterx/device.py +++ b/packages/modules/devices/batterx/batterx/device.py @@ -4,7 +4,7 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import MultiComponentUpdateContext +from modules.common.component_context import MultiComponentUpdateContext, SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common.store import get_inverter_value_store from modules.devices.batterx.batterx import bat, external_inverter @@ -23,23 +23,24 @@ def create_device(device_config: BatterX): def create_bat_component(component_config: BatterXBatSetup): - return bat.BatterXBat(device_config.id, component_config) + return bat.BatterXBat(component_config=component_config, device_id=device_config.id) def create_counter_component(component_config: BatterXCounterSetup): - return counter.BatterXCounter(device_config.id, component_config) + return counter.BatterXCounter(component_config=component_config, device_id=device_config.id) def create_inverter_component(component_config: BatterXInverterSetup): - return inverter.BatterXInverter(device_config.id, component_config) + return inverter.BatterXInverter(component_config=component_config, device_id=device_config.id) def create_external_inverter_component(component_config: BatterXExternalInverterSetup): - return external_inverter.BatterXExternalInverter(device_config.id, component_config) + return external_inverter.BatterXExternalInverter(component_config=component_config, device_id=device_config.id) def update_components(components: Iterable[batterx_component_classes]): resp_json = req.get_http_session().get( 'http://' + device_config.configuration.ip_address + '/api.php?get=currentstate', timeout=5).json() for component in components: - component.update(resp_json) + with SingleComponentUpdateContext(component.fault_state): + component.update(resp_json) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/batterx/batterx/external_inverter.py b/packages/modules/devices/batterx/batterx/external_inverter.py index b8fc4faed3..b103b507a5 100644 --- a/packages/modules/devices/batterx/batterx/external_inverter.py +++ b/packages/modules/devices/batterx/batterx/external_inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, Dict, TypedDict -from dataclass_utils import dataclass_from_dict from modules.devices.batterx.batterx.config import BatterXExternalInverterSetup from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -11,10 +10,17 @@ from modules.common.store import get_inverter_value_store +class KwargsDict(TypedDict): + device_id: int + + class BatterXExternalInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, BatterXExternalInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(BatterXExternalInverterSetup, component_config) + def __init__(self, component_config: BatterXExternalInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/batterx/batterx/inverter.py b/packages/modules/devices/batterx/batterx/inverter.py index 6bad375b11..65e110c65c 100644 --- a/packages/modules/devices/batterx/batterx/inverter.py +++ b/packages/modules/devices/batterx/batterx/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.devices.batterx.batterx.config import BatterXInverterSetup from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -11,10 +10,17 @@ from modules.common.store import get_inverter_value_store +class KwargsDict(TypedDict): + device_id: int + + class BatterXInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, BatterXInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(BatterXInverterSetup, component_config) + def __init__(self, component_config: BatterXInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/benning/benning/config.py b/packages/modules/devices/benning/benning/config.py index e040dce6ff..bb320652b3 100644 --- a/packages/modules/devices/benning/benning/config.py +++ b/packages/modules/devices/benning/benning/config.py @@ -9,7 +9,7 @@ @auto_str class BenningConfiguration(JsonConfiguration): def __init__(self, url: Optional[str] = None): - self.url = "http://" + url + "/getentries.cgi?oids=11369,19000" + self.url = f"http://{url}/getentries.cgi?oids=11369,19000" @auto_str diff --git a/packages/modules/devices/byd/byd/bat.py b/packages/modules/devices/byd/byd/bat.py index 0770b272ef..3157c95d58 100644 --- a/packages/modules/devices/byd/byd/bat.py +++ b/packages/modules/devices/byd/byd/bat.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 import logging from html.parser import HTMLParser -from typing import Dict, List, Union, Tuple +from typing import Any, List, Tuple, TypedDict -from dataclass_utils import dataclass_from_dict -from modules.devices.byd.byd.config import BYDBatSetup +from modules.devices.byd.byd.config import BYD, BYDBatSetup from modules.common import req from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -16,13 +15,18 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_config: BYD + + class BYDBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, BYDBatSetup], - device_config) -> None: - self.__device_config = device_config - self.component_config = dataclass_from_dict(BYDBatSetup, component_config) - self.sim_counter = SimCounter(self.__device_config.id, self.component_config.id, prefix="speicher") + def __init__(self, component_config: BYDBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.device_config: BYD = self.kwargs['device_config'] + self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -44,8 +48,8 @@ def get_values(self) -> Tuple[float, float]: RunData.asp auf ganze kW gerundet und somit für openWB nicht brauchbar. ''' resp = req.get_http_session().get( - 'http://' + self.__device_config.configuration.ip_address + '/asp/Home.asp', - auth=(self.__device_config.configuration.user, self.__device_config.configuration.password)) + 'http://' + self.device_config.configuration.ip_address + '/asp/Home.asp', + auth=(self.device_config.configuration.user, self.device_config.configuration.password)) return BydParser.parse(resp.text) @@ -53,7 +57,7 @@ class BydParser(HTMLParser): values = {"SOC:": 0, "Power:": 0} armed = None - @ staticmethod + @staticmethod def parse(html: str): parser = BydParser() parser.feed(html) diff --git a/packages/modules/devices/byd/byd/device.py b/packages/modules/devices/byd/byd/device.py index ac0adcc25c..ac82b4d897 100644 --- a/packages/modules/devices/byd/byd/device.py +++ b/packages/modules/devices/byd/byd/device.py @@ -11,7 +11,7 @@ def create_device(device_config: BYD): def create_bat_component(component_config: BYDBatSetup): - return bat.BYDBat(component_config, device_config) + return bat.BYDBat(component_config=component_config, device_config=device_config) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/counter.py b/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/counter.py index 3646e1d3e1..394d0bb2f0 100644 --- a/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/counter.py +++ b/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/counter.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.devices.carlo_gavazzi.carlo_gavazzi.config import CarloGavazziCounterSetup from modules.common import modbus from modules.common.abstract_device import AbstractCounter @@ -15,16 +14,21 @@ from modules.common.store import get_counter_value_store +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + class CarloGavazziCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, CarloGavazziCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(CarloGavazziCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: CarloGavazziCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/device.py b/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/device.py index 4d6bf627cd..341b49772f 100644 --- a/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/device.py +++ b/packages/modules/devices/carlo_gavazzi/carlo_gavazzi/device.py @@ -2,33 +2,38 @@ import logging from typing import Iterable +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.carlo_gavazzi.carlo_gavazzi import counter from modules.devices.carlo_gavazzi.carlo_gavazzi.config import CarloGavazzi, CarloGavazziCounterSetup from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext log = logging.getLogger(__name__) def create_device(device_config: CarloGavazzi): + client = None + def create_counter_component(component_config: CarloGavazziCounterSetup): - return counter.CarloGavazziCounter(device_config.id, component_config, client, - device_config.configuration.modbus_id) + nonlocal client + return counter.CarloGavazziCounter(component_config=component_config, device_id=device_config.id, + tcp_client=client, modbus_id=device_config.configuration.modbus_id) def update_components(components: Iterable[counter.CarloGavazziCounter]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component ), diff --git a/packages/modules/devices/deye/deye/bat.py b/packages/modules/devices/deye/deye/bat.py index 0584c89cf3..e7623cf975 100644 --- a/packages/modules/devices/deye/deye/bat.py +++ b/packages/modules/devices/deye/deye/bat.py @@ -1,6 +1,5 @@ -#!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -14,18 +13,24 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class DeyeBat(AbstractBat): - def __init__(self, device_id: int, - component_config: DeyeBatSetup, - client: ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(DeyeBatSetup, component_config) + def __init__(self, component_config: DeyeBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id - self.client = client self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.device_type = DeviceType(self.client.read_holding_registers( - 0, ModbusDataType.INT_16, unit=component_config.configuration.modbus_id)) + 0, ModbusDataType.INT_16, unit=self.component_config.configuration.modbus_id)) def update(self) -> None: unit = self.component_config.configuration.modbus_id @@ -35,9 +40,7 @@ def update(self) -> None: soc = self.client.read_holding_registers(184, ModbusDataType.INT_16, unit=unit) if self.device_type == DeviceType.SINGLE_PHASE_HYBRID: - # 516: Geladen in kWh * 0,1 imported = self.client.read_holding_registers(72, ModbusDataType.UINT_16, unit=unit) * 100 - # 518: Entladen in kWh * 0,1 exported = self.client.read_holding_registers(74, ModbusDataType.UINT_16, unit=unit) * 100 elif self.device_type == DeviceType.SINGLE_PHASE_STRING: @@ -49,10 +52,7 @@ def update(self) -> None: if self.device_type == DeviceType.THREE_PHASE_HV: power = power * 10 soc = self.client.read_holding_registers(588, ModbusDataType.INT_16, unit=unit) - # 516: Geladen in kWh * 0,1 - imported = self.client.read_holding_registers(516, ModbusDataType.UINT_16, unit=unit) * 100 - # 518: Entladen in kWh * 0,1 - exported = self.client.read_holding_registers(518, ModbusDataType.UINT_16, unit=unit) * 100 + imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( power=power, diff --git a/packages/modules/devices/deye/deye/counter.py b/packages/modules/devices/deye/deye/counter.py index ddc1f63ecb..a2769bc967 100644 --- a/packages/modules/devices/deye/deye/counter.py +++ b/packages/modules/devices/deye/deye/counter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -11,18 +12,24 @@ from modules.devices.deye.deye.device_type import DeviceType +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class DeyeCounter(AbstractCounter): - def __init__(self, device_id: int, - component_config: DeyeCounterSetup, - client: ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(DeyeCounterSetup, component_config) + def __init__(self, component_config: DeyeCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id - self.client = client self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.device_type = DeviceType(self.client.read_holding_registers( - 0, ModbusDataType.INT_16, unit=component_config.configuration.modbus_id)) + 0, ModbusDataType.INT_16, unit=self.component_config.configuration.modbus_id)) def update(self): unit = self.component_config.configuration.modbus_id @@ -35,7 +42,6 @@ def update(self): currents = [0]*3 voltages = [0]*3 power = [0] - # High und low word vom import sind nicht in aufeinanderfolgenden Registern imported, exported = self.sim_counter.sim_count(power) elif self.device_type == DeviceType.SINGLE_PHASE_STRING: @@ -51,20 +57,16 @@ def update(self): currents = [c / 100 for c in self.client.read_holding_registers(613, [ModbusDataType.INT_16]*3, unit=unit)] voltages = [v / 10 for v in self.client.read_holding_registers(644, [ModbusDataType.INT_16]*3, unit=unit)] powers = self.client.read_holding_registers(616, [ModbusDataType.INT_16]*3, unit=unit) - power = self.client.read_holding_registers(625, ModbusDataType.INT_16, unit=unit) - frequency = self.client.read_holding_registers(609, ModbusDataType.INT_16, unit=unit) / 100 - - # Wenn der Import/export Netz in wh gerechnet wird => *100 !! kommt in kw/h *0.1 - imported = self.client.read_holding_registers(522, ModbusDataType.INT_16, unit=unit) * 100 - exported = self.client.read_holding_registers(524, ModbusDataType.INT_16, unit=unit) * 100 + power = sum(powers) + imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( currents=currents, + voltages=voltages, + powers=powers, + power=power, imported=imported, exported=exported, - power=power, - powers=powers, - voltages=voltages, frequency=frequency ) self.store.set(counter_state) diff --git a/packages/modules/devices/deye/deye/device.py b/packages/modules/devices/deye/deye/device.py index bdbe5eb769..cf401a4aca 100644 --- a/packages/modules/devices/deye/deye/device.py +++ b/packages/modules/devices/deye/deye/device.py @@ -17,27 +17,34 @@ def create_device(device_config: Deye): + client = None + def create_bat_component(component_config: DeyeBatSetup): - return DeyeBat(device_config.id, component_config, client) + nonlocal client + return DeyeBat(component_config=component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: DeyeCounterSetup): - return DeyeCounter(device_config.id, component_config, client) + nonlocal client + return DeyeCounter(component_config=component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: DeyeInverterSetup): - return DeyeInverter(device_config.id, component_config, client) + nonlocal client + return DeyeInverter(component_config=component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[DeyeBat, DeyeCounter, DeyeInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/deye/deye/inverter.py b/packages/modules/devices/deye/deye/inverter.py index a2e66a3530..8688d97b07 100644 --- a/packages/modules/devices/deye/deye/inverter.py +++ b/packages/modules/devices/deye/deye/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -13,36 +12,41 @@ from modules.devices.deye.deye.device_type import DeviceType +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class DeyeInverter(AbstractInverter): - def __init__(self, device_id: int, - component_config: Union[Dict, DeyeInverterSetup], - client: ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(DeyeInverterSetup, component_config) + def __init__(self, component_config: DeyeInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id - self.client = client self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.device_type = DeviceType(self.client.read_holding_registers( - 0, ModbusDataType.INT_16, unit=component_config.configuration.modbus_id)) + 0, ModbusDataType.INT_16, unit=self.component_config.configuration.modbus_id)) def update(self) -> None: unit = self.component_config.configuration.modbus_id if self.device_type == DeviceType.SINGLE_PHASE_STRING or self.device_type == DeviceType.SINGLE_PHASE_HYBRID: power = sum(self.client.read_holding_registers(186, [ModbusDataType.INT_16]*4, unit=unit)) * -1 - exported = self.sim_counter.sim_count(power)[1] else: # THREE_PHASE_LV (0x0500, 0x0005), THREE_PHASE_HV (0x0006) power = sum(self.client.read_holding_registers(672, [ModbusDataType.INT_16]*2, unit=unit)) * -1 if self.device_type == DeviceType.THREE_PHASE_HV: power = power * 10 - # 534: Gesamt Produktion Wechselrichter unsigned integer in kWh * 0,1 - exported = self.client.read_holding_registers(534, ModbusDataType.UINT_16, unit=unit) * 100 + imported, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( power=power, + imported=imported, exported=exported, ) self.store.set(inverter_state) diff --git a/packages/modules/devices/discovergy/discovergy/counter.py b/packages/modules/devices/discovergy/discovergy/counter.py index aeb2347f8b..14f69ff0ab 100644 --- a/packages/modules/devices/discovergy/discovergy/counter.py +++ b/packages/modules/devices/discovergy/discovergy/counter.py @@ -11,6 +11,8 @@ class DiscovergyCounter(AbstractCounter): def __init__(self, component_config: DiscovergyCounterSetup) -> None: self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/discovergy/discovergy/device.py b/packages/modules/devices/discovergy/discovergy/device.py index 8edbfa85bd..c9bb1f125b 100644 --- a/packages/modules/devices/discovergy/discovergy/device.py +++ b/packages/modules/devices/discovergy/discovergy/device.py @@ -18,16 +18,22 @@ def create_device(device_config: Discovergy): + session = None + def create_counter_component(component_config: DiscovergyCounterSetup): - return counter.DiscovergyCounter(component_config) + return counter.DiscovergyCounter(component_config=component_config) def create_inverter_component(component_config: DiscovergyInverterSetup): - return inverter.DiscovergyInverter(component_config) + return inverter.DiscovergyInverter(component_config=component_config) + + def initializer(): + nonlocal session + session = get_http_session() + session.auth = (device_config.configuration.user, device_config.configuration.password) - session = get_http_session() - session.auth = (device_config.configuration.user, device_config.configuration.password) return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType(counter=create_counter_component, inverter=create_inverter_component), component_updater=IndependentComponentUpdater(lambda component: component.update(session)), ) diff --git a/packages/modules/devices/discovergy/discovergy/inverter.py b/packages/modules/devices/discovergy/discovergy/inverter.py index 57330c2ede..18eca9da3f 100644 --- a/packages/modules/devices/discovergy/discovergy/inverter.py +++ b/packages/modules/devices/discovergy/discovergy/inverter.py @@ -12,6 +12,8 @@ class DiscovergyInverter(AbstractInverter): def __init__(self, component_config: DiscovergyInverterSetup) -> None: self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/e3dc/e3dc/bat.py b/packages/modules/devices/e3dc/e3dc/bat.py index bbe3d01cce..5d4773ba80 100644 --- a/packages/modules/devices/e3dc/e3dc/bat.py +++ b/packages/modules/devices/e3dc/e3dc/bat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Tuple +from typing import Tuple, TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -11,7 +11,6 @@ from modules.common.simcount._simcounter import SimCounter from modules.devices.e3dc.e3dc.config import E3dcBatSetup - log = logging.getLogger(__name__) @@ -23,21 +22,27 @@ def read_bat(client: modbus.ModbusTcpClient_, modbus_id: int) -> Tuple[int, int] return soc, power +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: modbus.ModbusTcpClient_ + + class E3dcBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: E3dcBatSetup, - modbus_id: int) -> None: + def __init__(self, component_config: E3dcBatSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__modbus_id = modbus_id - # bat - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="speicher") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: modbus.ModbusTcpClient_) -> None: - - soc, power = read_bat(client, self.__modbus_id) + def update(self) -> None: + soc, power = read_bat(self.client, self.__modbus_id) imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( power=power, diff --git a/packages/modules/devices/e3dc/e3dc/counter.py b/packages/modules/devices/e3dc/e3dc/counter.py index d5a42e374e..7357d3180c 100644 --- a/packages/modules/devices/e3dc/e3dc/counter.py +++ b/packages/modules/devices/e3dc/e3dc/counter.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Tuple, List +from typing import Tuple, List, TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractCounter @@ -40,19 +40,27 @@ def read_counter(client: modbus.ModbusTcpClient_, modbus_id: int) -> Tuple[int, return power, powers +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: modbus.ModbusTcpClient_ + + class E3dcCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: E3dcCounterSetup, - modbus_id: int) -> None: + def __init__(self, component_config: E3dcCounterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__modbus_id = modbus_id - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: modbus.ModbusTcpClient_) -> None: - power, powers = read_counter(client, self.__modbus_id) + def update(self) -> None: + power, powers = read_counter(self.client, self.__modbus_id) imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( imported=imported, @@ -61,7 +69,6 @@ def update(self, client: modbus.ModbusTcpClient_) -> None: power=power ) self.store.set(counter_state) - log.debug("Update completed successfully") component_descriptor = ComponentDescriptor(configuration_factory=E3dcCounterSetup) diff --git a/packages/modules/devices/e3dc/e3dc/device.py b/packages/modules/devices/e3dc/e3dc/device.py index 549569c624..1e1992e658 100644 --- a/packages/modules/devices/e3dc/e3dc/device.py +++ b/packages/modules/devices/e3dc/e3dc/device.py @@ -4,8 +4,8 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common.abstract_device import DeviceDescriptor -from modules.common.configurable_device import (ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater, - SingleComponentUpdateContext) +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.common import modbus from modules.devices.e3dc.e3dc.bat import E3dcBat, read_bat from modules.devices.e3dc.e3dc.inverter import E3dcInverter, read_inverter @@ -25,40 +25,50 @@ def create_device(device_config: E3dc) -> ConfigurableDevice: + client = None + def create_bat_component(component_config: E3dcBatSetup) -> E3dcBat: - return E3dcBat(device_config.id, - component_config, - device_config.configuration.modbus_id) + nonlocal client + return E3dcBat(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_counter_component(component_config: E3dcCounterSetup) -> E3dcCounter: - return E3dcCounter(device_config.id, - component_config, - device_config.configuration.modbus_id) + nonlocal client + return E3dcCounter(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_inverter_component(component_config: E3dcInverterSetup) -> E3dcInverter: - return E3dcInverter(device_config.id, - component_config, - device_config.configuration.modbus_id) + nonlocal client + return E3dcInverter(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_external_inverter_component(component_config: E3dcExternalInverterSetup) -> E3dcExternalInverter: - return E3dcExternalInverter(device_config.id, - component_config, - device_config.configuration.modbus_id) + nonlocal client + return E3dcExternalInverter(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def update_components(components: Iterable[Union[E3dcBat, E3dcCounter, E3dcInverter, E3dcExternalInverter]]) -> None: - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") - return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/e3dc/e3dc/external_inverter.py b/packages/modules/devices/e3dc/e3dc/external_inverter.py index 64a71253d3..87e1e45233 100644 --- a/packages/modules/devices/e3dc/e3dc/external_inverter.py +++ b/packages/modules/devices/e3dc/e3dc/external_inverter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import logging +from typing import Any, TypedDict from modules.common import modbus from modules.common.abstract_device import AbstractInverter @@ -11,31 +12,36 @@ from modules.common.simcount._simcounter import SimCounter from modules.devices.e3dc.e3dc.config import E3dcExternalInverterSetup - log = logging.getLogger(__name__) def read_external_inverter(client: modbus.ModbusTcpClient_, modbus_id: int) -> int: - # 40075 externe PV Leistung pv_external = int(client.read_holding_registers( 40075, ModbusDataType.INT_32, wordorder=Endian.Little, unit=modbus_id)) return pv_external +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: modbus.ModbusTcpClient_ + + class E3dcExternalInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: E3dcExternalInverterSetup, - modbus_id: int) -> None: + def __init__(self, component_config: E3dcExternalInverterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__modbus_id = modbus_id - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="pv") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: modbus.ModbusTcpClient_) -> None: - - pv_external = read_external_inverter(client, self.__modbus_id) + def update(self) -> None: + pv_external = read_external_inverter(self.client, self.__modbus_id) # pv_external - > pv Leistung # die als externe Produktion an e3dc angeschlossen ist # Im gegensatz zur Implementierung in Version 1.9 wird nicht mehr die PV diff --git a/packages/modules/devices/e3dc/e3dc/inverter.py b/packages/modules/devices/e3dc/e3dc/inverter.py index 395589e43a..10914bd25d 100644 --- a/packages/modules/devices/e3dc/e3dc/inverter.py +++ b/packages/modules/devices/e3dc/e3dc/inverter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 import logging +from typing import Any, TypedDict from modules.common import modbus from modules.common.abstract_device import AbstractInverter @@ -11,30 +12,35 @@ from modules.common.simcount._simcounter import SimCounter from modules.devices.e3dc.e3dc.config import E3dcInverterSetup - log = logging.getLogger(__name__) def read_inverter(client: modbus.ModbusTcpClient_, modbus_id: int) -> int: - # 40067 PV Leistung pv = int(client.read_holding_registers(40067, ModbusDataType.INT_32, wordorder=Endian.Little, unit=modbus_id) * -1) return pv +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: modbus.ModbusTcpClient_ + + class E3dcInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: E3dcInverterSetup, - modbus_id: int) -> None: + def __init__(self, component_config: E3dcInverterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__modbus_id = modbus_id - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="pv") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: modbus.ModbusTcpClient_) -> None: - - pv = read_inverter(client, self.__modbus_id) + def update(self) -> None: + pv = read_inverter(self.client, self.__modbus_id) # Im gegensatz zur Implementierung in Version 1.9 wird nicht mehr die PV # Leistung vom WR1 gelesen, da die durch v2.0 separat gehandelt wird _, pv_exported = self.sim_counter.sim_count(pv) diff --git a/packages/modules/devices/elgris/__init__.py b/packages/modules/devices/elgris/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/elgris/elgris/__init__.py b/packages/modules/devices/elgris/elgris/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/elgris/elgris/bat.py b/packages/modules/devices/elgris/elgris/bat.py new file mode 100644 index 0000000000..6d45e76c4d --- /dev/null +++ b/packages/modules/devices/elgris/elgris/bat.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.common.component_state import BatState +from modules.devices.elgris.elgris.elgris import Elgris +from modules.devices.elgris.elgris.config import ElgrisBatSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractBat +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_bat_value_store + + +class KwargsDict(TypedDict): + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class ElgrisBat(AbstractBat): + def __init__(self, component_config: ElgrisBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.elgris = Elgris(self.__modbus_id, self.__tcp_client, self.fault_state) + self.store = get_bat_value_store(self.component_config.id) + + def update(self): + with self.__tcp_client: + counter_state = self.elgris.get_counter_state() + bat_state = BatState( + exported=counter_state.exported, + imported=counter_state.imported, + power=counter_state.power, + currents=counter_state.currents, + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ElgrisBatSetup) diff --git a/packages/modules/devices/elgris/elgris/config.py b/packages/modules/devices/elgris/elgris/config.py new file mode 100644 index 0000000000..cccf6e5c02 --- /dev/null +++ b/packages/modules/devices/elgris/elgris/config.py @@ -0,0 +1,66 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class ElgrisConfiguration: + def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: int = 502): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + + +class Elgris: + def __init__(self, + name: str = "Elgris", + type: str = "elgris", + id: int = 0, + configuration: ElgrisConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or ElgrisConfiguration() + + +class ElgrisBatConfiguration: + def __init__(self): + pass + + +class ElgrisBatSetup(ComponentSetup[ElgrisBatConfiguration]): + def __init__(self, + name: str = "Elgris Smart Meter Speicher", + type: str = "bat", + id: int = 0, + configuration: ElgrisBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ElgrisBatConfiguration()) + + +class ElgrisCounterConfiguration: + def __init__(self): + pass + + +class ElgrisCounterSetup(ComponentSetup[ElgrisCounterConfiguration]): + def __init__(self, + name: str = "Elgris Smart Meter", + type: str = "counter", + id: int = 0, + configuration: ElgrisCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ElgrisCounterConfiguration()) + + +class ElgrisInverterConfiguration: + def __init__(self): + pass + + +class ElgrisInverterSetup(ComponentSetup[ElgrisInverterConfiguration]): + def __init__(self, + name: str = "Elgris Smart Meter Welchselrichter", + type: str = "inverter", + id: int = 0, + configuration: ElgrisInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ElgrisInverterConfiguration()) diff --git a/packages/modules/devices/elgris/elgris/counter.py b/packages/modules/devices/elgris/elgris/counter.py new file mode 100644 index 0000000000..8456e3a134 --- /dev/null +++ b/packages/modules/devices/elgris/elgris/counter.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.devices.elgris.elgris.elgris import Elgris +from modules.devices.elgris.elgris.config import ElgrisCounterSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractCounter +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_counter_value_store + + +class KwargsDict(TypedDict): + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class ElgrisCounter(AbstractCounter): + def __init__(self, component_config: ElgrisCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.elgris = Elgris(self.__modbus_id, self.__tcp_client, self.fault_state) + self.store = get_counter_value_store(self.component_config.id) + + def update(self): + with self.__tcp_client: + counter_state = self.elgris.get_counter_state() + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ElgrisCounterSetup) diff --git a/packages/modules/devices/elgris/elgris/device.py b/packages/modules/devices/elgris/elgris/device.py new file mode 100644 index 0000000000..2c8a93c3cc --- /dev/null +++ b/packages/modules/devices/elgris/elgris/device.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.devices.elgris.elgris import bat, counter, inverter +from modules.devices.elgris.elgris.config import Elgris, ElgrisBatSetup, ElgrisCounterSetup +from modules.common import modbus +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.devices.elgris.elgris.config import ElgrisInverterSetup + +log = logging.getLogger(__name__) + + +def create_device(device_config: Elgris): + client = None + + def create_bat_component(component_config: ElgrisBatSetup): + nonlocal client + return bat.ElgrisBat(component_config=component_config, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_counter_component(component_config: ElgrisCounterSetup): + nonlocal client + return counter.ElgrisCounter(component_config=component_config, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_inverter_component(component_config: ElgrisInverterSetup): + nonlocal client + return inverter.ElgrisInverter(component_config=component_config, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[Union[bat.ElgrisBat, counter.ElgrisCounter, inverter.ElgrisInverter]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Elgris) diff --git a/packages/modules/devices/elgris/elgris/elgris.py b/packages/modules/devices/elgris/elgris/elgris.py new file mode 100644 index 0000000000..74d5e8272c --- /dev/null +++ b/packages/modules/devices/elgris/elgris/elgris.py @@ -0,0 +1,13 @@ +from modules.common import modbus +from modules.common.fault_state import FaultState +from modules.common.sdm import Sdm630_72 + + +class Elgris(Sdm630_72): + def __init__(self, modbus_id: int, client: modbus.ModbusTcpClient_, fault_state: FaultState) -> None: + self.client = client + self.id = modbus_id + self.last_query = self._get_time_ms() + self.WAIT_MS_BETWEEN_QUERIES = 100 + self.serial_number = "" + self.fault_state = fault_state diff --git a/packages/modules/devices/elgris/elgris/inverter.py b/packages/modules/devices/elgris/elgris/inverter.py new file mode 100644 index 0000000000..c85d133582 --- /dev/null +++ b/packages/modules/devices/elgris/elgris/inverter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict + +from modules.common.component_state import InverterState +from modules.devices.elgris.elgris.elgris import Elgris +from modules.common.store._inverter import get_inverter_value_store +from modules.devices.elgris.elgris.config import ElgrisInverterSetup +from modules.common import modbus +from modules.common.abstract_device import AbstractInverter +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState + + +class KwargsDict(TypedDict): + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class ElgrisInverter(AbstractInverter): + def __init__(self, component_config: ElgrisInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.elgris = Elgris(self.__modbus_id, self.__tcp_client, self.fault_state) + self.store = get_inverter_value_store(self.component_config.id) + + def update(self): + with self.__tcp_client: + counter_state = self.elgris.get_counter_state() + inverter_state = InverterState( + exported=counter_state.exported, + imported=counter_state.imported, + power=counter_state.power, + currents=counter_state.currents, + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ElgrisInverterSetup) diff --git a/packages/modules/devices/elgris/vendor.py b/packages/modules/devices/elgris/vendor.py new file mode 100644 index 0000000000..33ce570be5 --- /dev/null +++ b/packages/modules/devices/elgris/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Elgris" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/enphase/enphase/bat.py b/packages/modules/devices/enphase/enphase/bat.py index af0d906d11..3dd1cbb4e0 100644 --- a/packages/modules/devices/enphase/enphase/bat.py +++ b/packages/modules/devices/enphase/enphase/bat.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Any, Dict, Optional, Union +from typing import Any, Dict, Optional, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -14,12 +13,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class EnphaseBat(AbstractBat): - def __init__(self, device_id: int, component_config: Union[Dict, EnphaseBatSetup]) -> None: - self.component_config = dataclass_from_dict(EnphaseBatSetup, component_config) + def __init__(self, component_config: EnphaseBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") def update(self, response, live_data: Optional[Dict[str, Any]] = None) -> None: diff --git a/packages/modules/devices/enphase/enphase/counter.py b/packages/modules/devices/enphase/enphase/counter.py index 295c779243..638682c363 100644 --- a/packages/modules/devices/enphase/enphase/counter.py +++ b/packages/modules/devices/enphase/enphase/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Any, Dict, Union +from typing import Any, Dict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -14,8 +13,10 @@ class EnphaseCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, EnphaseCounterSetup]) -> None: - self.component_config = dataclass_from_dict(EnphaseCounterSetup, component_config) + def __init__(self, component_config: EnphaseCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/enphase/enphase/device.py b/packages/modules/devices/enphase/enphase/device.py index e054d46766..2928afe599 100644 --- a/packages/modules/devices/enphase/enphase/device.py +++ b/packages/modules/devices/enphase/enphase/device.py @@ -8,6 +8,7 @@ from helpermodules.pub import Pub from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.enphase.enphase.bat import EnphaseBat from modules.devices.enphase.enphase.config import (EnphaseVersion, @@ -25,13 +26,13 @@ def create_device(device_config: Enphase): def create_bat_component(component_config: EnphaseBatSetup): nonlocal read_live_data read_live_data = True - return EnphaseBat(device_config.id, component_config) + return EnphaseBat(component_config=component_config, device_id=device_config.id) def create_counter_component(component_config: EnphaseCounterSetup): - return EnphaseCounter(device_config.id, component_config) + return EnphaseCounter(component_config=component_config) def create_inverter_component(component_config: EnphaseInverterSetup): - return EnphaseInverter(device_config.id, component_config) + return EnphaseInverter(component_config=component_config) def check_token() -> bool: if (device_config.configuration.token is None or @@ -107,7 +108,7 @@ def receive_token() -> bool: def update_components(components: Iterable[Union[EnphaseBat, EnphaseCounter, EnphaseInverter]]): nonlocal token_fails - if device_config.configuration.version == EnphaseVersion.V2.value: + if device_config.configuration.version == EnphaseVersion.V2: # v2 requires token authentication if check_token() is False: log.error("no valid token to connect to envoy") @@ -115,12 +116,12 @@ def update_components(components: Iterable[Union[EnphaseBat, EnphaseCounter, Enp log.debug("Start device reading " + str(components)) with req.get_http_session() as session: json_live_data = None - if device_config.configuration.version == EnphaseVersion.V1.value: + if device_config.configuration.version == EnphaseVersion.V1: json_response = session.get( 'http://'+device_config.configuration.hostname+'/ivp/meters/readings', timeout=5).json() # json_live_data does not exist on older firmware - elif device_config.configuration.version == EnphaseVersion.V2.value: + elif device_config.configuration.version == EnphaseVersion.V2: response = session.get( 'https://'+device_config.configuration.hostname+'/ivp/meters/readings', timeout=5, verify=False, @@ -143,7 +144,8 @@ def update_components(components: Iterable[Union[EnphaseBat, EnphaseCounter, Enp log.error(f"unknown version: {device_config.configuration.version}") return for component in components: - component.update(json_response, json_live_data) + with SingleComponentUpdateContext(component.fault_state): + component.update(json_response, json_live_data) read_live_data = False token_tries = 0 diff --git a/packages/modules/devices/enphase/enphase/inverter.py b/packages/modules/devices/enphase/enphase/inverter.py index 7469922176..b728435943 100644 --- a/packages/modules/devices/enphase/enphase/inverter.py +++ b/packages/modules/devices/enphase/enphase/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Any, Dict, Union +from typing import Any, Dict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -14,8 +13,10 @@ class EnphaseInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, EnphaseInverterSetup]) -> None: - self.component_config = dataclass_from_dict(EnphaseInverterSetup, component_config) + def __init__(self, component_config: EnphaseInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fems/fems/bat.py b/packages/modules/devices/fems/fems/bat.py index 04709c0cfe..a30981958d 100644 --- a/packages/modules/devices/fems/fems/bat.py +++ b/packages/modules/devices/fems/fems/bat.py @@ -1,6 +1,6 @@ import logging from requests import Session -from helpermodules.scale_metric import scale_metric +from typing import TypedDict, Any from modules.devices.fems.fems.config import FemsBatSetup from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -8,14 +8,24 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.store import get_bat_value_store from modules.devices.fems.fems.version import FemsVersion, get_version +from helpermodules.scale_metric import scale_metric + log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + ip_address: str + session: Session + + class FemsBat(AbstractBat): - def __init__(self, ip_address: str, component_config: FemsBatSetup, session: Session) -> None: - self.ip_address = ip_address + def __init__(self, component_config: FemsBatSetup, **kwargs: Any) -> None: self.component_config = component_config - self.session = session + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] + self.session: Session = self.kwargs['session'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) if self.component_config.configuration.num == 1: @@ -75,7 +85,6 @@ def update(self) -> None: pv = scale_metric(singleValue['value'], singleValue.get('unit'), 'W') elif address == "_sum/ConsumptionActivePower": haus = scale_metric(singleValue['value'], singleValue.get('unit'), 'W') - # keine Berechnung im Gerät, da grid nicht der Leistung aus der Zählerkomponente entspricht. power = grid + pv - haus bat_state = BatState( power=power, diff --git a/packages/modules/devices/fems/fems/counter.py b/packages/modules/devices/fems/fems/counter.py index 5f2ee65d38..e9c0b14e76 100644 --- a/packages/modules/devices/fems/fems/counter.py +++ b/packages/modules/devices/fems/fems/counter.py @@ -1,5 +1,7 @@ import logging +from typing import Any, TypedDict from requests import Session + from helpermodules.scale_metric import scale_metric from modules.devices.fems.fems.config import FemsCounterSetup from modules.common.abstract_device import AbstractCounter @@ -12,11 +14,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + ip_address: str + session: Session + + class FemsCounter(AbstractCounter): - def __init__(self, ip_address: str, component_config: FemsCounterSetup, session: Session) -> None: - self.ip_address = ip_address + def __init__(self, component_config: FemsCounterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.session = session + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] + self.session: Session = self.kwargs['session'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.version = get_version(self.get_data_by_multiple_segement_regex_query) diff --git a/packages/modules/devices/fems/fems/device.py b/packages/modules/devices/fems/fems/device.py index 1ade03bc6f..9e1781ee71 100644 --- a/packages/modules/devices/fems/fems/device.py +++ b/packages/modules/devices/fems/fems/device.py @@ -13,27 +13,33 @@ def create_device(device_config: Fems): + session = None + def create_bat_component(component_config: FemsBatSetup): - return bat.FemsBat(device_config.configuration.ip_address, - component_config, session) + nonlocal session + return bat.FemsBat(component_config, ip_address=device_config.configuration.ip_address, session=session) def create_counter_component(component_config: FemsCounterSetup): - return counter.FemsCounter(device_config.configuration.ip_address, - component_config, session) + nonlocal session + return counter.FemsCounter(component_config, ip_address=device_config.configuration.ip_address, session=session) def create_inverter_component(component_config: FemsInverterSetup): - return inverter.FemsInverter(device_config.configuration.ip_address, - component_config, session) + nonlocal session + return inverter.FemsInverter(component_config, ip_address=device_config.configuration.ip_address, + session=session) def update_components(components: Iterable[Union[bat.FemsBat, counter.FemsCounter, inverter.FemsInverter]]): for component in components: component.update() - session = req.get_http_session() - session.auth = ("x", device_config.configuration.password) + def initializer(): + nonlocal session + session = req.get_http_session() + session.auth = ("x", device_config.configuration.password) return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/fems/fems/inverter.py b/packages/modules/devices/fems/fems/inverter.py index bcb5dfbc76..33425bb3e7 100644 --- a/packages/modules/devices/fems/fems/inverter.py +++ b/packages/modules/devices/fems/fems/inverter.py @@ -1,5 +1,6 @@ import logging from requests import Session +from typing import TypedDict, Any from helpermodules.scale_metric import scale_metric from modules.devices.fems.fems.config import FemsInverterSetup from modules.common.abstract_device import AbstractInverter @@ -8,14 +9,23 @@ from modules.common.fault_state import ComponentInfo, FaultState from modules.common.store import get_inverter_value_store from modules.devices.fems.fems.version import FemsVersion, get_version + log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + ip_address: str + session: Session + + class FemsInverter(AbstractInverter): - def __init__(self, ip_address: str, component_config: FemsInverterSetup, session: Session) -> None: - self.ip_address = ip_address + def __init__(self, component_config: FemsInverterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.session = session + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] + self.session: Session = self.kwargs['session'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.version = get_version(self.get_data_by_multiple_segement_regex_query) diff --git a/packages/modules/devices/fox_ess/fox_ess/bat.py b/packages/modules/devices/fox_ess/fox_ess/bat.py index 1e9f2c50e8..e9d27e69ae 100644 --- a/packages/modules/devices/fox_ess/fox_ess/bat.py +++ b/packages/modules/devices/fox_ess/fox_ess/bat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -12,21 +12,29 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + class FoxEssBat(AbstractBat): - def __init__(self, component_config: FoxEssBatSetup) -> None: - self.component_config = dataclass_from_dict(FoxEssBatSetup, component_config) + def __init__(self, component_config: FoxEssBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(31036, ModbusDataType.INT_16, unit=unit) * -1 - soc = client.read_holding_registers(31038, ModbusDataType.UINT_16, unit=unit) + power = self.client.read_holding_registers(31036, ModbusDataType.INT_16, unit=unit) * -1 + soc = self.client.read_holding_registers(31038, ModbusDataType.UINT_16, unit=unit) # Geladen in kWh * 0,1 - imported = client.read_holding_registers(32003, ModbusDataType.UINT_32, unit=unit) * 100 + imported = self.client.read_holding_registers(32003, ModbusDataType.UINT_32, unit=unit) * 100 # Entladen in kWh * 0,1 - exported = client.read_holding_registers(32006, ModbusDataType.UINT_32, unit=unit) * 100 + exported = self.client.read_holding_registers(32006, ModbusDataType.UINT_32, unit=unit) * 100 bat_state = BatState( power=power, diff --git a/packages/modules/devices/fox_ess/fox_ess/counter.py b/packages/modules/devices/fox_ess/fox_ess/counter.py index dc6a963434..ba96b14221 100644 --- a/packages/modules/devices/fox_ess/fox_ess/counter.py +++ b/packages/modules/devices/fox_ess/fox_ess/counter.py @@ -1,29 +1,36 @@ -#!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState -from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.store import get_counter_value_store from modules.devices.fox_ess.fox_ess.config import FoxEssCounterSetup +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from typing import TypedDict, Any + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ class FoxEssCounter(AbstractCounter): - def __init__(self, component_config: FoxEssCounterSetup) -> None: - self.component_config = dataclass_from_dict(FoxEssCounterSetup, component_config) + def __init__(self, component_config: FoxEssCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): + def update(self) -> None: unit = self.component_config.configuration.modbus_id powers = [val * -1 for val in - client.read_holding_registers(31026, [ModbusDataType.INT_16]*3, unit=unit)] + self.client.read_holding_registers(31026, [ModbusDataType.INT_16]*3, unit=unit)] power = sum(powers) - frequency = client.read_holding_registers(31015, ModbusDataType.UINT_16, unit=unit) / 100 - imported = client.read_holding_registers(32018, ModbusDataType.UINT_32, unit=unit) * 100 - exported = client.read_holding_registers(32015, ModbusDataType.UINT_32, unit=unit) * 100 + frequency = self.client.read_holding_registers(31015, ModbusDataType.UINT_16, unit=unit) / 100 + imported = self.client.read_holding_registers(32018, ModbusDataType.UINT_32, unit=unit) * 100 + exported = self.client.read_holding_registers(32015, ModbusDataType.UINT_32, unit=unit) * 100 counter_state = CounterState( imported=imported, diff --git a/packages/modules/devices/fox_ess/fox_ess/device.py b/packages/modules/devices/fox_ess/fox_ess/device.py index 3194ecfed7..8505073342 100644 --- a/packages/modules/devices/fox_ess/fox_ess/device.py +++ b/packages/modules/devices/fox_ess/fox_ess/device.py @@ -15,27 +15,34 @@ def create_device(device_config: FoxEss): + client = None + def create_bat_component(component_config: FoxEssBatSetup): - return FoxEssBat(component_config) + nonlocal client + return FoxEssBat(component_config=component_config, client=client) def create_counter_component(component_config: FoxEssCounterSetup): - return FoxEssCounter(component_config) + nonlocal client + return FoxEssCounter(component_config=component_config, client=client) def create_inverter_component(component_config: FoxEssInverterSetup): - return FoxEssInverter(component_config) + nonlocal client + return FoxEssInverter(component_config=component_config, client=client) def update_components(components: Iterable[Union[FoxEssBat, FoxEssCounter, FoxEssInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/fox_ess/fox_ess/inverter.py b/packages/modules/devices/fox_ess/fox_ess/inverter.py index b2b22e48d3..be8b608c43 100644 --- a/packages/modules/devices/fox_ess/fox_ess/inverter.py +++ b/packages/modules/devices/fox_ess/fox_ess/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,20 +10,28 @@ from modules.devices.fox_ess.fox_ess.config import FoxEssInverterSetup +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + class FoxEssInverter(AbstractInverter): - def __init__(self, component_config: Union[Dict, FoxEssInverterSetup]) -> None: - self.component_config = dataclass_from_dict(FoxEssInverterSetup, component_config) + def __init__(self, component_config: FoxEssInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id # PV1 + PV2 Power - power = sum([client.read_holding_registers( + power = sum([self.client.read_holding_registers( reg, ModbusDataType.INT_16, unit=unit) for reg in [31002, 31005]]) * -1 # Gesamt Produktion Wechselrichter unsigned integer in kWh * 0,1 - exported = client.read_holding_registers(32000, ModbusDataType.UINT_32, unit=unit) * 100 + exported = self.client.read_holding_registers(32000, ModbusDataType.UINT_32, unit=unit) * 100 inverter_state = InverterState( power=power, diff --git a/packages/modules/devices/fronius/fronius/bat.py b/packages/modules/devices/fronius/fronius/bat.py index 30c08215f0..528032c6e2 100644 --- a/packages/modules/devices/fronius/fronius/bat.py +++ b/packages/modules/devices/fronius/fronius/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,14 +12,19 @@ from modules.devices.fronius.fronius.config import FroniusConfiguration +class KwargsDict(TypedDict): + device_config: FroniusConfiguration + device_id: int + + class FroniusBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, FroniusBatSetup], - device_config: FroniusConfiguration) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(FroniusBatSetup, component_config) - self.device_config = device_config + def __init__(self, component_config: FroniusBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.device_config: FroniusConfiguration = self.kwargs['device_config'] + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fronius/fronius/bat_test.py b/packages/modules/devices/fronius/fronius/bat_test.py index 5a6a5cc449..64d43e19ce 100644 --- a/packages/modules/devices/fronius/fronius/bat_test.py +++ b/packages/modules/devices/fronius/fronius/bat_test.py @@ -23,7 +23,9 @@ def test_update(monkeypatch, requests_mock: requests_mock.Mocker, mock_ramdisk, device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP assert component_config.configuration.meter_id == 0 - battery = bat.FroniusBat(0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + battery = bat.FroniusBat(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + battery.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) diff --git a/packages/modules/devices/fronius/fronius/counter_s0.py b/packages/modules/devices/fronius/fronius/counter_s0.py index c9bf82884d..6528a168a4 100644 --- a/packages/modules/devices/fronius/fronius/counter_s0.py +++ b/packages/modules/devices/fronius/fronius/counter_s0.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -12,14 +11,19 @@ from modules.devices.fronius.fronius.config import FroniusConfiguration, FroniusS0CounterSetup +class KwargsDict(TypedDict): + device_id: int + device_config: FroniusConfiguration + + class FroniusS0Counter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, FroniusS0CounterSetup], - device_config: FroniusConfiguration) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(FroniusS0CounterSetup, component_config) - self.device_config = device_config + def __init__(self, component_config: FroniusS0CounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.device_config: FroniusConfiguration = self.kwargs['device_config'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fronius/fronius/counter_s0_test.py b/packages/modules/devices/fronius/fronius/counter_s0_test.py index 88725b49b5..e29f815e0c 100644 --- a/packages/modules/devices/fronius/fronius/counter_s0_test.py +++ b/packages/modules/devices/fronius/fronius/counter_s0_test.py @@ -22,8 +22,9 @@ def test_update(monkeypatch, requests_mock: requests_mock.Mocker, mock_ramdisk, component_config = FroniusS0CounterSetup() device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP - counter = counter_s0.FroniusS0Counter( - 0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + counter = counter_s0.FroniusS0Counter(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + counter.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) diff --git a/packages/modules/devices/fronius/fronius/counter_sm.py b/packages/modules/devices/fronius/fronius/counter_sm.py index 23ab966a5b..c7adfd6f74 100644 --- a/packages/modules/devices/fronius/fronius/counter_sm.py +++ b/packages/modules/devices/fronius/fronius/counter_sm.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Tuple, Union +from typing import Tuple, TypedDict, Any from requests import Session -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -18,14 +17,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + device_config: FroniusConfiguration + + class FroniusSmCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, FroniusSmCounterSetup], - device_config: FroniusConfiguration) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(FroniusSmCounterSetup, component_config) - self.device_config = device_config + def __init__(self, component_config: FroniusSmCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.device_config: FroniusConfiguration = self.kwargs['device_config'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fronius/fronius/counter_sm_test.py b/packages/modules/devices/fronius/fronius/counter_sm_test.py index c03558397a..1dfc235b44 100644 --- a/packages/modules/devices/fronius/fronius/counter_sm_test.py +++ b/packages/modules/devices/fronius/fronius/counter_sm_test.py @@ -24,7 +24,9 @@ def test_update_grid(monkeypatch, requests_mock: requests_mock.Mocker, mock_ramd device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP assert component_config.configuration.meter_id == 0 - counter = counter_sm.FroniusSmCounter(0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + counter = counter_sm.FroniusSmCounter(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + counter.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) @@ -54,7 +56,9 @@ def test_update_grid_var2(monkeypatch, requests_mock: requests_mock.Mocker, mock device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP assert component_config.configuration.meter_id == 0 - counter = counter_sm.FroniusSmCounter(0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + counter = counter_sm.FroniusSmCounter(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + counter.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) @@ -83,7 +87,9 @@ def test_update_external_var2(monkeypatch, requests_mock: requests_mock.Mocker, device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP component_config.configuration.meter_id = 1 - counter = counter_sm.FroniusSmCounter(0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + counter = counter_sm.FroniusSmCounter(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + counter.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) @@ -112,7 +118,9 @@ def test_update_load(monkeypatch, requests_mock: requests_mock.Mocker, mock_ramd device_config = FroniusConfiguration() device_config.ip_address = SAMPLE_IP component_config.configuration.meter_id = 2 - counter = counter_sm.FroniusSmCounter(0, component_config, dataclass_from_dict(FroniusConfiguration, device_config)) + counter = counter_sm.FroniusSmCounter(component_config, device_config=dataclass_from_dict( + FroniusConfiguration, device_config), device_id=0) + counter.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) diff --git a/packages/modules/devices/fronius/fronius/device.py b/packages/modules/devices/fronius/fronius/device.py index a5ea75c214..a8eb412a7b 100644 --- a/packages/modules/devices/fronius/fronius/device.py +++ b/packages/modules/devices/fronius/fronius/device.py @@ -6,7 +6,6 @@ from modules.common import req from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.fronius.fronius.bat import FroniusBat from modules.devices.fronius.fronius.config import (Fronius, FroniusBatSetup, FroniusSecondaryInverterSetup, @@ -25,47 +24,53 @@ def create_device(device_config: Fronius): def create_bat_component(component_config: FroniusBatSetup): - return FroniusBat(device_config.id, component_config, device_config.configuration) + return FroniusBat(component_config=component_config, + device_id=device_config.id, + device_config=device_config.configuration) def create_counter_sm_component(component_config: FroniusSmCounterSetup): - return FroniusSmCounter(device_config.id, component_config, device_config.configuration) + return FroniusSmCounter(component_config=component_config, + device_id=device_config.id, + device_config=device_config.configuration) def create_counter_s0_component(component_config: FroniusS0CounterSetup): - return FroniusS0Counter(device_config.id, component_config, device_config.configuration) + return FroniusS0Counter(component_config=component_config, + device_id=device_config.id, + device_config=device_config.configuration) def create_inverter_component(component_config: FroniusInverterSetup): - return FroniusInverter(device_config.id, component_config) + return FroniusInverter(component_config=component_config, + device_id=device_config.id) def create_inverter_secondary_component(component_config: FroniusSecondaryInverterSetup): - return FroniusSecondaryInverter(device_config.id, component_config) + return FroniusSecondaryInverter(component_config=component_config, + device_id=device_config.id) def update_components(components: Iterable[fronius_component_classes]): inverter_response = None for component in components: - with SingleComponentUpdateContext(component.fault_state): - if ( - component.component_config.type == "inverter" or - component.component_config.type == "inverter_secondary" - ): - if inverter_response is None: - try: - inverter_response = req.get_http_session().get( - (f'http://{device_config.configuration.ip_address}' - '/solar_api/v1/GetPowerFlowRealtimeData.fcgi'), - params=(('Scope', 'System'),), - timeout=3).json() - except (requests.ConnectTimeout, requests.ConnectionError) as e: - inverter_response = e - # Nachtmodus: WR ist ausgeschaltet - component.update(inverter_response) + if ( + component.component_config.type == "inverter" or + component.component_config.type == "inverter_secondary" + ): + if inverter_response is None: + try: + inverter_response = req.get_http_session().get( + (f'http://{device_config.configuration.ip_address}' + '/solar_api/v1/GetPowerFlowRealtimeData.fcgi'), + params=(('Scope', 'System'),), + timeout=3).json() + except (requests.ConnectTimeout, requests.ConnectionError) as e: + inverter_response = e + # Nachtmodus: WR ist ausgeschaltet + component.update(inverter_response) for component in components: - with SingleComponentUpdateContext(component.fault_state): - if ( - component.component_config.type != "inverter" and - component.component_config.type != "inverter_secondary" - ): - component.update() + if ( + component.component_config.type != "inverter" and + component.component_config.type != "inverter_secondary" + ): + component.update() return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/fronius/fronius/inverter.py b/packages/modules/devices/fronius/fronius/inverter.py index de9486ec07..b3afaa7eab 100644 --- a/packages/modules/devices/fronius/fronius/inverter.py +++ b/packages/modules/devices/fronius/fronius/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,12 +10,17 @@ from modules.devices.fronius.fronius.config import FroniusInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class FroniusInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, FroniusInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(FroniusInverterSetup, component_config) + def __init__(self, component_config: FroniusInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fronius/fronius/inverter_secondary.py b/packages/modules/devices/fronius/fronius/inverter_secondary.py index de4a64ac27..10bacae7b9 100644 --- a/packages/modules/devices/fronius/fronius/inverter_secondary.py +++ b/packages/modules/devices/fronius/fronius/inverter_secondary.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,12 +10,17 @@ from modules.devices.fronius.fronius.config import FroniusSecondaryInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class FroniusSecondaryInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, FroniusSecondaryInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(FroniusSecondaryInverterSetup, component_config) + def __init__(self, component_config: FroniusSecondaryInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/fronius/fronius/inverter_secondary_test.py b/packages/modules/devices/fronius/fronius/inverter_secondary_test.py index 5b4111f7e1..a8038e8c88 100644 --- a/packages/modules/devices/fronius/fronius/inverter_secondary_test.py +++ b/packages/modules/devices/fronius/fronius/inverter_secondary_test.py @@ -7,7 +7,9 @@ def test_update(monkeypatch, mock_simcount): - wr = FroniusSecondaryInverter(0, FroniusSecondaryInverterSetup(FroniusSecondaryInverterConfiguration(id=1))) + wr = FroniusSecondaryInverter(FroniusSecondaryInverterSetup( + FroniusSecondaryInverterConfiguration(id=1)), device_id=0) + wr.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) diff --git a/packages/modules/devices/fronius/fronius/inverter_test.py b/packages/modules/devices/fronius/fronius/inverter_test.py index 5b4111f7e1..a8038e8c88 100644 --- a/packages/modules/devices/fronius/fronius/inverter_test.py +++ b/packages/modules/devices/fronius/fronius/inverter_test.py @@ -7,7 +7,9 @@ def test_update(monkeypatch, mock_simcount): - wr = FroniusSecondaryInverter(0, FroniusSecondaryInverterSetup(FroniusSecondaryInverterConfiguration(id=1))) + wr = FroniusSecondaryInverter(FroniusSecondaryInverterSetup( + FroniusSecondaryInverterConfiguration(id=1)), device_id=0) + wr.initialize() mock = Mock(return_value=None) monkeypatch.setattr(LoggingValueStore, "set", mock) diff --git a/packages/modules/devices/generic/http/bat.py b/packages/modules/devices/generic/http/bat.py index 8b9bc5600a..d42c8060cf 100644 --- a/packages/modules/devices/generic/http/bat.py +++ b/packages/modules/devices/generic/http/bat.py @@ -1,31 +1,45 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict from requests import Session -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store -from modules.devices.generic.http.api import create_request_function +from modules.devices.generic.http.api import create_request_function, create_request_function_array from modules.devices.generic.http.config import HttpBatSetup +class KwargsDict(TypedDict): + device_id: int + url: str + + class HttpBat(AbstractBat): - def __init__(self, device_id: int, component_config: Union[Dict, HttpBatSetup], url: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HttpBatSetup, component_config) + def __init__(self, component_config: HttpBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__get_power = create_request_function(url, self.component_config.configuration.power_path) - self.__get_imported = create_request_function(url, self.component_config.configuration.imported_path) - self.__get_exported = create_request_function(url, self.component_config.configuration.exported_path) - self.__get_soc = create_request_function(url, self.component_config.configuration.soc_path) + self.__get_currents = create_request_function_array(self.kwargs['url'], [ + self.component_config.configuration.current_l1_path, + self.component_config.configuration.current_l2_path, + self.component_config.configuration.current_l3_path, + ]) + self.__get_power = create_request_function(self.kwargs['url'], self.component_config.configuration.power_path) + self.__get_imported = create_request_function( + self.kwargs['url'], self.component_config.configuration.imported_path) + self.__get_exported = create_request_function( + self.kwargs['url'], self.component_config.configuration.exported_path) + self.__get_soc = create_request_function(self.kwargs['url'], self.component_config.configuration.soc_path) def update(self, session: Session) -> None: power = self.__get_power(session) @@ -35,6 +49,7 @@ def update(self, session: Session) -> None: imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( + currents=self.__get_currents(session), power=power, exported=exported, imported=imported, diff --git a/packages/modules/devices/generic/http/config.py b/packages/modules/devices/generic/http/config.py index f2d6bcfa7d..f40fcee1e9 100644 --- a/packages/modules/devices/generic/http/config.py +++ b/packages/modules/devices/generic/http/config.py @@ -25,9 +25,18 @@ def __init__(self, @auto_str class HttpBatConfiguration: - def __init__(self, power_path=None, soc_path=None, imported_path=None, exported_path=None): + def __init__(self, power_path=None, + soc_path=None, + current_l1_path=None, + current_l2_path=None, + current_l3_path=None, + imported_path=None, + exported_path=None): self.power_path = power_path self.soc_path = soc_path + self.current_l1_path = current_l1_path + self.current_l2_path = current_l2_path + self.current_l3_path = current_l3_path self.imported_path = imported_path self.exported_path = exported_path diff --git a/packages/modules/devices/generic/http/counter.py b/packages/modules/devices/generic/http/counter.py index 99a771926b..2924c7c178 100644 --- a/packages/modules/devices/generic/http/counter.py +++ b/packages/modules/devices/generic/http/counter.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from requests import Session -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -14,21 +13,30 @@ from modules.devices.generic.http.config import HttpCounterSetup +class KwargsDict(TypedDict): + device_id: int + url: str + + class HttpCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, HttpCounterSetup], url: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HttpCounterSetup, component_config) + def __init__(self, component_config: HttpCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.url: str = self.kwargs['url'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__get_power = create_request_function(url, self.component_config.configuration.power_path) - self.__get_imported = create_request_function(url, self.component_config.configuration.imported_path) - self.__get_exported = create_request_function(url, self.component_config.configuration.exported_path) - self.__get_currents = create_request_function_array(url, [ - component_config.configuration.current_l1_path, - component_config.configuration.current_l2_path, - component_config.configuration.current_l3_path, + self.__get_power = create_request_function(self.url, self.component_config.configuration.power_path) + self.__get_imported = create_request_function(self.url, self.component_config.configuration.imported_path) + self.__get_exported = create_request_function(self.url, self.component_config.configuration.exported_path) + self.__get_currents = create_request_function_array(self.url, [ + self.component_config.configuration.current_l1_path, + self.component_config.configuration.current_l2_path, + self.component_config.configuration.current_l3_path, ]) def update(self, session: Session) -> None: diff --git a/packages/modules/devices/generic/http/device.py b/packages/modules/devices/generic/http/device.py index c18a2d34dc..34662d71de 100644 --- a/packages/modules/devices/generic/http/device.py +++ b/packages/modules/devices/generic/http/device.py @@ -19,18 +19,30 @@ def create_device(device_config: HTTP): + session = None + def create_bat_component(component_config: HttpBatSetup): - return HttpBat(device_config.id, component_config, device_config.configuration.url) + return HttpBat(component_config=component_config, + device_id=device_config.id, + url=device_config.configuration.url) def create_counter_component(component_config: HttpCounterSetup): - return HttpCounter(device_config.id, component_config, device_config.configuration.url) + return HttpCounter(component_config=component_config, + device_id=device_config.id, + url=device_config.configuration.url) def create_inverter_component(component_config: HttpInverterSetup): - return HttpInverter(device_config.id, component_config, device_config.configuration.url) + return HttpInverter(component_config=component_config, + device_id=device_config.id, + url=device_config.configuration.url) + + def initializer(): + nonlocal session + session = req.get_http_session() - session = req.get_http_session() return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/generic/http/inverter.py b/packages/modules/devices/generic/http/inverter.py index 750ee128ff..c542bb7eb3 100644 --- a/packages/modules/devices/generic/http/inverter.py +++ b/packages/modules/devices/generic/http/inverter.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from requests import Session -from dataclass_utils import dataclass_from_dict from helpermodules import compatibility from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -15,16 +14,25 @@ from modules.devices.generic.http.config import HttpInverterSetup +class KwargsDict(TypedDict): + device_id: int + url: str + + class HttpInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, HttpInverterSetup], url: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HttpInverterSetup, component_config) + def __init__(self, component_config: HttpInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.url: str = self.kwargs['url'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__get_power = create_request_function(url, self.component_config.configuration.power_path) - self.__get_exported = create_request_function(url, self.component_config.configuration.exported_path) + self.__get_power = create_request_function(self.url, self.component_config.configuration.power_path) + self.__get_exported = create_request_function(self.url, self.component_config.configuration.exported_path) def update(self, session: Session) -> None: power = self.__get_power(session) diff --git a/packages/modules/devices/generic/json/bat.py b/packages/modules/devices/generic/json/bat.py index 12f638c8ac..5dfc5fe74a 100644 --- a/packages/modules/devices/generic/json/bat.py +++ b/packages/modules/devices/generic/json/bat.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union - +from typing import TypedDict, Any import jq -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -13,30 +11,48 @@ from modules.devices.generic.json.config import JsonBatSetup +class KwargsDict(TypedDict): + device_id: int + + class JsonBat(AbstractBat): - def __init__(self, device_id: int, component_config: Union[Dict, JsonBatSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(JsonBatSetup, component_config) + def __init__(self, component_config: JsonBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def _compile_jq_filters(self) -> None: + config = self.component_config.configuration + self.jq_power = jq.compile(config.jq_power) + self.jq_soc = jq.compile(config.jq_soc) if config.jq_soc else None + self.jq_currents = [jq.compile(c) for c in config.jq_currents] if all(config.jq_currents) else [] + self.jq_imported = jq.compile(config.jq_imported) if config.jq_imported else None + self.jq_exported = jq.compile(config.jq_exported) if config.jq_exported else None + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) + self._compile_jq_filters() self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self, response) -> None: - config = self.component_config.configuration + currents = ( + [float(j.input(response).first()) for j in self.jq_currents] + if len(self.jq_currents) == 3 else None + ) - power = float(jq.compile(config.jq_power).input(response).first()) - if config.jq_soc != "": - soc = float(jq.compile(config.jq_soc).input(response).first()) - else: - soc = 0 + power = float(self.jq_power.input(response).first()) - if config.jq_imported is not None and config.jq_exported is not None: - imported = float(jq.compile(config.jq_imported).input(response).first()) - exported = float(jq.compile(config.jq_exported).input(response).first()) - else: + soc = float(self.jq_soc.input(response).first()) if self.jq_soc else 0 + + if self.jq_imported is None or self.jq_exported is None: imported, exported = self.sim_counter.sim_count(power) + else: + imported = float(self.jq_imported.input(response).first()) + exported = float(self.jq_exported.input(response).first()) bat_state = BatState( + currents=currents, power=power, soc=soc, imported=imported, diff --git a/packages/modules/devices/generic/json/config.py b/packages/modules/devices/generic/json/config.py index fc714bbc84..85228ccaf8 100644 --- a/packages/modules/devices/generic/json/config.py +++ b/packages/modules/devices/generic/json/config.py @@ -24,10 +24,14 @@ def __init__(self, class JsonBatConfiguration: def __init__(self, + jq_current_l1: Optional[str] = None, + jq_current_l2: Optional[str] = None, + jq_current_l3: Optional[str] = None, jq_imported: Optional[str] = None, jq_exported: Optional[str] = None, jq_soc: str = "", jq_power: str = ""): + self.jq_currents = (jq_current_l1, jq_current_l2, jq_current_l3) self.jq_imported = jq_imported self.jq_exported = jq_exported self.jq_soc = jq_soc @@ -44,18 +48,29 @@ def __init__(self, class JsonCounterConfiguration: - def __init__(self, jq_power: str = "", jq_exported: Optional[str] = None, jq_imported: Optional[str] = None, + def __init__(self, + jq_power: str = "", + jq_exported: Optional[str] = None, + jq_imported: Optional[str] = None, jq_power_l1: Optional[str] = None, jq_power_l2: Optional[str] = None, jq_power_l3: Optional[str] = None, + jq_power_factor_l1: Optional[str] = None, + jq_power_factor_l2: Optional[str] = None, + jq_power_factor_l3: Optional[str] = None, jq_current_l1: Optional[str] = None, jq_current_l2: Optional[str] = None, - jq_current_l3: Optional[str] = None): + jq_current_l3: Optional[str] = None, + jq_voltage_l1: Optional[str] = None, + jq_voltage_l2: Optional[str] = None, + jq_voltage_l3: Optional[str] = None): self.jq_power = jq_power self.jq_exported = jq_exported self.jq_imported = jq_imported self.jq_powers = (jq_power_l1, jq_power_l2, jq_power_l3) + self.jq_power_factors = (jq_power_factor_l1, jq_power_factor_l2, jq_power_factor_l3) self.jq_currents = (jq_current_l1, jq_current_l2, jq_current_l3) + self.jq_voltages = (jq_voltage_l1, jq_voltage_l2, jq_voltage_l3) class JsonCounterSetup(ComponentSetup[JsonCounterConfiguration]): @@ -68,9 +83,15 @@ def __init__(self, class JsonInverterConfiguration: - def __init__(self, jq_power: str = "", jq_exported: Optional[str] = None): + def __init__(self, + jq_power: str = "", + jq_exported: Optional[str] = None, + jq_current_l1: Optional[str] = None, + jq_current_l2: Optional[str] = None, + jq_current_l3: Optional[str] = None): self.jq_power = jq_power self.jq_exported = jq_exported + self.jq_currents = (jq_current_l1, jq_current_l2, jq_current_l3) class JsonInverterSetup(ComponentSetup[JsonInverterConfiguration]): diff --git a/packages/modules/devices/generic/json/counter.py b/packages/modules/devices/generic/json/counter.py index 5496b026ed..d8688097d2 100644 --- a/packages/modules/devices/generic/json/counter.py +++ b/packages/modules/devices/generic/json/counter.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union - +from typing import TypedDict, Any import jq -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -13,41 +11,70 @@ from modules.devices.generic.json.config import JsonCounterSetup +class KwargsDict(TypedDict): + device_id: int + + class JsonCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, JsonCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(JsonCounterSetup, component_config) + def __init__(self, component_config: JsonCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def _compile_jq_filters(self) -> None: + config = self.component_config.configuration + self.jq_power = jq.compile(config.jq_power) + self.jq_powers = [jq.compile(p) for p in config.jq_powers] if all(config.jq_powers) else None + self.jq_power_factors = [ + jq.compile(pf) for pf in config.jq_power_factors] if all(config.jq_power_factors) else None + self.jq_currents = [jq.compile(c) for c in config.jq_currents] if all(config.jq_currents) else None + self.jq_voltages = [jq.compile(v) for v in config.jq_voltages] if all(config.jq_voltages) else None + self.jq_imported = jq.compile(config.jq_imported) if config.jq_imported else None + self.jq_exported = jq.compile(config.jq_exported) if config.jq_exported else None + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) + self._compile_jq_filters() self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, response): - config = self.component_config.configuration + def update(self, response) -> None: + power = float(self.jq_power.input(response).first()) - power = float(jq.compile(config.jq_power).input(response).first()) + powers = ( + [float(j.input(response).first()) for j in self.jq_powers] + if self.jq_powers is not None else None + ) - if all(config.jq_powers): - powers = [float(jq.compile(p).input(response).first()) for p in config.jq_powers] - else: - powers = None + currents = ( + [float(j.input(response).first()) for j in self.jq_currents] + if self.jq_currents is not None else None + ) - if all(config.jq_currents): - currents = [float(jq.compile(c).input(response).first()) for c in config.jq_currents] - else: - currents = None + power_factors = ( + [float(j.input(response).first()) for j in self.jq_power_factors] + if self.jq_power_factors is not None else None + ) + + voltages = ( + [float(j.input(response).first()) for j in self.jq_voltages] + if self.jq_voltages is not None else None + ) - if config.jq_imported is None or config.jq_exported is None: + if self.jq_imported is None or self.jq_exported is None: imported, exported = self.sim_counter.sim_count(power) else: - imported = float(jq.compile(config.jq_imported).input(response).first()) - exported = float(jq.compile(config.jq_exported).input(response).first()) + imported = float(self.jq_imported.input(response).first()) + exported = float(self.jq_exported.input(response).first()) counter_state = CounterState( imported=imported, exported=exported, power=power, powers=powers, - currents=currents + currents=currents, + power_factors=power_factors, + voltages=voltages ) self.store.set(counter_state) diff --git a/packages/modules/devices/generic/json/device.py b/packages/modules/devices/generic/json/device.py index 43be25603e..a02c5c8012 100644 --- a/packages/modules/devices/generic/json/device.py +++ b/packages/modules/devices/generic/json/device.py @@ -5,6 +5,7 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.devices.generic.json import bat, counter, inverter from modules.devices.generic.json.bat import JsonBat @@ -25,18 +26,19 @@ def create_device(device_config: Json): def create_bat(component_config: JsonBatSetup) -> JsonBat: - return JsonBat(device_config.id, component_config) + return JsonBat(component_config=component_config, device_id=device_config.id) def create_counter(component_config: JsonCounterSetup) -> JsonCounter: - return JsonCounter(device_config.id, component_config) + return JsonCounter(component_config=component_config, device_id=device_config.id) def create_inverter(component_config: JsonInverterSetup) -> JsonInverter: - return JsonInverter(device_config.id, component_config) + return JsonInverter(component_config=component_config, device_id=device_config.id) def update_components(components: Iterable[JsonComponent]): response = req.get_http_session().get(device_config.configuration.url, timeout=5).json() for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config, diff --git a/packages/modules/devices/generic/json/inverter.py b/packages/modules/devices/generic/json/inverter.py index 409c966720..57e9d24440 100644 --- a/packages/modules/devices/generic/json/inverter.py +++ b/packages/modules/devices/generic/json/inverter.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union - +from typing import TypedDict, Any import jq -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -13,28 +11,47 @@ from modules.devices.generic.json.config import JsonInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class JsonInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, JsonInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(JsonInverterSetup, component_config) + def __init__(self, component_config: JsonInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def _compile_jq_filters(self) -> None: + config = self.component_config.configuration + self.jq_power = jq.compile(config.jq_power) + self.jq_exported = jq.compile(config.jq_exported) if config.jq_exported else None + self.jq_currents = [jq.compile(c) for c in config.jq_currents] if all(config.jq_currents) else None + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) + self._compile_jq_filters() self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self, response) -> None: - config = self.component_config.configuration - - power = float(jq.compile(config.jq_power).input(response).first()) + power = float(self.jq_power.input(response).first()) if power >= 0: power = power * -1 - if config.jq_exported is None: + + currents = ( + [float(j.input(response).first()) for j in self.jq_currents] + if self.jq_currents is not None else None + ) + + if self.jq_exported is None: _, exported = self.sim_counter.sim_count(power) else: - exported = float(jq.compile(config.jq_exported).input(response).first()) + exported = float(self.jq_exported.input(response).first()) inverter_state = InverterState( power=power, - exported=exported + exported=exported, + currents=currents ) self.store.set(inverter_state) diff --git a/packages/modules/devices/generic/mqtt/bat.py b/packages/modules/devices/generic/mqtt/bat.py index 5f518c26e7..bbb1026387 100644 --- a/packages/modules/devices/generic/mqtt/bat.py +++ b/packages/modules/devices/generic/mqtt/bat.py @@ -1,22 +1,60 @@ #!/usr/bin/env python3 -from typing import Dict, Optional, Union +from typing import Any, Dict, Optional, TypedDict + +from helpermodules.pub import Pub +from helpermodules.utils._get_default import get_default from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState from modules.common.fault_state import ComponentInfo, FaultState - -from dataclass_utils import dataclass_from_dict from modules.common.component_type import ComponentDescriptor +from modules.common.simcount._simcounter import SimCounter +from modules.common.store._battery import get_bat_value_store from modules.devices.generic.mqtt.config import MqttBatSetup +class KwargsDict(TypedDict): + device_id: int + + class MqttBat(AbstractBat): - def __init__(self, component_config: Union[Dict, MqttBatSetup]) -> None: - self.component_config = dataclass_from_dict(MqttBatSetup, component_config) - self.fault_state = FaultState(ComponentInfo.from_component_config(component_config)) + def __init__(self, component_config: MqttBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="bat") + self.store = get_bat_value_store(self.component_config.id) + + def update(self, received_topics: Dict) -> None: + def parse_received_topics(value: str): + return received_topics.get(f"{topic_prefix}{value}", get_default(BatState, value)) + # [] für erforderliche Topics, .get() für optionale Topics + topic_prefix = f"openWB/mqtt/bat/{self.component_config.id}/get/" + currents = parse_received_topics("currents") + power = received_topics[f"{topic_prefix}power"] + soc = received_topics[f"{topic_prefix}soc"] + if (received_topics.get(f"{topic_prefix}imported") and + received_topics.get(f"{topic_prefix}exported")): + imported = received_topics[f"{topic_prefix}imported"] + exported = received_topics[f"{topic_prefix}exported"] + else: + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + currents=currents, + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) def set_power_limit(self, power_limit: Optional[int]) -> None: - # wird bereits in Regelung gesetzt, eigene Implementierung notwendig, um zu erkennen, - # ob der Speicher die Funktion bietet - pass + Pub().pub(f"openWB/set/mqtt/bat/{self.component_config.id}/set/power_limit", power_limit) + + def power_limit_controllable(self) -> bool: + return self.component_config.configuration.power_limit_controllable component_descriptor = ComponentDescriptor(configuration_factory=MqttBatSetup) diff --git a/packages/modules/devices/generic/mqtt/config.py b/packages/modules/devices/generic/mqtt/config.py index cda7d72ca0..aa14d4ad4b 100644 --- a/packages/modules/devices/generic/mqtt/config.py +++ b/packages/modules/devices/generic/mqtt/config.py @@ -22,8 +22,8 @@ def __init__(self, class MqttBatConfiguration: - def __init__(self): - pass + def __init__(self, power_limit_controllable: bool = False): + self.power_limit_controllable = power_limit_controllable class MqttBatSetup(ComponentSetup[MqttBatConfiguration]): diff --git a/packages/modules/devices/generic/mqtt/counter.py b/packages/modules/devices/generic/mqtt/counter.py index 8fa9f4d5f2..5fc5ad5a88 100644 --- a/packages/modules/devices/generic/mqtt/counter.py +++ b/packages/modules/devices/generic/mqtt/counter.py @@ -1,17 +1,59 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, Dict, TypedDict + +from helpermodules.utils._get_default import get_default from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState from modules.common.fault_state import ComponentInfo, FaultState - -from dataclass_utils import dataclass_from_dict from modules.common.component_type import ComponentDescriptor +from modules.common.simcount._simcounter import SimCounter +from modules.common.store._counter import get_counter_value_store from modules.devices.generic.mqtt.config import MqttCounterSetup +class KwargsDict(TypedDict): + device_id: int + + class MqttCounter(AbstractCounter): - def __init__(self, component_config: Union[Dict, MqttCounterSetup]) -> None: - self.component_config = dataclass_from_dict(MqttCounterSetup, component_config) + def __init__(self, component_config: MqttCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + + def update(self, received_topics: Dict) -> None: + def parse_received_topics(value: str): + return received_topics.get(f"{topic_prefix}{value}", get_default(CounterState, value)) + # [] für erforderliche Topics, .get() für optionale Topics + topic_prefix = f"openWB/mqtt/counter/{self.component_config.id}/get/" + currents = parse_received_topics("currents") + power = received_topics[f"{topic_prefix}power"] + frequency = parse_received_topics("frequency") + power_factors = parse_received_topics("power_factors") + powers = parse_received_topics("powers") + voltages = parse_received_topics("voltages") + if (received_topics.get(f"{topic_prefix}imported") and + received_topics.get(f"{topic_prefix}exported")): + imported = received_topics[f"{topic_prefix}imported"] + exported = received_topics[f"{topic_prefix}exported"] + else: + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + frequency=frequency, + power_factors=power_factors, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) component_descriptor = ComponentDescriptor(configuration_factory=MqttCounterSetup) diff --git a/packages/modules/devices/generic/mqtt/device.py b/packages/modules/devices/generic/mqtt/device.py index f3a70ddf38..af862f342f 100644 --- a/packages/modules/devices/generic/mqtt/device.py +++ b/packages/modules/devices/generic/mqtt/device.py @@ -2,7 +2,11 @@ from typing import Iterable, Union import logging +from helpermodules.broker import BrokerClient +from helpermodules.utils.topic_parser import decode_payload from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.component_type import type_to_topic_mapping from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.generic.mqtt import bat, counter, inverter from modules.devices.generic.mqtt.config import Mqtt, MqttBatSetup, MqttCounterSetup, MqttInverterSetup @@ -12,16 +16,42 @@ def create_device(device_config: Mqtt): def create_bat_component(component_config: MqttBatSetup): - return bat.MqttBat(component_config) + return bat.MqttBat(component_config, device_id=device_config.id) def create_counter_component(component_config: MqttCounterSetup): - return counter.MqttCounter(component_config) + return counter.MqttCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: MqttInverterSetup): - return inverter.MqttInverter(component_config) + return inverter.MqttInverter(component_config, device_id=device_config.id) def update_components(components: Iterable[Union[bat.MqttBat, counter.MqttCounter, inverter.MqttInverter]]): - log.debug("MQTT-Module müssen nicht ausgelesen werden.") + def on_connect(client, userdata, flags, rc): + for component in components: + client.subscribe(f"openWB/mqtt/{type_to_topic_mapping(component.component_config.type)}/" + f"{component.component_config.id}/#") + + def on_message(client, userdata, message): + received_topics.update({message.topic: decode_payload(message.payload)}) + + received_topics = {} + BrokerClient(f"subscribeMqttDevice{device_config.id}", on_connect, on_message).start_finite_loop() + + if received_topics: + log.debug(f"Empfange MQTT Daten für Gerät {device_config.id}: {received_topics}") + for component in components: + with SingleComponentUpdateContext(component.fault_state): + try: + component.update(received_topics) + except KeyError: + raise KeyError( + "Fehlende MQTT-Daten: Stelle sicher, dass Du Werte an die erforderlichen Topics " + "(beschrieben in den Komponenten-Einstellungen) veröffentlichst (Publish)." + ) + else: + for component in components: + component.fault_state.warning( + f"Keine MQTT-Daten für Komponente {component.component_config.name} empfangen oder es werden " + "veraltete, abwärtskompatible Topics verwendet. Bitte die Doku in den Einstellungen beachten.") return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/generic/mqtt/inverter.py b/packages/modules/devices/generic/mqtt/inverter.py index 039a0a4a61..26593035d4 100644 --- a/packages/modules/devices/generic/mqtt/inverter.py +++ b/packages/modules/devices/generic/mqtt/inverter.py @@ -1,17 +1,50 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, Dict, TypedDict + +from helpermodules.utils._get_default import get_default from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState from modules.common.fault_state import ComponentInfo, FaultState - -from dataclass_utils import dataclass_from_dict from modules.common.component_type import ComponentDescriptor +from modules.common.simcount._simcounter import SimCounter +from modules.common.store._inverter import get_inverter_value_store from modules.devices.generic.mqtt.config import MqttInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class MqttInverter(AbstractInverter): - def __init__(self, component_config: Union[Dict, MqttInverterSetup]) -> None: - self.component_config = dataclass_from_dict(MqttInverterSetup, component_config) + def __init__(self, component_config: MqttInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + + def update(self, received_topics: Dict) -> None: + def parse_received_topics(value: str): + return received_topics.get(f"{topic_prefix}{value}", get_default(InverterState, value)) + # [] für erforderliche Topics, .get() für optionale Topics + topic_prefix = f"openWB/mqtt/pv/{self.component_config.id}/get/" + power = received_topics[f"{topic_prefix}power"] + if received_topics.get(f"openWB/mqtt/pv/{self.component_config.id}/get/exported"): + exported = received_topics[f"{topic_prefix}exported"] + else: + exported = self.sim_counter.sim_count(power)[1] + currents = parse_received_topics("currents") + dc_power = parse_received_topics("dc_power") + + inverter_state = InverterState( + currents=currents, + power=power, + exported=exported, + dc_power=dc_power + ) + self.store.set(inverter_state) component_descriptor = ComponentDescriptor(configuration_factory=MqttInverterSetup) diff --git a/packages/modules/devices/generic/virtual/counter.py b/packages/modules/devices/generic/virtual/counter.py index e324f155ba..295483a25c 100644 --- a/packages/modules/devices/generic/virtual/counter.py +++ b/packages/modules/devices/generic/virtual/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -11,21 +10,26 @@ from modules.devices.generic.virtual.config import VirtualCounterSetup +class KwargsDict(TypedDict): + device_id: int + + class VirtualCounter(AbstractCounter): + def __init__(self, component_config: VirtualCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs - def __init__(self, device_id: int, component_config: Union[Dict, VirtualCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VirtualCounterSetup, component_config) + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - self.store = get_counter_value_store(self.component_config.id, add_child_values=True) + self.store = get_counter_value_store( + self.component_config.id, add_child_values=True, simcounter=self.sim_counter) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self): - imported, exported = self.sim_counter.sim_count(self.component_config.configuration.external_consumption) - counter_state = CounterState( - imported=imported, - exported=exported, + imported=None, + exported=None, power=self.component_config.configuration.external_consumption, currents=[self.component_config.configuration.external_consumption/3/230]*3 ) diff --git a/packages/modules/devices/generic/virtual/counter_test.py b/packages/modules/devices/generic/virtual/counter_test.py index 9f2c71879c..a19c60436a 100644 --- a/packages/modules/devices/generic/virtual/counter_test.py +++ b/packages/modules/devices/generic/virtual/counter_test.py @@ -1,4 +1,4 @@ -import threading +from threading import Event from typing import Callable from unittest.mock import Mock @@ -7,7 +7,16 @@ from control import data from control.chargepoint.chargepoint import Chargepoint from control.counter_all import CounterAll -from modules.common.component_state import CounterState +from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule +from modules.chargepoints.mqtt.config import Mqtt +from modules.common.component_state import BatState, ChargepointState, CounterState, InverterState +from modules.common.store import _counter +from modules.common.store._api import LoggingValueStore +from modules.common.store._battery import BatteryValueStoreBroker, PurgeBatteryState +from modules.common.store._inverter import InverterValueStoreBroker, PurgeInverterState +from modules.devices.generic.mqtt.bat import MqttBat +from modules.devices.generic.mqtt.counter import MqttCounter +from modules.devices.generic.mqtt.inverter import MqttInverter from modules.devices.generic.virtual import counter from modules.devices.generic.virtual.config import VirtualCounterConfiguration, VirtualCounterSetup from packages.conftest import hierarchy_standard, hierarchy_hybrid, hierarchy_nested @@ -15,15 +24,29 @@ @pytest.fixture(autouse=True) def init_data() -> None: - data.data_init(threading.Event()) + data.data_init(Event()) data.data.counter_all_data = CounterAll() data.data.counter_all_data.data.get.hierarchy = [{"id": 0, "type": "counter", "children": [ {"id": 6, "type": "counter", "children": [ {"id": 3, "type": "cp", "children": []}, {"id": 4, "type": "cp", "children": []}]}]}] data.data.cp_data["cp3"] = Chargepoint(3, None) - data.data.cp_data["cp3"].data.get.currents = [16, 16, 0] + data.data.cp_data["cp3"].chargepoint_module = ChargepointModule(Mqtt()) + data.data.cp_data["cp3"].chargepoint_module.store.delegate.state = ChargepointState(currents=[16, 16, 0], + plug_state=False, + charge_state=False, + imported=None, + exported=None, + phases_in_use=0, + power=0) data.data.cp_data["cp4"] = Chargepoint(4, None) - data.data.cp_data["cp4"].data.get.currents = [16, 16, 16] + data.data.cp_data["cp4"].chargepoint_module = ChargepointModule(Mqtt()) + data.data.cp_data["cp4"].chargepoint_module.store.delegate.state = ChargepointState(currents=[16, 16, 16], + plug_state=False, + charge_state=False, + imported=None, + exported=None, + phases_in_use=0, + power=0) def init_twisted_cp() -> None: @@ -50,8 +73,9 @@ def __init__(self, name: str, init_func: Callable[[], None], expected_state: Cou @pytest.mark.parametrize("params", cases, ids=[c.name for c in cases]) def test_virtual_counter(mock_pub: Mock, params): # setup - c = counter.VirtualCounter(0, VirtualCounterSetup( - id=6, configuration=VirtualCounterConfiguration(external_consumption=0))) + c = counter.VirtualCounter(VirtualCounterSetup( + id=6, configuration=VirtualCounterConfiguration(external_consumption=0)), device_id=0) + c.initialize() params.init_func() # execution @@ -70,15 +94,55 @@ def test_virtual_counter(mock_pub: Mock, params): pytest.fail("Topic openWB/set/counter/6/get/currents is missing") -@pytest.mark.parametrize("counter_all", - [pytest.param(hierarchy_standard, id="standard"), - pytest.param(hierarchy_hybrid, id="hybrid"), - pytest.param(hierarchy_nested, id="nested")]) -def test_virtual_counter_hierarchies(counter_all: Callable[[], CounterAll], data_, mock_pub: Mock): +mock_comp_obj_inv_bat = [Mock(spec=MqttInverter, + store=Mock(spec=PurgeInverterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=InverterValueStoreBroker, + state=InverterState(power=-10000, + exported=27000))))), + Mock(spec=MqttBat, + store=Mock(spec=PurgeBatteryState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=BatteryValueStoreBroker, + state=BatState(power=-5000, + imported=12000, + exported=10000))))) + ] +mock_comp_obj_counter_inv_bat = [Mock(spec=MqttCounter, + store=Mock(spec=_counter.PurgeCounterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=_counter.CounterValueStoreBroker, + state=CounterState(currents=[25, 10, 25], + power=13800, + imported=14000, + exported=18000))))), + Mock(spec=MqttInverter, + store=Mock(spec=PurgeInverterState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=InverterValueStoreBroker, + state=InverterState(power=-10000, + exported=27000))))), + Mock(spec=MqttBat, + store=Mock(spec=PurgeBatteryState, + delegate=Mock(spec=LoggingValueStore, + delegate=Mock(spec=BatteryValueStoreBroker, + state=BatState(power=-5000, + imported=12000, + exported=10000)))))] + + +@pytest.mark.parametrize("mock, counter_all", + [pytest.param(mock_comp_obj_inv_bat, hierarchy_standard, id="standard"), + pytest.param(mock_comp_obj_inv_bat, hierarchy_hybrid, id="hybrid"), + pytest.param(mock_comp_obj_counter_inv_bat, hierarchy_nested, id="nested")]) +def test_virtual_counter_hierarchies(mock, counter_all: Callable[[], CounterAll], data_, mock_pub: Mock, monkeypatch): # setup - virtual_counter = counter.VirtualCounter(0, VirtualCounterSetup( - id=0, configuration=VirtualCounterConfiguration(external_consumption=0))) + virtual_counter = counter.VirtualCounter(VirtualCounterSetup( + id=0, configuration=VirtualCounterConfiguration(external_consumption=0)), device_id=0) + virtual_counter.initialize() data.data.counter_all_data = counter_all() + mock_comp_obj = Mock(side_effect=mock) + monkeypatch.setattr(_counter, "get_component_obj_by_id", mock_comp_obj) # execution virtual_counter.update() diff --git a/packages/modules/devices/generic/virtual/device.py b/packages/modules/devices/generic/virtual/device.py index 47e1c2e67b..ed111b480b 100644 --- a/packages/modules/devices/generic/virtual/device.py +++ b/packages/modules/devices/generic/virtual/device.py @@ -10,7 +10,7 @@ def create_device(device_config: Virtual): def create_counter_component(component_config: VirtualCounterSetup): - return VirtualCounter(device_config.id, component_config) + return VirtualCounter(component_config, device_id=device_config.id) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/good_we/good_we/bat.py b/packages/modules/devices/good_we/good_we/bat.py index f203bcfddd..670f675415 100644 --- a/packages/modules/devices/good_we/good_we/bat.py +++ b/packages/modules/devices/good_we/good_we/bat.py @@ -1,44 +1,60 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.modbus import ModbusDataType from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store from modules.devices.good_we.good_we.config import GoodWeBatSetup from modules.devices.good_we.good_we.version import GoodWeVersion +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + version: GoodWeVersion + firmware: int + client: modbus.ModbusTcpClient_ + + class GoodWeBat(AbstractBat): - def __init__(self, - modbus_id: int, - version: GoodWeVersion, - firmware: int, - component_config: Union[Dict, GoodWeBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__modbus_id = modbus_id - self.version = version - self.firmware = firmware - self.component_config = dataclass_from_dict(GoodWeBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: GoodWeBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GoodWeVersion = self.kwargs['version'] + self.firmware: int = self.kwargs['firmware'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self) -> None: + battery_index = getattr(self.component_config.configuration, "battery_index", 1) with self.__tcp_client: - if self.version == GoodWeVersion.V_1_7: - power = self.__tcp_client.read_holding_registers(35183, ModbusDataType.INT_16, unit=self.__modbus_id)*-1 + if battery_index == 1: + if self.version == GoodWeVersion.V_1_7: + power = self.__tcp_client.read_holding_registers( + 35183, ModbusDataType.INT_16, unit=self.__modbus_id)*-1 + else: + power = self.__tcp_client.read_holding_registers( + 35182, ModbusDataType.INT_32, unit=self.__modbus_id)*-1 + soc = self.__tcp_client.read_holding_registers(37007, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported = self.__tcp_client.read_holding_registers( + 35206, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + exported = self.__tcp_client.read_holding_registers( + 35209, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 else: - power = self.__tcp_client.read_holding_registers(35182, ModbusDataType.INT_32, unit=self.__modbus_id)*-1 - soc = self.__tcp_client.read_holding_registers(37007, ModbusDataType.UINT_16, unit=self.__modbus_id) - imported = self.__tcp_client.read_holding_registers( - 35206, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 - exported = self.__tcp_client.read_holding_registers( - 35209, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + power = self.__tcp_client.read_holding_registers(35264, ModbusDataType.INT_32, unit=self.__modbus_id)*-1 + soc = self.__tcp_client.read_holding_registers(39005, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( power=power, diff --git a/packages/modules/devices/good_we/good_we/config.py b/packages/modules/devices/good_we/good_we/config.py index d8ab88b200..c244be0697 100644 --- a/packages/modules/devices/good_we/good_we/config.py +++ b/packages/modules/devices/good_we/good_we/config.py @@ -32,8 +32,8 @@ def __init__(self, class GoodWeBatConfiguration: - def __init__(self): - pass + def __init__(self, battery_index: int = 1): + self.battery_index = battery_index class GoodWeBatSetup(ComponentSetup[GoodWeBatConfiguration]): diff --git a/packages/modules/devices/good_we/good_we/counter.py b/packages/modules/devices/good_we/good_we/counter.py index 24f0516625..56c1f82e0d 100644 --- a/packages/modules/devices/good_we/good_we/counter.py +++ b/packages/modules/devices/good_we/good_we/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -14,25 +13,29 @@ from modules.devices.good_we.good_we.version import GoodWeVersion +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + version: GoodWeVersion + firmware: int + client: modbus.ModbusTcpClient_ + + class GoodWeCounter(AbstractCounter): - def __init__(self, - device_id: int, - modbus_id: int, - version: GoodWeVersion, - firmware: int, - component_config: Union[Dict, GoodWeCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.__modbus_id = modbus_id - self.version = version - self.firmware = firmware - self.component_config = dataclass_from_dict(GoodWeCounterSetup, component_config) - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - self.__tcp_client = tcp_client + def __init__(self, component_config: GoodWeCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GoodWeVersion = self.kwargs['version'] + self.firmware: int = self.kwargs['firmware'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): + def update(self) -> None: with self.__tcp_client: if self.firmware < 9: power = self.__tcp_client.read_holding_registers( diff --git a/packages/modules/devices/good_we/good_we/device.py b/packages/modules/devices/good_we/good_we/device.py index 03c6543406..3c7e9b4e63 100644 --- a/packages/modules/devices/good_we/good_we/device.py +++ b/packages/modules/devices/good_we/good_we/device.py @@ -18,36 +18,47 @@ def create_device(device_config: GoodWe): + client = None + def create_bat_component(component_config: GoodWeBatSetup): - return bat.GoodWeBat(device_config.configuration.modbus_id, - GoodWeVersion(device_config.configuration.version), - device_config.configuration.firmware, - component_config, client) + nonlocal client + return bat.GoodWeBat(component_config=component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + version=GoodWeVersion(device_config.configuration.version), + firmware=device_config.configuration.firmware, + client=client) def create_counter_component(component_config: GoodWeCounterSetup): - return counter.GoodWeCounter(device_config.id, device_config.configuration.modbus_id, - GoodWeVersion(device_config.configuration.version), - device_config.configuration.firmware, - component_config, client) + nonlocal client + return counter.GoodWeCounter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + version=GoodWeVersion(device_config.configuration.version), + firmware=device_config.configuration.firmware, + client=client, + device_id=device_config.id) def create_inverter_component(component_config: GoodWeInverterSetup): - return inverter.GoodWeInverter(device_config.configuration.modbus_id, - GoodWeVersion(device_config.configuration.version), - device_config.configuration.firmware, - component_config, client) + nonlocal client + return inverter.GoodWeInverter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + version=GoodWeVersion(device_config.configuration.version), + firmware=device_config.configuration.firmware, + client=client) def update_components(components: Iterable[good_we_component_classes]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/good_we/good_we/inverter.py b/packages/modules/devices/good_we/good_we/inverter.py index 41acbdaaf6..10f66a44c1 100644 --- a/packages/modules/devices/good_we/good_we/inverter.py +++ b/packages/modules/devices/good_we/good_we/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -13,18 +12,23 @@ from modules.devices.good_we.good_we.version import GoodWeVersion +class KwargsDict(TypedDict): + modbus_id: int + version: GoodWeVersion + firmware: int + client: modbus.ModbusTcpClient_ + + class GoodWeInverter(AbstractInverter): - def __init__(self, - modbus_id: int, - version: GoodWeVersion, - firmware: int, - component_config: Union[Dict, GoodWeInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__modbus_id = modbus_id - self.version = version - self.firmware = firmware - self.component_config = dataclass_from_dict(GoodWeInverterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: GoodWeInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GoodWeVersion = self.kwargs['version'] + self.firmware: int = self.kwargs['firmware'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/growatt/__init__.py b/packages/modules/devices/growatt/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/growatt/growatt/__init__.py b/packages/modules/devices/growatt/growatt/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/growatt/growatt/bat.py b/packages/modules/devices/growatt/growatt/bat.py index 71d6a75f15..66054b257f 100644 --- a/packages/modules/devices/growatt/growatt/bat.py +++ b/packages/modules/devices/growatt/growatt/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -12,41 +11,48 @@ from modules.devices.growatt.growatt.version import GrowattVersion +class KwargsDict(TypedDict): + modbus_id: int + version: GrowattVersion + client: ModbusTcpClient_ + + class GrowattBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, GrowattBatSetup], - modbus_id: int, - version: GrowattVersion) -> None: - self.__modbus_id = modbus_id - self.version = version - self.component_config = dataclass_from_dict(GrowattBatSetup, component_config) + def __init__(self, component_config: GrowattBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GrowattVersion = self.kwargs['version'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: if self.version == GrowattVersion.max_series: - power_in = client.read_input_registers( + power_in = self.client.read_input_registers( 1011, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 - power_out = client.read_input_registers( + power_out = self.client.read_input_registers( 1009, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 power = power_in + power_out - soc = client.read_input_registers(1014, ModbusDataType.UINT_16, unit=self.__modbus_id) - imported = client.read_input_registers( + soc = self.client.read_input_registers(1014, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported = self.client.read_input_registers( 1058, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 - exported = client.read_input_registers( + exported = self.client.read_input_registers( 1054, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 else: - power_in = client.read_input_registers( + power_in = self.client.read_input_registers( 3180, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 - power_out = client.read_input_registers( + power_out = self.client.read_input_registers( 3178, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 power = power_in + power_out - soc = client.read_input_registers(3171, ModbusDataType.UINT_16, unit=self.__modbus_id) - imported = client.read_input_registers( + soc = self.client.read_input_registers(3171, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported = self.client.read_input_registers( 3131, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 - exported = client.read_input_registers( + exported = self.client.read_input_registers( 3127, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 bat_state = BatState( diff --git a/packages/modules/devices/growatt/growatt/counter.py b/packages/modules/devices/growatt/growatt/counter.py index af75551031..cc69bd150a 100644 --- a/packages/modules/devices/growatt/growatt/counter.py +++ b/packages/modules/devices/growatt/growatt/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -12,53 +11,60 @@ from modules.devices.growatt.growatt.version import GrowattVersion +class KwargsDict(TypedDict): + modbus_id: int + version: GrowattVersion + client: ModbusTcpClient_ + + class GrowattCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, GrowattCounterSetup], - modbus_id: int, - version: GrowattVersion) -> None: - self.component_config = dataclass_from_dict(GrowattCounterSetup, component_config) - self.__modbus_id = modbus_id - self.version = version + def __init__(self, component_config: GrowattCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GrowattVersion = self.kwargs['version'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): + def update(self) -> None: if self.version == GrowattVersion.max_series: - power_in = client.read_input_registers(1021, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 - power_out = client.read_input_registers(1029, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 + power_in = self.client.read_input_registers(1021, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 + power_out = self.client.read_input_registers(1029, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 power = power_in + power_out powers = [ - client.read_input_registers( + self.client.read_input_registers( 40, ModbusDataType.INT_32, unit=self.__modbus_id) / 10, - client.read_input_registers( + self.client.read_input_registers( 44, ModbusDataType.INT_32, unit=self.__modbus_id) / 10, - client.read_input_registers( + self.client.read_input_registers( 48, ModbusDataType.INT_32, unit=self.__modbus_id) / 10] # Einheit 0.1 kWh - exported = client.read_input_registers(1050, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 - imported = client.read_input_registers(1046, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + exported = self.client.read_input_registers(1050, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + imported = self.client.read_input_registers(1046, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 # TL-X Dokumentation hat die gleichen Register wie die MAX Serie, # zusätzlich sind aber auch unten abweichende enthalten else: - power_in = client.read_input_registers(3041, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 - power_out = client.read_input_registers(3043, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 + power_in = self.client.read_input_registers(3041, ModbusDataType.UINT_32, unit=self.__modbus_id) * 0.1 + power_out = self.client.read_input_registers(3043, ModbusDataType.UINT_32, unit=self.__modbus_id) * -0.1 power = power_in + power_out powers = [ - client.read_input_registers( + self.client.read_input_registers( 3028, ModbusDataType.INT_32, unit=self.__modbus_id) / 10, - client.read_input_registers( + self.client.read_input_registers( 3032, ModbusDataType.INT_32, unit=self.__modbus_id) / 10, - client.read_input_registers( + self.client.read_input_registers( 3036, ModbusDataType.INT_32, unit=self.__modbus_id) / 10] # Einheit 0.1 kWh - exported = client.read_input_registers(3073, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 - imported = client.read_input_registers(3069, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + exported = self.client.read_input_registers(3073, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + imported = self.client.read_input_registers(3069, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 counter_state = CounterState( imported=imported, diff --git a/packages/modules/devices/growatt/growatt/device.py b/packages/modules/devices/growatt/growatt/device.py index 84faf5d728..139c168b4b 100644 --- a/packages/modules/devices/growatt/growatt/device.py +++ b/packages/modules/devices/growatt/growatt/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater @@ -16,34 +18,47 @@ def create_device(device_config: Growatt): + client = None + def create_bat_component(component_config: GrowattBatSetup): - return GrowattBat(component_config, - device_config.configuration.modbus_id, - GrowattVersion(device_config.configuration.version)) + nonlocal client + return GrowattBat(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + version=GrowattVersion(device_config.configuration.version), + client=client) def create_counter_component(component_config: GrowattCounterSetup): - return GrowattCounter(component_config, - device_config.configuration.modbus_id, - GrowattVersion(device_config.configuration.version)) + nonlocal client + return GrowattCounter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + version=GrowattVersion(device_config.configuration.version), + client=client) def create_inverter_component(component_config: GrowattInverterSetup): - return GrowattInverter(component_config, - device_config.configuration.modbus_id, - GrowattVersion(device_config.configuration.version)) + nonlocal client + return GrowattInverter(component_config=component_config, + modbus_id=device_config.configuration.modbus_id, + version=GrowattVersion(device_config.configuration.version), + client=client) def update_components(components: Iterable[Union[GrowattBat, GrowattCounter, GrowattInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/growatt/growatt/inverter.py b/packages/modules/devices/growatt/growatt/inverter.py index fe7e9fa0e7..0044b8f5e9 100644 --- a/packages/modules/devices/growatt/growatt/inverter.py +++ b/packages/modules/devices/growatt/growatt/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,27 +11,34 @@ from modules.devices.growatt.growatt.version import GrowattVersion +class KwargsDict(TypedDict): + modbus_id: int + version: GrowattVersion + client: ModbusTcpClient_ + + class GrowattInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, GrowattInverterSetup], - modbus_id: int, - version: GrowattVersion) -> None: - self.component_config = dataclass_from_dict(GrowattInverterSetup, component_config) - self.__modbus_id = modbus_id - self.version = version + def __init__(self, component_config: GrowattInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.version: GrowattVersion = self.kwargs['version'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: if self.version == GrowattVersion.max_series: - power = client.read_input_registers( + power = self.client.read_input_registers( 1, ModbusDataType.UINT_32, unit=self.__modbus_id) / -10 - exported = client.read_input_registers( + exported = self.client.read_input_registers( 91, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 else: - power = client.read_input_registers( + power = self.client.read_input_registers( 3001, ModbusDataType.UINT_32, unit=self.__modbus_id) / -10 - exported = client.read_input_registers( + exported = self.client.read_input_registers( 3053, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 inverter_state = InverterState( diff --git a/packages/modules/devices/huawei/huawei/bat.py b/packages/modules/devices/huawei/huawei/bat.py index b9539968a2..7e74530e40 100644 --- a/packages/modules/devices/huawei/huawei/bat.py +++ b/packages/modules/devices/huawei/huawei/bat.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import time -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -14,27 +13,34 @@ from modules.devices.huawei.huawei.type import HuaweiType +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + type: HuaweiType + client: ModbusTcpClient_ + + class HuaweiBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, HuaweiBatSetup], - modbus_id: int, - type: HuaweiType) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HuaweiBatSetup, component_config) - self.modbus_id = modbus_id - self.type = type + def __init__(self, component_config: HuaweiBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.type: HuaweiType = self.kwargs['type'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: if self.type == HuaweiType.SDongle: time.sleep(1) - power = client.read_holding_registers(37765, ModbusDataType.INT_32, unit=self.modbus_id) + power = self.client.read_holding_registers(37765, ModbusDataType.INT_32, unit=self.modbus_id) if self.type == HuaweiType.SDongle: time.sleep(1) - soc = client.read_holding_registers(37760, ModbusDataType.INT_16, unit=self.modbus_id) / 10 + soc = self.client.read_holding_registers(37760, ModbusDataType.INT_16, unit=self.modbus_id) / 10 imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( diff --git a/packages/modules/devices/huawei/huawei/counter.py b/packages/modules/devices/huawei/huawei/counter.py index ecaa1e7236..4c2e440073 100644 --- a/packages/modules/devices/huawei/huawei/counter.py +++ b/packages/modules/devices/huawei/huawei/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import time -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -14,28 +13,35 @@ from modules.devices.huawei.huawei.type import HuaweiType +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + type: HuaweiType + client: ModbusTcpClient_ + + class HuaweiCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, HuaweiCounterSetup], - modbus_id: int, - type: HuaweiType) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HuaweiCounterSetup, component_config) - self.modbus_id = modbus_id - self.type = type + def __init__(self, component_config: HuaweiCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.type: HuaweiType = self.kwargs['type'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): + def update(self) -> None: if self.type == HuaweiType.SDongle: time.sleep(1) - currents = client.read_holding_registers(37107, [ModbusDataType.INT_32]*3, unit=self.modbus_id) + currents = self.client.read_holding_registers(37107, [ModbusDataType.INT_32]*3, unit=self.modbus_id) currents = [val / -100 for val in currents] if self.type == HuaweiType.SDongle: time.sleep(1) - power = client.read_holding_registers(37113, ModbusDataType.INT_32, unit=self.modbus_id) * -1 + power = self.client.read_holding_registers(37113, ModbusDataType.INT_32, unit=self.modbus_id) * -1 imported, exported = self.sim_counter.sim_count(power) diff --git a/packages/modules/devices/huawei/huawei/device.py b/packages/modules/devices/huawei/huawei/device.py index 59d1fbe8fe..df77e1702a 100644 --- a/packages/modules/devices/huawei/huawei/device.py +++ b/packages/modules/devices/huawei/huawei/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater @@ -16,41 +18,57 @@ def create_device(device_config: Huawei): + client = None + def create_bat_component(component_config: HuaweiBatSetup): - return HuaweiBat(device_config.id, - component_config, - device_config.configuration.modbus_id, - HuaweiType(device_config.configuration.type)) + nonlocal client + return HuaweiBat(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + type=HuaweiType(device_config.configuration.type), + client=client) def create_counter_component(component_config: HuaweiCounterSetup): - return HuaweiCounter(device_config.id, - component_config, - device_config.configuration.modbus_id, - HuaweiType(device_config.configuration.type)) + nonlocal client + return HuaweiCounter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + type=HuaweiType(device_config.configuration.type), + client=client) def create_inverter_component(component_config: HuaweiInverterSetup): - return HuaweiInverter(device_config.id, - component_config, - device_config.configuration.modbus_id, - HuaweiType(device_config.configuration.type)) + nonlocal client + return HuaweiInverter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + type=HuaweiType(device_config.configuration.type), + client=client) def update_components(components: Iterable[Union[HuaweiBat, HuaweiCounter, HuaweiInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(client) + component.update() - try: + def initializer(): + nonlocal client if HuaweiType(device_config.configuration.type) == HuaweiType.SDongle: client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port, sleep_after_connect=7) + if HuaweiType(device_config.configuration.type) == HuaweiType.HuaweiKit: + client = ModbusTcpClient_("192.168.193.126", 8899) else: client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/huawei/huawei/inverter.py b/packages/modules/devices/huawei/huawei/inverter.py index 125da608d0..56ada1ce8b 100644 --- a/packages/modules/devices/huawei/huawei/inverter.py +++ b/packages/modules/devices/huawei/huawei/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import time -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -14,24 +13,31 @@ from modules.devices.huawei.huawei.type import HuaweiType +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + type: HuaweiType + client: ModbusTcpClient_ + + class HuaweiInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, HuaweiInverterSetup], - modbus_id: int, - type: HuaweiType) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(HuaweiInverterSetup, component_config) - self.modbus_id = modbus_id - self.type = type + def __init__(self, component_config: HuaweiInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.type: HuaweiType = self.kwargs['type'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: if self.type == HuaweiType.SDongle: time.sleep(1) - power = client.read_holding_registers(32064, ModbusDataType.INT_32, unit=self.modbus_id) * -1 + power = self.client.read_holding_registers(32064, ModbusDataType.INT_32, unit=self.modbus_id) * -1 _, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( diff --git a/packages/modules/devices/huawei/huawei/type.py b/packages/modules/devices/huawei/huawei/type.py index f6579ca0bc..ab57d087c2 100644 --- a/packages/modules/devices/huawei/huawei/type.py +++ b/packages/modules/devices/huawei/huawei/type.py @@ -4,3 +4,4 @@ class HuaweiType(Enum): SDongle = "s_dongle" ModbusRTU = "modbus_rtu" + Huawei_Kit = "huawei_kit" diff --git a/packages/modules/devices/huawei/huawei_emma/bat.py b/packages/modules/devices/huawei/huawei_emma/bat.py new file mode 100644 index 0000000000..cdf43d8a13 --- /dev/null +++ b/packages/modules/devices/huawei/huawei_emma/bat.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.huawei.huawei_emma.config import Huawei_EmmaBatSetup + + +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + +class Huawei_EmmaBat(AbstractBat): + def __init__(self, component_config: Huawei_EmmaBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + power = self.client.read_holding_registers(30360, ModbusDataType.INT_32, unit=self.modbus_id) + soc = self.client.read_holding_registers(30368, ModbusDataType.UINT_16, unit=self.modbus_id) * 0.01 + + imported, exported = self.sim_counter.sim_count(power) + bat_state = BatState( + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=Huawei_EmmaBatSetup) diff --git a/packages/modules/devices/huawei/huawei_emma/config.py b/packages/modules/devices/huawei/huawei_emma/config.py new file mode 100644 index 0000000000..45b3c0a9da --- /dev/null +++ b/packages/modules/devices/huawei/huawei_emma/config.py @@ -0,0 +1,68 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class Huawei_EmmaConfiguration: + def __init__(self, modbus_id: int = 0, + ip_address: Optional[str] = None, + port: int = 502): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + + +class Huawei_Emma: + def __init__(self, + name: str = "Huawei EMMA", + type: str = "huawei_emma", + id: int = 0, + configuration: Huawei_EmmaConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or Huawei_EmmaConfiguration() + + +class Huawei_EmmaBatConfiguration: + def __init__(self): + pass + + +class Huawei_EmmaBatSetup(ComponentSetup[Huawei_EmmaBatConfiguration]): + def __init__(self, + name: str = "Huawei EMMA Speicher", + type: str = "bat", + id: int = 0, + configuration: Huawei_EmmaBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or Huawei_EmmaBatConfiguration()) + + +class Huawei_EmmaCounterConfiguration: + def __init__(self): + pass + + +class Huawei_EmmaCounterSetup(ComponentSetup[Huawei_EmmaCounterConfiguration]): + def __init__(self, + name: str = "Huawei EMMA Zähler", + type: str = "counter", + id: int = 0, + configuration: Huawei_EmmaCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or Huawei_EmmaCounterConfiguration()) + + +class Huawei_EmmaInverterConfiguration: + def __init__(self): + pass + + +class Huawei_EmmaInverterSetup(ComponentSetup[Huawei_EmmaInverterConfiguration]): + def __init__(self, + name: str = "Huawei EMMA Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: Huawei_EmmaInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or Huawei_EmmaInverterConfiguration()) diff --git a/packages/modules/devices/huawei/huawei_emma/counter.py b/packages/modules/devices/huawei/huawei_emma/counter.py new file mode 100644 index 0000000000..912f70a97e --- /dev/null +++ b/packages/modules/devices/huawei/huawei_emma/counter.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.huawei.huawei_emma.config import Huawei_EmmaCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + +class Huawei_EmmaCounter(AbstractCounter): + def __init__(self, component_config: Huawei_EmmaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + currents = self.client.read_holding_registers(31651, [ModbusDataType.INT_32]*3, unit=self.modbus_id) + currents = [val * 0.1 for val in currents] + power = self.client.read_holding_registers(31657, ModbusDataType.INT_32, unit=self.modbus_id) + + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=Huawei_EmmaCounterSetup) diff --git a/packages/modules/devices/huawei/huawei_emma/device.py b/packages/modules/devices/huawei/huawei_emma/device.py new file mode 100644 index 0000000000..3ccd4e04b1 --- /dev/null +++ b/packages/modules/devices/huawei/huawei_emma/device.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +import logging +from pathlib import Path +from typing import Iterable, Union + +from helpermodules.utils.run_command import run_command +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.huawei.huawei_emma.bat import Huawei_EmmaBat +from modules.devices.huawei.huawei_emma.config import Huawei_Emma, Huawei_EmmaBatSetup +from modules.devices.huawei.huawei_emma.config import Huawei_EmmaCounterSetup, Huawei_EmmaInverterSetup +from modules.devices.huawei.huawei_emma.counter import Huawei_EmmaCounter +from modules.devices.huawei.huawei_emma.inverter import Huawei_EmmaInverter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Huawei_Emma): + client = None + + def create_bat_component(component_config: Huawei_EmmaBatSetup): + nonlocal client + return Huawei_EmmaBat(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) + + def create_counter_component(component_config: Huawei_EmmaCounterSetup): + nonlocal client + return Huawei_EmmaCounter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) + + def create_inverter_component(component_config: Huawei_EmmaInverterSetup): + nonlocal client + return Huawei_EmmaInverter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) + + def update_components(components: Iterable[Union[Huawei_EmmaBat, Huawei_EmmaCounter, Huawei_EmmaInverter]]): + nonlocal client + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, + device_config.configuration.port) + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + error_handler=error_handler, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Huawei_Emma) diff --git a/packages/modules/devices/huawei/huawei_emma/inverter.py b/packages/modules/devices/huawei/huawei_emma/inverter.py new file mode 100644 index 0000000000..7e66d36ed0 --- /dev/null +++ b/packages/modules/devices/huawei/huawei_emma/inverter.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.huawei.huawei_emma.config import Huawei_EmmaInverterSetup + + +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + +class Huawei_EmmaInverter(AbstractInverter): + def __init__(self, component_config: Huawei_EmmaInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + power = self.client.read_holding_registers(30354, ModbusDataType.INT_32, unit=self.modbus_id) * -1 + exported = self.client.read_holding_registers(30344, ModbusDataType.UINT_32, unit=self.modbus_id) * 0.01 + + inverter_state = InverterState( + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=Huawei_EmmaInverterSetup) diff --git a/packages/modules/devices/huawei/huawei_smartlogger/bat.py b/packages/modules/devices/huawei/huawei_smartlogger/bat.py index 342ee8b5dd..b2349ecad0 100644 --- a/packages/modules/devices/huawei/huawei_smartlogger/bat.py +++ b/packages/modules/devices/huawei/huawei_smartlogger/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,14 +12,19 @@ from modules.devices.huawei.huawei_smartlogger.config import Huawei_SmartloggerBatSetup +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + + class Huawei_SmartloggerBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, Huawei_SmartloggerBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(Huawei_SmartloggerBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: Huawei_SmartloggerBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/huawei/huawei_smartlogger/counter.py b/packages/modules/devices/huawei/huawei_smartlogger/counter.py index 0190e76c36..733d61759f 100644 --- a/packages/modules/devices/huawei/huawei_smartlogger/counter.py +++ b/packages/modules/devices/huawei/huawei_smartlogger/counter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -11,26 +11,31 @@ from modules.devices.huawei.huawei_smartlogger.config import Huawei_SmartloggerCounterSetup +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + + class Huawei_SmartloggerCounter(AbstractCounter): - def __init__(self, device_id: int, - component_config: Huawei_SmartloggerCounterSetup, - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(Huawei_SmartloggerCounterSetup, component_config) - self.client = tcp_client + def __init__(self, component_config: Huawei_SmartloggerCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): + def update(self) -> None: modbus_id = self.component_config.configuration.modbus_id power = self.client.read_holding_registers(32278, ModbusDataType.INT_32, unit=modbus_id) - currents = [val / 100 for val in self.client.read_holding_registers( + currents = [val / 10 for val in self.client.read_holding_registers( 32272, [ModbusDataType.INT_32] * 3, unit=modbus_id)] voltages = [val / 100 for val in self.client.read_holding_registers( 32260, [ModbusDataType.INT_32] * 3, unit=modbus_id)] - powers = [val / 1000 for val in self.client.read_holding_registers( - 32335, [ModbusDataType.INT_32] * 3, unit=modbus_id)] + powers = self.client.read_holding_registers(32335, [ModbusDataType.INT_32] * 3, unit=modbus_id) imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( currents=currents, diff --git a/packages/modules/devices/huawei/huawei_smartlogger/device.py b/packages/modules/devices/huawei/huawei_smartlogger/device.py index cfb7333a7a..92ad36aff2 100644 --- a/packages/modules/devices/huawei/huawei_smartlogger/device.py +++ b/packages/modules/devices/huawei/huawei_smartlogger/device.py @@ -3,8 +3,8 @@ from typing import Iterable, Union from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext from modules.common import modbus +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.huawei.huawei_smartlogger import counter from modules.devices.huawei.huawei_smartlogger import inverter @@ -23,27 +23,34 @@ def create_device(device_config: Huawei_Smartlogger): + client = None + def create_bat_component(component_config: Huawei_SmartloggerBatSetup): - return bat.Huawei_SmartloggerBat(device_config.id, component_config, client) + nonlocal client + return bat.Huawei_SmartloggerBat(component_config, device_id=device_config.id, tcp_client=client) def create_counter_component(component_config: Huawei_SmartloggerCounterSetup): - return counter.Huawei_SmartloggerCounter(device_config.id, component_config, client) + nonlocal client + return counter.Huawei_SmartloggerCounter(component_config, device_id=device_config.id, tcp_client=client) def create_inverter_component(component_config: Huawei_SmartloggerInverterSetup): - return inverter.Huawei_SmartloggerInverter(device_config.id, component_config, client) + nonlocal client + return inverter.Huawei_SmartloggerInverter(component_config, device_id=device_config.id, tcp_client=client) def update_components(components: Iterable[huawei_smartlogger_component_classes]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/huawei/huawei_smartlogger/inverter.py b/packages/modules/devices/huawei/huawei_smartlogger/inverter.py index 76bd2528e3..3ad1ce9f1d 100644 --- a/packages/modules/devices/huawei/huawei_smartlogger/inverter.py +++ b/packages/modules/devices/huawei/huawei_smartlogger/inverter.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -14,14 +15,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + + class Huawei_SmartloggerInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Huawei_SmartloggerInverterSetup, - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(Huawei_SmartloggerInverterSetup, component_config) - self.client = tcp_client + def __init__(self, component_config: Huawei_SmartloggerInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/janitza/janitza/bat.py b/packages/modules/devices/janitza/janitza/bat.py new file mode 100644 index 0000000000..c1e58908b0 --- /dev/null +++ b/packages/modules/devices/janitza/janitza/bat.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common import modbus +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.janitza.janitza.config import JanitzaBatSetup + + +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class JanitzaBat(AbstractBat): + def __init__(self, component_config: JanitzaBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + power = self.__tcp_client.read_holding_registers(19026, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * -1 + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=JanitzaBatSetup) diff --git a/packages/modules/devices/janitza/janitza/config.py b/packages/modules/devices/janitza/janitza/config.py index ba63b0e06d..f0992dfa7b 100644 --- a/packages/modules/devices/janitza/janitza/config.py +++ b/packages/modules/devices/janitza/janitza/config.py @@ -36,3 +36,31 @@ def __init__(self, id: int = 0, configuration: JanitzaCounterConfiguration = None) -> None: super().__init__(name, type, id, configuration or JanitzaCounterConfiguration()) + + +class JanitzaInverterConfiguration: + def __init__(self): + pass + + +class JanitzaInverterSetup(ComponentSetup[JanitzaInverterConfiguration]): + def __init__(self, + name: str = "Janitza PV-Zähler", + type: str = "inverter", + id: int = 0, + configuration: JanitzaInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or JanitzaInverterConfiguration()) + + +class JanitzaBatConfiguration: + def __init__(self): + pass + + +class JanitzaBatSetup(ComponentSetup[JanitzaBatConfiguration]): + def __init__(self, + name: str = "Janitza Speicher-Zähler", + type: str = "bat", + id: int = 0, + configuration: JanitzaBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or JanitzaBatConfiguration()) diff --git a/packages/modules/devices/janitza/janitza/counter.py b/packages/modules/devices/janitza/janitza/counter.py index 5ab5948452..5cb2c4f64e 100644 --- a/packages/modules/devices/janitza/janitza/counter.py +++ b/packages/modules/devices/janitza/janitza/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,21 +12,26 @@ from modules.devices.janitza.janitza.config import JanitzaCounterSetup +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + class JanitzaCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, JanitzaCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(JanitzaCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: JanitzaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): + def update(self) -> None: with self.__tcp_client: power = self.__tcp_client.read_holding_registers(19026, ModbusDataType.FLOAT_32, unit=self.__modbus_id) powers = self.__tcp_client.read_holding_registers( diff --git a/packages/modules/devices/janitza/janitza/device.py b/packages/modules/devices/janitza/janitza/device.py index f3b268be94..0a0cda0501 100644 --- a/packages/modules/devices/janitza/janitza/device.py +++ b/packages/modules/devices/janitza/janitza/device.py @@ -1,36 +1,54 @@ #!/usr/bin/env python3 import logging -from typing import Iterable +from typing import Iterable, Union +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext -from modules.devices.janitza.janitza import counter -from modules.devices.janitza.janitza.config import Janitza, JanitzaCounterSetup +from modules.devices.janitza.janitza import counter, inverter, bat +from modules.devices.janitza.janitza.config import Janitza, JanitzaCounterSetup, JanitzaInverterSetup, JanitzaBatSetup log = logging.getLogger(__name__) def create_device(device_config: Janitza): - def create_counter_component(component_config: JanitzaCounterSetup): - return counter.JanitzaCounter(device_config.id, component_config, client, - device_config.configuration.modbus_id) + client = None - def update_components(components: Iterable[counter.JanitzaCounter]): + def create_counter_component(component_config: JanitzaCounterSetup): + nonlocal client + return counter.JanitzaCounter(component_config, device_id=device_config.id, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_inverter_component(component_config: JanitzaInverterSetup): + nonlocal client + return inverter.JanitzaInverter(component_config, device_id=device_config.id, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_bat_component(component_config: JanitzaBatSetup): + nonlocal client + return bat.JanitzaBat(component_config, device_id=device_config.id, tcp_client=client, + modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[Union[counter.JanitzaCounter, inverter.JanitzaInverter, + bat.JanitzaBat]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, + inverter=create_inverter_component, + bat=create_bat_component ), component_updater=MultiComponentUpdater(update_components) ) diff --git a/packages/modules/devices/janitza/janitza/inverter.py b/packages/modules/devices/janitza/janitza/inverter.py new file mode 100644 index 0000000000..4fd99e6c0c --- /dev/null +++ b/packages/modules/devices/janitza/janitza/inverter.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common import modbus +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.simcount import SimCounter +from modules.common.store import get_inverter_value_store +from modules.devices.janitza.janitza.config import JanitzaInverterSetup + + +class KwargsDict(TypedDict): + device_id: int + tcp_client: modbus.ModbusTcpClient_ + modbus_id: int + + +class JanitzaInverter(AbstractInverter): + def __init__(self, component_config: JanitzaInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['tcp_client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + power = self.__tcp_client.read_holding_registers(19026, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * -1 + _, exported = self.sim_counter.sim_count(power) + + inverter_state = InverterState( + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=JanitzaInverterSetup) diff --git a/packages/modules/devices/kaco/kaco_tx/config.py b/packages/modules/devices/kaco/kaco_tx/config.py new file mode 100644 index 0000000000..211fb5a2e2 --- /dev/null +++ b/packages/modules/devices/kaco/kaco_tx/config.py @@ -0,0 +1,37 @@ +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class KacoConfiguration: + def __init__(self, + port: int = 502, + ip_address=None): + self.port = port + self.ip_address = ip_address + + +class Kaco: + def __init__(self, + name: str = "Kaco Tx1 & Tx3 Serie", + type: str = "kaco_tx", + id: int = 0, + configuration: KacoConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or KacoConfiguration() + + +class KacoInverterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +class KacoInverterSetup(ComponentSetup[KacoInverterConfiguration]): + def __init__(self, + name: str = "Kaco Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: KacoInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or KacoInverterConfiguration()) diff --git a/packages/modules/devices/kaco/kaco_tx/device.py b/packages/modules/devices/kaco/kaco_tx/device.py new file mode 100644 index 0000000000..60e917b3b9 --- /dev/null +++ b/packages/modules/devices/kaco/kaco_tx/device.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable + +from modules.common import modbus +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater +from modules.devices.kaco.kaco_tx.inverter import KacoInverter +from modules.devices.kaco.kaco_tx.config import (Kaco, KacoInverterSetup) + +log = logging.getLogger(__name__) + +reconnect_delay = 1.2 + + +def create_device(device_config: Kaco): + client = None + + def create_inverter_component(component_config: KacoInverterSetup): + nonlocal client + return KacoInverter(component_config, client=client) + + def update_components(components: Iterable[KacoInverter]): + nonlocal client + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, + device_config.configuration.port, + reconnect_delay=reconnect_delay) + + device = ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + inverter=create_inverter_component + ), + component_updater=MultiComponentUpdater(update_components) + ) + return device + + +device_descriptor = DeviceDescriptor(configuration_factory=Kaco) diff --git a/packages/modules/devices/kaco/kaco_tx/inverter.py b/packages/modules/devices/kaco/kaco_tx/inverter.py new file mode 100644 index 0000000000..d089b3d17f --- /dev/null +++ b/packages/modules/devices/kaco/kaco_tx/inverter.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common import modbus +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.store import get_inverter_value_store +from modules.devices.kaco.kaco_tx.config import KacoInverterSetup +from modules.devices.kaco.kaco_tx.scale import create_scaled_reader + + +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + + +class KacoInverter(AbstractInverter): + def __init__(self, + component_config: KacoInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self._read_scaled_int16 = create_scaled_reader( + self.__tcp_client, self.component_config.configuration.modbus_id, ModbusDataType.INT_16 + ) + self._read_scaled_int32 = create_scaled_reader( + self.__tcp_client, self.component_config.configuration.modbus_id, ModbusDataType.INT_32 + ) + + def update(self) -> None: + self.store.set(self.read_state()) + + def read_state(self): + # 40084 | Total AC Power | int16 + # 40085 | AC Power scale factor | sunssf + power = self._read_scaled_int16(40084, 1)[0] * -1 + + # 40094 | AC Energy | acc32 + # 40096 | AC Energy scale factor | sunssf + exported = self._read_scaled_int32(40094, 1)[0] + + return InverterState( + power=power, + exported=exported + ) + + +component_descriptor = ComponentDescriptor(configuration_factory=KacoInverterSetup) diff --git a/packages/modules/devices/kaco/kaco_tx/scale.py b/packages/modules/devices/kaco/kaco_tx/scale.py new file mode 100644 index 0000000000..bfe78d2ddd --- /dev/null +++ b/packages/modules/devices/kaco/kaco_tx/scale.py @@ -0,0 +1,27 @@ +import logging +import math +from typing import List + +from modules.common.modbus import ModbusDataType, ModbusTcpClient_, Number + +log = logging.getLogger(__name__) + +# Registers that are not applicable to a meter class return the unsupported value. (e.g. Single Phase +# meters will support only summary and phase A values): + +UINT16_UNSUPPORTED = 0xFFFF + + +def scale_registers(registers: List[Number]) -> List[float]: + log.debug("Registers %s, Scale %s", registers[:-1], registers[-1]) + scale = math.pow(10, registers[-1]) + return [register * scale if register != UINT16_UNSUPPORTED else 0 for register in registers[:-1]] + + +def create_scaled_reader(client: ModbusTcpClient_, modbus_id: int, type: ModbusDataType): + def scaled_reader(address: int, count: int): + return scale_registers( + client.read_holding_registers(address, [type] * count + [ModbusDataType.INT_16], unit=modbus_id) + ) + + return scaled_reader diff --git a/packages/modules/devices/kaco/vendor.py b/packages/modules/devices/kaco/vendor.py new file mode 100644 index 0000000000..f68b3c044e --- /dev/null +++ b/packages/modules/devices/kaco/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Kaco" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/kostal/kostal_piko/counter.py b/packages/modules/devices/kostal/kostal_piko/counter.py index 4210348b23..683fbf4129 100644 --- a/packages/modules/devices/kostal/kostal_piko/counter.py +++ b/packages/modules/devices/kostal/kostal_piko/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, List, Tuple, Union +from typing import Any, List, Tuple, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -15,11 +14,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + class KostalPikoCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, KostalPikoCounterSetup], ip_address: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(KostalPikoCounterSetup, component_config) - self.ip_address = ip_address + def __init__(self, component_config: KostalPikoCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/kostal/kostal_piko/device.py b/packages/modules/devices/kostal/kostal_piko/device.py index 464dfcad02..fa5675ac6c 100644 --- a/packages/modules/devices/kostal/kostal_piko/device.py +++ b/packages/modules/devices/kostal/kostal_piko/device.py @@ -12,10 +12,13 @@ def create_device(device_config: KostalPiko): def create_counter_component(component_config: KostalPikoCounterSetup): - return counter.KostalPikoCounter(device_config.id, component_config, device_config.configuration.ip_address) + return counter.KostalPikoCounter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) def create_inverter_component(component_config: KostalPikoInverterSetup): - return inverter.KostalPikoInverter(device_config.id, component_config, device_config.configuration.ip_address) + return inverter.KostalPikoInverter(component_config, + ip_address=device_config.configuration.ip_address) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/kostal/kostal_piko/inverter.py b/packages/modules/devices/kostal/kostal_piko/inverter.py index e3e3427189..47ee4e8357 100644 --- a/packages/modules/devices/kostal/kostal_piko/inverter.py +++ b/packages/modules/devices/kostal/kostal_piko/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Tuple, Union +from typing import Tuple, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,13 +10,17 @@ from modules.devices.kostal.kostal_piko.config import KostalPikoInverterSetup +class KwargsDict(TypedDict): + ip_address: str + + class KostalPikoInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, KostalPikoInverterSetup], - ip_address: str) -> None: - self.component_config = dataclass_from_dict(KostalPikoInverterSetup, component_config) - self.ip_address = ip_address + def __init__(self, component_config: KostalPikoInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -30,7 +33,7 @@ def update(self) -> Tuple[float, float]: resp = req.get_http_session().get('http://'+self.ip_address+'/api/dxs.json', params=params, timeout=3).json() power = float(resp["dxsEntries"][0]["value"]) if power > 5: - power = power*-1 + power = power * -1 exported = float(resp["dxsEntries"][1]["value"]) * 1000 diff --git a/packages/modules/devices/kostal/kostal_piko_old/device.py b/packages/modules/devices/kostal/kostal_piko_old/device.py index 5e972c48fd..79c92daa0a 100644 --- a/packages/modules/devices/kostal/kostal_piko_old/device.py +++ b/packages/modules/devices/kostal/kostal_piko_old/device.py @@ -6,6 +6,7 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.devices.kostal.kostal_piko_old import inverter from modules.devices.kostal.kostal_piko_old.config import (KostalPikoOld, @@ -18,13 +19,14 @@ def create_device(device_config: KostalPikoOld): def create_inverter_component(component_config: KostalPikoOldInverterSetup): - return KostalPikoOldInverter(device_config.id, component_config) + return KostalPikoOldInverter(component_config, device_id=device_config.id) def update_components(components: Iterable[KostalPikoOldInverter]): response = req.get_http_session().get(device_config.configuration.url, verify=False, auth=( device_config.configuration.user, device_config.configuration.password), timeout=5).text for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/kostal/kostal_piko_old/inverter.py b/packages/modules/devices/kostal/kostal_piko_old/inverter.py index ac61403b00..a785aa10b6 100644 --- a/packages/modules/devices/kostal/kostal_piko_old/inverter.py +++ b/packages/modules/devices/kostal/kostal_piko_old/inverter.py @@ -1,8 +1,7 @@ -#!/usr/bin/env python3 import logging import re +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,19 +10,25 @@ from modules.common.store import get_inverter_value_store from modules.devices.kostal.kostal_piko_old.config import KostalPikoOldInverterSetup - log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class KostalPikoOldInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: KostalPikoOldInverterSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(KostalPikoOldInverterSetup, component_config) + def __init__(self, component_config: KostalPikoOldInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, response) -> None: + def update(self, response: str) -> None: # power may be a string "xxx" when the inverter is offline, so we cannot match as a number # state is just for debugging currently known states: # - Aus diff --git a/packages/modules/devices/kostal/kostal_piko_old/inverter_test.py b/packages/modules/devices/kostal/kostal_piko_old/inverter_test.py index ea1dbb42b1..3a6655bea9 100644 --- a/packages/modules/devices/kostal/kostal_piko_old/inverter_test.py +++ b/packages/modules/devices/kostal/kostal_piko_old/inverter_test.py @@ -18,7 +18,8 @@ def test_parse_html(sample_file_name, expected_inverter_state, monkeypatch): sample = (Path(__file__).parent / sample_file_name).read_text() mock_inverter_value_store = Mock() monkeypatch.setattr(inverter, 'get_inverter_value_store', Mock(return_value=mock_inverter_value_store)) - inv = inverter.KostalPikoOldInverter(0, KostalPikoOldInverterSetup()) + inv = inverter.KostalPikoOldInverter(KostalPikoOldInverterSetup(), device_id=0) + inv.initialize() # execution inv.update(sample) diff --git a/packages/modules/devices/kostal/kostal_plenticore/bat.py b/packages/modules/devices/kostal/kostal_plenticore/bat.py index 2296969127..92202b51df 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/bat.py +++ b/packages/modules/devices/kostal/kostal_plenticore/bat.py @@ -1,11 +1,13 @@ #!/usr/bin/env python3 import logging -from typing import Any, Callable +from typing import TypedDict, Any +from pymodbus.constants import Endian + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor -from modules.common.modbus import ModbusDataType from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreBatSetup @@ -13,34 +15,44 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + endianess: Endian + client: ModbusTcpClient_ + + class KostalPlenticoreBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: KostalPlenticoreBatSetup) -> None: + def __init__(self, component_config: KostalPlenticoreBatSetup, **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.endianess: Endian = self.kwargs['endianess'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="speicher") self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - def read_state(self, reader: Callable[[int, ModbusDataType], Any]) -> BatState: - power = reader(582, ModbusDataType.INT_16) * -1 - soc = reader(514, ModbusDataType.INT_16) - imported, exported = self.sim_counter.sim_count(power) - log.debug("raw bat power "+str(power)) - # Speicherladung muss durch Wandlungsverluste und internen Verbrauch korrigiert werden, sonst - # wird ein falscher Hausverbrauch berechnet. Die Verluste fallen hier unter den Tisch. + def update(self) -> None: + power = self.client.read_holding_registers( + 582, ModbusDataType.INT_16, unit=self.modbus_id, wordorder=self.endianess) * -1 + soc = self.client.read_holding_registers( + 514, ModbusDataType.INT_16, unit=self.modbus_id, wordorder=self.endianess) if power < 0: - power = reader(106, ModbusDataType.FLOAT_32) * -1 + power = self.client.read_holding_registers( + 106, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) * -1 + imported, exported = self.sim_counter.sim_count(power) - return BatState( + bat_state = BatState( power=power, soc=soc, imported=imported, - exported=exported, + exported=exported ) - - def update(self, state): - self.store.set(state) + self.store.set(bat_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreBatSetup) diff --git a/packages/modules/devices/kostal/kostal_plenticore/counter.py b/packages/modules/devices/kostal/kostal_plenticore/counter.py index a3950a6fe4..1c8c5e867a 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/counter.py +++ b/packages/modules/devices/kostal/kostal_plenticore/counter.py @@ -1,47 +1,64 @@ #!/usr/bin/env python3 -from typing import Any, Callable +from typing import TypedDict, Any +from pymodbus.constants import Endian + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_counter_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreCounterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + endianess: Endian + client: ModbusTcpClient_ + + class KostalPlenticoreCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: KostalPlenticoreCounterSetup) -> None: + def __init__(self, component_config: KostalPlenticoreCounterSetup, **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.endianess: Endian = self.kwargs['endianess'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - def get_values(self, reader: Callable[[int, ModbusDataType], Any]) -> CounterState: - power_factor = reader(150, ModbusDataType.FLOAT_32) - currents = [reader(register, ModbusDataType.FLOAT_32) for register in [222, 232, 242]] - voltages = [reader(register, ModbusDataType.FLOAT_32) for register in [230, 240, 250]] - powers = [reader(register, ModbusDataType.FLOAT_32) for register in [224, 234, 244]] - power = reader(252, ModbusDataType.FLOAT_32) - frequency = reader(220, ModbusDataType.FLOAT_32) + def update(self) -> None: + power = self.client.read_holding_registers( + 252, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) + imported, exported = self.sim_counter.sim_count(power) + power_factor = self.client.read_holding_registers( + 150, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) + currents = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) for reg in [222, 232, 242]] + voltages = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) for reg in [230, 240, 250]] + powers = [self.client.read_holding_registers( + reg, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) for reg in [224, 234, 244]] + frequency = self.client.read_holding_registers( + 220, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) - return CounterState( + counter_state = CounterState( powers=powers, currents=currents, voltages=voltages, power=power, power_factors=[power_factor]*3, - frequency=frequency + frequency=frequency, + imported=imported, + exported=exported ) - - def update_imported_exported(self, state: CounterState) -> CounterState: - state.imported, state.exported = self.sim_counter.sim_count(state.power) - return state - - def update(self, reader: Callable[[int, ModbusDataType], Any]): - self.store.set(self.update_imported_exported(self.get_values(reader))) + self.store.set(counter_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreCounterSetup) diff --git a/packages/modules/devices/kostal/kostal_plenticore/device.py b/packages/modules/devices/kostal/kostal_plenticore/device.py index b164807d46..e911c90a4d 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/device.py +++ b/packages/modules/devices/kostal/kostal_plenticore/device.py @@ -1,83 +1,71 @@ -# !/usr/bin/env python3 -from enum import IntEnum -from typing import Any, Callable, Iterable, Union -from pymodbus.constants import Endian -import functools +#!/usr/bin/env python3 import logging +from typing import Iterable, Union +from pymodbus.constants import Endian -from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.devices.kostal.kostal_plenticore.bat import KostalPlenticoreBat -from modules.devices.kostal.kostal_plenticore.inverter import KostalPlenticoreInverter -from modules.devices.kostal.kostal_plenticore.config import (KostalPlenticore, KostalPlenticoreBatSetup, - KostalPlenticoreCounterSetup, - KostalPlenticoreInverterSetup) from modules.devices.kostal.kostal_plenticore.counter import KostalPlenticoreCounter - +from modules.devices.kostal.kostal_plenticore.inverter import KostalPlenticoreInverter +from modules.devices.kostal.kostal_plenticore.config import KostalPlenticore, KostalPlenticoreBatSetup +from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreCounterSetup, KostalPlenticoreInverterSetup log = logging.getLogger(__name__) -class LegacyCounterPosition(IntEnum): - HOME_CONSUMPTION = 0 - GRID = 1 - - -def update( - components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]], - reader: Callable[[int, modbus.ModbusDataType], Any], - set_inverter_state: bool = True): - battery = next((component for component in components if isinstance(component, KostalPlenticoreBat)), None) - bat_state = battery.read_state(reader) if battery else None - for component in components: - if isinstance(component, KostalPlenticoreInverter): - # Fürs erste nur die WR-Werte nutzen ohne Verlustberechnung. - # power: R575(inverter generation power (actual)) - # exported: R320 (Total yield) - inverter_state = component.read_state(reader) - pv_state = inverter_state - if set_inverter_state: - component.update(pv_state) - elif isinstance(component, KostalPlenticoreCounter): - component.update(reader) - if bat_state: - battery.update(bat_state) - if set_inverter_state is False: - return pv_state - - def create_device(device_config: KostalPlenticore): + client = None + endianess = None + def create_bat_component(component_config: KostalPlenticoreBatSetup): - return KostalPlenticoreBat(device_config.id, component_config) + nonlocal client + return KostalPlenticoreBat(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + endianess=endianess, + client=client) def create_counter_component(component_config: KostalPlenticoreCounterSetup): - return KostalPlenticoreCounter(device_config.id, component_config) + nonlocal client + return KostalPlenticoreCounter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + endianess=endianess, + client=client) def create_inverter_component(component_config: KostalPlenticoreInverterSetup): - return KostalPlenticoreInverter(component_config) + nonlocal client + return KostalPlenticoreInverter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + endianess=endianess, + client=client) def update_components( - components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]] - ): - with tcp_client: - update(components, reader) + components: Iterable[Union[KostalPlenticoreBat, KostalPlenticoreCounter, KostalPlenticoreInverter]]): + nonlocal client + with client: + for component in components: + component.update() + + def initializer(): + nonlocal client, endianess + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + endianess = Endian.Big if client.read_holding_registers( + 5, ModbusDataType.UINT_16, unit=device_config.configuration.modbus_id) else Endian.Little - try: - tcp_client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - reader = _create_reader(tcp_client, device_config.configuration.modbus_id) - except Exception: - log.exception("Fehler in create_device") return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( - bat=create_bat_component, counter=create_counter_component, inverter=create_inverter_component), - component_updater=MultiComponentUpdater(update_components), + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) ) -def _create_reader(tcp_client: modbus.ModbusTcpClient_, modbus_id: int) -> Callable[[int, modbus.ModbusDataType], Any]: - return functools.partial(tcp_client.read_holding_registers, unit=modbus_id, wordorder=Endian.Little) - - device_descriptor = DeviceDescriptor(configuration_factory=KostalPlenticore) diff --git a/packages/modules/devices/kostal/kostal_plenticore/inverter.py b/packages/modules/devices/kostal/kostal_plenticore/inverter.py index 14fd66f817..e8904a4d48 100644 --- a/packages/modules/devices/kostal/kostal_plenticore/inverter.py +++ b/packages/modules/devices/kostal/kostal_plenticore/inverter.py @@ -1,36 +1,64 @@ #!/usr/bin/env python3 -from typing import Any, Callable +from typing import TypedDict, Any +from pymodbus.constants import Endian + from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter from modules.common.store import get_inverter_value_store from modules.devices.kostal.kostal_plenticore.config import KostalPlenticoreInverterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + endianess: Endian + client: ModbusTcpClient_ + + class KostalPlenticoreInverter(AbstractInverter): - def __init__(self, - component_config: KostalPlenticoreInverterSetup) -> None: + def __init__(self, component_config: KostalPlenticoreInverterSetup, **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.endianess: Endian = self.kwargs['endianess'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="Wechselrichter") + self.fault_text = ( + "Es kann keine DC-Leistung aus dem Wechselrichter ausgelesen werden, " + "möglicherweise kann ein Firmware-Update für den Wechselrichter nötig sein." + ) - def read_state(self, reader: Callable[[int, ModbusDataType], Any]) -> InverterState: - # PV-Anlage kann nichts verbrauchen, also ggf. Register-/Rundungsfehler korrigieren. - power = reader(575, ModbusDataType.INT_16) * -1 - exported = reader(320, ModbusDataType.FLOAT_32) + def update(self) -> None: + power = self.client.read_holding_registers( + 575, ModbusDataType.INT_16, unit=self.modbus_id, wordorder=self.endianess) * -1 + exported = self.client.read_holding_registers( + 320, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) + # Try to read dc_power, if it fails just skip it and set to None + try: + dc_power = self.client.read_holding_registers( + 1066, ModbusDataType.FLOAT_32, unit=self.modbus_id, wordorder=self.endianess) * -1 + self.fault_state.no_error() + except Exception: + dc_power = None + self.fault_state.no_error(self.fault_text) + imported, _ = self.sim_counter.sim_count(power) - return InverterState( + inverter_state = InverterState( power=power, - exported=exported + exported=exported, + dc_power=dc_power, + imported=imported ) - - def dc_in_string_1_2(self, reader: Callable[[int, ModbusDataType], Any]): - return reader(260, ModbusDataType.FLOAT_32) + reader(270, ModbusDataType.FLOAT_32) - - def update(self, state): - self.store.set(state) + self.store.set(inverter_state) component_descriptor = ComponentDescriptor(configuration_factory=KostalPlenticoreInverterSetup) diff --git a/packages/modules/devices/kostal/kostal_sem/counter.py b/packages/modules/devices/kostal/kostal_sem/counter.py index aeb6219e7d..f14fbc0dbf 100644 --- a/packages/modules/devices/kostal/kostal_sem/counter.py +++ b/packages/modules/devices/kostal/kostal_sem/counter.py @@ -1,4 +1,5 @@ #!/usr/bin/env python3 +from typing import TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -9,14 +10,19 @@ from modules.devices.kostal.kostal_sem.config import KostalSemCounterSetup +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + modbus_id: int + + class KostalSemCounter(AbstractCounter): - def __init__(self, - component_config: KostalSemCounterSetup, - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: + def __init__(self, component_config: KostalSemCounterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/kostal/kostal_sem/device.py b/packages/modules/devices/kostal/kostal_sem/device.py index 46dd917f53..75416056f4 100644 --- a/packages/modules/devices/kostal/kostal_sem/device.py +++ b/packages/modules/devices/kostal/kostal_sem/device.py @@ -1,37 +1,27 @@ -import logging -from typing import List - -from helpermodules.cli import run_using_positional_cli_args from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater from modules.devices.kostal.kostal_sem.counter import KostalSemCounter -from modules.devices.kostal.kostal_sem.config import KostalSem, KostalSemConfiguration, KostalSemCounterSetup - -log = logging.getLogger(__name__) +from modules.devices.kostal.kostal_sem.config import KostalSem, KostalSemCounterSetup def create_device(device_config: KostalSem): + client = None + def create_counter_component(component_config: KostalSemCounterSetup): - return KostalSemCounter(component_config, client, device_config.configuration.modbus_id) + nonlocal client + return KostalSemCounter(component_config, client=client, modbus_id=device_config.configuration.modbus_id) + + def initializer(): + nonlocal client + client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType(counter=create_counter_component), component_updater=IndependentComponentUpdater(lambda component: component.update()), ) -def read_legacy(address: str) -> None: - device = create_device(KostalSem(configuration=KostalSemConfiguration(ip_address=address))) - device.add_component(KostalSemCounterSetup(id=None)) - log.debug('KSEM address: ' + address) - device.update() - - -def main(argv: List[str]): - run_using_positional_cli_args(read_legacy, argv) - - device_descriptor = DeviceDescriptor(configuration_factory=KostalSem) diff --git a/packages/modules/devices/kostal/kostal_steca/device.py b/packages/modules/devices/kostal/kostal_steca/device.py index c1a489a832..dd3169ebe0 100644 --- a/packages/modules/devices/kostal/kostal_steca/device.py +++ b/packages/modules/devices/kostal/kostal_steca/device.py @@ -1,22 +1,14 @@ #!/usr/bin/env python3 -import logging -from typing import Optional, List -from helpermodules.cli import run_using_positional_cli_args from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext -from modules.common.component_state import InverterState from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater -from modules.devices.kostal.kostal_steca import inverter from modules.devices.kostal.kostal_steca.config import KostalSteca, KostalStecaInverterSetup from modules.devices.kostal.kostal_steca.inverter import KostalStecaInverter -log = logging.getLogger(__name__) - def create_device(device_config: KostalSteca): def create_inverter_component(component_config: KostalStecaInverterSetup): - return KostalStecaInverter(component_config, device_config.configuration.ip_address) + return KostalStecaInverter(component_config, ip_address=device_config.configuration.ip_address) return ConfigurableDevice( device_config=device_config, @@ -27,38 +19,4 @@ def create_inverter_component(component_config: KostalStecaInverterSetup): ) -COMPONENT_TYPE_TO_MODULE = { - "inverter": inverter -} - - -def read_legacy(component_type: str, ip_address: str, variant: int, num: Optional[int]) -> None: - if component_type in COMPONENT_TYPE_TO_MODULE: - component_config = COMPONENT_TYPE_TO_MODULE[component_type].component_descriptor.configuration_factory() - else: - raise Exception( - "illegal component type " + component_type + ". Allowed values: " + - ','.join(COMPONENT_TYPE_TO_MODULE.keys()) - ) - component_config.id = num - component_config.configuration.variant_steca = True if variant == 0 else False - inverter = KostalStecaInverter(component_config, ip_address) - - log.debug('KostalSteca IP-Adresse: ' + ip_address) - log.debug('KostalSteca Variant: ' + str(variant)) - - with SingleComponentUpdateContext(inverter.component_info): - power, exported = inverter.get_values() - if exported is None: - log.debug("PVkWh: NaN get prev. Value") - with open("/var/www/html/openWB/ramdisk/pv2kwh", "r") as f: - exported = f.read() - - inverter.store.set(InverterState(power=power, exported=exported)) - - -def main(argv: List[str]): - run_using_positional_cli_args(read_legacy, argv) - - device_descriptor = DeviceDescriptor(configuration_factory=KostalSteca) diff --git a/packages/modules/devices/kostal/kostal_steca/inverter.py b/packages/modules/devices/kostal/kostal_steca/inverter.py index 38fe3105a3..b05e3d3089 100644 --- a/packages/modules/devices/kostal/kostal_steca/inverter.py +++ b/packages/modules/devices/kostal/kostal_steca/inverter.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import logging -from typing import Optional, Tuple -import xml.etree.ElementTree as ET import re +from typing import Any, Optional, Tuple, TypedDict +import xml.etree.ElementTree as ET from math import isnan from modules.common import req @@ -16,10 +16,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + ip_address: str + + class KostalStecaInverter(AbstractInverter): - def __init__(self, component_config: KostalStecaInverterSetup, ip_address: str) -> None: - self.ip_address = ip_address + def __init__(self, component_config: KostalStecaInverterSetup, **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/kostal/kostal_steca/inverter_test.py b/packages/modules/devices/kostal/kostal_steca/inverter_test.py index 1a2553ad60..b0a7f3d0c6 100644 --- a/packages/modules/devices/kostal/kostal_steca/inverter_test.py +++ b/packages/modules/devices/kostal/kostal_steca/inverter_test.py @@ -14,7 +14,8 @@ ]) def test_get_values(measurements_file, expected_power, requests_mock): # setup - inverter = KostalStecaInverter(KostalStecaInverterSetup(), SAMPLE_IP) + inverter = KostalStecaInverter(KostalStecaInverterSetup(), ip_address=SAMPLE_IP) + inverter.initialize() with open("packages/modules/devices/kostal/kostal_steca/"+measurements_file, "r") as f: measurements_sample = f.read() diff --git a/packages/modules/devices/lg/lg/bat.py b/packages/modules/devices/lg/lg/bat.py index 82d30cf11e..0890f187c6 100644 --- a/packages/modules/devices/lg/lg/bat.py +++ b/packages/modules/devices/lg/lg/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -11,10 +10,17 @@ from modules.devices.lg.lg.config import LgBatSetup +class KwargsDict(TypedDict): + device_id: int + + class LgBat(AbstractBat): - def __init__(self, device_id: int, component_config: Union[Dict, LgBatSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(LgBatSetup, component_config) + def __init__(self, component_config: LgBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/lg/lg/counter.py b/packages/modules/devices/lg/lg/counter.py index 9cdcdc9473..fba44df091 100644 --- a/packages/modules/devices/lg/lg/counter.py +++ b/packages/modules/devices/lg/lg/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -11,10 +10,17 @@ from modules.devices.lg.lg.config import LgCounterSetup +class KwargsDict(TypedDict): + device_id: int + + class LgCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, LgCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(LgCounterSetup, component_config) + def __init__(self, component_config: LgCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/lg/lg/device.py b/packages/modules/devices/lg/lg/device.py index 39b96c865a..9ebed79b6b 100644 --- a/packages/modules/devices/lg/lg/device.py +++ b/packages/modules/devices/lg/lg/device.py @@ -6,6 +6,7 @@ from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.lg.lg.bat import LgBat from modules.devices.lg.lg.config import LG, LgBatSetup, LgCounterSetup, LgInverterSetup @@ -39,13 +40,13 @@ def _request_data(session: Session, session_key: str, ip_address: str) -> Dict: def create_device(device_config: LG): def create_bat_component(component_config: LgBatSetup): - return LgBat(device_config.id, component_config) + return LgBat(component_config, device_id=device_config.id) def create_counter_component(component_config: LgCounterSetup): - return LgCounter(device_config.id, component_config) + return LgCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: LgInverterSetup): - return LgInverter(device_config.id, component_config) + return LgInverter(component_config, device_id=device_config.id) def update_components(components: Iterable[Union[LgBat, LgCounter, LgInverter]]): nonlocal session_key @@ -58,7 +59,8 @@ def update_components(components: Iterable[Union[LgBat, LgCounter, LgInverter]]) response = _request_data(session, session_key, device_config.configuration.ip_address) for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) session_key = " " return ConfigurableDevice( diff --git a/packages/modules/devices/lg/lg/inverter.py b/packages/modules/devices/lg/lg/inverter.py index f7c2782a00..c94b747de1 100644 --- a/packages/modules/devices/lg/lg/inverter.py +++ b/packages/modules/devices/lg/lg/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,10 +10,17 @@ from modules.devices.lg.lg.config import LgInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class LgInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, LgInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(LgInverterSetup, component_config) + def __init__(self, component_config: LgInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/mtec/mtec/bat.py b/packages/modules/devices/mtec/mtec/bat.py index 98c2c40585..bbff74c22d 100644 --- a/packages/modules/devices/mtec/mtec/bat.py +++ b/packages/modules/devices/mtec/mtec/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -13,25 +14,34 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class MTecBat(AbstractBat): - def __init__(self, device_id: int, component_config: MTecBatSetup) -> None: - self.component_config = dataclass_from_dict(MTecBatSetup, component_config) + def __init__(self, component_config: MTecBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id generation = self.component_config.configuration.generation if generation == 2: - power = client.read_holding_registers(40258, ModbusDataType.INT_32, unit=unit) * -1 + power = self.client.read_holding_registers(40258, ModbusDataType.INT_32, unit=unit) * -1 # soc unit 0.01% - soc = client.read_holding_registers(43000, ModbusDataType.UINT_16, unit=unit) / 100 + soc = self.client.read_holding_registers(43000, ModbusDataType.UINT_16, unit=unit) / 100 else: - power = client.read_holding_registers(30258, ModbusDataType.INT_32, unit=unit) * -1 - soc = client.read_holding_registers(33000, ModbusDataType.UINT_16, unit=unit) / 100 + power = self.client.read_holding_registers(30258, ModbusDataType.INT_32, unit=unit) * -1 + soc = self.client.read_holding_registers(33000, ModbusDataType.UINT_16, unit=unit) / 100 imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( diff --git a/packages/modules/devices/mtec/mtec/counter.py b/packages/modules/devices/mtec/mtec/counter.py index e9c6ea5a34..87049381e7 100644 --- a/packages/modules/devices/mtec/mtec/counter.py +++ b/packages/modules/devices/mtec/mtec/counter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -10,19 +11,28 @@ from modules.devices.mtec.mtec.config import MTecCounterSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class MTecCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: MTecCounterSetup) -> None: - self.component_config = dataclass_from_dict(MTecCounterSetup, component_config) + def __init__(self, component_config: MTecCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - def update(self, client: ModbusTcpClient_): + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(11000, ModbusDataType.INT_32, unit=unit) - powers = client.read_holding_registers(10994, [ModbusDataType.INT_32]*3, unit=unit) + power = self.client.read_holding_registers(11000, ModbusDataType.INT_32, unit=unit) + powers = self.client.read_holding_registers(10994, [ModbusDataType.INT_32]*3, unit=unit) imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( diff --git a/packages/modules/devices/mtec/mtec/device.py b/packages/modules/devices/mtec/mtec/device.py index bab588d6eb..47581ff939 100644 --- a/packages/modules/devices/mtec/mtec/device.py +++ b/packages/modules/devices/mtec/mtec/device.py @@ -15,27 +15,34 @@ def create_device(device_config: MTec): + client = None + def create_bat_component(component_config: MTecBatSetup): - return MTecBat(device_config.id, component_config) + nonlocal client + return MTecBat(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: MTecCounterSetup): - return MTecCounter(device_config.id, component_config) + nonlocal client + return MTecCounter(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: MTecInverterSetup): - return MTecInverter(device_config.id, component_config) + nonlocal client + return MTecInverter(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[MTecBat, MTecCounter, MTecInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/mtec/mtec/inverter.py b/packages/modules/devices/mtec/mtec/inverter.py index 0036cb24a0..15ddd58dd8 100644 --- a/packages/modules/devices/mtec/mtec/inverter.py +++ b/packages/modules/devices/mtec/mtec/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,18 +11,27 @@ from modules.devices.mtec.mtec.config import MTecInverterSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class MTecInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, MTecInverterSetup]) -> None: - self.component_config = dataclass_from_dict(MTecInverterSetup, component_config) + def __init__(self, component_config: MTecInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(11028, ModbusDataType.UINT_32, unit=unit) * -1 + power = self.client.read_holding_registers(11028, ModbusDataType.UINT_32, unit=unit) * -1 _, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( diff --git a/packages/modules/devices/mystrom/__init__.py b/packages/modules/devices/mystrom/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/mystrom/mystrom/__init__.py b/packages/modules/devices/mystrom/mystrom/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/mystrom/mystrom/config.py b/packages/modules/devices/mystrom/mystrom/config.py new file mode 100644 index 0000000000..7a92d551c6 --- /dev/null +++ b/packages/modules/devices/mystrom/mystrom/config.py @@ -0,0 +1,41 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class MystromConfiguration: + def __init__(self, ip_address: Optional[str] = None): + self.ip_address = ip_address + + +@auto_str +class Mystrom: + def __init__(self, + name: str = "mystrom", + type: str = "mystrom", + id: int = 0, + configuration: MystromConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or MystromConfiguration() + + +@auto_str +class MystromCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class MystromCounterSetup(ComponentSetup[MystromCounterConfiguration]): + def __init__(self, + name: str = "mystrom Zähler", + type: str = "counter", + id: int = 0, + configuration: MystromCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or MystromCounterConfiguration()) diff --git a/packages/modules/devices/mystrom/mystrom/counter.py b/packages/modules/devices/mystrom/mystrom/counter.py new file mode 100644 index 0000000000..17b82538e4 --- /dev/null +++ b/packages/modules/devices/mystrom/mystrom/counter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from requests import Session +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.mystrom.mystrom.config import MystromCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + +class MystromCounter(AbstractCounter): + def __init__(self, component_config: MystromCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self, session: Session): + resp = session.get(f"http://{self.ip_address}/report").json() + power = resp["power"] + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + imported=imported, + exported=exported, + power=power, + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=MystromCounterSetup) diff --git a/packages/modules/devices/mystrom/mystrom/device.py b/packages/modules/devices/mystrom/mystrom/device.py new file mode 100644 index 0000000000..51c3a0a019 --- /dev/null +++ b/packages/modules/devices/mystrom/mystrom/device.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +import logging + +from modules.common import req +from modules.common.abstract_device import DeviceDescriptor +from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, IndependentComponentUpdater +from modules.devices.mystrom.mystrom.config import Mystrom, MystromCounterSetup +from modules.devices.mystrom.mystrom.counter import MystromCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Mystrom): + session = None + + def create_counter_component(component_config: MystromCounterSetup): + return MystromCounter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) + + def initializer(): + nonlocal session + session = req.get_http_session() + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=IndependentComponentUpdater(lambda component: component.update(session)) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Mystrom) diff --git a/packages/modules/devices/mystrom/vendor.py b/packages/modules/devices/mystrom/vendor.py new file mode 100644 index 0000000000..bea7943107 --- /dev/null +++ b/packages/modules/devices/mystrom/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "mystrom" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/nibe/__init__ .py b/packages/modules/devices/nibe/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/nibe/nibe/__init__.py b/packages/modules/devices/nibe/nibe/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/nibe/nibe/config.py b/packages/modules/devices/nibe/nibe/config.py new file mode 100644 index 0000000000..713da6d9a2 --- /dev/null +++ b/packages/modules/devices/nibe/nibe/config.py @@ -0,0 +1,42 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class NibeConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502): + self.ip_address = ip_address + self.port = port + + +@auto_str +class Nibe: + def __init__(self, + name: str = "Nibe S-Series", + type: str = "nibe", + id: int = 0, + configuration: NibeConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or NibeConfiguration() + + +@auto_str +class NibeCounterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class NibeCounterSetup(ComponentSetup[NibeCounterConfiguration]): + def __init__(self, + name: str = "Nibe Zähler", + type: str = "counter", + id: int = 0, + configuration: NibeCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or NibeCounterConfiguration()) diff --git a/packages/modules/devices/nibe/nibe/counter.py b/packages/modules/devices/nibe/nibe/counter.py new file mode 100644 index 0000000000..1c16e00b51 --- /dev/null +++ b/packages/modules/devices/nibe/nibe/counter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.nibe.nibe.config import NibeCounterSetup + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + +class NibeCounter(AbstractCounter): + def __init__(self, component_config: NibeCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + unit = self.component_config.configuration.modbus_id + power = self.client.read_input_registers(2166, ModbusDataType.UINT_32, unit=unit) / 10 + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + imported=imported, + exported=exported, + power=power, + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=NibeCounterSetup) diff --git a/packages/modules/devices/nibe/nibe/device.py b/packages/modules/devices/nibe/nibe/device.py new file mode 100644 index 0000000000..6a03378060 --- /dev/null +++ b/packages/modules/devices/nibe/nibe/device.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.nibe.nibe.config import Nibe, NibeCounterSetup +from modules.devices.nibe.nibe.counter import NibeCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Nibe): + client = None + + def create_counter_component(component_config: NibeCounterSetup): + nonlocal client + return NibeCounter(component_config, device_id=device_config.id, client=client) + + def update_components(components: Iterable[NibeCounter]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Nibe) diff --git a/packages/modules/devices/nibe/vendor.py b/packages/modules/devices/nibe/vendor.py new file mode 100644 index 0000000000..48ccad8553 --- /dev/null +++ b/packages/modules/devices/nibe/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Nibe" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/opendtu/opendtu/config.py b/packages/modules/devices/opendtu/opendtu/config.py index 794fe1dbd7..c55dc62b82 100644 --- a/packages/modules/devices/opendtu/opendtu/config.py +++ b/packages/modules/devices/opendtu/opendtu/config.py @@ -9,7 +9,7 @@ @auto_str class OpenDTUConfiguration(JsonConfiguration): def __init__(self, url: Optional[str] = None): - self.url = "http://" + url + "/api/livedata/status" + self.url = f"http://{url}/api/livedata/status" @auto_str diff --git a/packages/modules/devices/openwb/openwb_bat_kit/bat.py b/packages/modules/devices/openwb/openwb_bat_kit/bat.py index b39e8b1f4c..818b7e3653 100644 --- a/packages/modules/devices/openwb/openwb_bat_kit/bat.py +++ b/packages/modules/devices/openwb/openwb_bat_kit/bat.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from typing import Union +from typing import Union, TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractBat @@ -10,12 +10,19 @@ from modules.devices.openwb.openwb_flex.config import convert_to_flex_setup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class BatKit(BatKitFlex, AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[BatKitBatSetup, EvuKitBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: + def __init__(self, component_config: Union[BatKitBatSetup, EvuKitBatSetup], **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: modbus.ModbusTcpClient_ = self.kwargs['client'] version = self.component_config.configuration.version if version == 0: id = 1 @@ -26,7 +33,10 @@ def __init__(self, else: raise ValueError("Version " + str(version) + " unbekannt.") - super().__init__(device_id, convert_to_flex_setup(self.component_config, id), tcp_client) + super().__init__(convert_to_flex_setup(self.component_config, id), + device_id=self.__device_id, + client=self.client) + super().initialize() component_descriptor = ComponentDescriptor(configuration_factory=BatKitBatSetup) diff --git a/packages/modules/devices/openwb/openwb_bat_kit/device.py b/packages/modules/devices/openwb/openwb_bat_kit/device.py index a363126239..f1cf0f1f4f 100644 --- a/packages/modules/devices/openwb/openwb_bat_kit/device.py +++ b/packages/modules/devices/openwb/openwb_bat_kit/device.py @@ -1,9 +1,11 @@ import logging +from pathlib import Path from typing import Iterable +from helpermodules.utils.run_command import run_command from modules.common import modbus -from modules.common.component_context import SingleComponentUpdateContext from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.openwb.openwb_bat_kit.config import BatKitSetup, BatKitBatSetup from modules.devices.openwb.openwb_bat_kit.bat import BatKit @@ -12,21 +14,30 @@ def create_device(device_config: BatKitSetup): + client = None + def create_bat_component(component_config: BatKitBatSetup): - return BatKit(device_config.id, component_config, client) + nonlocal client + return BatKit(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[BatKit]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_("192.168.193.19", 8899) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, ), diff --git a/packages/modules/devices/openwb/openwb_evu_kit/counter.py b/packages/modules/devices/openwb/openwb_evu_kit/counter.py index 0fd755c71b..2cb3cfdbd3 100644 --- a/packages/modules/devices/openwb/openwb_evu_kit/counter.py +++ b/packages/modules/devices/openwb/openwb_evu_kit/counter.py @@ -1,7 +1,6 @@ -# !/usr/bin/env python3 -from typing import Dict, Union +#!/usr/bin/env python3 +from typing import Dict, Union, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_type import ComponentDescriptor @@ -10,12 +9,21 @@ from modules.devices.openwb.openwb_flex.config import convert_to_flex_setup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class EvuKit(EvuKitFlex, AbstractCounter): def __init__(self, - device_id: int, - component_config: Union[Dict, EvuKitCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(EvuKitCounterSetup, component_config) + component_config: Union[Dict, EvuKitCounterSetup], + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] version = self.component_config.configuration.version if version == 0: id = 5 @@ -28,7 +36,10 @@ def __init__(self, else: raise ValueError("Version " + str(version) + " unbekannt.") - super().__init__(device_id, convert_to_flex_setup(self.component_config, id), tcp_client) + super().__init__(convert_to_flex_setup(self.component_config, id), + device_id=self.__device_id, + client=self.__tcp_client) + super().initialize() component_descriptor = ComponentDescriptor(configuration_factory=EvuKitCounterSetup) diff --git a/packages/modules/devices/openwb/openwb_evu_kit/device.py b/packages/modules/devices/openwb/openwb_evu_kit/device.py index d7661b08b5..5e99c22d33 100644 --- a/packages/modules/devices/openwb/openwb_evu_kit/device.py +++ b/packages/modules/devices/openwb/openwb_evu_kit/device.py @@ -1,7 +1,8 @@ import logging -import time +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext @@ -16,28 +17,38 @@ def create_device(device_config: EvuKitSetup): + client = None + def create_bat_component(component_config: EvuKitBatSetup): - return BatKit(device_config.id, component_config, client) + nonlocal client + return BatKit(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: EvuKitCounterSetup): - return EvuKit(device_config.id, component_config, client) + nonlocal client + return EvuKit(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: EvuKitInverterSetup): - return PvKit(device_config.id, component_config, client) + nonlocal client + return PvKit(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[BatKit, EvuKit, PvKit]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - time.sleep(0.2) - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_("192.168.193.15", 8899) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/openwb/openwb_flex/bat.py b/packages/modules/devices/openwb/openwb_flex/bat.py index 1861eb24f6..06d927ca35 100644 --- a/packages/modules/devices/openwb/openwb_flex/bat.py +++ b/packages/modules/devices/openwb/openwb_flex/bat.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.lovato import Lovato +from modules.common.mpm3pm import Mpm3pm from modules.common.sdm import Sdm120 from modules.common.sdm import Sdm630_72 from modules.common.simcount import SimCounter @@ -16,38 +16,50 @@ from modules.devices.openwb.openwb_flex.versions import kit_bat_version_factory +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class BatKitFlex(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, BatKitFlexSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(BatKitFlexSetup, component_config) - factory = kit_bat_version_factory( - self.component_config.configuration.version) - self.__client = factory(self.component_config.configuration.id, - tcp_client) - self.__tcp_client = tcp_client + def __init__(self, component_config: BatKitFlexSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + factory = kit_bat_version_factory(self.component_config.configuration.version) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.__client = factory(self.component_config.configuration.id, self.__tcp_client, self.fault_state) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self): # TCP-Verbindung schließen möglichst bevor etwas anderes gemacht wird, um im Fehlerfall zu verhindern, # dass offene Verbindungen den Modbus-Adapter blockieren. with self.__tcp_client: - if isinstance(self.__client, Sdm630_72): - _, power = self.__client.get_power() - power = power * -1 - else: - _, power = self.__client.get_power() - if isinstance(self.__client, Lovato) or isinstance(self.__client, Sdm120): - imported, exported = self.sim_counter.sim_count(power) + counter_state = self.__client.get_counter_state() + + power = counter_state.power + if isinstance(self.__client, Sdm630_72): + power = power * -1 + if isinstance(self.__client, Lovato) or isinstance(self.__client, Sdm120): + imported, exported = self.sim_counter.sim_count(power) + else: + imported = counter_state.imported + exported = counter_state.exported + + voltages = self.__client.get_voltages() + powers, power = self.__client.get_power() + + if isinstance(self.__client, Mpm3pm): + currents = [powers[i] / voltages[i] for i in range(3)] else: - imported = self.__client.get_imported() - exported = self.__client.get_exported() + currents = self.__client.get_currents() bat_state = BatState( + currents=currents, imported=imported, exported=exported, power=power diff --git a/packages/modules/devices/openwb/openwb_flex/consumption_counter.py b/packages/modules/devices/openwb/openwb_flex/consumption_counter.py index 21dda11082..01ec98933e 100644 --- a/packages/modules/devices/openwb/openwb_flex/consumption_counter.py +++ b/packages/modules/devices/openwb/openwb_flex/consumption_counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,22 +12,27 @@ from modules.devices.openwb.openwb_flex.versions import consumption_counter_factory +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class ConsumptionCounterFlex(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, ConsumptionCounterFlexSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(ConsumptionCounterFlexSetup, component_config) - factory = consumption_counter_factory( - self.component_config.configuration.type) - self.__client = factory(self.component_config.configuration.id, tcp_client) - self.__tcp_client = tcp_client + def __init__(self, component_config: ConsumptionCounterFlexSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + factory = consumption_counter_factory(self.component_config.configuration.type) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.__client = factory(self.component_config.configuration.id, self.__tcp_client, self.fault_state) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): + def update(self) -> None: with self.__tcp_client: voltages = self.__client.get_voltages() powers, power = self.__client.get_power() diff --git a/packages/modules/devices/openwb/openwb_flex/counter.py b/packages/modules/devices/openwb/openwb_flex/counter.py index b0ecc9301e..bfc25ed810 100644 --- a/packages/modules/devices/openwb/openwb_flex/counter.py +++ b/packages/modules/devices/openwb/openwb_flex/counter.py @@ -1,13 +1,10 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.lovato import Lovato from modules.common.mpm3pm import Mpm3pm from modules.common.b23 import B23 from modules.common.simcount import SimCounter @@ -16,53 +13,36 @@ from modules.devices.openwb.openwb_flex.versions import kit_counter_version_factory +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class EvuKitFlex(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, EvuKitFlexSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(EvuKitFlexSetup, component_config) - factory = kit_counter_version_factory( - self.component_config.configuration.version) - self.__client = factory(self.component_config.configuration.id, - tcp_client) - self.__tcp_client = tcp_client + def __init__(self, component_config: EvuKitFlexSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + factory = kit_counter_version_factory(self.component_config.configuration.version) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.__client = factory(self.component_config.configuration.id, self.__tcp_client, self.fault_state) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self): # TCP-Verbindung schließen möglichst bevor etwas anderes gemacht wird, um im Fehlerfall zu verhindern, # dass offene Verbindungen den Modbus-Adapter blockieren. with self.__tcp_client: - voltages = self.__client.get_voltages() - powers, power = self.__client.get_power() - frequency = self.__client.get_frequency() - power_factors = self.__client.get_power_factors() - - if isinstance(self.__client, Mpm3pm or B23): - imported = self.__client.get_imported() - exported = self.__client.get_exported() - else: - currents = self.__client.get_currents() + counter_state = self.__client.get_counter_state() if isinstance(self.__client, Mpm3pm or B23): - currents = [powers[i] / voltages[i] for i in range(3)] + counter_state.currents = [counter_state.powers[i] / counter_state.voltages[i] for i in range(3)] else: - if isinstance(self.__client, Lovato): - power = sum(powers) - imported, exported = self.sim_counter.sim_count(power) - counter_state = CounterState( - voltages=voltages, - currents=currents, - powers=powers, - power_factors=power_factors, - imported=imported, - exported=exported, - power=power, - frequency=frequency - ) + counter_state.imported, counter_state.exported = self.sim_counter.sim_count(counter_state.power) + self.store.set(counter_state) diff --git a/packages/modules/devices/openwb/openwb_flex/device.py b/packages/modules/devices/openwb/openwb_flex/device.py index d74764b4d0..351b019a14 100644 --- a/packages/modules/devices/openwb/openwb_flex/device.py +++ b/packages/modules/devices/openwb/openwb_flex/device.py @@ -1,7 +1,9 @@ #!/usr/bin/env python3 import logging +from pathlib import Path from typing import Iterable, Union +from helpermodules.utils.run_command import run_command from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater @@ -20,29 +22,40 @@ def create_device(device_config: Flex): + client = None + def create_bat_component(component_config: BatKitFlexSetup): - return BatKitFlex(device_config.id, component_config, client) + nonlocal client + return BatKitFlex(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: EvuKitFlexSetup): - return EvuKitFlex(device_config.id, component_config, client) + nonlocal client + return EvuKitFlex(component_config, device_id=device_config.id, client=client) def create_consumption_counter_component(component_config: ConsumptionCounterFlexSetup): - return ConsumptionCounterFlex(device_config.id, component_config, client) + nonlocal client + return ConsumptionCounterFlex(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: PvKitFlexSetup): - return PvKitFlex(device_config.id, component_config, client) + nonlocal client + return PvKitFlex(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[BatKitFlex, ConsumptionCounterFlex, EvuKitFlex, PvKitFlex]]): for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( bat=create_bat_component, consumption_counter=create_consumption_counter_component, diff --git a/packages/modules/devices/openwb/openwb_flex/inverter.py b/packages/modules/devices/openwb/openwb_flex/inverter.py index b1b68a47a7..1761cc01e6 100644 --- a/packages/modules/devices/openwb/openwb_flex/inverter.py +++ b/packages/modules/devices/openwb/openwb_flex/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -15,44 +14,47 @@ from modules.devices.openwb.openwb_flex.versions import kit_inverter_version_factory +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class PvKitFlex(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, PvKitFlexSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(PvKitFlexSetup, component_config) - factory = kit_inverter_version_factory( - self.component_config.configuration.version) - self.__client = factory(self.component_config.configuration.id, tcp_client) - self.__tcp_client = tcp_client + def __init__(self, component_config: PvKitFlexSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + factory = kit_inverter_version_factory(self.component_config.configuration.version) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.__client = factory(self.component_config.configuration.id, self.__tcp_client, self.fault_state) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.simulation = {} self.store = get_inverter_value_store(self.component_config.id) - self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self) -> None: """ liest die Werte des Moduls aus. """ with self.__tcp_client: - powers, power = self.__client.get_power() - - version = self.component_config.configuration.version - if version == 1: - power = sum(powers) - if power > 10: - power = power*-1 - currents = self.__client.get_currents() - - if isinstance(self.__client, Lovato) or isinstance(self.__client, Sdm120): - _, exported = self.sim_counter.sim_count(power) - else: - exported = self.__client.get_exported() + counter_state = self.__client.get_counter_state() + + power = counter_state.power + version = self.component_config.configuration.version + if version == 1: + power = sum(counter_state.powers) + if power > 10: + power = power*-1 + if isinstance(self.__client, Lovato) or isinstance(self.__client, Sdm120): + _, exported = self.sim_counter.sim_count(power) + else: + exported = counter_state.exported inverter_state = InverterState( power=power, exported=exported, - currents=currents + currents=counter_state.currents ) self.store.set(inverter_state) diff --git a/packages/modules/devices/openwb/openwb_pv_kit/device.py b/packages/modules/devices/openwb/openwb_pv_kit/device.py index e2704eeee2..f2cd8cf71b 100644 --- a/packages/modules/devices/openwb/openwb_pv_kit/device.py +++ b/packages/modules/devices/openwb/openwb_pv_kit/device.py @@ -1,6 +1,8 @@ import logging +from pathlib import Path from typing import Iterable +from helpermodules.utils.run_command import run_command from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext @@ -12,21 +14,30 @@ def create_device(device_config: PvKitSetup): + client = None + def create_inverter_component(component_config: PvKitInverterSetup): - return inverter.PvKit(device_config.id, component_config, client) + nonlocal client + return inverter.PvKit(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[inverter.PvKit]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_("192.168.193.13", 8899) - except Exception: - log.exception("Fehler in create_device") + + def error_handler(): + run_command(f"{Path(__file__).resolve().parents[4]}/modules/common/restart_protoss_admin") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, + error_handler=error_handler, component_factory=ComponentFactoryByType( inverter=create_inverter_component, ), diff --git a/packages/modules/devices/openwb/openwb_pv_kit/inverter.py b/packages/modules/devices/openwb/openwb_pv_kit/inverter.py index 4659b68bc5..5de66dcd22 100644 --- a/packages/modules/devices/openwb/openwb_pv_kit/inverter.py +++ b/packages/modules/devices/openwb/openwb_pv_kit/inverter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from typing import Union +from typing import Union, TypedDict, Any from modules.common import modbus from modules.common.abstract_device import AbstractInverter @@ -10,12 +10,21 @@ from modules.devices.openwb.openwb_pv_kit.config import PvKitInverterSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class PvKit(PvKitFlex, AbstractInverter): def __init__(self, - device_id: int, component_config: Union[EvuKitInverterSetup, PvKitInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: + **kwargs: Any) -> None: self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] version = self.component_config.configuration.version if version == 0 or version == 1: id = 8 @@ -24,7 +33,10 @@ def __init__(self, else: raise ValueError("Version "+str(version) + " unbekannt.") - super().__init__(device_id, convert_to_flex_setup(self.component_config, id), tcp_client) + super().__init__(convert_to_flex_setup(self.component_config, id), + device_id=self.__device_id, + client=self.__tcp_client) + super().initialize() component_descriptor = ComponentDescriptor(configuration_factory=PvKitInverterSetup) diff --git a/packages/modules/devices/orno/__init__ .py b/packages/modules/devices/orno/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/orno/orno/__init__.py b/packages/modules/devices/orno/orno/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/orno/orno/config.py b/packages/modules/devices/orno/orno/config.py new file mode 100644 index 0000000000..e767d3a373 --- /dev/null +++ b/packages/modules/devices/orno/orno/config.py @@ -0,0 +1,42 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup + +from ..vendor import vendor_descriptor + + +@auto_str +class OrnoConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502) -> None: + self.ip_address = ip_address + self.port = port + + +@auto_str +class Orno: + def __init__(self, + name: str = "Orno WE-514", + type: str = "orno", + id: int = 0, + configuration: OrnoConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or OrnoConfiguration() + + +@auto_str +class OrnoCounterConfiguration: + def __init__(self, modbus_id: int = 1): + self.modbus_id = modbus_id + + +@auto_str +class OrnoCounterSetup(ComponentSetup[OrnoCounterConfiguration]): + def __init__(self, + name: str = "Orno Zähler", + type: str = "counter", + id: int = 0, + configuration: OrnoCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or OrnoCounterConfiguration()) diff --git a/packages/modules/devices/orno/orno/counter.py b/packages/modules/devices/orno/orno/counter.py new file mode 100644 index 0000000000..6f34f1ede7 --- /dev/null +++ b/packages/modules/devices/orno/orno/counter.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_counter_value_store +from modules.devices.orno.orno.config import OrnoCounterSetup + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + +class OrnoCounter(AbstractCounter): + def __init__(self, component_config: OrnoCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + power = self.client.read_holding_registers( + 0x141, ModbusDataType.INT_32, unit=self.component_config.configuration.modbus_id) + imported = self.client.read_holding_registers( + 0xA001, ModbusDataType.INT_32, unit=self.component_config.configuration.modbus_id) * 10 + + counter_state = CounterState( + imported=imported, + exported=0, + power=power, + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=OrnoCounterSetup) diff --git a/packages/modules/devices/orno/orno/device.py b/packages/modules/devices/orno/orno/device.py new file mode 100644 index 0000000000..169bc971fd --- /dev/null +++ b/packages/modules/devices/orno/orno/device.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +import logging +from pymodbus.transaction import ModbusRtuFramer +from typing import Iterable + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.orno.orno.config import Orno, OrnoCounterSetup +from modules.devices.orno.orno.counter import OrnoCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Orno): + client = None + + def create_counter_component(component_config: OrnoCounterSetup): + nonlocal client + return OrnoCounter(component_config, client=client) + + def update_components(components: Iterable[OrnoCounter]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, + device_config.configuration.port, framer=ModbusRtuFramer) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Orno) diff --git a/packages/modules/devices/orno/vendor.py b/packages/modules/devices/orno/vendor.py new file mode 100644 index 0000000000..ba5dcd3586 --- /dev/null +++ b/packages/modules/devices/orno/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Orno" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/powerdog/powerdog/counter.py b/packages/modules/devices/powerdog/powerdog/counter.py index ca83073f5d..9aa98760c6 100644 --- a/packages/modules/devices/powerdog/powerdog/counter.py +++ b/packages/modules/devices/powerdog/powerdog/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -16,21 +15,26 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class PowerdogCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, PowerdogCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(PowerdogCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: PowerdogCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, inverter_power: float): + def update(self, inverter_power: float) -> None: with self.__tcp_client: if self.component_config.configuration.position_evu: export_power = self.__tcp_client.read_input_registers( diff --git a/packages/modules/devices/powerdog/powerdog/device.py b/packages/modules/devices/powerdog/powerdog/device.py index 697320a827..901b8f0d2e 100644 --- a/packages/modules/devices/powerdog/powerdog/device.py +++ b/packages/modules/devices/powerdog/powerdog/device.py @@ -4,7 +4,6 @@ from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.powerdog.powerdog.config import Powerdog, PowerdogCounterSetup, PowerdogInverterSetup from modules.devices.powerdog.powerdog.counter import PowerdogCounter @@ -14,19 +13,29 @@ def create_device(device_config: Powerdog): + client = None + def create_counter_component(component_config: PowerdogCounterSetup): - return PowerdogCounter(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return PowerdogCounter(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def create_inverter_component(component_config: PowerdogInverterSetup): - return PowerdogInverter(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return PowerdogInverter(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def update_components(components: Iterable[Union[PowerdogCounter, PowerdogInverter]]): + nonlocal client with client: if len(components) == 1: for component in components: if isinstance(component, PowerdogInverter): - with SingleComponentUpdateContext(component.fault_state): - component.update() + component.update() else: raise Exception( "Wenn ein EVU-Zähler konfiguriert wurde, muss immer auch ein WR konfiguriert sein.") @@ -47,12 +56,13 @@ def update_components(components: Iterable[Union[PowerdogCounter, PowerdogInvert + "wurden." ) - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, inverter=create_inverter_component, diff --git a/packages/modules/devices/powerdog/powerdog/inverter.py b/packages/modules/devices/powerdog/powerdog/inverter.py index 93dfb3d96b..83abb5faeb 100644 --- a/packages/modules/devices/powerdog/powerdog/inverter.py +++ b/packages/modules/devices/powerdog/powerdog/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -16,16 +15,21 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class PowerdogInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, PowerdogInverterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(PowerdogInverterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: PowerdogInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/powerfox/powerfox/counter.py b/packages/modules/devices/powerfox/powerfox/counter.py index 33edb6ec65..ee572767d2 100644 --- a/packages/modules/devices/powerfox/powerfox/counter.py +++ b/packages/modules/devices/powerfox/powerfox/counter.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union from requests import Session - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -16,14 +13,15 @@ class PowerfoxCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, PowerfoxCounterSetup]) -> None: - self.component_config = dataclass_from_dict(PowerfoxCounterSetup, component_config) + def __init__(self, component_config: PowerfoxCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self, session: Session) -> None: - response = session.get('https://backend.powerfox.energy/api/2.0/my/'+self.component_config.configuration.id + + response = session.get('https://backend.powerfox.energy/api/2.0/my/' + self.component_config.configuration.id + '/current', timeout=3).json() self.store.set(CounterState( diff --git a/packages/modules/devices/powerfox/powerfox/device.py b/packages/modules/devices/powerfox/powerfox/device.py index 472b00e5d2..216e992c37 100644 --- a/packages/modules/devices/powerfox/powerfox/device.py +++ b/packages/modules/devices/powerfox/powerfox/device.py @@ -15,16 +15,22 @@ def create_device(device_config: Powerfox): + session = None + def create_counter_component(component_config: PowerfoxCounterSetup): return PowerfoxCounter(component_config) def create_inverter_component(component_config: PowerfoxInverterSetup): return PowerfoxInverter(component_config) - session = get_http_session() - session.auth = (device_config.configuration.user, device_config.configuration.password) + def initializer(): + nonlocal session + session = get_http_session() + session.auth = (device_config.configuration.user, device_config.configuration.password) + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, inverter=create_inverter_component, diff --git a/packages/modules/devices/powerfox/powerfox/inverter.py b/packages/modules/devices/powerfox/powerfox/inverter.py index 808f27e3fa..646daf97b4 100644 --- a/packages/modules/devices/powerfox/powerfox/inverter.py +++ b/packages/modules/devices/powerfox/powerfox/inverter.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union from requests import Session - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -16,14 +13,15 @@ class PowerfoxInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, PowerfoxInverterSetup]) -> None: - self.component_config = dataclass_from_dict(PowerfoxInverterSetup, component_config) + def __init__(self, component_config: PowerfoxInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self, session: Session) -> None: - response = session.get('https://backend.powerfox.energy/api/2.0/my/'+self.component_config.configuration.id + + response = session.get('https://backend.powerfox.energy/api/2.0/my/' + self.component_config.configuration.id + '/current', timeout=3).json() self.store.set(InverterState( diff --git a/packages/modules/devices/qcells/qcells/bat.py b/packages/modules/devices/qcells/qcells/bat.py index ebef8b12d0..f381a7894f 100644 --- a/packages/modules/devices/qcells/qcells/bat.py +++ b/packages/modules/devices/qcells/qcells/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -11,21 +10,28 @@ from modules.devices.qcells.qcells.config import QCellsBatSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class QCellsBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, QCellsBatSetup], - modbus_id: int) -> None: - self.__modbus_id = modbus_id - self.component_config = dataclass_from_dict(QCellsBatSetup, component_config) + def __init__(self, component_config: QCellsBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - power = client.read_input_registers(0x0016, ModbusDataType.INT_16, unit=self.__modbus_id) - soc = client.read_input_registers(0x001C, ModbusDataType.UINT_16, unit=self.__modbus_id) - imported = client.read_input_registers( + def update(self) -> None: + power = self.client.read_input_registers(0x0016, ModbusDataType.INT_16, unit=self.__modbus_id) + soc = self.client.read_input_registers(0x001C, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported = self.client.read_input_registers( 0x0021, ModbusDataType.UINT_16, unit=self.__modbus_id) * 100 - exported = client.read_input_registers( + exported = self.client.read_input_registers( 0x001D, ModbusDataType.UINT_16, unit=self.__modbus_id) * 100 bat_state = BatState( diff --git a/packages/modules/devices/qcells/qcells/counter.py b/packages/modules/devices/qcells/qcells/counter.py index 9af9320be6..6e5d04eabc 100644 --- a/packages/modules/devices/qcells/qcells/counter.py +++ b/packages/modules/devices/qcells/qcells/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -12,32 +11,39 @@ from modules.devices.qcells.qcells.config import QCellsCounterSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class QCellsCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, QCellsCounterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(QCellsCounterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: QCellsCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): - power = client.read_input_registers(0x0046, ModbusDataType.INT_32, wordorder=Endian.Little, - unit=self.__modbus_id) * -1 - frequency = client.read_input_registers( + def update(self) -> None: + power = self.client.read_input_registers(0x0046, ModbusDataType.INT_32, wordorder=Endian.Little, + unit=self.__modbus_id) * -1 + frequency = self.client.read_input_registers( 0x0007, ModbusDataType.UINT_16, unit=self.__modbus_id) / 100 try: - powers = [-value for value in client.read_input_registers( + powers = [-value for value in self.client.read_input_registers( 0x0082, [ModbusDataType.INT_32] * 3, wordorder=Endian.Little, unit=self.__modbus_id )] except Exception: powers = None try: - voltages = [client.read_input_registers( + voltages = [self.client.read_input_registers( 0x006A, ModbusDataType.UINT_16, unit=self.__modbus_id - ) / 10, client.read_input_registers( + ) / 10, self.client.read_input_registers( 0x006E, ModbusDataType.UINT_16, unit=self.__modbus_id - ) / 10, client.read_input_registers( + ) / 10, self.client.read_input_registers( 0x0072, ModbusDataType.UINT_16, unit=self.__modbus_id ) / 10] if voltages[0] < 1: @@ -49,7 +55,7 @@ def update(self, client: ModbusTcpClient_): except Exception: voltages = [230, 230, 230] exported, imported = [value * 10 - for value in client.read_input_registers( + for value in self.client.read_input_registers( 0x0048, [ModbusDataType.UINT_32] * 2, wordorder=Endian.Little, unit=self.__modbus_id )] diff --git a/packages/modules/devices/qcells/qcells/device.py b/packages/modules/devices/qcells/qcells/device.py index f3228596ed..a5c9b8eb18 100644 --- a/packages/modules/devices/qcells/qcells/device.py +++ b/packages/modules/devices/qcells/qcells/device.py @@ -15,31 +15,34 @@ def create_device(device_config: QCells): + client = None + def create_bat_component(component_config: QCellsBatSetup): - return QCellsBat(component_config, - device_config.configuration.modbus_id) + nonlocal client + return QCellsBat(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def create_counter_component(component_config: QCellsCounterSetup): - return QCellsCounter(component_config, - device_config.configuration.modbus_id) + nonlocal client + return QCellsCounter(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def create_inverter_component(component_config: QCellsInverterSetup): - return QCellsInverter(component_config, - device_config.configuration.modbus_id) + nonlocal client + return QCellsInverter(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def update_components(components: Iterable[Union[QCellsBat, QCellsCounter, QCellsInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/qcells/qcells/inverter.py b/packages/modules/devices/qcells/qcells/inverter.py index 0d2d5dab27..4d0a638145 100644 --- a/packages/modules/devices/qcells/qcells/inverter.py +++ b/packages/modules/devices/qcells/qcells/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,25 +11,32 @@ from modules.devices.qcells.qcells.config import QCellsInverterSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class QCellsInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, QCellsInverterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(QCellsInverterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: QCellsInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - power_string1 = (client.read_input_registers( + def update(self) -> None: + power_string1 = (self.client.read_input_registers( 0x0003, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) * \ - (client.read_input_registers(0x0005, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) - power_string2 = (client.read_input_registers( + (self.client.read_input_registers(0x0005, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) + power_string2 = (self.client.read_input_registers( 0x0004, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) * \ - (client.read_input_registers(0x0006, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) + (self.client.read_input_registers(0x0006, ModbusDataType.INT_16, unit=self.__modbus_id) / 10) power = (power_string1 + power_string2) * -1 - exported = client.read_input_registers(0x0094, ModbusDataType.UINT_32, wordorder=Endian.Little, - unit=self.__modbus_id) * 100 + exported = self.client.read_input_registers(0x0094, ModbusDataType.UINT_32, wordorder=Endian.Little, + unit=self.__modbus_id) * 100 inverter_state = InverterState( power=power, diff --git a/packages/modules/devices/rct/rct/bat.py b/packages/modules/devices/rct/rct/bat.py index f0f7673471..5a5fd7643b 100644 --- a/packages/modules/devices/rct/rct/bat.py +++ b/packages/modules/devices/rct/rct/bat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -14,7 +14,9 @@ class RctBat(AbstractBat): def __init__(self, component_config: RctBatSetup) -> None: - self.component_config = dataclass_from_dict(RctBatSetup, component_config) + self.component_config = component_config + + def initialize(self) -> None: self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/rct/rct/counter.py b/packages/modules/devices/rct/rct/counter.py index 7060ee900f..9f75bafbf4 100644 --- a/packages/modules/devices/rct/rct/counter.py +++ b/packages/modules/devices/rct/rct/counter.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -14,7 +14,9 @@ class RctCounter(AbstractCounter): def __init__(self, component_config: RctCounterSetup) -> None: - self.component_config = dataclass_from_dict(RctCounterSetup, component_config) + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/rct/rct/inverter.py b/packages/modules/devices/rct/rct/inverter.py index 8255a95295..026fa3de46 100644 --- a/packages/modules/devices/rct/rct/inverter.py +++ b/packages/modules/devices/rct/rct/inverter.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,7 +10,9 @@ class RctInverter(AbstractInverter): def __init__(self, component_config: RctInverterSetup) -> None: - self.component_config = dataclass_from_dict(RctInverterSetup, component_config) + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/rct/rct/rct_lib.py b/packages/modules/devices/rct/rct/rct_lib.py index 9023698e52..2b92c065b5 100644 --- a/packages/modules/devices/rct/rct/rct_lib.py +++ b/packages/modules/devices/rct/rct/rct_lib.py @@ -181,7 +181,7 @@ def __init__(self, command=0, address=0, frame_type=FRAME_TYPE_STANDARD): # add a rct_id item to a frame def add(self, item): - if type(item) == rct_id: + if isinstance(item, rct_id): if len(item.name) > self.name_len: self.name_len = len(item.name) if len(item.desc) > self.desc_len: @@ -534,9 +534,9 @@ def read(self, idList): # add all ids to a new frame def read_setup_frame(self, id): frame = Frame(cmd_read) - if type(id) == list: + if isinstance(id, list): for item in id: - if type(item) == rct_id: + if isinstance(item, rct_id): frame.add(item) else: obj = self.find_by_id(item) diff --git a/packages/modules/devices/saxpower/saxpower/bat.py b/packages/modules/devices/saxpower/saxpower/bat.py index 9cfc481163..887fe74c84 100644 --- a/packages/modules/devices/saxpower/saxpower/bat.py +++ b/packages/modules/devices/saxpower/saxpower/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,16 +12,21 @@ from modules.devices.saxpower.saxpower.config import SaxpowerBatSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class SaxpowerBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, SaxpowerBatSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SaxpowerBatSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: SaxpowerBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/saxpower/saxpower/device.py b/packages/modules/devices/saxpower/saxpower/device.py index 5078f3c887..6914ac931a 100644 --- a/packages/modules/devices/saxpower/saxpower/device.py +++ b/packages/modules/devices/saxpower/saxpower/device.py @@ -13,21 +13,29 @@ def create_device(device_config: Saxpower): + client = None + def create_bat_component(component_config: SaxpowerBatSetup): - return SaxpowerBat(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return SaxpowerBat(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def update_components(components: Iterable[SaxpowerBat]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, ), diff --git a/packages/modules/devices/shelly/shelly/bat.py b/packages/modules/devices/shelly/shelly/bat.py index 7d4fdc91ee..abbcb15ab3 100644 --- a/packages/modules/devices/shelly/shelly/bat.py +++ b/packages/modules/devices/shelly/shelly/bat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Optional +from typing import Optional, TypedDict, Any from modules.common import req from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,29 +13,35 @@ log = logging.getLogger(__name__) -class ShellyBat(AbstractBat): +class KwargsDict(TypedDict): + device_id: int + ip_address: str + factor: int + generation: Optional[int] + - def __init__(self, - device_id: int, - component_config: ShellyBatSetup, - address: str, - factor: int, - generation: Optional[int]) -> None: +class ShellyBat(AbstractBat): + def __init__(self, component_config: ShellyBatSetup, **kwargs: Any) -> None: self.component_config = component_config - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="speicher") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.address: str = self.kwargs['ip_address'] + self.factor: int = self.kwargs['factor'] + self.generation: Optional[int] = self.kwargs['generation'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.address = address - self.factor = factor - self.generation = generation - def total_power_from_shelly(self) -> int: - total = 0 + def update(self) -> None: + power = 0 if self.generation == 1: status_url = "http://" + self.address + "/status" else: status_url = "http://" + self.address + "/rpc/Shelly.GetStatus" status = req.get_http_session().get(status_url, timeout=3).json() + try: if self.generation == 1: if 'meters' in status: @@ -44,28 +50,34 @@ def total_power_from_shelly(self) -> int: meters = status['emeters'] # shellyEM & shelly3EM # shellyEM has one meter, shelly3EM has three meters: for meter in meters: - total = total + meter['power'] + power = power + meter['power'] + currents = [0, 0, 0] else: - if 'switch:0' in status: - total = status['switch:0']['apower'] + if 'switch:0' in status and 'apower' in status['switch:0']: + power = status['switch:0']['apower'] + currents = [status['switch:0']['current'], 0, 0] + elif 'em1:0' in status: + power = status['em1:0']['act_power'] # shelly Pro EM Gen 2 + currents = [status['em1:0']['current'], 0, 0] elif 'pm1:0' in status: - total = status['pm1:0']['apower'] # shelly PM Mini Gen 3 + power = status['pm1:0']['apower'] # shelly PM Mini Gen 3 + currents = [status['pm1:0']['current'], 0, 0] else: - total = status['em:0']['total_act_power'] # shelly Pro3EM - except KeyError: - log.exception("unsupported shelly device?") - finally: - return int(total) + power = status['em:0']['total_act_power'] # shelly Pro3EM + currents = [meter[f'{i}_current'] for i in 'abc'] - def update(self) -> None: - bat = self.total_power_from_shelly() * self.factor - imported, exported = self.sim_counter.sim_count(bat) - bat_state = BatState( - power=bat, - imported=imported, - exported=exported - ) - self.store.set(bat_state) + power = power * self.factor + imported, exported = self.sim_counter.sim_count(power) + bat_state = BatState( + power=power, + imported=imported, + exported=exported + ) + if 'currents' in locals(): + bat_state.currents = currents + self.store.set(bat_state) + except KeyError: + log.exception("unsupported shelly device.") component_descriptor = ComponentDescriptor(configuration_factory=ShellyBatSetup) diff --git a/packages/modules/devices/shelly/shelly/counter.py b/packages/modules/devices/shelly/shelly/counter.py index 6bbb8900a8..3c4ebadf41 100644 --- a/packages/modules/devices/shelly/shelly/counter.py +++ b/packages/modules/devices/shelly/shelly/counter.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Optional +from typing import Optional, TypedDict, Any from modules.common import req from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,21 +13,26 @@ log = logging.getLogger(__name__) -class ShellyCounter(AbstractCounter): +class KwargsDict(TypedDict): + device_id: int + ip_address: str + factor: int + generation: Optional[int] + - def __init__(self, - device_id: int, - component_config: ShellyCounterSetup, - address: str, - factor: int, - generation: Optional[int]) -> None: +class ShellyCounter(AbstractCounter): + def __init__(self, component_config: ShellyCounterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.address: str = self.kwargs['ip_address'] + self.factor: int = self.kwargs['factor'] + self.generation: Optional[int] = self.kwargs['generation'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.address = address - self.factor = factor - self.generation = generation def update(self) -> None: power = 0 @@ -58,13 +63,32 @@ def update(self) -> None: power_factors = [meter[f'{i}_pf'] for i in 'abc'] power = meter['total_act_power'] * self.factor # Shelly MiniPM G3 - else: - meter = status['pm1:0'] + elif "pm1:0" in status: log.debug("single phase shelly") + meter = status['pm1:0'] voltages = [meter['voltage'], 0, 0] currents = [meter['current'], 0, 0] power = meter['apower'] frequency = meter['freq'] + powers = [meter['apower'], 0, 0] + elif 'switch:0' in status and 'apower' in status['switch:0']: + log.debug("single phase shelly") + meter = status['switch:0'] + power = meter['apower'] + voltages = [meter['voltage'], 0, 0] + currents = [meter['current'], 0, 0] + frequency = meter['freq'] + power_factors = [meter['pf'], 0, 0] + powers = [meter['apower'], 0, 0] + else: + log.debug("single phase shelly") + meter = status['em1:0'] + power = meter['act_power'] # shelly Pro EM Gen 2 + voltages = [meter['voltage'], 0, 0] + currents = [meter['current'], 0, 0] + frequency = meter['freq'] + power_factors = [meter['pf'], 0, 0] + powers = [meter['act_power'], 0, 0] imported, exported = self.sim_counter.sim_count(power) diff --git a/packages/modules/devices/shelly/shelly/device.py b/packages/modules/devices/shelly/shelly/device.py index 6b207bddaf..47097ad383 100644 --- a/packages/modules/devices/shelly/shelly/device.py +++ b/packages/modules/devices/shelly/shelly/device.py @@ -1,51 +1,57 @@ #!/usr/bin/env python3 import logging -from typing import List -import os from modules.common import req -from helpermodules.cli import run_using_positional_cli_args from modules.common.abstract_device import DeviceDescriptor -from modules.common.configurable_device import (ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater) +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, IndependentComponentUpdater from modules.devices.shelly.shelly.inverter import ShellyInverter from modules.devices.shelly.shelly.bat import ShellyBat from modules.devices.shelly.shelly.counter import ShellyCounter -from modules.devices.shelly.shelly.config import Shelly, ShellyConfiguration -from modules.devices.shelly.shelly.config import ShellyInverterSetup, ShellyInverterConfiguration -from modules.devices.shelly.shelly.config import ShellyBatSetup -from modules.devices.shelly.shelly.config import ShellyCounterSetup +from modules.devices.shelly.shelly.config import Shelly, ShellyInverterSetup, ShellyBatSetup, ShellyCounterSetup log = logging.getLogger(__name__) -def get_device_generation(address: str) -> int: - url = "http://" + address + "/shelly" +def create_device(device_config: Shelly) -> ConfigurableDevice: generation = 1 - device_info = req.get_http_session().get(url, timeout=3).json() - if 'gen' in device_info: # gen 2+ - generation = int(device_info['gen']) - return generation - -def create_device(device_config: Shelly) -> ConfigurableDevice: def create_counter_component(component_config: ShellyCounterSetup) -> ShellyCounter: - return ShellyCounter(device_config.id, component_config, device_config.configuration.ip_address, - device_config.configuration.factor, - get_device_generation(device_config.configuration.ip_address)) + nonlocal generation + return ShellyCounter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + factor=device_config.configuration.factor, + generation=generation) def create_inverter_component(component_config: ShellyInverterSetup) -> ShellyInverter: - return ShellyInverter(device_config.id, component_config, device_config.configuration.ip_address, - device_config.configuration.factor, - get_device_generation(device_config.configuration.ip_address)) + nonlocal generation + return ShellyInverter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + factor=device_config.configuration.factor, + generation=generation) def create_bat_component(component_config: ShellyBatSetup) -> ShellyBat: - return ShellyBat(device_config.id, component_config, device_config.configuration.ip_address, - device_config.configuration.factor, - get_device_generation(device_config.configuration.ip_address)) + nonlocal generation + return ShellyBat(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + factor=device_config.configuration.factor, + generation=generation) + + def initializer() -> None: + nonlocal generation + device_info = req.get_http_session().get( + f"http://{device_config.configuration.ip_address}/shelly", timeout=3).json() + if 'gen' in device_info: # gen 2+ + generation = int(device_info['gen']) return ConfigurableDevice( device_config=device_config, + initializer=initializer, + # wenn das Auslesen nicht klappt, konnte evlt beim Start die Generation nicht ermittelt werden + error_handler=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, inverter=create_inverter_component, @@ -55,41 +61,4 @@ def create_bat_component(component_config: ShellyBatSetup) -> ShellyBat: ) -def run_device_legacy(device_config: Shelly, - component_config: ShellyInverterSetup) -> None: - device = create_device(device_config) - device.add_component(component_config) - log.debug("Shelly Configuration: %s, Component Configuration: %s", device_config, component_config) - device.update() - - -def create_legacy_device_config(address: str, generation: int, - num: int) -> Shelly: - device_config = Shelly(configuration=ShellyConfiguration(ip_address=address, generation=generation), id=num) - log.debug("Config: %s", device_config.configuration) - return device_config - - -def read_legacy_inverter(address: str, num: int) -> None: - component_config = ShellyInverterSetup(configuration=ShellyInverterConfiguration()) - component_config.id = num - generation = 1 - generation_file_name = '/var/www/html/openWB/ramdisk/shelly_wr_ret.' + address + '_shelly_infog' - # ToDo: remove hardcoded path to ramdisk! - if os.path.isfile(generation_file_name): - with open(generation_file_name, 'r') as file: - generation = int(file.read()) - else: - generation = get_device_generation(address) - with open(generation_file_name, 'w') as file: - file.write(str(generation)) - run_device_legacy(create_legacy_device_config(address, generation, - num), component_config) - - -def main(argv: List[str]) -> None: - run_using_positional_cli_args( - {"inverter": read_legacy_inverter}, argv) - - device_descriptor = DeviceDescriptor(configuration_factory=Shelly) diff --git a/packages/modules/devices/shelly/shelly/inverter.py b/packages/modules/devices/shelly/shelly/inverter.py index 81d67e4aad..cc57a0959f 100644 --- a/packages/modules/devices/shelly/shelly/inverter.py +++ b/packages/modules/devices/shelly/shelly/inverter.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Optional +from typing import Optional, TypedDict, Any from modules.common import req from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -13,24 +13,29 @@ log = logging.getLogger(__name__) -class ShellyInverter(AbstractInverter): +class KwargsDict(TypedDict): + device_id: int + ip_address: str + factor: int + generation: Optional[int] + - def __init__(self, - device_id: int, - component_config: ShellyInverterSetup, - address: str, - factor: int, - generation: Optional[int]) -> None: +class ShellyInverter(AbstractInverter): + def __init__(self, component_config: ShellyInverterSetup, **kwargs: Any) -> None: self.component_config = component_config - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="pv") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.address: str = self.kwargs['ip_address'] + self.factor: int = self.kwargs['factor'] + self.generation: Optional[int] = self.kwargs['generation'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.address = address - self.factor = factor - self.generation = generation - def total_power_from_shelly(self) -> int: - total = 0 + def update(self) -> None: + power = 0 if self.generation == 1: status_url = "http://" + self.address + "/status" else: @@ -44,27 +49,32 @@ def total_power_from_shelly(self) -> int: meters = status['emeters'] # shellyEM & shelly3EM # shellyEM has one meter, shelly3EM has three meters: for meter in meters: - total = total + meter['power'] + power = power + meter['power'] else: - if 'switch:0' in status: - total = status['switch:0']['apower'] + if 'switch:0' in status and 'apower' in status['switch:0']: + power = status['switch:0']['apower'] + currents = [status['switch:0']['current'], 0, 0] + elif 'em1:0' in status: + power = status['em1:0']['act_power'] # shelly Pro EM Gen 2 + currents = [status['em1:0']['current'], 0, 0] elif 'pm1:0' in status: - total = status['pm1:0']['apower'] # shelly PM Mini Gen 3 + power = status['pm1:0']['apower'] # shelly PM Mini Gen 3 + currents = [status['pm1:0']['current'], 0, 0] else: - total = status['em:0']['total_act_power'] # shelly Pro3EM - except KeyError: - log.exception("unsupported shelly device?") - finally: - return int(total) + power = status['em:0']['total_act_power'] # shelly Pro3EM + currents = [meter[f'{i}_current'] for i in 'abc'] - def update(self) -> None: - pv = self.total_power_from_shelly() * self.factor - _, pv_exported = self.sim_counter.sim_count(pv) - inverter_state = InverterState( - power=pv, - exported=pv_exported - ) - self.store.set(inverter_state) + power = power * self.factor + _, exported = self.sim_counter.sim_count(power) + inverter_state = InverterState( + power=power, + exported=exported + ) + if 'currents' in locals(): + inverter_state.currents = currents + self.store.set(inverter_state) + except KeyError: + log.exception("unsupported shelly device.") component_descriptor = ComponentDescriptor(configuration_factory=ShellyInverterSetup) diff --git a/packages/modules/devices/shelly/shelly/shelly_test.py b/packages/modules/devices/shelly/shelly/shelly_test.py index 8a07f69809..8be2aa50e5 100644 --- a/packages/modules/devices/shelly/shelly/shelly_test.py +++ b/packages/modules/devices/shelly/shelly/shelly_test.py @@ -45,7 +45,8 @@ def test_counter_shelly_minipm_g3(monkeypatch, requests_mock: requests_mock.mock requests_mock.get(f"http://{SAMPLE_IP}/rpc/Shelly.GetStatus", json=DATA_MINPM_G3) mock_counter_value_store = Mock() monkeypatch.setattr(counter, "get_counter_value_store", Mock(return_value=mock_counter_value_store)) - c = counter.ShellyCounter(0, ShellyCounterSetup(), SAMPLE_IP, 1, 2) + c = counter.ShellyCounter(ShellyCounterSetup(), device_id=0, ip_address=SAMPLE_IP, factor=1, generation=2) + c.initialize() # execution c.update() @@ -55,4 +56,4 @@ def test_counter_shelly_minipm_g3(monkeypatch, requests_mock: requests_mock.mock SAMPLE_COUNTER_STATE = CounterState(voltages=[230.9, 0, 0], power=230, currents=[ - 1, 0, 0], frequency=51, imported=100, exported=200) + 1, 0, 0], frequency=51, imported=100, exported=200, powers=[230, 0, 0]) diff --git a/packages/modules/devices/siemens/siemens/bat.py b/packages/modules/devices/siemens/siemens/bat.py index ce974fbdde..594856e24a 100644 --- a/packages/modules/devices/siemens/siemens/bat.py +++ b/packages/modules/devices/siemens/siemens/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,16 +12,21 @@ from modules.devices.siemens.siemens.config import SiemensBatSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class SiemensBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, SiemensBatSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SiemensBatSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: SiemensBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/siemens/siemens/counter.py b/packages/modules/devices/siemens/siemens/counter.py index 00e873c4b8..499692cd44 100644 --- a/packages/modules/devices/siemens/siemens/counter.py +++ b/packages/modules/devices/siemens/siemens/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,22 +12,26 @@ from modules.devices.siemens.siemens.config import SiemensCounterSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class SiemensCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SiemensCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SiemensCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: SiemensCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): - + def update(self) -> None: with self.__tcp_client: power = self.__tcp_client.read_holding_registers(14, ModbusDataType.INT_32, unit=self.__modbus_id) diff --git a/packages/modules/devices/siemens/siemens/device.py b/packages/modules/devices/siemens/siemens/device.py index 9689db1312..667af3607e 100644 --- a/packages/modules/devices/siemens/siemens/device.py +++ b/packages/modules/devices/siemens/siemens/device.py @@ -18,27 +18,43 @@ def create_device(device_config: Siemens): + client = None + def create_bat_component(component_config: SiemensBatSetup): - return SiemensBat(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return SiemensBat(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def create_counter_component(component_config: SiemensCounterSetup): - return SiemensCounter(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return SiemensCounter(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def create_inverter_component(component_config: SiemensInverterSetup): - return SiemensInverter(device_config.id, component_config, client, device_config.configuration.modbus_id) + nonlocal client + return SiemensInverter(component_config, + device_id=device_config.id, + client=client, + modbus_id=device_config.configuration.modbus_id) def update_components(components: Iterable[siemens_component_classes]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/siemens/siemens/inverter.py b/packages/modules/devices/siemens/siemens/inverter.py index 93307209bb..de765d5ccd 100644 --- a/packages/modules/devices/siemens/siemens/inverter.py +++ b/packages/modules/devices/siemens/siemens/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -13,16 +12,21 @@ from modules.devices.siemens.siemens.config import SiemensInverterSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + modbus_id: int + + class SiemensInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SiemensInverterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SiemensInverterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: SiemensInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/siemens/siemens_sentron/bat.py b/packages/modules/devices/siemens/siemens_sentron/bat.py new file mode 100644 index 0000000000..b1a5d39a0c --- /dev/null +++ b/packages/modules/devices/siemens/siemens_sentron/bat.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common import modbus +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.store import get_bat_value_store +from modules.devices.siemens.siemens_sentron.config import SiemensSentronBatSetup + + +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + modbus_id: int + + +class SiemensSentronBat(AbstractBat): + def __init__(self, component_config: SiemensSentronBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + with self.__tcp_client: + power = self.__tcp_client.read_holding_registers(65, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * -1 + imported = self.__tcp_client.read_holding_registers(801, ModbusDataType.FLOAT_64, unit=self.__modbus_id) + exported = self.__tcp_client.read_holding_registers(809, ModbusDataType.FLOAT_64, unit=self.__modbus_id) + + bat_state = BatState( + imported=imported, + exported=exported, + power=power + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SiemensSentronBatSetup) diff --git a/packages/modules/devices/siemens/siemens_sentron/config.py b/packages/modules/devices/siemens/siemens_sentron/config.py index 11f23c0b8e..b610cf47df 100644 --- a/packages/modules/devices/siemens/siemens_sentron/config.py +++ b/packages/modules/devices/siemens/siemens_sentron/config.py @@ -36,3 +36,31 @@ def __init__(self, id: int = 0, configuration: SiemensSentronCounterConfiguration = None) -> None: super().__init__(name, type, id, configuration or SiemensSentronCounterConfiguration()) + + +class SiemensSentronInverterConfiguration: + def __init__(self): + pass + + +class SiemensSentronInverterSetup(ComponentSetup[SiemensSentronInverterConfiguration]): + def __init__(self, + name: str = "Siemens Sentron PV-Zähler", + type: str = "inverter", + id: int = 0, + configuration: SiemensSentronInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SiemensSentronInverterConfiguration()) + + +class SiemensSentronBatConfiguration: + def __init__(self): + pass + + +class SiemensSentronBatSetup(ComponentSetup[SiemensSentronBatConfiguration]): + def __init__(self, + name: str = "Siemens Sentron Speicher-Zähler", + type: str = "bat", + id: int = 0, + configuration: SiemensSentronBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SiemensSentronBatConfiguration()) diff --git a/packages/modules/devices/siemens/siemens_sentron/counter.py b/packages/modules/devices/siemens/siemens_sentron/counter.py index 05bca774ed..3d74f9d5f1 100644 --- a/packages/modules/devices/siemens/siemens_sentron/counter.py +++ b/packages/modules/devices/siemens/siemens_sentron/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -12,18 +11,23 @@ from modules.devices.siemens.siemens_sentron.config import SiemensSentronCounterSetup +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + modbus_id: int + + class SiemensSentronCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, SiemensSentronCounterSetup], - tcp_client: modbus.ModbusTcpClient_, - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(SiemensSentronCounterSetup, component_config) - self.__tcp_client = tcp_client - self.__modbus_id = modbus_id + def __init__(self, component_config: SiemensSentronCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self): + def update(self) -> None: with self.__tcp_client: imported = self.__tcp_client.read_holding_registers(801, ModbusDataType.FLOAT_64, unit=self.__modbus_id) exported = self.__tcp_client.read_holding_registers(809, ModbusDataType.FLOAT_64, unit=self.__modbus_id) diff --git a/packages/modules/devices/siemens/siemens_sentron/device.py b/packages/modules/devices/siemens/siemens_sentron/device.py index 4347f62ab4..354ccbe274 100644 --- a/packages/modules/devices/siemens/siemens_sentron/device.py +++ b/packages/modules/devices/siemens/siemens_sentron/device.py @@ -1,35 +1,55 @@ #!/usr/bin/env python3 import logging -from typing import Iterable +from typing import Iterable, Union from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common.modbus import ModbusTcpClient_ -from modules.devices.siemens.siemens_sentron.config import SiemensSentron, SiemensSentronCounterSetup -from modules.devices.siemens.siemens_sentron.counter import SiemensSentronCounter +from modules.devices.siemens.siemens_sentron.config import (SiemensSentron, SiemensSentronCounterSetup, + SiemensSentronInverterSetup, SiemensSentronBatSetup) +from modules.devices.siemens.siemens_sentron import counter, inverter, bat log = logging.getLogger(__name__) def create_device(device_config: SiemensSentron): - def create_counter_component(component_config: SiemensSentronCounterSetup): - return SiemensSentronCounter(component_config, client, device_config.configuration.modbus_id) + client = None - def update_components(components: Iterable[SiemensSentronCounter]): + def create_counter_component(component_config: SiemensSentronCounterSetup): + nonlocal client + return counter.SiemensSentronCounter(component_config, client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_inverter_component(component_config: SiemensSentronInverterSetup): + nonlocal client + return inverter.SiemensSentronInverter(component_config, client=client, + modbus_id=device_config.configuration.modbus_id) + + def create_bat_component(component_config: SiemensSentronBatSetup): + nonlocal client + return bat.SiemensSentronBat(component_config, client=client, + modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[Union[counter.SiemensSentronCounter, inverter.SiemensSentronInverter, + bat.SiemensSentronBat]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, + inverter=create_inverter_component, + bat=create_bat_component ), component_updater=MultiComponentUpdater(update_components) ) diff --git a/packages/modules/devices/siemens/siemens_sentron/inverter.py b/packages/modules/devices/siemens/siemens_sentron/inverter.py new file mode 100644 index 0000000000..2cd4d3c038 --- /dev/null +++ b/packages/modules/devices/siemens/siemens_sentron/inverter.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any + +from modules.common import modbus +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType +from modules.common.store import get_inverter_value_store +from modules.devices.siemens.siemens_sentron.config import SiemensSentronInverterSetup + + +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + modbus_id: int + + +class SiemensSentronInverter(AbstractInverter): + def __init__(self, component_config: SiemensSentronInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + with self.__tcp_client: + power = self.__tcp_client.read_holding_registers(65, ModbusDataType.FLOAT_32, unit=self.__modbus_id) * -1 + exported = self.__tcp_client.read_holding_registers(809, ModbusDataType.FLOAT_64, unit=self.__modbus_id) + + inverter_state = InverterState( + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=SiemensSentronInverterSetup) diff --git a/packages/modules/devices/sigenergy/sigenergy/bat.py b/packages/modules/devices/sigenergy/sigenergy/bat.py index d6ac7662ea..55adf95bd1 100644 --- a/packages/modules/devices/sigenergy/sigenergy/bat.py +++ b/packages/modules/devices/sigenergy/sigenergy/bat.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -13,20 +13,29 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class SigenergyBat(AbstractBat): - def __init__(self, device_id: int, component_config: SigenergyBatSetup) -> None: - self.component_config = dataclass_from_dict(SigenergyBatSetup, component_config) + def __init__(self, component_config: SigenergyBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(30037, ModbusDataType.INT_32, unit=unit) + power = self.client.read_holding_registers(30037, ModbusDataType.INT_32, unit=unit) # soc unit 0.1% - soc = client.read_holding_registers(30014, ModbusDataType.UINT_16, unit=unit) / 10 + soc = self.client.read_holding_registers(30014, ModbusDataType.UINT_16, unit=unit) / 10 imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( diff --git a/packages/modules/devices/sigenergy/sigenergy/counter.py b/packages/modules/devices/sigenergy/sigenergy/counter.py index 6c36db2762..589d8c06cf 100644 --- a/packages/modules/devices/sigenergy/sigenergy/counter.py +++ b/packages/modules/devices/sigenergy/sigenergy/counter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -9,19 +9,28 @@ from modules.devices.sigenergy.sigenergy.config import SigenergyCounterSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class SigenergyCounter: - def __init__(self, device_id: int, component_config: SigenergyCounterSetup) -> None: - self.component_config = dataclass_from_dict(SigenergyCounterSetup, component_config) + def __init__(self, component_config: SigenergyCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") - def update(self, client: ModbusTcpClient_): + def update(self): unit = self.component_config.configuration.modbus_id - powers = client.read_holding_registers(30052, [ModbusDataType.INT_32]*3, unit=unit) - power = client.read_holding_registers(30005, ModbusDataType.INT_32, unit=unit) + powers = self.client.read_holding_registers(30052, [ModbusDataType.INT_32]*3, unit=unit) + power = self.client.read_holding_registers(30005, ModbusDataType.INT_32, unit=unit) imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( diff --git a/packages/modules/devices/sigenergy/sigenergy/device.py b/packages/modules/devices/sigenergy/sigenergy/device.py index df90216673..4667401cec 100644 --- a/packages/modules/devices/sigenergy/sigenergy/device.py +++ b/packages/modules/devices/sigenergy/sigenergy/device.py @@ -20,27 +20,34 @@ def create_device(device_config: Sigenergy): + client = None + def create_bat_component(component_config: SigenergyBatSetup): - return SigenergyBat(device_config.id, component_config) + nonlocal client + return SigenergyBat(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: SigenergyCounterSetup): - return SigenergyCounter(device_config.id, component_config) + nonlocal client + return SigenergyCounter(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: SigenergyInverterSetup): - return SigenergyInverter(device_config.id, component_config) + nonlocal client + return SigenergyInverter(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[SigenergyBat, SigenergyCounter, SigenergyInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/sigenergy/sigenergy/inverter.py b/packages/modules/devices/sigenergy/sigenergy/inverter.py index 489e3a15ed..3ceffcf844 100644 --- a/packages/modules/devices/sigenergy/sigenergy/inverter.py +++ b/packages/modules/devices/sigenergy/sigenergy/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,18 +10,27 @@ from modules.devices.sigenergy.sigenergy.config import SigenergyInverterSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class SigenergyInverter: - def __init__(self, device_id: int, component_config: Union[Dict, SigenergyInverterSetup]) -> None: - self.component_config = dataclass_from_dict(SigenergyInverterSetup, component_config) + def __init__(self, component_config: SigenergyInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.__device_id = device_id self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(30035, ModbusDataType.INT_32, unit=unit) * -1 + power = self.client.read_holding_registers(30035, ModbusDataType.INT_32, unit=unit) * -1 _, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( diff --git a/packages/modules/devices/sma/sma_shm/counter.py b/packages/modules/devices/sma/sma_shm/counter.py index bb61df6b1b..8683e8faef 100644 --- a/packages/modules/devices/sma/sma_shm/counter.py +++ b/packages/modules/devices/sma/sma_shm/counter.py @@ -34,7 +34,7 @@ def get_power(phase_str: str = ""): def create_component(component_config: SmaHomeManagerCounterSetup): - return SpeedwireComponent(get_counter_value_store, parse_datagram, component_config) + return SpeedwireComponent(component_config, value_store_factory=get_counter_value_store, parser=parse_datagram) component_descriptor = ComponentDescriptor(configuration_factory=SmaHomeManagerCounterSetup) diff --git a/packages/modules/devices/sma/sma_shm/counter_test.py b/packages/modules/devices/sma/sma_shm/counter_test.py index a457c6c778..33fc7ee062 100644 --- a/packages/modules/devices/sma/sma_shm/counter_test.py +++ b/packages/modules/devices/sma/sma_shm/counter_test.py @@ -32,6 +32,7 @@ def test_process_datagram_energy_meter(mock_ramdisk): data = base64.b64decode(SAMPLE_SMA_ENERGY_EM) sma_data = speedwiredecoder.decode_speedwire(data) sma_counter = counter.create_component(counter.component_descriptor.configuration_factory()) + sma_counter.initialize() # execution sma_counter.read_datagram(sma_data) diff --git a/packages/modules/devices/sma/sma_shm/inverter.py b/packages/modules/devices/sma/sma_shm/inverter.py index fe54b52aa6..3467e8988d 100644 --- a/packages/modules/devices/sma/sma_shm/inverter.py +++ b/packages/modules/devices/sma/sma_shm/inverter.py @@ -15,7 +15,7 @@ def parse_datagram(sma_data: dict): def create_component(component_config: SmaHomeManagerInverterSetup): - return SpeedwireComponent(get_inverter_value_store, parse_datagram, component_config) + return SpeedwireComponent(component_config, value_store_factory=get_inverter_value_store, parser=parse_datagram) component_descriptor = ComponentDescriptor(configuration_factory=SmaHomeManagerInverterSetup) diff --git a/packages/modules/devices/sma/sma_shm/utils.py b/packages/modules/devices/sma/sma_shm/utils.py index cf17307f8e..31b81f3785 100644 --- a/packages/modules/devices/sma/sma_shm/utils.py +++ b/packages/modules/devices/sma/sma_shm/utils.py @@ -1,5 +1,5 @@ import logging -from typing import TypeVar, Generic, Callable, Optional, Union +from typing import Any, TypeVar, Generic, Callable, Optional, Union, TypedDict from modules.common.component_context import SingleComponentUpdateContext from modules.common.fault_state import ComponentInfo, FaultState @@ -14,21 +14,28 @@ def _create_serial_matcher(serial: Optional[int]) -> Callable[[dict], bool]: if isinstance(serial, int): return lambda sma_data: sma_data["serial"] == serial if serial is not None: - log.error("Serial <%s> must bei an int or None, but is <%s>. Assuming None.", serial, type(serial)) + log.error("Serial <%s> must be an int or None, but is <%s>. Assuming None.", serial, type(serial)) return lambda _: True +class KwargsDict(TypedDict): + value_store_factory: Callable[[int], ValueStore[T]] + parser: Callable[[dict], T] + + class SpeedwireComponent(Generic[T]): def __init__(self, - value_store_factory: Callable[[int], ValueStore[T]], - parser: Callable[[dict], T], - component_config: Union[SmaHomeManagerCounterSetup, SmaHomeManagerInverterSetup]): - self.store = value_store_factory(component_config.id) - self.__parser = parser - self.__serial_matcher = _create_serial_matcher(component_config.configuration.serials) - self.fault_state = FaultState(ComponentInfo.from_component_config(component_config)) + component_config: Union[SmaHomeManagerCounterSetup, SmaHomeManagerInverterSetup], + **kwargs: Any): + self.kwargs: KwargsDict = kwargs self.component_config = component_config + def initialize(self) -> None: + self.store = self.kwargs['value_store_factory'](self.component_config.id) + self.__parser = self.kwargs['parser'] + self.__serial_matcher = _create_serial_matcher(self.component_config.configuration.serials) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + def read_datagram(self, datagram: dict) -> bool: if self.__serial_matcher(datagram): with SingleComponentUpdateContext(self.fault_state): diff --git a/packages/modules/devices/sma/sma_sunny_boy/bat.py b/packages/modules/devices/sma/sma_sunny_boy/bat.py index 99624e1295..6d03cd8e60 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/bat.py +++ b/packages/modules/devices/sma/sma_sunny_boy/bat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +import logging +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -10,16 +10,22 @@ from modules.common.store import get_bat_value_store from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoyBatSetup +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + class SunnyBoyBat(AbstractBat): SMA_UINT_64_NAN = 0xFFFFFFFFFFFFFFFF # SMA uses this value to represent NaN - def __init__(self, - device_id: int, - component_config: Union[Dict, SmaSunnyBoyBatSetup], - tcp_client: ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SmaSunnyBoyBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SmaSunnyBoyBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -42,12 +48,14 @@ def read(self) -> BatState: 'Sobald die Batterie geladen/entladen wird sollte sich dieser Wert ändern, ', 'andernfalls kann ein Defekt vorliegen.') - return BatState( + bat_state = BatState( power=power, soc=soc, imported=imported, exported=exported ) + log.debug("Bat {}: {}".format(self.__tcp_client.address, bat_state)) + return bat_state def update(self) -> None: self.store.set(self.read()) diff --git a/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py b/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py index b5b5d61845..f5e5ab7394 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py +++ b/packages/modules/devices/sma/sma_sunny_boy/bat_smart_energy.py @@ -1,31 +1,50 @@ #!/usr/bin/env python3 -from typing import Dict, Union +import pymodbus +from typing import TypedDict, Any, Dict, Union, Optional +import logging -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractBat -from modules.common.component_state import BatState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusTcpClient_, ModbusDataType -from modules.common.simcount import SimCounter -from modules.common.store import get_bat_value_store from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoySmartEnergyBatSetup +from modules.common.store import get_bat_value_store +from modules.common.modbus import ModbusTcpClient_, ModbusDataType +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.component_type import ComponentDescriptor +from modules.common.component_state import BatState +from modules.common.abstract_device import AbstractBat + + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ class SunnyBoySmartEnergyBat(AbstractBat): SMA_UINT32_NAN = 0xFFFFFFFF # SMA uses this value to represent NaN SMA_UINT_64_NAN = 0xFFFFFFFFFFFFFFFF # SMA uses this value to represent NaN - def __init__(self, - device_id: int, - component_config: Union[Dict, SmaSunnyBoySmartEnergyBatSetup], - tcp_client: ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SmaSunnyBoySmartEnergyBatSetup, component_config) - self.__tcp_client = tcp_client - self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + # Define all possible registers with their data types + REGISTERS = { + "Battery_SoC": (30845, ModbusDataType.UINT_32), + "Battery_ChargePower": (31393, ModbusDataType.INT_32), + "Battery_DischargePower": (31395, ModbusDataType.INT_32), + "Battery_ChargedEnergy": (31397, ModbusDataType.UINT_64), + "Battery_DischargedEnergy": (31401, ModbusDataType.UINT_64), + "Inverter_Type": (30053, ModbusDataType.UINT_32), + "Externe_Steuerung": (40151, ModbusDataType.UINT_32), + "Wirkleistungsvorgabe": (40149, ModbusDataType.UINT_32), + } + + def __init__(self, component_config: SmaSunnyBoySmartEnergyBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.last_mode = 'Undefined' + self.inverter_type = None def update(self) -> None: self.store.set(self.read()) @@ -33,30 +52,109 @@ def update(self) -> None: def read(self) -> BatState: unit = self.component_config.configuration.modbus_id - soc = self.__tcp_client.read_holding_registers(30845, ModbusDataType.UINT_32, unit=unit) - current = self.__tcp_client.read_holding_registers(30843, ModbusDataType.INT_32, unit=unit)/-1000 - voltage = self.__tcp_client.read_holding_registers(30851, ModbusDataType.INT_32, unit=unit)/100 + registers_to_read = [ + "Battery_SoC", + "Battery_ChargePower", + "Battery_DischargePower", + "Battery_ChargedEnergy", + "Battery_DischargedEnergy" + ] + + if self.inverter_type is None: # Only read Inverter_Type if not already set + registers_to_read.append("Inverter_Type") + + values = self._read_registers(registers_to_read, unit) - if soc == self.SMA_UINT32_NAN: + if values["Battery_SoC"] == self.SMA_UINT32_NAN: # If the storage is empty and nothing is produced on the DC side, the inverter does not supply any values. - soc = 0 + values["Battery_SoC"] = 0 power = 0 else: - power = current*voltage - exported = self.__tcp_client.read_holding_registers(31401, ModbusDataType.UINT_64, unit=3) - imported = self.__tcp_client.read_holding_registers(31397, ModbusDataType.UINT_64, unit=3) + if values["Battery_ChargePower"] > 5: + power = values["Battery_ChargePower"] + else: + power = values["Battery_DischargePower"] * -1 - if exported == self.SMA_UINT_64_NAN or imported == self.SMA_UINT_64_NAN: - raise ValueError(f'Batterie lieferte nicht plausible Werte. Export: {exported}, Import: {imported}. ', - 'Sobald die Batterie geladen/entladen wird sollte sich dieser Wert ändern, ', - 'andernfalls kann ein Defekt vorliegen.') + if (values["Battery_ChargedEnergy"] == self.SMA_UINT_64_NAN or + values["Battery_DischargedEnergy"] == self.SMA_UINT_64_NAN): + raise ValueError( + f'Batterie lieferte nicht plausible Werte. Geladene Energie: {values["Battery_ChargedEnergy"]}, ' + f'Entladene Energie: {values["Battery_DischargedEnergy"]}. ', + 'Sobald die Batterie geladen/entladen wird sollte sich dieser Wert ändern, ', + 'andernfalls kann ein Defekt vorliegen.' + ) - return BatState( + bat_state = BatState( power=power, - soc=soc, - imported=imported, - exported=exported + soc=values["Battery_SoC"], + exported=values["Battery_DischargedEnergy"], + imported=values["Battery_ChargedEnergy"] + ) + if self.inverter_type is None: + self.inverter_type = values["Inverter_Type"] + log.debug(f"Inverter Type: {self.inverter_type}") + log.debug(f"Bat {self.__tcp_client.address}: {bat_state}") + return bat_state + + def set_power_limit(self, power_limit: Optional[int]) -> None: + unit = self.component_config.configuration.modbus_id + + if power_limit is None: + if self.last_mode is not None: + # Kein Powerlimit gefordert, externe Steuerung war aktiv, externe Steuerung deaktivieren + log.debug("Keine Batteriesteuerung gefordert, deaktiviere externe Steuerung.") + values_to_write = { + "Externe_Steuerung": 803, + "Wirkleistungsvorgabe": 0, + } + self._write_registers(values_to_write, unit) + self.last_mode = None + else: + # Powerlimit gefordert, externe Steuerung aktivieren, Limit setzen + log.debug("Aktive Batteriesteuerung vorhanden. Setze externe Steuerung.") + values_to_write = { + "Externe_Steuerung": 802, + "Wirkleistungsvorgabe": abs(power_limit) + } + self._write_registers(values_to_write, unit) + self.last_mode = 'limited' + + def _read_registers(self, register_names: list, unit: int) -> Dict[str, Union[int, float]]: + values = {} + for key in register_names: + address, data_type = self.REGISTERS[key] + values[key] = self.__tcp_client.read_holding_registers(address, data_type, unit=unit) + log.debug(f"Bat raw values {self.__tcp_client.address}: {values}") + return values + + def _write_registers(self, values_to_write: Dict[str, Union[int, float]], unit: int) -> None: + for key, value in values_to_write.items(): + address, data_type = self.REGISTERS[key] + encoded_value = self._encode_value(value, data_type) + self.__tcp_client.write_registers(address, encoded_value, unit=unit) + log.debug(f"Neuer Wert {encoded_value} in Register {address} geschrieben.") + + def _encode_value(self, value: Union[int, float], data_type: ModbusDataType) -> list: + builder = pymodbus.payload.BinaryPayloadBuilder( + byteorder=pymodbus.constants.Endian.Big, + wordorder=pymodbus.constants.Endian.Big ) + encode_methods = { + ModbusDataType.UINT_32: builder.add_32bit_uint, + ModbusDataType.INT_32: builder.add_32bit_int, + ModbusDataType.UINT_16: builder.add_16bit_uint, + ModbusDataType.INT_16: builder.add_16bit_int, + } + + if data_type in encode_methods: + encode_methods[data_type](int(value)) + else: + raise ValueError(f"Unsupported data type: {data_type}") + + return builder.to_registers() + + def power_limit_controllable(self) -> bool: + return True component_descriptor = ComponentDescriptor(configuration_factory=SmaSunnyBoySmartEnergyBatSetup) diff --git a/packages/modules/devices/sma/sma_sunny_boy/bat_tesvolt.py b/packages/modules/devices/sma/sma_sunny_boy/bat_tesvolt.py index 9fe38a8ed1..ca348e1708 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/bat_tesvolt.py +++ b/packages/modules/devices/sma/sma_sunny_boy/bat_tesvolt.py @@ -1,4 +1,6 @@ #!/usr/bin/env python3 +import logging +from typing import TypedDict, Any from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -9,15 +11,23 @@ from modules.common.store import get_bat_value_store from modules.devices.sma.sma_sunny_boy.config import SmaTesvoltBatSetup +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + class TesvoltBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: SmaTesvoltBatSetup, - tcp_client: ModbusTcpClient_) -> None: + def __init__(self, component_config: SmaTesvoltBatSetup, **kwargs: Any) -> None: self.component_config = component_config - self.__tcp_client = tcp_client - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -32,7 +42,7 @@ def update(self) -> None: imported=imported, exported=exported ) - + log.debug("Bat {}: {}".format(self.__tcp_client.address, bat_state)) self.store.set(bat_state) diff --git a/packages/modules/devices/sma/sma_sunny_boy/counter.py b/packages/modules/devices/sma/sma_sunny_boy/counter.py index 2210c320da..4c3e641770 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/counter.py +++ b/packages/modules/devices/sma/sma_sunny_boy/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,14 +12,19 @@ from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoyCounterSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class SmaSunnyBoyCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SmaSunnyBoyCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SmaSunnyBoyCounterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SmaSunnyBoyCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/sma/sma_sunny_boy/device.py b/packages/modules/devices/sma/sma_sunny_boy/device.py index a09f4547dc..f793b3f229 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/device.py +++ b/packages/modules/devices/sma/sma_sunny_boy/device.py @@ -27,33 +27,42 @@ def create_device(device_config: SmaSunnyBoy): + client = None + def create_bat_component(component_config: SmaSunnyBoyBatSetup): - return SunnyBoyBat(device_config.id, component_config, client) + nonlocal client + return SunnyBoyBat(component_config, device_id=device_config.id, client=client) def create_bat_smart_energy_component(component_config: SmaSunnyBoySmartEnergyBatSetup): - return SunnyBoySmartEnergyBat(device_config.id, component_config, client) + nonlocal client + return SunnyBoySmartEnergyBat(component_config, client=client) def create_bat_tesvolt_component(component_config: SmaTesvoltBatSetup): - return TesvoltBat(device_config.id, component_config, client) + nonlocal client + return TesvoltBat(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: SmaSunnyBoyCounterSetup): - return SmaSunnyBoyCounter(device_config.id, component_config, client) + nonlocal client + return SmaSunnyBoyCounter(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: SmaSunnyBoyInverterSetup): - return SmaSunnyBoyInverter(device_config.id, component_config, client) + nonlocal client + return SmaSunnyBoyInverter(component_config, client=client, device_id=device_config.id) def update_components(components: Iterable[sma_modbus_tcp_component_classes]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, bat_smart_energy=create_bat_smart_energy_component, diff --git a/packages/modules/devices/sma/sma_sunny_boy/inverter.py b/packages/modules/devices/sma/sma_sunny_boy/inverter.py index 8685fd7410..5c3b206d0d 100644 --- a/packages/modules/devices/sma/sma_sunny_boy/inverter.py +++ b/packages/modules/devices/sma/sma_sunny_boy/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -12,10 +11,16 @@ from modules.common.store import get_inverter_value_store from modules.devices.sma.sma_sunny_boy.config import SmaSunnyBoyInverterSetup from modules.devices.sma.sma_sunny_boy.inv_version import SmaInverterVersion +from modules.common.simcount import SimCounter log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + device_id: int + + class SmaSunnyBoyInverter(AbstractInverter): SMA_INT32_NAN = -0x80000000 # SMA uses this value to represent NaN @@ -23,13 +28,16 @@ class SmaSunnyBoyInverter(AbstractInverter): SMA_NAN = -0xC000 def __init__(self, - device_id: int, - component_config: Union[Dict, SmaSunnyBoyInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SmaSunnyBoyInverterSetup, component_config) - self.tcp_client = tcp_client + component_config: SmaSunnyBoyInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.tcp_client = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="Wechselrichter") def update(self) -> None: self.store.set(self.read()) @@ -49,6 +57,10 @@ def read(self) -> InverterState: # Leistung DC an Eingang 1 und 2 dc_power = (self.tcp_client.read_holding_registers(30773, ModbusDataType.INT_32, unit=unit) + self.tcp_client.read_holding_registers(30961, ModbusDataType.INT_32, unit=unit)) + current_L1 = self.tcp_client.read_holding_registers(30977, ModbusDataType.INT_32, unit=unit) * -1 + current_L2 = self.tcp_client.read_holding_registers(30979, ModbusDataType.INT_32, unit=unit) * -1 + current_L3 = self.tcp_client.read_holding_registers(30981, ModbusDataType.INT_32, unit=unit) * -1 + currents = [current_L1 / 1000, current_L2 / 1000, current_L3 / 1000] elif self.component_config.configuration.version == SmaInverterVersion.core2: # AC Wirkleistung über alle Phasen (W) [Pac] power_total = self.tcp_client.read_holding_registers(40084, ModbusDataType.INT_16, unit=unit) * 10 @@ -68,6 +80,8 @@ def read(self) -> InverterState: raise ValueError("Unbekannte Version "+str(self.component_config.configuration.version)) if power_total == self.SMA_INT32_NAN or power_total == self.SMA_NAN: power_total = 0 + # Bei keiner AC Wirkleistung müssen auch die Ströme der Phasen 0 sein. + currents = [0, 0, 0] if energy == self.SMA_UINT32_NAN: raise ValueError( @@ -76,10 +90,14 @@ def read(self) -> InverterState: 'andernfalls kann ein Defekt vorliegen.' ) + imported, _ = self.sim_counter.sim_count(power_total * -1) + inverter_state = InverterState( power=power_total * -1, dc_power=dc_power * -1, - exported=energy + currents=currents, + exported=energy, + imported=imported ) log.debug("WR {}: {}".format(self.tcp_client.address, inverter_state)) return inverter_state diff --git a/packages/modules/devices/sma/sma_sunny_island/bat.py b/packages/modules/devices/sma/sma_sunny_island/bat.py index 30fc0479a8..fbf58abf9c 100644 --- a/packages/modules/devices/sma/sma_sunny_island/bat.py +++ b/packages/modules/devices/sma/sma_sunny_island/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -12,12 +11,17 @@ from modules.devices.sma.sma_sunny_island.config import SmaSunnyIslandBatSetup +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + + class SunnyIslandBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, SmaSunnyIslandBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SmaSunnyIslandBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SmaSunnyIslandBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/sma/sma_sunny_island/device.py b/packages/modules/devices/sma/sma_sunny_island/device.py index 9d11cbafec..6629eadcfa 100644 --- a/packages/modules/devices/sma/sma_sunny_island/device.py +++ b/packages/modules/devices/sma/sma_sunny_island/device.py @@ -13,21 +13,26 @@ def create_device(device_config: SmaSunnyIsland): + client = None + def create_bat_component(component_config: SmaSunnyIslandBatSetup): - return SunnyIslandBat(component_config, client) + nonlocal client + return SunnyIslandBat(component_config, client=client) def update_components(components: Iterable[SunnyIslandBat]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, ), diff --git a/packages/modules/devices/sma/sma_webbox/device.py b/packages/modules/devices/sma/sma_webbox/device.py index 8e5ee62baf..87db5b6de6 100644 --- a/packages/modules/devices/sma/sma_webbox/device.py +++ b/packages/modules/devices/sma/sma_webbox/device.py @@ -12,7 +12,7 @@ def create_device(device_config: SmaWebbox): def create_inverter_component(component_config: SmaWebboxInverterSetup): - return SmaWebboxInverter(device_config.configuration.ip_address, component_config) + return SmaWebboxInverter(component_config, ip_address=device_config.configuration.ip_address) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/sma/sma_webbox/inverter.py b/packages/modules/devices/sma/sma_webbox/inverter.py index f652365dbf..3447f884ac 100644 --- a/packages/modules/devices/sma/sma_webbox/inverter.py +++ b/packages/modules/devices/sma/sma_webbox/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, Dict, Union, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -11,10 +10,17 @@ from modules.devices.sma.sma_webbox.config import SmaWebboxInverterSetup +class KwargsDict(TypedDict): + ip_address: str + + class SmaWebboxInverter(AbstractInverter): - def __init__(self, device_address: str, component_config: Union[Dict, SmaWebboxInverterSetup]) -> None: - self.__device_address = device_address - self.component_config = dataclass_from_dict(SmaWebboxInverterSetup, component_config) + def __init__(self, component_config: Union[Dict, SmaWebboxInverterSetup], **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -23,8 +29,7 @@ def update(self) -> None: def read(self) -> InverterState: data = {'RPC': '{"version": "1.0","proc": "GetPlantOverview","id": "1","format": "JSON"}'} - response = req.get_http_session().post( - 'http://' + self.__device_address + '/rpc', data=data, timeout=3).json() + response = req.get_http_session().post(f'http://{self.ip_address}/rpc', data=data, timeout=3).json() return InverterState( exported=float(response["result"]["overview"][2]["value"]) * 1000, diff --git a/packages/modules/devices/smart_me/smart_me/counter.py b/packages/modules/devices/smart_me/smart_me/counter.py index 1eb957ad63..3a4c9f0db3 100644 --- a/packages/modules/devices/smart_me/smart_me/counter.py +++ b/packages/modules/devices/smart_me/smart_me/counter.py @@ -1,10 +1,8 @@ #!/usr/bin/env python3 import logging -from typing import Dict, List, Union +from typing import List from requests import Session - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -16,9 +14,10 @@ class SmartMeCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, SmartMeCounterSetup]) -> None: - self.component_config = dataclass_from_dict(SmartMeCounterSetup, component_config) + def __init__(self, component_config: SmartMeCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/smart_me/smart_me/device.py b/packages/modules/devices/smart_me/smart_me/device.py index 8a2046e8cd..50029ccb01 100644 --- a/packages/modules/devices/smart_me/smart_me/device.py +++ b/packages/modules/devices/smart_me/smart_me/device.py @@ -15,16 +15,22 @@ def create_device(device_config: SmartMe): + session = None + def create_counter_component(component_config: SmartMeCounterSetup): return SmartMeCounter(component_config) def create_inverter_component(component_config: SmartMeInverterSetup): return SmartMeInverter(component_config) - session = get_http_session() - session.auth = (device_config.configuration.user, device_config.configuration.password) + def initializer(): + nonlocal session + session = get_http_session() + session.auth = (device_config.configuration.user, device_config.configuration.password) + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( counter=create_counter_component, inverter=create_inverter_component, diff --git a/packages/modules/devices/smart_me/smart_me/inverter.py b/packages/modules/devices/smart_me/smart_me/inverter.py index af113d297d..6db8543b5e 100644 --- a/packages/modules/devices/smart_me/smart_me/inverter.py +++ b/packages/modules/devices/smart_me/smart_me/inverter.py @@ -1,10 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union from requests import Session - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -16,9 +13,10 @@ class SmartMeInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, SmartMeInverterSetup]) -> None: - self.component_config = dataclass_from_dict(SmartMeInverterSetup, component_config) + def __init__(self, component_config: SmartMeInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/smartfox/smartfox/counter.py b/packages/modules/devices/smartfox/smartfox/counter.py index 29c14db4ac..5950c7eb01 100644 --- a/packages/modules/devices/smartfox/smartfox/counter.py +++ b/packages/modules/devices/smartfox/smartfox/counter.py @@ -1,9 +1,8 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, TypedDict import xml.etree.ElementTree as ET -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -15,12 +14,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + ip_address: str + + class SmartfoxCounter(AbstractCounter): - def __init__(self, - device_address: str, - component_config: Union[Dict, SmartfoxCounterSetup]) -> None: - self.__device_address = device_address - self.component_config = dataclass_from_dict(SmartfoxCounterSetup, component_config) + def __init__(self, component_config: SmartfoxCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: int = self.kwargs['ip_address'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -35,11 +39,11 @@ def get_xml_text(attribute_value: str) -> str: headers = { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', - 'Host': self.__device_address, + 'Host': self.ip_address, 'Connection': 'keep-alive)', } - response = req.get_http_session().get('http://'+self.__device_address+'/values.xml', + response = req.get_http_session().get('http://'+self.ip_address+'/values.xml', headers=headers, timeout=5) response.encoding = 'utf-8' diff --git a/packages/modules/devices/smartfox/smartfox/device.py b/packages/modules/devices/smartfox/smartfox/device.py index 162c23ce75..e8d2eba6b8 100644 --- a/packages/modules/devices/smartfox/smartfox/device.py +++ b/packages/modules/devices/smartfox/smartfox/device.py @@ -12,7 +12,7 @@ def create_device(device_config: Smartfox): def create_counter_component(component_config: SmartfoxCounterSetup): - return SmartfoxCounter(device_config.configuration.ip_address, component_config) + return SmartfoxCounter(component_config, ip_address=device_config.configuration.ip_address) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/sofar/sofar/bat.py b/packages/modules/devices/sofar/sofar/bat.py index 000b64378e..334ddc10db 100644 --- a/packages/modules/devices/sofar/sofar/bat.py +++ b/packages/modules/devices/sofar/sofar/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -11,37 +10,39 @@ from modules.devices.sofar.sofar.config import SofarBatSetup +class KwargsDict(TypedDict): + modbus_id: int + client: ModbusTcpClient_ + + class SofarBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, SofarBatSetup], - modbus_id: int) -> None: - self.__modbus_id = modbus_id - self.component_config = dataclass_from_dict(SofarBatSetup, component_config) + def __init__(self, component_config: SofarBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - # 0x0606 Power_bat1 Int16 in kW accuracy 0,01 - # 0x060D Power_bat2, 0x0614 Power_bat3, 0x061B Power_bat4, 0x0622 Power_bat5, - # 0x0629 Power_bat6, 0x0630 Power_bat7, 0x0637 Power_bat8 - # 0x0646 Power_bat9, 0x064D Power_bat10, 0x0654 Power_bat11, 0x065B Power_bat12 - power = sum(client.read_input_registers(reg, ModbusDataType.INT_16, unit=self.__modbus_id) - for reg in [0x0606, 0x060D, 0x0614, 0x061B, 0x0622, 0x0629, 0x0630, - 0x0637, 0x0646, 0x064D, 0x0654, 0x065B]) - # 0x0608 SOC_Bat1 UInt16 in % accuracy 1 - # 0x060F SOC_bat2, 0x0616 SOC_bat3, 0x061D SOC_bat4, 0x0624 SOC_bat5, - # 0x062B SOC_bat6, 0x0632 SOC_bat7, 0x0639 SOC_bat_8 - # 0x0648 SOC_bat9, 0x064F SOC_bat10, 0x0656 SOC_bat11, 0x065D SOC_bat12 - soc = sum(client.read_input_registers(0x0608, ModbusDataType.UINT_16, unit=self.__modbus_id) - for reg in [0x0608, 0x060F, 0x0616, 0x061D, 0x0624, 0x062B, 0x0632, - 0x0639, 0x0648, 0x064F, 0x0656, 0x065D]) + def update(self) -> None: + # 0x900D High 8 bits: the number of battery packs in parallel + # Lower 8 bits: the number of battery strings in the battery pack + battery_packs = self.client.read_holding_registers(0x900D, ModbusDataType.UINT_16, unit=self.__modbus_id) >> 8 + # Power bat1 - bat12: INT_16 in kW accuracy 0,01 + power_regs = [0x0606, 0x060D, 0x0614, 0x061B, 0x0622, 0x0629, 0x0630, 0x0637, 0x0646, 0x064D, 0x0654, 0x065B] + + power = sum(self.client.read_holding_registers(power_regs[idx], ModbusDataType.INT_16, unit=self.__modbus_id) + for idx in range(battery_packs)) * 10 + soc = self.client.read_holding_registers(0x9012, ModbusDataType.UINT_16, unit=self.__modbus_id) # 0x0696 Bat_charge_total LSB UInt32 0,1 kWh # 0x0697 Bat_charge_total UInt32 0,1 kWh - imported = client.read_input_registers( + imported = self.client.read_holding_registers( 0x0696, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 # 0x069A Bat_discharge_total LSB UInt32 0,1 kWh # 0x069B Bat:discharge_total UInt32 0,1 kWh - exported = client.read_input_registers( + exported = self.client.read_holding_registers( 0x069A, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 bat_state = BatState( diff --git a/packages/modules/devices/sofar/sofar/counter.py b/packages/modules/devices/sofar/sofar/counter.py index 91bc7e6b19..ae0ac2d5f2 100644 --- a/packages/modules/devices/sofar/sofar/counter.py +++ b/packages/modules/devices/sofar/sofar/counter.py @@ -1,81 +1,58 @@ #!/usr/bin/env python3 -from typing import Dict, Union -from pymodbus.constants import Endian +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict -from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState -from modules.common.component_type import ComponentDescriptor -from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, ModbusTcpClient_ -from modules.common.store import get_counter_value_store from modules.devices.sofar.sofar.config import SofarCounterSetup +from modules.common.store import get_counter_value_store +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.component_type import ComponentDescriptor +from modules.common.component_state import CounterState +from modules.common.abstract_device import AbstractCounter + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + modbus_id: int class SofarCounter(AbstractCounter): - def __init__(self, - component_config: Union[Dict, SofarCounterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(SofarCounterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: SofarCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): + def update(self): # 0x0485 ActivePower_output_total Int16 in kW accuracy 0,01 discharge + charge - # 0x0488 ActivePower_PCC_total Int16 0,01 kW - power = client.read_input_registers(0x0488, ModbusDataType.INT_16, wordorder=Endian.Little, - unit=self.__modbus_id) * -1 + power = self.client.read_holding_registers(0x0488, ModbusDataType.INT_16, unit=self.__modbus_id) * -10 # 0x0484 Frequency_Grid UInt16 in Hz accuracy 0,01 - frequency = client.read_input_registers( + frequency = self.client.read_holding_registers( 0x0484, ModbusDataType.UINT_16, unit=self.__modbus_id) / 100 try: - # 0x048F ActivePower_Output_R UInt16 in V accuracy 0,1 - # 0x0493 ActivePower_PCC_R Int16 in kW accuracy 0,01 - powers = [-value for value in client.read_input_registers( - 0x0493, [ModbusDataType.INT_16] * 1, wordorder=Endian.Little, unit=self.__modbus_id - )] + powers = [ + self.client.read_holding_registers(0x0493, ModbusDataType.INT_16, unit=self.__modbus_id) * -10, + self.client.read_holding_registers(0x049E, ModbusDataType.INT_16, unit=self.__modbus_id) * -10, + self.client.read_holding_registers(0x04A9, ModbusDataType.INT_16, unit=self.__modbus_id) * -10] except Exception: powers = None - try: - voltages = [client.read_input_registers( - # 048D Voltage_Phase_R UInt16 in V accuracy 0,1 - 0x048D, ModbusDataType.UINT_16, unit=self.__modbus_id - ) / 10, client.read_input_registers( - # 0498 Voltage_Phase_S UInt16 in V accuracy 0,1 - 0x0498, ModbusDataType.UINT_16, unit=self.__modbus_id - ) / 10, client.read_input_registers( - # 04A3 Voltage_Phase_T UInt16 in V accuracy 0,1 - 0x04A3, ModbusDataType.UINT_16, unit=self.__modbus_id - ) / 10] - if voltages[0] < 1: - voltages[0] = 230 - if voltages[1] < 1: - voltages[1] = 230 - if voltages[2] < 1: - voltages[2] = 230 - except Exception: - voltages = [230, 230, 230] - exported = [value * 10 - for value in client.read_input_registers( - # 0x0692 Energy_Selling_Total UInt32 in kwH accuracy 0,01 LSB - # 0x0693 Energy_Selling_Total UInt32 in kwH accuracy 0,01 - 0x0692, [ModbusDataType.UINT_32] * 10, - wordorder=Endian.Little, unit=self.__modbus_id)] - imported = [value * 10 - for value in client.read_input_registers( - # 0x068E Energy_Purchase_Total UInt32 in kwH accuracy 0,01 LSB - # 0x068F Energy_Purchase_Total UInt32 in kwH accuracy 0,01 - 0x068E, [ModbusDataType.UINT_32] * 10, - wordorder=Endian.Little, unit=self.__modbus_id)] + # 0x0692 Energy_Selling_Total UInt32 in kwH accuracy 0,01 LSB + # 0x0693 Energy_Selling_Total UInt32 in kwH accuracy 0,01 + exported = self.client.read_holding_registers(0x0692, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + # 0x068E Energy_Purchase_Total UInt32 in kwH accuracy 0,01 LSB + # 0x068F Energy_Purchase_Total UInt32 in kwH accuracy 0,01 + imported = self.client.read_holding_registers(0x068E, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 counter_state = CounterState( imported=imported, exported=exported, power=power, powers=powers, - frequency=frequency, - voltages=voltages, + frequency=frequency ) self.store.set(counter_state) diff --git a/packages/modules/devices/sofar/sofar/device.py b/packages/modules/devices/sofar/sofar/device.py index f1a9fb7746..3ab2b80c69 100644 --- a/packages/modules/devices/sofar/sofar/device.py +++ b/packages/modules/devices/sofar/sofar/device.py @@ -15,27 +15,34 @@ def create_device(device_config: Sofar): + client = None + def create_bat_component(component_config: SofarBatSetup): - return SofarBat(component_config, device_config.configuration.modbus_id) + nonlocal client + return SofarBat(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def create_counter_component(component_config: SofarCounterSetup): - return SofarCounter(component_config, device_config.configuration.modbus_id) + nonlocal client + return SofarCounter(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def create_inverter_component(component_config: SofarInverterSetup): - return SofarInverter(component_config, device_config.configuration.modbus_id) + nonlocal client + return SofarInverter(component_config, modbus_id=device_config.configuration.modbus_id, client=client) def update_components(components: Iterable[Union[SofarBat, SofarCounter, SofarInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/sofar/sofar/inverter.py b/packages/modules/devices/sofar/sofar/inverter.py index d73cdfb9f7..8cb7878b50 100644 --- a/packages/modules/devices/sofar/sofar/inverter.py +++ b/packages/modules/devices/sofar/sofar/inverter.py @@ -1,8 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union -from pymodbus.constants import Endian +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,30 +10,26 @@ from modules.devices.sofar.sofar.config import SofarInverterSetup +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + modbus_id: int + + class SofarInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, SofarInverterSetup], - modbus_id: int) -> None: - self.component_config = dataclass_from_dict(SofarInverterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: SofarInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.__modbus_id: int = self.kwargs['modbus_id'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self, client: ModbusTcpClient_) -> None: - # 0x0586 Power_PV1 UInt16 in kW accuracy 0,01 - # 0x0589 Power_PV2, 0x058C Power_PV3, 0x058F Power_PV4, 0x0592 Power_PV5, - # 0x0595 Power_PV6, 0x0598 Power_PV7, 0x059B Power_PV8, 0x059E Power_PV9, 0x05A1 Power_PV10, - # 0x05A4 Power_PV11, 0x05A7 Power_PV12, 0x05AA Power_PV13, - # 0x05AD Power_PV14, 0x05B0 Power_PV15, 0x05B3 Power_PV16 - power = sum([client.read_input_registers(reg, ModbusDataType.UINT_16, - unit=self.__modbus_id) for reg in [0x0586, 0x0589, 0x058C, 0x058F, 0x0592, - 0x0595, 0x0598, 0x059B, 0x059E, 0x05A1, 0x05A4, - 0x05A7, 0x05AA, 0x05AD, 0x05B0, 0x05B3]]) * -1 - # 0x05C4 Power_PV_Total UInt16 in kW accuracy 0,1 - # 0x0686 PV_Generation_Total UInt32 0,1 kW LSB - # 0x0687 PV_Generation_Total UInt32 0,1 kW - exported = client.read_input_registers(0x0686, ModbusDataType.UINT_32, wordorder=Endian.Little, - unit=self.__modbus_id) * 100 + # 0x05C4 Power_PV_Total UINT16 in kW accuracy 0,1 + power = self.client.read_holding_registers(0x05C4, ModbusDataType.UINT_16, unit=self.__modbus_id) * -100 + exported = self.client.read_holding_registers(0x0686, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 inverter_state = InverterState( power=power, diff --git a/packages/modules/devices/solar_log/solar_log/counter.py b/packages/modules/devices/solar_log/solar_log/counter.py index a5267b2b68..82abc683cb 100644 --- a/packages/modules/devices/solar_log/solar_log/counter.py +++ b/packages/modules/devices/solar_log/solar_log/counter.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Dict, TypedDict, Any - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -15,12 +13,18 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class SolarLogCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolarLogCounterSetup]) -> None: - self.component_config = dataclass_from_dict(SolarLogCounterSetup, component_config) - self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") + def __init__(self, component_config: SolarLogCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -36,7 +40,7 @@ def store_values(self, power) -> None: power=power )) - def get_power(self, response: Dict) -> CounterState: + def get_power(self, response: Dict) -> int: return int(float(response["801"]["170"]["110"])) diff --git a/packages/modules/devices/solar_log/solar_log/device.py b/packages/modules/devices/solar_log/solar_log/device.py index 85b4639b63..ab84095125 100644 --- a/packages/modules/devices/solar_log/solar_log/device.py +++ b/packages/modules/devices/solar_log/solar_log/device.py @@ -16,16 +16,17 @@ def create_device(device_config: SolarLog): def create_counter_component(component_config: SolarLogCounterSetup): - return SolarLogCounter(device_config.id, component_config) + return SolarLogCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: SolarLogInverterSetup): - return SolarLogInverter(device_config.id, component_config) + return SolarLogInverter(component_config) def update_components(components: Iterable[Union[SolarLogCounter, SolarLogInverter]]): response = req.get_http_session().post('http://'+device_config.configuration.ip_address+'/getjp', data=json.dumps({"801": {"170": None}}), timeout=5).json() for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/solar_log/solar_log/inverter.py b/packages/modules/devices/solar_log/solar_log/inverter.py index fb70845a81..07b4698f1b 100644 --- a/packages/modules/devices/solar_log/solar_log/inverter.py +++ b/packages/modules/devices/solar_log/solar_log/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Dict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -15,9 +14,10 @@ class SolarLogInverter(AbstractInverter): def __init__(self, - device_id: int, - component_config: Union[Dict, SolarLogInverterSetup]) -> None: - self.component_config = dataclass_from_dict(SolarLogInverterSetup, component_config) + component_config: SolarLogInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_view/solar_view/counter.py b/packages/modules/devices/solar_view/solar_view/counter.py index db8ecf38d3..08e3e2a65d 100644 --- a/packages/modules/devices/solar_view/solar_view/counter.py +++ b/packages/modules/devices/solar_view/solar_view/counter.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -16,8 +13,10 @@ class SolarViewCounter(AbstractCounter): - def __init__(self, component_config: Union[Dict, SolarViewCounterSetup]) -> None: - self.component_config = dataclass_from_dict(SolarViewCounterSetup, component_config) + def __init__(self, component_config: SolarViewCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_view/solar_view/inverter.py b/packages/modules/devices/solar_view/solar_view/inverter.py index 371d59d256..d57d4bc876 100644 --- a/packages/modules/devices/solar_view/solar_view/inverter.py +++ b/packages/modules/devices/solar_view/solar_view/inverter.py @@ -1,9 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -16,8 +13,10 @@ class SolarViewInverter(AbstractInverter): - def __init__(self, component_config: Union[Dict, SolarViewInverterSetup]) -> None: - self.component_config = dataclass_from_dict(SolarViewInverterSetup, component_config) + def __init__(self, component_config: SolarViewInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_watt/solar_watt/bat.py b/packages/modules/devices/solar_watt/solar_watt/bat.py index e015e3c9c1..bfe1e8a4c1 100644 --- a/packages/modules/devices/solar_watt/solar_watt/bat.py +++ b/packages/modules/devices/solar_watt/solar_watt/bat.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, Dict, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -15,12 +14,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class SolarWattBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolarWattBatSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarWattBatSetup, component_config) + def __init__(self, component_config: SolarWattBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_watt/solar_watt/counter.py b/packages/modules/devices/solar_watt/solar_watt/counter.py index 381d10a568..6ca53ea90b 100644 --- a/packages/modules/devices/solar_watt/solar_watt/counter.py +++ b/packages/modules/devices/solar_watt/solar_watt/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, Dict, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -15,12 +14,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class SolarWattCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolarWattCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarWattCounterSetup, component_config) + def __init__(self, component_config: SolarWattCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_watt/solar_watt/device.py b/packages/modules/devices/solar_watt/solar_watt/device.py index 7873afaa7d..aded618a55 100644 --- a/packages/modules/devices/solar_watt/solar_watt/device.py +++ b/packages/modules/devices/solar_watt/solar_watt/device.py @@ -46,13 +46,13 @@ def request(url: str) -> Dict: def create_device(device_config: SolarWatt): def create_bat_component(component_config: SolarWattBatSetup): - return SolarWattBat(device_config.id, component_config) + return SolarWattBat(component_config, device_id=device_config.id) def create_counter_component(component_config: SolarWattCounterSetup): - return SolarWattCounter(device_config.id, component_config) + return SolarWattCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: SolarWattInverterSetup): - return SolarWattInverter(device_config.id, component_config) + return SolarWattInverter(component_config, device_id=device_config.id) def update_components(components: Dict[str, Union[SolarWattBat, SolarWattCounter, SolarWattInverter]]): update(components, device_config.configuration.energy_manager, device_config.configuration.ip_address) diff --git a/packages/modules/devices/solar_watt/solar_watt/inverter.py b/packages/modules/devices/solar_watt/solar_watt/inverter.py index f4e403f51e..28c14ee47a 100644 --- a/packages/modules/devices/solar_watt/solar_watt/inverter.py +++ b/packages/modules/devices/solar_watt/solar_watt/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, Dict, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -15,12 +14,17 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + + class SolarWattInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolarWattInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarWattInverterSetup, component_config) + def __init__(self, component_config: SolarWattInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_world/solar_world/counter.py b/packages/modules/devices/solar_world/solar_world/counter.py index 5b4437959a..b6885a1404 100644 --- a/packages/modules/devices/solar_world/solar_world/counter.py +++ b/packages/modules/devices/solar_world/solar_world/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -11,10 +10,17 @@ from modules.devices.solar_world.solar_world.config import SolarWorldCounterSetup +class KwargsDict(TypedDict): + device_id: int + + class SolarWorldCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: Union[Dict, SolarWorldCounterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarWorldCounterSetup, component_config) + def __init__(self, component_config: SolarWorldCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solar_world/solar_world/device.py b/packages/modules/devices/solar_world/solar_world/device.py index 86469138b6..0dc7f812d2 100644 --- a/packages/modules/devices/solar_world/solar_world/device.py +++ b/packages/modules/devices/solar_world/solar_world/device.py @@ -5,6 +5,7 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.devices.solar_world.solar_world import counter, inverter from modules.devices.solar_world.solar_world.config import ( @@ -17,16 +18,17 @@ def create_device(device_config: SolarWorld): def create_counter_component(component_config: SolarWorldCounterSetup): - return SolarWorldCounter(device_config.id, component_config) + return SolarWorldCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: SolarWorldInverterSetup): - return SolarWorldInverter(device_config.id, component_config) + return SolarWorldInverter(component_config, device_id=device_config.id) def update_components(components: Iterable[Union[SolarWorldCounter, SolarWorldInverter]]): response = req.get_http_session().get("http://"+str(device_config.configuration.ip_address) + "/rest/solarworld/lpvm/powerAndBatteryData", timeout=5).json() for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/solar_world/solar_world/inverter.py b/packages/modules/devices/solar_world/solar_world/inverter.py index 7d6519ada3..3eefa6f404 100644 --- a/packages/modules/devices/solar_world/solar_world/inverter.py +++ b/packages/modules/devices/solar_world/solar_world/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -11,10 +10,17 @@ from modules.devices.solar_world.solar_world.config import SolarWorldInverterSetup +class KwargsDict(TypedDict): + device_id: int + + class SolarWorldInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: Union[Dict, SolarWorldInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarWorldInverterSetup, component_config) + def __init__(self, component_config: SolarWorldInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solaredge/solaredge/bat.py b/packages/modules/devices/solaredge/solaredge/bat.py index 59483aefa0..cb81757c2a 100644 --- a/packages/modules/devices/solaredge/solaredge/bat.py +++ b/packages/modules/devices/solaredge/solaredge/bat.py @@ -1,10 +1,16 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Tuple, Union + +from typing import Any, TypedDict, Dict, Union, Optional, Tuple + from pymodbus.constants import Endian +import pymodbus + + +from control import data + -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -18,19 +24,45 @@ log = logging.getLogger(__name__) FLOAT32_UNSUPPORTED = -0xffffff00000000000000000000000000 +MAX_DISCHARGE_LIMIT = 5000 +DEFAULT_CONTROL_MODE = 1 # Control Mode Max Eigenverbrauch +REMOTE_CONTROL_MODE = 4 # Control Mode Remotesteuerung +DEFAULT_COMMAND_MODE = 0 # Command Mode ohne Steuerung +ACTIVE_COMMAND_MODE = 7 # Command Mode Max Eigenverbrauch bei Steuerung + + +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ class SolaredgeBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolaredgeBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolaredgeBatSetup, component_config) - self.__tcp_client = tcp_client + # Define all possible registers with their data types + REGISTERS = { + "Battery1StateOfEnergy": (0xe184, ModbusDataType.FLOAT_32,), # Mirror: 0xf584 + "Battery1InstantaneousPower": (0xe174, ModbusDataType.FLOAT_32,), # Mirror: 0xf574 + "Battery2StateOfEnergy": (0xe284, ModbusDataType.FLOAT_32,), + "Battery2InstantaneousPower": (0xe274, ModbusDataType.FLOAT_32,), + "StorageControlMode": (0xe004, ModbusDataType.UINT_16,), + "StorageBackupReserved": (0xe008, ModbusDataType.FLOAT_32,), + "StorageChargeDischargeDefaultMode": (0xe00a, ModbusDataType.UINT_16,), + "RemoteControlCommandMode": (0xe00d, ModbusDataType.UINT_16,), + "RemoteControlCommandDischargeLimit": (0xe010, ModbusDataType.FLOAT_32,), + } + + def __init__(self, component_config: SolaredgeBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.min_soc = 13 + self.StorageControlMode_Read = DEFAULT_CONTROL_MODE + self.last_mode = 'undefined' def update(self) -> None: self.store.set(self.read_state()) @@ -47,16 +79,193 @@ def read_state(self): def get_values(self) -> Tuple[float, float]: unit = self.component_config.configuration.modbus_id + # Use 1 as fallback if battery_index is not set + battery_index = getattr(self.component_config.configuration, "battery_index", 1) + + # Define base registers for Battery 1 in hex + base_soc_reg = 0xE184 # Battery 1 SoC + base_power_reg = 0xE174 # Battery 1 Power + offset = 0x100 # 256 bytes in hex + + # Adjust registers based on battery_index + if battery_index == 1: + soc_reg = base_soc_reg + power_reg = base_power_reg + elif battery_index == 2: + soc_reg = base_soc_reg + offset # 0xE284 + power_reg = base_power_reg + offset # 0xE274 + else: + raise ValueError(f"Invalid battery_index: {battery_index}. Must be 1 or 2.") + + # Read SoC and Power from the appropriate registers soc = self.__tcp_client.read_holding_registers( - 62852, ModbusDataType.FLOAT_32, wordorder=Endian.Little, unit=unit) + soc_reg, ModbusDataType.FLOAT_32, wordorder=Endian.Little, unit=unit + ) power = self.__tcp_client.read_holding_registers( - 62836, ModbusDataType.FLOAT_32, wordorder=Endian.Little, unit=unit) + power_reg, ModbusDataType.FLOAT_32, wordorder=Endian.Little, unit=unit + ) + + # Handle unsupported case if power == FLOAT32_UNSUPPORTED: power = 0 + if soc == FLOAT32_UNSUPPORTED or not 0 <= soc <= 100: + log.warning(f"Invalid SoC Speicher{battery_index}: {soc}") + else: + self.min_soc = min(int(soc), int(self.min_soc)) + log.debug(f"Min-SoC Speicher{battery_index}: {int(self.min_soc)}%.") + return power, soc def get_imported_exported(self, power: float) -> Tuple[float, float]: return self.sim_counter.sim_count(power) + def set_power_limit(self, power_limit: Optional[int]) -> None: + unit = self.component_config.configuration.modbus_id + # Use 1 as fallback if battery_index is not set + battery_index = getattr(self.component_config.configuration, "battery_index", 1) + + try: + power_limit_mode = data.data.bat_all_data.data.config.power_limit_mode + except AttributeError: + log.warning("power_limit_mode not found, assuming 'no_limit'") + power_limit_mode = 'no_limit' + + if power_limit_mode == 'no_limit' and self.last_mode != 'limited': + """ + Keine Speichersteuerung, andere Steuerungen zulassen (SolarEdge One, ioBroker, Node-Red etc.). + Falls andere Steuerungen vorhanden sind, sollten diese nicht beeinflusst werden, + daher erfolgt im Modus "Immer" der Speichersteuerung keine Steuerung. + """ + return + + if power_limit is None: + # Keine Ladung mit Speichersteuerung. + if self.last_mode == 'limited': + # Steuerung deaktivieren. + log.debug(f"Speicher{battery_index}:Keine Steuerung gefordert, Steuerung deaktivieren.") + values_to_write = { + "RemoteControlCommandDischargeLimit": MAX_DISCHARGE_LIMIT, + "StorageChargeDischargeDefaultMode": DEFAULT_COMMAND_MODE, + "RemoteControlCommandMode": DEFAULT_COMMAND_MODE, + "StorageControlMode": self.StorageControlMode_Read, + } + self._write_registers(values_to_write, unit) + self.last_mode = None + else: + return + + elif abs(power_limit) >= 0: + """ + Ladung mit Speichersteuerung. + SolarEdge entlaedt den Speicher immer nur bis zur SoC-Reserve. + Steuerung beenden, wenn der SoC vom Speicher die SoC-Reserve unterschreitet. + """ + registers_to_read = [ + f"Battery{battery_index}StateOfEnergy", + "StorageControlMode", + "StorageBackupReserved", + "RemoteControlCommandDischargeLimit", + ] + try: + values = self._read_registers(registers_to_read, unit) + except pymodbus.exceptions.ModbusException as e: + log.error(f"Failed to read registers: {e}") + self.fault_state.error(f"Modbus read error: {e}") + return + soc = values[f"Battery{battery_index}StateOfEnergy"] + if soc == FLOAT32_UNSUPPORTED or not 0 <= soc <= 100: + log.warning(f"Speicher{battery_index}: Invalid SoC: {soc}") + soc_reserve = max(int(self.min_soc + 2), int(values["StorageBackupReserved"])) + log.debug(f"SoC-Reserve Speicher{battery_index}: {int(soc_reserve)}%.") + discharge_limit = int(values["RemoteControlCommandDischargeLimit"]) + + if values["StorageControlMode"] == REMOTE_CONTROL_MODE: # Speichersteuerung ist aktiv. + if soc_reserve > soc: + # Speichersteuerung erst deaktivieren, wenn SoC-Reserve unterschritten wird. + # Darf wegen 2 Speichern nicht bereits bei SoC-Reserve deaktiviert werden! + log.debug(f"Speicher{battery_index}: Steuerung deaktivieren. SoC-Reserve unterschritten") + values_to_write = { + "RemoteControlCommandDischargeLimit": MAX_DISCHARGE_LIMIT, + "StorageChargeDischargeDefaultMode": DEFAULT_COMMAND_MODE, + "RemoteControlCommandMode": DEFAULT_COMMAND_MODE, + "StorageControlMode": self.StorageControlMode_Read, + } + self._write_registers(values_to_write, unit) + self.last_mode = None + + elif discharge_limit not in range(int(abs(power_limit)) - 10, int(abs(power_limit)) + 10): + # Limit nur bei Abweichung von mehr als 10W, um Konflikte bei 2 Speichern zu verhindern. + log.debug(f"Discharge-Limit Speicher{battery_index}: {int(abs(power_limit))}W.") + values_to_write = { + "RemoteControlCommandDischargeLimit": int(min(abs(power_limit), MAX_DISCHARGE_LIMIT)) + } + self._write_registers(values_to_write, unit) + self.last_mode = 'limited' + + else: # Speichersteuerung ist inaktiv. + if soc_reserve < soc: + # Speichersteuerung nur aktivieren, wenn SoC ueber SoC-Reserve. + log.debug(f"Discharge-Limit aktivieren, Speicher{battery_index}: {int(abs(power_limit))}W.") + self.StorageControlMode_Read = values["StorageControlMode"] + values_to_write = { + "StorageControlMode": REMOTE_CONTROL_MODE, + "StorageChargeDischargeDefaultMode": ACTIVE_COMMAND_MODE, + "RemoteControlCommandMode": ACTIVE_COMMAND_MODE, + "RemoteControlCommandDischargeLimit": int(min(abs(power_limit), MAX_DISCHARGE_LIMIT)) + } + self._write_registers(values_to_write, unit) + self.last_mode = 'limited' + + def _read_registers(self, register_names: list, unit: int) -> Dict[str, Union[int, float]]: + values = {} + for key in register_names: + address, data_type = self.REGISTERS[key] + try: + values[key] = self.__tcp_client.read_holding_registers( + address, data_type, wordorder=Endian.Little, unit=unit + ) + except pymodbus.exceptions.ModbusException as e: + log.error(f"Failed to read register {key} at address {address}: {e}") + self.fault_state.error(f"Modbus read error: {e}") + values[key] = 0 # Fallback value + log.debug(f"Bat raw values {self.__tcp_client.address}: {values}") + return values + # TODO: Optimize to read multiple contiguous registers in a single request if supported by ModbusTcpClient_ + + def _write_registers(self, values_to_write: Dict[str, Union[int, float]], unit: int) -> None: + for key, value in values_to_write.items(): + address, data_type = self.REGISTERS[key] + encoded_value = self._encode_value(value, data_type) + try: + self.__tcp_client.write_registers(address, encoded_value, unit=unit) + log.debug(f"Neuer Wert {encoded_value} in Register {address} geschrieben.") + except pymodbus.exceptions.ModbusException as e: + log.error(f"Failed to write register {key} at address {address}: {e}") + self.fault_state.error(f"Modbus write error: {e}") + + def _encode_value(self, value: Union[int, float], data_type: ModbusDataType) -> list: + builder = pymodbus.payload.BinaryPayloadBuilder( + byteorder=pymodbus.constants.Endian.Big, + wordorder=pymodbus.constants.Endian.Little + ) + encode_methods = { + ModbusDataType.UINT_32: builder.add_32bit_uint, + ModbusDataType.INT_32: builder.add_32bit_int, + ModbusDataType.UINT_16: builder.add_16bit_uint, + ModbusDataType.INT_16: builder.add_16bit_int, + ModbusDataType.FLOAT_32: builder.add_32bit_float, + } + if data_type in encode_methods: + if data_type == ModbusDataType.FLOAT_32: + encode_methods[data_type](float(value)) + else: + encode_methods[data_type](int(value)) + else: + raise ValueError(f"Unsupported data type: {data_type}") + return builder.to_registers() + + def power_limit_controllable(self) -> bool: + return True + component_descriptor = ComponentDescriptor(configuration_factory=SolaredgeBatSetup) diff --git a/packages/modules/devices/solaredge/solaredge/config.py b/packages/modules/devices/solaredge/solaredge/config.py index d09a46137a..a0e370cb45 100644 --- a/packages/modules/devices/solaredge/solaredge/config.py +++ b/packages/modules/devices/solaredge/solaredge/config.py @@ -24,8 +24,9 @@ def __init__(self, class SolaredgeBatConfiguration: - def __init__(self, modbus_id: int = 1): + def __init__(self, modbus_id: int = 1, battery_index: int = 1): self.modbus_id = modbus_id + self.battery_index = battery_index class SolaredgeBatSetup(ComponentSetup[SolaredgeBatConfiguration]): diff --git a/packages/modules/devices/solaredge/solaredge/counter.py b/packages/modules/devices/solaredge/solaredge/counter.py index ee5be24628..03ea24a0a6 100644 --- a/packages/modules/devices/solaredge/solaredge/counter.py +++ b/packages/modules/devices/solaredge/solaredge/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -12,21 +11,31 @@ from modules.common.store import get_counter_value_store from modules.devices.solaredge.solaredge.config import SolaredgeCounterSetup from modules.devices.solaredge.solaredge.scale import create_scaled_reader -from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters +from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters, set_component_registers log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + components: Dict + + class SolaredgeCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, SolaredgeCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SolaredgeCounterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SolaredgeCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.registers = SolaredgeMeterRegisters() self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + components = list(self.kwargs['components'].values()) + components.append(self) + set_component_registers(self.component_config, self.__tcp_client, components) + self._read_scaled_int16 = create_scaled_reader( self.__tcp_client, self.component_config.configuration.modbus_id, ModbusDataType.INT_16 ) diff --git a/packages/modules/devices/solaredge/solaredge/device.py b/packages/modules/devices/solaredge/solaredge/device.py index 3426bb3124..6b4bb205e4 100644 --- a/packages/modules/devices/solaredge/solaredge/device.py +++ b/packages/modules/devices/solaredge/solaredge/device.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 import logging -from typing import Iterable, Union, List +from typing import Iterable, Union from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor @@ -12,107 +12,56 @@ from modules.devices.solaredge.solaredge.inverter import SolaredgeInverter from modules.devices.solaredge.solaredge.config import (Solaredge, SolaredgeBatSetup, SolaredgeCounterSetup, SolaredgeExternalInverterSetup, SolaredgeInverterSetup) -from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters log = logging.getLogger(__name__) -solaredge_component_classes = Union[SolaredgeBat, SolaredgeCounter, - SolaredgeExternalInverter, SolaredgeInverter] -default_unit_id = 85 -synergy_unit_identifier = 160 -reconnect_delay = 1.2 - - -def set_component_registers(components: Iterable[solaredge_component_classes], - synergy_units: int, - modbus_id: int) -> None: - meters: List[Union[SolaredgeExternalInverter, SolaredgeCounter, None]] = [None]*3 - for component in components: - if (isinstance(component, (SolaredgeExternalInverter, SolaredgeCounter)) and - component.component_config.configuration.modbus_id == modbus_id): - # Registerverschibung nur für Komponenten mit gleicher Modbus-ID, da diese am gleichen Haupt-WR hängen und - # die gleichen Synergy-Units haben. - meters[component.component_config.configuration.meter_id-1] = component - # https://www.solaredge.com/sites/default/files/sunspec-implementation-technical-note.pdf: - # Only enabled meters are readable, i.e. if meter 1 and 3 are enabled, they are readable as 1st meter and 2nd - # meter (and the 3rd meter isn't readable). - for meter_id, meter in enumerate(filter(None, meters), start=1): - log.debug( - "%s: internal meter id: %d, synergy units: %s", meter.component_config.name, meter_id, synergy_units - ) - meter.registers = SolaredgeMeterRegisters(meter_id, synergy_units) +reconnect_delay = 1.2 def create_device(device_config: Solaredge): + client = None + def create_bat_component(component_config: SolaredgeBatSetup): - return SolaredgeBat(device_config.id, component_config, client) + nonlocal client + return SolaredgeBat(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: SolaredgeCounterSetup): - nonlocal device - synergy_units = get_synergy_units(component_config) - counter = SolaredgeCounter(device_config.id, component_config, client) - # neue Komponente wird erst nach Instanziierung device.components hinzugefügt - components = list(device.components.values()) - components.append(counter) - set_component_registers(components, synergy_units, component_config.configuration.modbus_id) - return counter + nonlocal client, device + return SolaredgeCounter(component_config, client=client, components=device.components) def create_inverter_component(component_config: SolaredgeInverterSetup): - return SolaredgeInverter(device_config.id, component_config, client) + nonlocal client + return SolaredgeInverter(component_config, client=client, device_id=device_config.id) def create_external_inverter_component(component_config: SolaredgeExternalInverterSetup): - nonlocal device - synergy_units = get_synergy_units(component_config) - external_inverter = SolaredgeExternalInverter(device_config.id, component_config, client) - components = list(device.components.values()) - components.append(external_inverter) - set_component_registers(components, synergy_units, component_config.configuration.modbus_id) - return external_inverter + nonlocal client, device + return SolaredgeExternalInverter(component_config, client=client, components=device.components) def update_components(components: Iterable[Union[SolaredgeBat, SolaredgeCounter, SolaredgeInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - def get_synergy_units(component_config: Union[SolaredgeBatSetup, - SolaredgeCounterSetup, - SolaredgeInverterSetup, - SolaredgeExternalInverterSetup]) -> None: - try: - if client.read_holding_registers(40121, modbus.ModbusDataType.UINT_16, - unit=component_config.configuration.modbus_id - ) == synergy_unit_identifier: - # Snyergy-Units vom Haupt-WR des angeschlossenen Meters ermitteln. Es kann mehrere Haupt-WR mit - # unterschiedlichen Modbus-IDs im Verbund geben. - log.debug("Synergy Units supported") - synergy_units = int(client.read_holding_registers( - 40129, modbus.ModbusDataType.UINT_16, - unit=component_config.configuration.modbus_id)) or 1 - log.debug( - f"Synergy Units detected for Modbus ID {component_config.configuration.modbus_id}: {synergy_units}") - else: - synergy_units = 1 - except Exception: - synergy_units = 1 - return synergy_units - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port, reconnect_delay=reconnect_delay) - device = ConfigurableDevice( - device_config=device_config, - component_factory=ComponentFactoryByType( - bat=create_bat_component, - counter=create_counter_component, - external_inverter=create_external_inverter_component, - inverter=create_inverter_component, - ), - component_updater=MultiComponentUpdater(update_components) - ) - except Exception: - log.exception("Fehler in create_device") + + device = ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + external_inverter=create_external_inverter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) return device diff --git a/packages/modules/devices/solaredge/solaredge/device_test.py b/packages/modules/devices/solaredge/solaredge/device_test.py deleted file mode 100644 index 5547a1f598..0000000000 --- a/packages/modules/devices/solaredge/solaredge/device_test.py +++ /dev/null @@ -1,64 +0,0 @@ -from typing import List, NamedTuple, Type -from unittest.mock import Mock - -import pytest - -from modules.devices.solaredge.solaredge import device -from modules.devices.solaredge.solaredge.bat import SolaredgeBat -from modules.devices.solaredge.solaredge.counter import SolaredgeCounter -from modules.devices.solaredge.solaredge.external_inverter import SolaredgeExternalInverter -from modules.devices.solaredge.solaredge.inverter import SolaredgeInverter - - -Params = NamedTuple("Params", [("configured_meter_ids", List[int]), ("effective_meter_ids", List[int])]) - - -@pytest.mark.parametrize(["params"], [ - pytest.param( - Params(configured_meter_ids=[1, 2], effective_meter_ids=[1, 2]), - id="ids unchanged if meter_ids are continuous starting from 1" - ), - pytest.param( - Params(configured_meter_ids=[2, 3], effective_meter_ids=[1, 2]), - id="ids move forward if not starting at 1" - ), - pytest.param( - Params(configured_meter_ids=[1, 3], effective_meter_ids=[1, 2]), - id="gaps in ids are closed" - ) -]) -def test_set_component_registers_assigns_effective_meter_ids(monkeypatch, params: Params): - # setup - monkeypatch.setattr( - device, "SolaredgeMeterRegisters", Mock(side_effect=lambda internal_meter_id, _: internal_meter_id) - ) - components_list = [ - Mock(spec=SolaredgeCounter, component_config=Mock(configuration=Mock(meter_id=meter_id, modbus_id=1))) - for meter_id in params.configured_meter_ids - ] - - # execution - device.set_component_registers(components_list, synergy_units=1, modbus_id=1) - - # evaluation - assert [component.registers for component in components_list] == params.effective_meter_ids - - -@pytest.mark.parametrize("type,should_use", [ - (SolaredgeCounter, True), - (SolaredgeExternalInverter, True), - (SolaredgeBat, False), - (SolaredgeInverter, False), -]) -def test_set_component_registers_ignores_wrong_types(monkeypatch, type: Type, should_use: bool): - # setup - monkeypatch.setattr( - device, "SolaredgeMeterRegisters", Mock(side_effect=lambda *args: True) - ) - components = [Mock(spec=type, component_config=Mock(configuration=Mock(meter_id=1, modbus_id=1))) - ] - # execution - device.set_component_registers(components, synergy_units=1, modbus_id=1) - - # evaluation - assert hasattr(components[0], "registers") == should_use diff --git a/packages/modules/devices/solaredge/solaredge/external_inverter.py b/packages/modules/devices/solaredge/solaredge/external_inverter.py index 44243cd208..e4f4185d62 100644 --- a/packages/modules/devices/solaredge/solaredge/external_inverter.py +++ b/packages/modules/devices/solaredge/solaredge/external_inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Dict, TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -12,21 +11,33 @@ from modules.common.store import get_inverter_value_store from modules.devices.solaredge.solaredge.config import SolaredgeExternalInverterSetup from modules.devices.solaredge.solaredge.scale import create_scaled_reader -from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters +from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters, set_component_registers log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + components: Dict + + class SolaredgeExternalInverter(AbstractInverter): def __init__(self, - device_id: int, - component_config: Union[Dict, SolaredgeExternalInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SolaredgeExternalInverterSetup, component_config) - self.__tcp_client = tcp_client + component_config: SolaredgeExternalInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] self.registers = SolaredgeMeterRegisters(self.component_config.configuration.meter_id) self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + components = list(self.kwargs['components'].values()) + components.append(self) + set_component_registers(self.component_config, self.__tcp_client, components) + self._read_scaled_int16 = create_scaled_reader( self.__tcp_client, self.component_config.configuration.modbus_id, ModbusDataType.INT_16 ) diff --git a/packages/modules/devices/solaredge/solaredge/inverter.py b/packages/modules/devices/solaredge/solaredge/inverter.py index 948b782f85..708e45ed4c 100644 --- a/packages/modules/devices/solaredge/solaredge/inverter.py +++ b/packages/modules/devices/solaredge/solaredge/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -11,15 +10,23 @@ from modules.common.store import get_inverter_value_store from modules.devices.solaredge.solaredge.config import SolaredgeInverterSetup from modules.devices.solaredge.solaredge.scale import create_scaled_reader +from modules.common.simcount import SimCounter + + +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + device_id: int class SolaredgeInverter(AbstractInverter): def __init__(self, - device_id: int, - component_config: Union[Dict, SolaredgeInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(SolaredgeInverterSetup, component_config) - self.__tcp_client = tcp_client + component_config: SolaredgeInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self._read_scaled_int16 = create_scaled_reader( @@ -31,6 +38,7 @@ def __init__(self, self._read_scaled_uint32 = create_scaled_reader( self.__tcp_client, self.component_config.configuration.modbus_id, ModbusDataType.UINT_32 ) + self.sim_counter = SimCounter(self.kwargs['device_id'], self.component_config.id, prefix="Wechselrichter") def update(self) -> None: self.store.set(self.read_state()) @@ -51,11 +59,14 @@ def read_state(self): # Wenn bei Hybrid-Systemen der Speicher aus dem Netz geladen wird, ist die DC-Leistung negativ. dc_power = self._read_scaled_int16(40100, 1)[0] * -1 + imported, _ = self.sim_counter.sim_count(power) + return InverterState( power=power, exported=exported, currents=currents, - dc_power=dc_power + dc_power=dc_power, + imported=imported, ) diff --git a/packages/modules/devices/solaredge/solaredge/inverter_test.py b/packages/modules/devices/solaredge/solaredge/inverter_test.py index 6fc88254ec..496ad865ae 100644 --- a/packages/modules/devices/solaredge/solaredge/inverter_test.py +++ b/packages/modules/devices/solaredge/solaredge/inverter_test.py @@ -15,8 +15,9 @@ def test_read_state(): [616, 65535, 65535, -2], [14368, -1] ]) - inverter = SolaredgeInverter(0, SolaredgeInverterSetup(), Mock( - spec=ModbusTcpClient_, read_holding_registers=mock_read_holding_registers)) + inverter = SolaredgeInverter(SolaredgeInverterSetup(), client=Mock( + spec=ModbusTcpClient_, read_holding_registers=mock_read_holding_registers), device_id=1) + inverter.initialize() # execution inverter_state = inverter.read_state() @@ -26,6 +27,7 @@ def test_read_state(): power=-1415.2, exported=8980404, currents=[6.16, 0, 0], - dc_power=-1436.8000000000002 + dc_power=-1436.8000000000002, + imported=100, ) ) diff --git a/packages/modules/devices/solaredge/solaredge/meter.py b/packages/modules/devices/solaredge/solaredge/meter.py index c759a27d05..ebf7d2c257 100644 --- a/packages/modules/devices/solaredge/solaredge/meter.py +++ b/packages/modules/devices/solaredge/solaredge/meter.py @@ -1,10 +1,16 @@ import logging +from typing import Iterable, Union, List - +from modules.common import modbus +from modules.devices.solaredge.solaredge.config import (SolaredgeBatSetup, SolaredgeCounterSetup, + SolaredgeExternalInverterSetup, SolaredgeInverterSetup) log = logging.getLogger(__name__) +synergy_unit_identifier = 160 + + class SolaredgeMeterRegisters: def __init__(self, internal_meter_id: int = 1, synergy_units: int = 1): # 40206: Total Real Power (sum of active phases) @@ -53,3 +59,55 @@ def _update_offset_synergy_units(self, synergy_units: int) -> None: def _add_offset(self, offset: int) -> None: for name, value in self.__dict__.items(): setattr(self, name, value+offset) + + +def _set_registers(components: Iterable, + synergy_units: int, + modbus_id: int) -> None: + meters: List = [None]*3 + for component in components: + if (isinstance(component.component_config, (SolaredgeCounterSetup, SolaredgeExternalInverterSetup)) and + component.component_config.configuration.modbus_id == modbus_id): + # Registerverschibung nur für Komponenten mit gleicher Modbus-ID, da diese am gleichen Haupt-WR hängen + # und die gleichen Synergy-Units haben. + meters[component.component_config.configuration.meter_id-1] = component + + # https://www.solaredge.com/sites/default/files/sunspec-implementation-technical-note.pdf: + # Only enabled meters are readable, i.e. if meter 1 and 3 are enabled, they are readable as 1st meter and 2nd + # meter (and the 3rd meter isn't readable). + for meter_id, meter in enumerate(filter(None, meters), start=1): + log.debug( + "%s: internal meter id: %d, synergy units: %s", meter.component_config.name, meter_id, synergy_units + ) + meter.registers = SolaredgeMeterRegisters(meter_id, synergy_units) + + +def _get_synergy_units(component_config: Union[SolaredgeBatSetup, + SolaredgeCounterSetup, + SolaredgeInverterSetup, + SolaredgeExternalInverterSetup], + client) -> int: + if client.read_holding_registers(40121, modbus.ModbusDataType.UINT_16, + unit=component_config.configuration.modbus_id + ) == synergy_unit_identifier: + # Snyergy-Units vom Haupt-WR des angeschlossenen Meters ermitteln. Es kann mehrere Haupt-WR mit + # unterschiedlichen Modbus-IDs im Verbund geben. + log.debug("Synergy Units supported") + synergy_units = int(client.read_holding_registers( + 40129, modbus.ModbusDataType.UINT_16, + unit=component_config.configuration.modbus_id)) or 1 + log.debug( + f"Synergy Units detected for Modbus ID {component_config.configuration.modbus_id}: {synergy_units}") + else: + synergy_units = 1 + return synergy_units + + +def set_component_registers(component_config: Union[SolaredgeBatSetup, + SolaredgeCounterSetup, + SolaredgeInverterSetup, + SolaredgeExternalInverterSetup], + client, + components: Iterable) -> None: + synergy_units = _get_synergy_units(component_config, client) + _set_registers(components, synergy_units, component_config.configuration.modbus_id) diff --git a/packages/modules/devices/solaredge/solaredge/meter_test.py b/packages/modules/devices/solaredge/solaredge/meter_test.py index 72a1aa4dae..02223c9d26 100644 --- a/packages/modules/devices/solaredge/solaredge/meter_test.py +++ b/packages/modules/devices/solaredge/solaredge/meter_test.py @@ -1,7 +1,10 @@ -from typing import NamedTuple +from typing import List, NamedTuple +from unittest.mock import Mock import pytest -from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters +from modules.devices.solaredge.solaredge.config import SolaredgeCounterSetup +from modules.devices.solaredge.solaredge.counter import SolaredgeCounter +from modules.devices.solaredge.solaredge.meter import SolaredgeMeterRegisters, _set_registers Params = NamedTuple("Params", [("meter_id", int), @@ -24,3 +27,39 @@ def test_meter(params: Params): # assert assert registers.powers == params.expected_power_register + + +Params = NamedTuple("Params", [("configured_meter_ids", List[int])]) + + +@pytest.mark.parametrize(["params"], [ + pytest.param( + Params(configured_meter_ids=[1, 2]), + id="ids unchanged if meter_ids are continuous starting from 1" + ), + pytest.param( + Params(configured_meter_ids=[2, 3]), + id="ids move forward if not starting at 1" + ), + pytest.param( + Params(configured_meter_ids=[1, 3]), + id="gaps in ids are closed" + ) +]) +def test_set_component_registers_assigns_effective_meter_regs(params: Params): + # setup + components_list = [] + for meter_id in params.configured_meter_ids: + counter = SolaredgeCounter(component_config=Mock(spec=SolaredgeCounterSetup, + configuration=Mock(meter_id=meter_id, modbus_id=1))) + counter.component_config.configure_mock(name="counter") + components_list.append(counter) + + for counter in components_list: + counter.registers = SolaredgeMeterRegisters(internal_meter_id=counter.component_config.configuration.meter_id) + # execution + _set_registers(components_list, synergy_units=1, modbus_id=1) + + # evaluation + assert components_list[0].registers.powers == 40206 + assert components_list[1].registers.powers == 40380 diff --git a/packages/modules/devices/solarmax/solarmax/bat.py b/packages/modules/devices/solarmax/solarmax/bat.py index 3d323c18e3..c15135050e 100644 --- a/packages/modules/devices/solarmax/solarmax/bat.py +++ b/packages/modules/devices/solarmax/solarmax/bat.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -10,18 +11,27 @@ from modules.devices.solarmax.solarmax.config import SolarmaxBatSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class SolarmaxBat(AbstractBat): - def __init__(self, device_id: int, component_config: SolarmaxBatSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarmaxBatSetup, component_config) + def __init__(self, component_config: SolarmaxBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_holding_registers(114, ModbusDataType.INT_32, unit=unit) - soc = client.read_holding_registers(122, ModbusDataType.INT_16, unit=unit) + power = self.client.read_holding_registers(114, ModbusDataType.INT_32, unit=unit) + soc = self.client.read_holding_registers(122, ModbusDataType.INT_16, unit=unit) imported, exported = self.sim_counter.sim_count(power) bat_state = BatState( diff --git a/packages/modules/devices/solarmax/solarmax/device.py b/packages/modules/devices/solarmax/solarmax/device.py index 2a71a6ba04..3fb514d09d 100644 --- a/packages/modules/devices/solarmax/solarmax/device.py +++ b/packages/modules/devices/solarmax/solarmax/device.py @@ -16,24 +16,30 @@ def create_device(device_config: Solarmax): + client = None + def create_bat_component(component_config: SolarmaxBatSetup): - return SolarmaxBat(device_config.id, component_config) + nonlocal client + return SolarmaxBat(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: SolarmaxInverterSetup): - return inverter.SolarmaxInverter(device_config.id, component_config) + nonlocal client + return inverter.SolarmaxInverter(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[SolarmaxBat, inverter.SolarmaxInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, inverter=create_inverter_component, diff --git a/packages/modules/devices/solarmax/solarmax/inverter.py b/packages/modules/devices/solarmax/solarmax/inverter.py index 11d5bffe7d..b2ea19d55f 100644 --- a/packages/modules/devices/solarmax/solarmax/inverter.py +++ b/packages/modules/devices/solarmax/solarmax/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,19 +11,28 @@ from modules.devices.solarmax.solarmax.config import SolarmaxInverterSetup +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + + class SolarmaxInverter(AbstractInverter): def __init__(self, - device_id: int, - component_config: Union[Dict, SolarmaxInverterSetup]) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(SolarmaxInverterSetup, component_config) + component_config: SolarmaxInverterSetup, + **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - power = client.read_holding_registers(4151, ModbusDataType.UINT_32, - unit=self.component_config.configuration.modbus_id) * -1 + def update(self) -> None: + power = self.client.read_holding_registers(4151, ModbusDataType.UINT_32, + unit=self.component_config.configuration.modbus_id) * -1 power = power / 10 _, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( diff --git a/packages/modules/devices/solax/solax/bat.py b/packages/modules/devices/solax/solax/bat.py index b6cb8aedd8..59b89cadb6 100644 --- a/packages/modules/devices/solax/solax/bat.py +++ b/packages/modules/devices/solax/solax/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -13,14 +12,19 @@ from modules.devices.solax.solax.config import SolaxBatSetup, Solax +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + device_config: Solax + + class SolaxBat(AbstractBat): - def __init__(self, - device_config: Solax, - component_config: Union[Dict, SolaxBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SolaxBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SolaxBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] + self.device_config = self.kwargs['device_config'] self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solax/solax/counter.py b/packages/modules/devices/solax/solax/counter.py index 53f55788ff..f25f556e51 100644 --- a/packages/modules/devices/solax/solax/counter.py +++ b/packages/modules/devices/solax/solax/counter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -14,14 +13,19 @@ from modules.devices.solax.solax.version import SolaxVersion +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + device_config: Solax + + class SolaxCounter(AbstractCounter): - def __init__(self, - device_config: Solax, - component_config: Union[Dict, SolaxCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SolaxCounterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SolaxCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] + self.device_config = self.kwargs['device_config'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solax/solax/device.py b/packages/modules/devices/solax/solax/device.py index 711f0e0b60..09a0d76940 100644 --- a/packages/modules/devices/solax/solax/device.py +++ b/packages/modules/devices/solax/solax/device.py @@ -15,27 +15,31 @@ def create_device(device_config: Solax): + client = None + def create_bat_component(component_config: SolaxBatSetup): - return SolaxBat(device_config, component_config, client) + return SolaxBat(component_config, device_config=device_config, client=client) def create_counter_component(component_config: SolaxCounterSetup): - return SolaxCounter(device_config, component_config, client) + return SolaxCounter(component_config, device_config=device_config, client=client) def create_inverter_component(component_config: SolaxInverterSetup): - return SolaxInverter(device_config, component_config, client) + return SolaxInverter(component_config, device_config=device_config, client=client) def update_components(components: Iterable[Union[SolaxBat, SolaxCounter, SolaxInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/solax/solax/inverter.py b/packages/modules/devices/solax/solax/inverter.py index a65e4d15ea..eb3a7129e6 100644 --- a/packages/modules/devices/solax/solax/inverter.py +++ b/packages/modules/devices/solax/solax/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict from pymodbus.constants import Endian -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -14,14 +13,19 @@ from modules.devices.solax.solax.version import SolaxVersion +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + device_config: Solax + + class SolaxInverter(AbstractInverter): - def __init__(self, - device_config: Solax, - component_config: Union[Dict, SolaxInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SolaxInverterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SolaxInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client = self.kwargs['client'] + self.device_config = self.kwargs['device_config'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/solis/solis/bat.py b/packages/modules/devices/solis/solis/bat.py index 3bc72335e2..9c1cb5578b 100644 --- a/packages/modules/devices/solis/solis/bat.py +++ b/packages/modules/devices/solis/solis/bat.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import logging -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -12,21 +13,29 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + class SolisBat(AbstractBat): - def __init__(self, component_config: SolisBatSetup) -> None: - self.component_config = dataclass_from_dict(SolisBatSetup, component_config) + def __init__(self, component_config: SolisBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id - power = client.read_input_registers(33149, ModbusDataType.INT_32, unit=unit) * -1 - soc = client.read_input_registers(33139, ModbusDataType.UINT_16, unit=unit) + power = self.client.read_input_registers(33149, ModbusDataType.INT_32, unit=unit) + soc = self.client.read_input_registers(33139, ModbusDataType.UINT_16, unit=unit) # Geladen in kWh - imported = client.read_input_registers(33161, ModbusDataType.UINT_32, unit=unit) * 1000 + imported = self.client.read_input_registers(33161, ModbusDataType.UINT_32, unit=unit) * 1000 # Entladen in kWh - exported = client.read_input_registers(33165, ModbusDataType.UINT_32, unit=unit) * 1000 + exported = self.client.read_input_registers(33165, ModbusDataType.UINT_32, unit=unit) * 1000 bat_state = BatState( power=power, diff --git a/packages/modules/devices/solis/solis/counter.py b/packages/modules/devices/solis/solis/counter.py index 346c5d4f02..8a500f94b9 100644 --- a/packages/modules/devices/solis/solis/counter.py +++ b/packages/modules/devices/solis/solis/counter.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import Any, TypedDict from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -9,26 +9,36 @@ from modules.devices.solis.solis.version import SolisVersion +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + version: SolisVersion + + class SolisCounter: - def __init__(self, component_config: SolisCounterSetup, - version: SolisVersion) -> None: - self.component_config = dataclass_from_dict(SolisCounterSetup, component_config) + def __init__(self, component_config: SolisCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.version: SolisVersion = self.kwargs['version'] self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.version = version + self.version = self.kwargs['version'] + self.client = self.kwargs['client'] - def update(self, client: ModbusTcpClient_): + def update(self): unit = self.component_config.configuration.modbus_id register_offset = 30000 if self.version == SolisVersion.inverter: register_offset = -1 - power = client.read_input_registers(3263 + register_offset, ModbusDataType.INT_32, unit=unit) - powers = client.read_input_registers(3257 + register_offset, [ModbusDataType.INT_32]*3, unit=unit) - frequency = client.read_input_registers(3282 + register_offset, ModbusDataType.UINT_16, unit=unit) / 100 - imported = client.read_input_registers(3283 + register_offset, ModbusDataType.UINT_32, unit=unit) * 10 - exported = client.read_input_registers(3285 + register_offset, ModbusDataType.UINT_32, unit=unit) * 10 + power = self.client.read_input_registers(3263 + register_offset, ModbusDataType.INT_32, unit=unit) * -1 + powers = self.client.read_input_registers(3257 + register_offset, [ModbusDataType.INT_32]*3, unit=unit) + frequency = self.client.read_input_registers(3282 + register_offset, ModbusDataType.UINT_16, unit=unit) / 100 + imported = self.client.read_input_registers(3283 + register_offset, ModbusDataType.UINT_32, unit=unit) * 10 + exported = self.client.read_input_registers(3285 + register_offset, ModbusDataType.UINT_32, unit=unit) * 10 counter_state = CounterState( imported=imported, diff --git a/packages/modules/devices/solis/solis/device.py b/packages/modules/devices/solis/solis/device.py index 65cdaa4f60..6b52c6a3cc 100644 --- a/packages/modules/devices/solis/solis/device.py +++ b/packages/modules/devices/solis/solis/device.py @@ -16,27 +16,36 @@ def create_device(device_config: Solis): + client = None + def create_bat_component(component_config: SolisBatSetup): - return SolisBat(component_config) + nonlocal client + return SolisBat(component_config, client=client) def create_counter_component(component_config: SolisCounterSetup): - return SolisCounter(component_config, SolisVersion(device_config.configuration.version)) + nonlocal client + return SolisCounter(component_config, version=SolisVersion(device_config.configuration.version), client=client) def create_inverter_component(component_config: SolisInverterSetup): - return SolisInverter(component_config, SolisVersion(device_config.configuration.version)) + nonlocal client + return SolisInverter(component_config, + version=SolisVersion(device_config.configuration.version), + client=client) def update_components(components: Iterable[Union[SolisBat, SolisCounter, SolisInverter]]): - with client as c: + nonlocal client + with client: for component in components: with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/solis/solis/inverter.py b/packages/modules/devices/solis/solis/inverter.py index b8fcb55887..9e18df42c6 100644 --- a/packages/modules/devices/solis/solis/inverter.py +++ b/packages/modules/devices/solis/solis/inverter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -11,23 +10,31 @@ from modules.devices.solis.solis.version import SolisVersion +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + version: SolisVersion + + class SolisInverter: - def __init__(self, component_config: Union[Dict, SolisInverterSetup], - version: SolisVersion) -> None: - self.component_config = dataclass_from_dict(SolisInverterSetup, component_config) + def __init__(self, component_config: SolisInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.version: SolisVersion = self.kwargs['version'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - self.version = version - def update(self, client: ModbusTcpClient_) -> None: + def update(self) -> None: unit = self.component_config.configuration.modbus_id if self.version == SolisVersion.inverter: - power = client.read_input_registers(3004, ModbusDataType.UINT_32, unit=unit) * -1 - exported = client.read_input_registers(3008, ModbusDataType.UINT_32, unit=unit) * 1000 + power = self.client.read_input_registers(3004, ModbusDataType.UINT_32, unit=unit) * -1 + exported = self.client.read_input_registers(3008, ModbusDataType.UINT_32, unit=unit) * 1000 elif self.version == SolisVersion.hybrid: - power = client.read_input_registers(33057, ModbusDataType.UINT_32, unit=unit) * -1 - exported = client.read_input_registers(33029, ModbusDataType.UINT_32, unit=unit) * 1000 + power = self.client.read_input_registers(33057, ModbusDataType.UINT_32, unit=unit) * -1 + exported = self.client.read_input_registers(33029, ModbusDataType.UINT_32, unit=unit) * 1000 inverter_state = InverterState( power=power, diff --git a/packages/modules/devices/sonnen/sonnenbatterie/api.py b/packages/modules/devices/sonnen/sonnenbatterie/api.py new file mode 100644 index 0000000000..f0bc51680d --- /dev/null +++ b/packages/modules/devices/sonnen/sonnenbatterie/api.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python3 +from enum import Enum +from typing import Dict, List, Optional, TypedDict, Union +from modules.common import req +from modules.common.component_state import BatState, CounterState, InverterState +from modules.common.simcount import SimCounter + + +class RestApi1(): + def __init__(self, host: str) -> None: + self.host = host + + def power_limit_controllable(self) -> bool: + """ + Checks if the power limit is controllable via the REST API. + Returns: + bool: True if controllable, False otherwise. + """ + return False + + def __read(self, device_endpoint: str = 'battery') -> dict: + """ + Reads data from the Sonnenbatterie REST API. + Args: + device_endpoint (str): The device to read data from. Defaults to 'battery'. + Returns: + dict: The JSON response from the API. + """ + return req.get_http_session().get( + f'http://{self.host}:7979/rest/devices/{device_endpoint}', + timeout=5).json() + + def update_battery(self, sim_counter: SimCounter) -> BatState: + """ + Updates the battery state by reading data from the REST API. + Returns: + BatState: The updated battery state. + """ + battery_state = self.__read(device_endpoint="battery") + battery_soc = int(battery_state["M05"]) + battery_export_power = int(battery_state["M34"]) + battery_import_power = int(battery_state["M35"]) + battery_power = battery_import_power - battery_export_power + imported, exported = sim_counter.sim_count(battery_power) + return BatState(power=battery_power, + soc=battery_soc, + imported=imported, + exported=exported) + + +class RestApi2(): + def __init__(self, host: str) -> None: + self.host = host + + def power_limit_controllable(self) -> bool: + """ + Checks if the power limit is controllable via the REST API. + Returns: + bool: True if controllable, False otherwise. + """ + return False + + def __read_element(self, device: str, element: str) -> str: + """ + Reads a specific element from the Sonnenbatterie REST API v2. + Args: + device (str): The device to read data from. + element (str): The specific element to read. + Returns: + str: The value of the specified element. + """ + response = req.get_http_session().get( + f'http://{self.host}:7979/rest/devices/{device}/{element}', + timeout=5) + response.encoding = 'utf-8' + return response.text.strip(" \n\r") + + def update_inverter(self, sim_counter: SimCounter) -> InverterState: + """ + Updates the inverter state by reading data from the REST API v2. + Returns: + InverterState: The updated inverter state. + """ + pv_power = -int(float(self.__read_element(device="battery", element="M03"))) + _, exported = sim_counter.sim_count(pv_power) + return InverterState(exported=exported, + power=pv_power) + + def update_grid_counter(self, sim_counter: SimCounter) -> CounterState: + """ + Updates the grid counter state by reading data from the REST API v2. + Returns: + CounterState: The updated grid counter state. + """ + grid_import_power = -int(float(self.__read_element(device="battery", element="M39"))) + grid_export_power = -int(float(self.__read_element(device="battery", element="M38"))) + grid_power = grid_import_power - grid_export_power + imported, exported = sim_counter.sim_count(grid_power) + return CounterState(power=grid_power, + imported=imported, + exported=exported) + + def update_battery(self, sim_counter: SimCounter) -> BatState: + """ + Updates the battery state by reading data from the REST API v2. + Returns: + BatState: The updated battery state. + """ + battery_soc = int(float(self.__read_element(device="battery", element="M05"))) + battery_export_power = int(float(self.__read_element(device="battery", element="M01"))) + battery_import_power = int(float(self.__read_element(device="battery", element="M02"))) + battery_power = battery_import_power - battery_export_power + imported, exported = sim_counter.sim_count(battery_power) + return BatState(power=battery_power, + soc=battery_soc, + imported=imported, + exported=exported) + + +class JsonApiVersion(Enum): + V1 = "v1" + V2 = "v2" + + +class JsonApi(): + class OperatingMode(Enum): + MANUAL = "1" + SELF_CONSUMPTION = "2" + TIME_OF_USE = "10" + + class PowerMeterDirection(Enum): + PRODUCTION = "production" + CONSUMPTION = "consumption" + + class StatusDict(TypedDict): + Apparent_output: int + BackupBuffer: str + BatteryCharging: bool + BatteryDischarging: bool + Consumption_Avg: int + Consumption_W: int + Fac: float + FlowConsumptionBattery: bool + FlowConsumptionGrid: bool + FlowConsumptionProduction: bool + FlowGridBattery: bool + FlowProductionBattery: bool + FlowProductionGrid: bool + GridFeedIn_W: int + IsSystemInstalled: int + OperatingMode: str + Pac_total_W: int + Production_W: int + RSOC: int + RemainingCapacity_Wh: int + Sac1: int + Sac2: int + Sac3: int + SystemStatus: str + Timestamp: str + USOC: int + Uac: float + Ubat: float + + class ChannelDict(TypedDict): + a_l1: int + a_l2: int + a_l3: int + channel: int + deviceid: int + direction: str + error: int + kwh_exported: float + kwh_imported: float + v_l1_l2: float + v_l1_n: float + v_l2_l3: float + v_l2_n: float + v_l3_l1: float + v_l3_n: float + va_total: float + var_total: float + w_l1: float + w_l2: float + w_l3: float + w_total: float + + default_operating_mode: Optional[OperatingMode] = None + + def __init__(self, + host: str, + api_version: JsonApiVersion = JsonApiVersion.V1, + auth_token: Optional[str] = None) -> None: + self.host = host + self.api_version = api_version + self.auth_token = auth_token + if self.api_version == JsonApiVersion.V2 and self.auth_token is None: + raise ValueError("API v2 requires an auth_token.") + self.headers = {"auth-token": auth_token} if api_version == JsonApiVersion.V2 else {} + + def __del__(self) -> None: + """ + Destructor to clean up the object. + """ + # restore normal operating mode + if self.api_version == JsonApiVersion.V2: + self.set_power_limit(None) + + def __read(self, endpoint: str = "status") -> Dict: + """ + Reads data from the Sonnenbatterie JSON API. + Args: + endpoint (str): The endpoint to fetch data from. Defaults to "status". + Returns: + Dict: The JSON response from the API as a dictionary. + """ + return req.get_http_session().get( + f"http://{self.host}/api/{self.api_version.value}/{endpoint}", + timeout=5, + headers=self.headers + ).json() + + def __read_status(self) -> StatusDict: + """ + Reads the status data from the JSON API. + Returns: + StatusDict: The status data as a dictionary. + """ + return self.__read(endpoint="status") + + def __read_power_meter(self, direction: Optional[PowerMeterDirection] = None) -> List[ChannelDict]: + """ + Reads the power meter data from the JSON API. + Args: + direction (Optional[PowerMeterDirection]): The direction of the power meter data. + If None, all data is returned. Defaults to None. + Returns: + List[ChannelDict]: The power meter data as a list of dictionaries. + """ + data = self.__read(endpoint="powermeter") + if direction is not None: + data = [item for item in data if item["direction"] == direction.value] + if len(data) == 0: + raise ValueError(f"No data found for direction: {direction.value}") + return data + + def __state_from_channel(self, channel: ChannelDict) -> Union[CounterState, InverterState]: + """ + Converts a channel dictionary to a CounterState or InverterState object based on channel["direction"]. + If the direction is "consumption", it returns a CounterState object. + If the direction is "production", it returns an InverterState object. + If the direction is neither, it raises a ValueError. + Args: + channel (ChannelDict): The channel data as a dictionary. + Returns: + CounterState|InverterState: The converted State object. + Raises: + ValueError: If the direction is neither "consumption" nor "production". + """ + if channel["direction"] == self.PowerMeterDirection.CONSUMPTION.value: + powers = [channel[f"w_l{phase}"] for phase in range(1, 4)] + currents = [channel[f"a_l{phase}"] for phase in range(1, 4)] + voltages = [channel[f"v_l{phase}_n"] for phase in range(1, 4)] + power_factors = [ + powers[phase] / (voltages[phase] * currents[phase]) + if voltages[phase] and currents[phase] else None + for phase in range(0, 3) + ] + return CounterState(power=channel["w_total"], + powers=powers, + currents=currents, + voltages=voltages, + power_factors=power_factors, + imported=channel["kwh_imported"] * 1000, + exported=channel["kwh_exported"] * 1000) + elif channel["direction"] == self.PowerMeterDirection.PRODUCTION.value: + return InverterState(power=-channel["w_total"], + # powers=[-channel[f"w_l{phase}"] for phase in range(1, 4)], + currents=[-channel[f"a_l{phase}"] for phase in range(1, 4)], + # voltages=[channel[f"v_l{phase}_n"] for phase in range(1, 4)], + # imported=channel["kwh_exported"] * 1000, + exported=channel["kwh_imported"] * 1000) + else: + raise ValueError(f"Unknown direction: {channel['direction']}") + + def __get_configurations(self) -> Dict: + """ + Reads the configurations from the JSON API. + Returns: + Dict: The configurations as a dictionary. + Raises: + ValueError: If the API version is not v2. + """ + if self.api_version != JsonApiVersion.V2: + raise ValueError("Diese Methode erfordert die JSON API v2!") + return self.__read(endpoint="configurations") + + def __set_configurations(self, configuration: Dict) -> None: + """ + Sets the configurations for the battery system. + Args: + configuration (Dict): The configurations to set. + Raises: + ValueError: If the API version is not v2. + """ + if self.api_version != JsonApiVersion.V2: + raise ValueError("Diese Methode erfordert die JSON API v2!") + req.get_http_session().put(f"http://{self.host}/api/v2/configurations", + json=configuration, + headers={"Auth-Token": self.auth_token}) + + def __update_set_point(self, power_limit: int) -> None: + """ + Updates the set point for the battery system. + Args: + power_limit (int): The desired power limit in watts. A positive value indicates + charging, while a negative value indicates discharging. + Raises: + ValueError: If the API version is not v2. + """ + if self.api_version != JsonApiVersion.V2: + raise ValueError("Diese Methode erfordert die JSON API v2!") + command = "charge" + if power_limit < 0: + command = "discharge" + power_limit = -power_limit + req.get_http_session().post(f"http://{self.host}/api/v2/setpoint/{command}/{power_limit}", + headers={"Auth-Token": self.auth_token, + "Content-Type": "application/json"}) + + def power_limit_controllable(self) -> bool: + """ + Checks if the power limit is controllable via the JSON API. + Returns: + bool: True if controllable, False otherwise. + """ + return self.api_version == JsonApiVersion.V2 and self.auth_token is not None + + def update_battery(self, sim_counter: SimCounter) -> BatState: + """ + Updates the battery state by reading data from the JSON API. + Returns: + InverterState: The updated battery state. + """ + battery_state = self.__read_status() + battery_power = -battery_state["Pac_total_W"] + battery_soc = battery_state["USOC"] + # try to calculate the individual line currents as no data is provided by the API + # we assume that the voltage is the same for all three phases + # this is not correct, but we have no other way to get the currents + # the current is calculated as apparent power / voltage + battery_ac_voltage = battery_state["Uac"] + currents = [float(battery_state[f"Sac{phase}"]) / battery_ac_voltage + if battery_state[f"Sac{phase}"] else None + for phase in range(1, 4)] + imported, exported = sim_counter.sim_count(battery_power) + return BatState(power=battery_power, + currents=currents if None not in currents else None, + soc=battery_soc, + imported=imported, + exported=exported) + + def update_grid_counter(self, sim_counter: SimCounter) -> CounterState: + """ + Updates the grid counter state by reading data from the JSON API. + Returns: + CounterState: The updated grid counter state. + """ + counter_state = self.__read_status() + grid_power = -counter_state["GridFeedIn_W"] + grid_voltage = counter_state["Uac"] + grid_frequency = counter_state["Fac"] + imported, exported = sim_counter.sim_count(grid_power) + return CounterState(power=grid_power, + voltages=[grid_voltage]*3, + frequency=grid_frequency, + imported=imported, + exported=exported) + + def update_inverter(self, sim_counter: SimCounter) -> InverterState: + """ + Updates the inverter state by reading data from the JSON API. + Returns: + InverterState: The updated inverter state. + """ + if self.api_version == JsonApiVersion.V1: + inverter_state = self.__read_status() + pv_power = -inverter_state["Production_W"] + _, exported = sim_counter.sim_count(pv_power) + return InverterState(exported=exported, + power=pv_power) + else: + inverter_state = self.__state_from_channel( + self.__read_power_meter(direction=self.PowerMeterDirection.PRODUCTION)[0]) + # meter value is updated way too slow, so we use a sim counter to get the exported energy + _, inverter_state.exported = sim_counter.sim_count(inverter_state.power) + return inverter_state + + def update_consumption_counter(self, sim_counter: SimCounter) -> CounterState: + """ + Updates the consumption counter state by reading data from the JSON API. + Returns: + CounterState: The updated consumption counter state. + """ + counter_state = self.__state_from_channel( + self.__read_power_meter(direction=self.PowerMeterDirection.CONSUMPTION)[0]) + # meter value is updated way too slow, so we use a sim counter to get the im-/exported energy + counter_state.imported, counter_state.exported = sim_counter.sim_count(counter_state.power) + return counter_state + + def set_power_limit(self, power_limit: Optional[int]) -> None: + """ + Sets the power limit for the battery system. + + This method adjusts the operating mode and power limit of the battery system + based on the provided `power_limit` value. If `power_limit` is None, the method + switches the operating mode to "Self Consumption". Otherwise, it switches the + operating mode to "Manual" and sets the specified power limit. + + Args: + power_limit (Optional[int]): The desired power limit in watts. A positive value + indicates charging, while a negative value indicates + discharging. If None, the power limit is removed. + + Raises: + ValueError: If the power limit control is not supported or the API version is not v2. + KeyError: If the required key 'EM_OperatingMode' is missing in the API response. + """ + if self.power_limit_controllable() is False: + raise ValueError("Leistungsvorgabe wird nur für 'JSON-API v2' unterstützt!") + configurations = self.__get_configurations() + if "EM_OperatingMode" not in configurations: + raise KeyError("The key 'EM_OperatingMode' is missing in the API response.") + if self.default_operating_mode is None: + # Store the default operating mode for later restoration + self.default_operating_mode = self.OperatingMode(configurations["EM_OperatingMode"]) + operating_mode = self.OperatingMode(configurations["EM_OperatingMode"]) + if power_limit is None: + # No specific power limit is set, activating default mode to allow the system to optimize energy usage by it + # self. + if operating_mode == self.OperatingMode.MANUAL: + self.__set_configurations({"EM_OperatingMode": self.default_operating_mode.value}) + else: + # Activate "Manual" operating mode to allow direct control of the power limit + # when a specific `power_limit` value is provided. + if operating_mode != self.OperatingMode.MANUAL: + self.__set_configurations({"EM_OperatingMode": self.OperatingMode.MANUAL.value}) + self.__update_set_point(power_limit) diff --git a/packages/modules/devices/sonnen/sonnenbatterie/bat.py b/packages/modules/devices/sonnen/sonnenbatterie/bat.py index 5ac3cc8bbb..0705216c59 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/bat.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/bat.py @@ -1,137 +1,59 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, TypedDict, Optional -from dataclass_utils import dataclass_from_dict -from modules.common import req from modules.common.abstract_device import AbstractBat -from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store + +from modules.devices.sonnen.sonnenbatterie.api import JsonApi, JsonApiVersion, RestApi1, RestApi2 from modules.devices.sonnen.sonnenbatterie.config import SonnenbatterieBatSetup + log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + api_v2_token: str + device_id: int + device_address: str + device_variant: int + + class SonnenbatterieBat(AbstractBat): - def __init__(self, - device_id: int, - device_address: str, - device_variant: int, - component_config: Union[Dict, SonnenbatterieBatSetup]) -> None: - self.__device_id = device_id - self.__device_address = device_address - self.__device_variant = device_variant - self.component_config = dataclass_from_dict(SonnenbatterieBatSetup, component_config) + def __init__(self, component_config: SonnenbatterieBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__device_address: str = self.kwargs['device_address'] + self.__device_variant: int = self.kwargs['device_variant'] + self.__api_v2_token: Optional[str] = self.kwargs['device_api_v2_token'] + if self.__device_variant not in [0, 1, 2, 3]: + raise ValueError("Unbekannte API: " + str(self.__device_variant)) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def __read_variant_0(self): - return req.get_http_session().get('http://' + self.__device_address + ':7979/rest/devices/battery', - timeout=5).json() - - def __update_variant_0(self) -> BatState: - # Auslesen einer Sonnenbatterie Eco 4 über die integrierte JSON-API des Batteriesystems - battery_state = self.__read_variant_0() - battery_soc = int(battery_state["M05"]) - battery_export_power = int(battery_state["M34"]) - battery_import_power = int(battery_state["M35"]) - battery_power = battery_import_power - battery_export_power - return BatState( - power=battery_power, - soc=battery_soc - ) - - def __read_variant_1(self, api: str = "v1"): - return req.get_http_session().get( - "http://" + self.__device_address + "/api/" + api + "/status", timeout=5 - ).json() - - def __update_variant_1(self, api: str = "v1") -> BatState: - # Auslesen einer Sonnenbatterie 8 oder 10 über die integrierte JSON-API v1/v2 des Batteriesystems - ''' - example data: - { - "Apparent_output": 225, - "BackupBuffer": "0", - "BatteryCharging": false, - "BatteryDischarging": false, - "Consumption_Avg": 2114, - "Consumption_W": 2101, - "Fac": 49.97200393676758, - "FlowConsumptionBattery": false, - "FlowConsumptionGrid": true, - "FlowConsumptionProduction": false, - "FlowGridBattery": false, - "FlowProductionBattery": false, - "FlowProductionGrid": false, - "GridFeedIn_W": -2106, - "IsSystemInstalled": 1, - "OperatingMode": "2", - "Pac_total_W": -5, - "Production_W": 0, - "RSOC": 6, - "RemainingCapacity_Wh": 2377, - "Sac1": 75, - "Sac2": 75, - "Sac3": 75, - "SystemStatus": "OnGrid", - "Timestamp": "2021-12-13 07:54:48", - "USOC": 0, - "Uac": 231, - "Ubat": 48, - "dischargeNotAllowed": true, - "generator_autostart": false, - "NVM_REINIT_STATUS": 0 - } - ''' - battery_state = self.__read_variant_1(api) - battery_power = -battery_state["Pac_total_W"] - log.debug('Speicher Leistung: ' + str(battery_power)) - battery_soc = battery_state["USOC"] - log.debug('Speicher SoC: ' + str(battery_soc)) - imported, exported = self.sim_counter.sim_count(battery_power) - return BatState( - power=battery_power, - soc=battery_soc, - imported=imported, - exported=exported - ) - - def __read_variant_2_element(self, element: str) -> str: - response = req.get_http_session().get( - 'http://' + self.__device_address + ':7979/rest/devices/battery/' + element, - timeout=5) - response.encoding = 'utf-8' - return response.text.strip(" \n\r") - - def __update_variant_2(self) -> BatState: - # Auslesen einer Sonnenbatterie Eco 6 über die integrierte REST-API des Batteriesystems - battery_soc = int(float(self.__read_variant_2_element("M05"))) - battery_export_power = int(float(self.__read_variant_2_element("M01"))) - battery_import_power = int(float(self.__read_variant_2_element("M02"))) - battery_power = battery_import_power - battery_export_power - return BatState( - power=battery_power, - soc=battery_soc - ) - - def update(self) -> None: - log.debug("Variante: " + str(self.__device_variant)) if self.__device_variant == 0: - state = self.__update_variant_0() - elif self.__device_variant == 1: - state = self.__update_variant_1() + self.api = RestApi1(host=self.__device_address) elif self.__device_variant == 2: - state = self.__update_variant_2() - elif self.__device_variant == 3: - state = self.__update_variant_1("v2") + self.api = RestApi2(host=self.__device_address) else: - raise ValueError("Unbekannte Variante: " + str(self.__device_variant)) - self.store.set(state) + self.api = JsonApi(host=self.__device_address, + api_version=JsonApiVersion.V2 if self.__device_variant == 3 else JsonApiVersion.V1, + auth_token=self.__api_v2_token if self.__device_variant == 3 else None) + + def update(self) -> None: + self.store.set(self.api.update_battery(sim_counter=self.sim_counter)) + + def set_power_limit(self, power_limit: Optional[int]) -> None: + self.api.set_power_limit(power_limit=power_limit) + + def power_limit_controllable(self) -> bool: + return self.api.power_limit_controllable() component_descriptor = ComponentDescriptor(configuration_factory=SonnenbatterieBatSetup) diff --git a/packages/modules/devices/sonnen/sonnenbatterie/config.py b/packages/modules/devices/sonnen/sonnenbatterie/config.py index ea0df8814e..469e4f23d9 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/config.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/config.py @@ -5,9 +5,10 @@ class SonnenBatterieConfiguration: - def __init__(self, variant: int = 0, ip_address: Optional[str] = None): + def __init__(self, variant: int = 0, ip_address: Optional[str] = None, api_v2_token: Optional[str] = None): self.variant = variant self.ip_address = ip_address + self.api_v2_token = api_v2_token class SonnenBatterie: @@ -44,13 +45,27 @@ def __init__(self): class SonnenbatterieCounterSetup(ComponentSetup[SonnenbatterieCounterConfiguration]): def __init__(self, - name: str = "SonnenBatterie Zähler", + name: str = "SonnenBatterie EVU-Zähler", type: str = "counter", id: int = 0, configuration: SonnenbatterieCounterConfiguration = None) -> None: super().__init__(name, type, id, configuration or SonnenbatterieCounterConfiguration()) +class SonnenbatterieConsumptionCounterConfiguration: + def __init__(self): + pass + + +class SonnenbatterieConsumptionCounterSetup(ComponentSetup[SonnenbatterieConsumptionCounterConfiguration]): + def __init__(self, + name: str = "SonnenBatterie Verbrauchs-Zähler", + type: str = "counter_consumption", + id: int = 0, + configuration: SonnenbatterieConsumptionCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or SonnenbatterieConsumptionCounterConfiguration()) + + class SonnenbatterieInverterConfiguration: def __init__(self): pass diff --git a/packages/modules/devices/sonnen/sonnenbatterie/counter.py b/packages/modules/devices/sonnen/sonnenbatterie/counter.py index fa2a266a03..ab4c154c52 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/counter.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/counter.py @@ -1,126 +1,52 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, TypedDict, Optional -from dataclass_utils import dataclass_from_dict -from modules.common import req from modules.common.abstract_device import AbstractCounter -from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.simcount import SimCounter from modules.common.store import get_counter_value_store + +from modules.devices.sonnen.sonnenbatterie.api import JsonApi, RestApi2, JsonApiVersion from modules.devices.sonnen.sonnenbatterie.config import SonnenbatterieCounterSetup log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + api_v2_token: str + device_id: int + device_address: str + device_variant: int + + class SonnenbatterieCounter(AbstractCounter): - def __init__(self, - device_id: int, - device_address: str, - device_variant: int, - component_config: Union[Dict, SonnenbatterieCounterSetup]) -> None: - self.__device_id = device_id - self.__device_address = device_address - self.__device_variant = device_variant - self.component_config = dataclass_from_dict(SonnenbatterieCounterSetup, component_config) + def __init__(self, component_config: SonnenbatterieCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__device_address: str = self.kwargs['device_address'] + self.__device_variant: int = self.kwargs['device_variant'] + self.__api_v2_token: Optional[str] = self.kwargs['device_api_v2_token'] + if self.__device_variant == 0: + raise ValueError("Die API 'Rest-API 1' bietet keine EVU Daten!") + if self.__device_variant not in [1, 2, 3]: + raise ValueError("Unbekannte API: " + str(self.__device_variant)) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def __read_variant_1(self, api: str = "v1"): - return req.get_http_session().get( - "http://" + self.__device_address + "/api/" + api + "/status", timeout=5 - ).json() - - def __update_variant_1(self, api: str = "v1") -> CounterState: - # Auslesen einer Sonnenbatterie 8 oder 10 über die integrierte JSON-API v1/v2 des Batteriesystems - ''' - example data: - { - "Apparent_output": 225, - "BackupBuffer": "0", - "BatteryCharging": false, - "BatteryDischarging": false, - "Consumption_Avg": 2114, - "Consumption_W": 2101, - "Fac": 49.97200393676758, - "FlowConsumptionBattery": false, - "FlowConsumptionGrid": true, - "FlowConsumptionProduction": false, - "FlowGridBattery": false, - "FlowProductionBattery": false, - "FlowProductionGrid": false, - "GridFeedIn_W": -2106, - "IsSystemInstalled": 1, - "OperatingMode": "2", - "Pac_total_W": -5, - "Production_W": 0, - "RSOC": 6, - "RemainingCapacity_Wh": 2377, - "Sac1": 75, - "Sac2": 75, - "Sac3": 75, - "SystemStatus": "OnGrid", - "Timestamp": "2021-12-13 07:54:48", - "USOC": 0, - "Uac": 231, - "Ubat": 48, - "dischargeNotAllowed": true, - "generator_autostart": false, - "NVM_REINIT_STATUS": 0 - } - ''' - counter_state = self.__read_variant_1(api) - grid_power = -counter_state["GridFeedIn_W"] - log.debug('EVU Leistung: ' + str(grid_power)) - # Es wird nur eine Spannung ausgegeben - grid_voltage = counter_state["Uac"] - log.debug('EVU Spannung: ' + str(grid_voltage)) - grid_frequency = counter_state["Fac"] - log.debug('EVU Netzfrequenz: ' + str(grid_frequency)) - imported, exported = self.sim_counter.sim_count(grid_power) - return CounterState( - power=grid_power, - voltages=[grid_voltage]*3, - frequency=grid_frequency, - imported=imported, - exported=exported, - ) - - def __read_variant_2_element(self, element: str) -> str: - response = req.get_http_session().get( - 'http://' + self.__device_address + ':7979/rest/devices/battery/' + element, - timeout=5) - response.encoding = 'utf-8' - return response.text.strip(" \n\r") - - def __update_variant_2(self) -> CounterState: - # Auslesen einer Sonnenbatterie Eco 6 über die integrierte REST-API des Batteriesystems - grid_import_power = int(float(self.__read_variant_2_element("M39"))) - grid_export_power = int(float(self.__read_variant_2_element("M38"))) - grid_power = grid_import_power - grid_export_power - imported, exported = self.sim_counter.sim_count(grid_power) - return CounterState( - power=grid_power, - imported=imported, - exported=exported, - ) + if self.__device_variant == 2: + self.api = RestApi2(host=self.__device_address) + else: + self.api = JsonApi(host=self.__device_address, + api_version=JsonApiVersion.V2 if self.__device_variant == 3 else JsonApiVersion.V1, + auth_token=self.__api_v2_token if self.__device_variant == 3 else None) def update(self) -> None: - log.debug("Variante: " + str(self.__device_variant)) - if self.__device_variant == 0: - log.debug("Die Variante '0' bietet keine EVU Daten!") - elif self.__device_variant == 1: - state = self.__update_variant_1() - elif self.__device_variant == 2: - state = self.__update_variant_2() - elif self.__device_variant == 3: - state = self.__update_variant_1("v2") - else: - raise ValueError("Unbekannte Variante: " + str(self.__device_variant)) - self.store.set(state) + self.store.set(self.api.update_grid_counter(sim_counter=self.sim_counter)) component_descriptor = ComponentDescriptor(configuration_factory=SonnenbatterieCounterSetup) diff --git a/packages/modules/devices/sonnen/sonnenbatterie/counter_consumption.py b/packages/modules/devices/sonnen/sonnenbatterie/counter_consumption.py new file mode 100644 index 0000000000..a7e46f7163 --- /dev/null +++ b/packages/modules/devices/sonnen/sonnenbatterie/counter_consumption.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +import logging +from typing import Any, TypedDict, Optional + +from modules.common.abstract_device import AbstractCounter +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store + +from modules.devices.sonnen.sonnenbatterie.api import JsonApi, JsonApiVersion +from modules.devices.sonnen.sonnenbatterie.config import SonnenbatterieConsumptionCounterSetup + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_address: str + device_variant: int + api_v2_token: Optional[str] + + +class SonnenbatterieConsumptionCounter(AbstractCounter): + def __init__(self, component_config: SonnenbatterieConsumptionCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__device_address: str = self.kwargs['device_address'] + self.__device_variant: int = self.kwargs['device_variant'] + self.__api_v2_token: Optional[str] = self.kwargs['device_api_v2_token'] + if self.__device_variant in [0, 1, 2]: + raise ValueError("Die ausgewählte API bietet keine Verbrauchsdaten!") + if self.__device_variant != 3: + raise ValueError("Unbekannte API: " + str(self.__device_variant)) + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="consumption") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.api = JsonApi(host=self.__device_address, + api_version=JsonApiVersion.V2, + auth_token=self.__api_v2_token) + + def update(self) -> None: + self.store.set(self.api.update_consumption_counter(sim_counter=self.sim_counter)) + + +component_descriptor = ComponentDescriptor(configuration_factory=SonnenbatterieConsumptionCounterSetup) diff --git a/packages/modules/devices/sonnen/sonnenbatterie/device.py b/packages/modules/devices/sonnen/sonnenbatterie/device.py index bfd73cdb81..e70a6e6b83 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/device.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/device.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -""" Modul zum Auslesen von sonnenBatterie Speichern. +""" Modul zum Auslesen von SonnenBatterie Speichern. """ import logging @@ -8,8 +8,10 @@ from modules.devices.sonnen.sonnenbatterie.bat import SonnenbatterieBat from modules.devices.sonnen.sonnenbatterie.config import (SonnenBatterie, SonnenbatterieBatSetup, SonnenbatterieCounterSetup, + SonnenbatterieConsumptionCounterSetup, SonnenbatterieInverterSetup) from modules.devices.sonnen.sonnenbatterie.counter import SonnenbatterieCounter +from modules.devices.sonnen.sonnenbatterie.counter_consumption import SonnenbatterieConsumptionCounter from modules.devices.sonnen.sonnenbatterie.inverter import SonnenbatterieInverter @@ -18,28 +20,39 @@ def create_device(device_config: SonnenBatterie): def create_bat_component(component_config: SonnenbatterieBatSetup): - return SonnenbatterieBat(device_config.id, - device_config.configuration.ip_address, - device_config.configuration.variant, - component_config) - - def create_counter_component(component_config: SonnenbatterieCounterSetup): - return SonnenbatterieCounter(device_config.id, - device_config.configuration.ip_address, - device_config.configuration.variant, - component_config) + return SonnenbatterieBat(component_config, + device_id=device_config.id, + device_address=device_config.configuration.ip_address, + device_variant=device_config.configuration.variant, + device_api_v2_token=device_config.configuration.api_v2_token) + + def create_evu_counter_component(component_config: SonnenbatterieCounterSetup): + return SonnenbatterieCounter(component_config, + device_id=device_config.id, + device_address=device_config.configuration.ip_address, + device_variant=device_config.configuration.variant, + device_api_v2_token=device_config.configuration.api_v2_token) + + def create_consumption_counter_component(component_config: SonnenbatterieConsumptionCounterSetup): + return SonnenbatterieConsumptionCounter(component_config, + device_id=device_config.id, + device_address=device_config.configuration.ip_address, + device_variant=device_config.configuration.variant, + device_api_v2_token=device_config.configuration.api_v2_token) def create_inverter_component(component_config: SonnenbatterieInverterSetup): - return SonnenbatterieInverter(device_config.id, - device_config.configuration.ip_address, - device_config.configuration.variant, - component_config) + return SonnenbatterieInverter(component_config, + device_id=device_config.id, + device_address=device_config.configuration.ip_address, + device_variant=device_config.configuration.variant, + device_api_v2_token=device_config.configuration.api_v2_token) return ConfigurableDevice( device_config=device_config, component_factory=ComponentFactoryByType( bat=create_bat_component, - counter=create_counter_component, + counter=create_evu_counter_component, + counter_consumption=create_consumption_counter_component, inverter=create_inverter_component, ), component_updater=IndependentComponentUpdater(lambda component: component.update()) diff --git a/packages/modules/devices/sonnen/sonnenbatterie/inverter.py b/packages/modules/devices/sonnen/sonnenbatterie/inverter.py index a8b4122d0a..446420297e 100644 --- a/packages/modules/devices/sonnen/sonnenbatterie/inverter.py +++ b/packages/modules/devices/sonnen/sonnenbatterie/inverter.py @@ -1,115 +1,52 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import Any, TypedDict, Optional -from dataclass_utils import dataclass_from_dict -from modules.common import req from modules.common.abstract_device import AbstractInverter -from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.simcount import SimCounter from modules.common.store import get_inverter_value_store + +from modules.devices.sonnen.sonnenbatterie.api import JsonApi, RestApi2, JsonApiVersion from modules.devices.sonnen.sonnenbatterie.config import SonnenbatterieInverterSetup log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + api_v2_token: str + device_id: int + device_address: str + device_variant: int + + class SonnenbatterieInverter(AbstractInverter): - def __init__(self, - device_id: int, - device_address: str, - device_variant: int, - component_config: Union[Dict, SonnenbatterieInverterSetup]) -> None: - self.__device_id = device_id - self.__device_address = device_address - self.__device_variant = device_variant - self.component_config = dataclass_from_dict(SonnenbatterieInverterSetup, component_config) + def __init__(self, component_config: SonnenbatterieInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__device_address: str = self.kwargs['device_address'] + self.__device_variant: int = self.kwargs['device_variant'] + self.__api_v2_token: Optional[str] = self.kwargs['device_api_v2_token'] + if self.__device_variant == 0: + raise ValueError("Die Variante '0' bietet keine PV Daten!") + if self.__device_variant not in [1, 2, 3]: + raise ValueError("Unbekannte API: " + str(self.__device_variant)) self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - - def __read_variant_1(self, api: str = "v1"): - return req.get_http_session().get( - "http://" + self.__device_address + "/api/" + api + "/status", timeout=5 - ).json() - - def __update_variant_1(self, api: str = "v1") -> InverterState: - # Auslesen einer Sonnenbatterie 8 oder 10 über die integrierte JSON-API v1/v2 des Batteriesystems - ''' - example data: - { - "Apparent_output": 225, - "BackupBuffer": "0", - "BatteryCharging": false, - "BatteryDischarging": false, - "Consumption_Avg": 2114, - "Consumption_W": 2101, - "Fac": 49.97200393676758, - "FlowConsumptionBattery": false, - "FlowConsumptionGrid": true, - "FlowConsumptionProduction": false, - "FlowGridBattery": false, - "FlowProductionBattery": false, - "FlowProductionGrid": false, - "GridFeedIn_W": -2106, - "IsSystemInstalled": 1, - "OperatingMode": "2", - "Pac_total_W": -5, - "Production_W": 0, - "RSOC": 6, - "RemainingCapacity_Wh": 2377, - "Sac1": 75, - "Sac2": 75, - "Sac3": 75, - "SystemStatus": "OnGrid", - "Timestamp": "2021-12-13 07:54:48", - "USOC": 0, - "Uac": 231, - "Ubat": 48, - "dischargeNotAllowed": true, - "generator_autostart": false, - "NVM_REINIT_STATUS": 0 - } - ''' - inverter_state = self.__read_variant_1(api) - pv_power = -inverter_state["Production_W"] - log.debug('Speicher PV Leistung: ' + str(pv_power)) - _, exported = self.sim_counter.sim_count(pv_power) - return InverterState( - exported=exported, - power=pv_power - ) - - def __read_variant_2_element(self, element: str) -> str: - response = req.get_http_session().get('http://' + self.__device_address + - ':7979/rest/devices/battery/' + element, timeout=5) - response.encoding = 'utf-8' - return response.text.strip(" \n\r") - - def __update_variant_2(self) -> InverterState: - # Auslesen einer Sonnenbatterie Eco 6 über die integrierte REST-API des Batteriesystems - pv_power = -int(float(self.__read_variant_2_element("M03"))) - log.debug('Speicher PV Leistung: ' + str(pv_power)) - _, exported = self.sim_counter.sim_count(pv_power) - return InverterState( - exported=exported, - power=pv_power - ) + if self.__device_variant == 2: + self.api = RestApi2(host=self.__device_address) + else: + self.api = JsonApi(host=self.__device_address, + api_version=JsonApiVersion.V2 if self.__device_variant == 3 else JsonApiVersion.V1, + auth_token=self.__api_v2_token if self.__device_variant == 3 else None) def update(self) -> None: - log.debug("Variante: " + str(self.__device_variant)) - if self.__device_variant == 0: - log.debug("Die Variante '0' bietet keine PV Daten!") - elif self.__device_variant == 1: - state = self.__update_variant_1() - elif self.__device_variant == 2: - state = self.__update_variant_2() - elif self.__device_variant == 3: - state = self.__update_variant_1("v2") - else: - raise ValueError("Unbekannte Variante: " + str(self.__device_variant)) - self.store.set(state) + self.store.set(self.api.update_inverter(sim_counter=self.sim_counter)) component_descriptor = ComponentDescriptor(configuration_factory=SonnenbatterieInverterSetup) diff --git a/packages/modules/devices/studer/studer/bat.py b/packages/modules/devices/studer/studer/bat.py index 8505f06761..707e7814fd 100644 --- a/packages/modules/devices/studer/studer/bat.py +++ b/packages/modules/devices/studer/studer/bat.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -12,12 +11,17 @@ from modules.devices.studer.studer.config import StuderBatSetup +class KwargsDict(TypedDict): + client: modbus.ModbusTcpClient_ + + class StuderBat(AbstractBat): - def __init__(self, - component_config: Union[Dict, StuderBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(StuderBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: StuderBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/studer/studer/device.py b/packages/modules/devices/studer/studer/device.py index dbbd51528a..ae0d85fb4b 100644 --- a/packages/modules/devices/studer/studer/device.py +++ b/packages/modules/devices/studer/studer/device.py @@ -1,6 +1,4 @@ #!/usr/bin/env python3 -""" Modul zum Auslesen von Alpha Ess Speichern, Zählern und Wechselrichtern. -""" import logging from typing import Iterable, Union @@ -16,24 +14,30 @@ def create_device(device_config: Studer): + client = None + def create_bat_component(component_config: StuderBatSetup): - return StuderBat(component_config, client) + nonlocal client + return StuderBat(component_config, client=client) def create_inverter_component(component_config: StuderInverterSetup): - return StuderInverter(component_config, client) + nonlocal client + return StuderInverter(component_config, client=client) def update_components(components: Iterable[Union[StuderBat, StuderInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, inverter=create_inverter_component, diff --git a/packages/modules/devices/studer/studer/inverter.py b/packages/modules/devices/studer/studer/inverter.py index 1384cbb4cc..ed267c57d3 100644 --- a/packages/modules/devices/studer/studer/inverter.py +++ b/packages/modules/devices/studer/studer/inverter.py @@ -1,23 +1,26 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict -from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ from modules.common.store import get_inverter_value_store from modules.devices.studer.studer.config import StuderInverterSetup +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + + class StuderInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, StuderInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.component_config = dataclass_from_dict(StuderInverterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: StuderInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/sungrow/sungrow/bat.py b/packages/modules/devices/sungrow/sungrow/bat.py index 9dbeeea25e..86c145e6cd 100644 --- a/packages/modules/devices/sungrow/sungrow/bat.py +++ b/packages/modules/devices/sungrow/sungrow/bat.py @@ -1,68 +1,126 @@ #!/usr/bin/env python3 -from typing import Dict, Union +import logging +from typing import Any, Optional, TypedDict -from dataclass_utils import dataclass_from_dict -from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, Endian +from modules.common.modbus import ModbusDataType, Endian, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_bat_value_store from modules.devices.sungrow.sungrow.config import SungrowBatSetup, Sungrow from modules.devices.sungrow.sungrow.version import Version from modules.devices.sungrow.sungrow.firmware import Firmware +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + device_config: Sungrow + class SungrowBat(AbstractBat): - def __init__(self, - device_config: Union[Dict, Sungrow], - component_config: Union[Dict, SungrowBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SungrowBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SungrowBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.device_config: Sungrow = self.kwargs['device_config'] + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.last_mode = 'Undefined' + self.firmware_check = self.check_firmware_register() + + def check_firmware_register(self) -> bool: + if Firmware(self.device_config.configuration.firmware) == Firmware.v1: + return False + unit = self.device_config.configuration.modbus_id + try: + self.__tcp_client.read_input_registers(5213, ModbusDataType.INT_32, + wordorder=Endian.Little, unit=unit) + log.debug("Wechselrichter Firmware ist größer gleich 95.09") + return True + except Exception: + log.debug("Wechselrichter Firmware ist kleiner als 95.09") + return False def update(self) -> None: unit = self.device_config.configuration.modbus_id + soc = int(self.__tcp_client.read_input_registers(13022, ModbusDataType.UINT_16, unit=unit) / 10) + version = Version(self.device_config.configuration.version) - if ( - Firmware(self.device_config.configuration.firmware) == Firmware.v2 - and self.device_config.configuration.version == Version.SH - ): - bat_power = self.__tcp_client.read_input_registers(13021, ModbusDataType.INT_16, unit=unit) * -1 - else: + if Firmware(self.device_config.configuration.firmware) == Firmware.v2: + if self.firmware_check: # Firmware >= 95.09 + bat_current = self.__tcp_client.read_input_registers(5630, ModbusDataType.INT_16, unit=unit) * -0.1 + bat_power = self.__tcp_client.read_input_registers(5213, ModbusDataType.INT_32, + wordorder=Endian.Little, unit=unit) * -1 + else: # Firmware between 95.03 and 95.09 + bat_current = self.__tcp_client.read_input_registers(13020, ModbusDataType.INT_16, unit=unit) * -0.1 + if version == Version.SH: + bat_power = self.__tcp_client.read_input_registers(13021, ModbusDataType.INT_16, unit=unit) + elif version == Version.SH_winet_dongle: + bat_power = self.__tcp_client.read_input_registers(13021, ModbusDataType.UINT_16, unit=unit) + total_power = self.__tcp_client.read_input_registers(13033, ModbusDataType.INT_32, + wordorder=Endian.Little, unit=unit) + pv_power = self.__tcp_client.read_input_registers(5016, ModbusDataType.UINT_32, + wordorder=Endian.Little, unit=unit) + if total_power > pv_power: + bat_power = bat_power * -1 + else: # Firmware.v1 (Firmware < 95.03) + bat_current = self.__tcp_client.read_input_registers(13020, ModbusDataType.INT_16, unit=unit) * -0.1 bat_power = self.__tcp_client.read_input_registers(13021, ModbusDataType.UINT_16, unit=unit) - - # Beim WiNet S-Dongle fehlt das Register für das Vorzeichen der Speicherleistung - if self.device_config.configuration.version == Version.SH_winet_dongle: - total_power = self.__tcp_client.read_input_registers(13033, ModbusDataType.INT_32, - wordorder=Endian.Little, unit=unit) - pv_power = self.__tcp_client.read_input_registers(5016, ModbusDataType.UINT_32, - wordorder=Endian.Little, unit=unit) - - # Ist die Gesamtleistung des WR größer als die PV-Erzeugung wird der Speicher entladen - if total_power > pv_power: - bat_power = bat_power * -1 - else: + if version in (Version.SH, Version.SH_winet_dongle): resp = self.__tcp_client._delegate.read_input_registers(13000, 1, unit=unit) binary = bin(resp.registers[0])[2:].zfill(8) if binary[5] == "1": bat_power = bat_power * -1 + currents = [bat_current / 3] * 3 + imported, exported = self.sim_counter.sim_count(bat_power) bat_state = BatState( power=bat_power, soc=soc, + currents=currents, imported=imported, exported=exported ) self.store.set(bat_state) + def set_power_limit(self, power_limit: Optional[int]) -> None: + unit = self.device_config.configuration.modbus_id + log.debug(f'last_mode: {self.last_mode}') + + if power_limit is None: + log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") + if self.last_mode is not None: + self.__tcp_client.write_registers(13049, [0], data_type=ModbusDataType.UINT_16, unit=unit) + self.__tcp_client.write_registers(13050, [0xCC], data_type=ModbusDataType.UINT_16, unit=unit) + self.last_mode = None + elif power_limit == 0: + log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen") + if self.last_mode != 'stop': + self.__tcp_client.write_registers(13049, [2], data_type=ModbusDataType.UINT_16, unit=unit) + self.__tcp_client.write_registers(13050, [0xCC], data_type=ModbusDataType.UINT_16, unit=unit) + self.last_mode = 'stop' + elif power_limit < 0: + log.debug(f"Aktive Batteriesteuerung. Batterie wird mit {power_limit} W entladen für den Hausverbrauch") + if self.last_mode != 'discharge': + self.__tcp_client.write_registers(13049, [2], data_type=ModbusDataType.UINT_16, unit=unit) + self.__tcp_client.write_registers(13050, [0xBB], data_type=ModbusDataType.UINT_16, unit=unit) + self.last_mode = 'discharge' + # Die maximale Entladeleistung begrenzen auf 5000W, maximaler Wertebereich Modbusregister. + power_value = int(min(abs(power_limit), 5000)) + log.debug(f"Aktive Batteriesteuerung. Batterie wird mit {power_value} W entladen für den Hausverbrauch") + self.__tcp_client.write_registers(13051, [power_value], data_type=ModbusDataType.UINT_16, unit=unit) + + def power_limit_controllable(self) -> bool: + return True + component_descriptor = ComponentDescriptor(configuration_factory=SungrowBatSetup) diff --git a/packages/modules/devices/sungrow/sungrow/counter.py b/packages/modules/devices/sungrow/sungrow/counter.py index 03bf21cdf3..67e6cf849d 100644 --- a/packages/modules/devices/sungrow/sungrow/counter.py +++ b/packages/modules/devices/sungrow/sungrow/counter.py @@ -1,28 +1,31 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict -from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, Endian -from modules.common.simcount import SimCounter +from modules.common.modbus import Endian, ModbusDataType, ModbusTcpClient_ +from modules.common.simcount._simcounter import SimCounter from modules.common.store import get_counter_value_store -from modules.devices.sungrow.sungrow.config import SungrowCounterSetup, Sungrow +from modules.devices.sungrow.sungrow.config import Sungrow, SungrowCounterSetup from modules.devices.sungrow.sungrow.version import Version +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + device_config: Sungrow + + class SungrowCounter(AbstractCounter): - def __init__(self, - device_config: Union[Dict, Sungrow], - component_config: Union[Dict, SungrowCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SungrowCounterSetup, component_config) - self.__tcp_client = tcp_client - self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="bezug") + def __init__(self, component_config: SungrowCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.device_config: Sungrow = self.kwargs['device_config'] + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] + self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="evu") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) self.fault_text = "Dieser Sungrow Zähler liefert von Werk aus (entgegen der Dokumentation) "\ diff --git a/packages/modules/devices/sungrow/sungrow/device.py b/packages/modules/devices/sungrow/sungrow/device.py index 8de4c559c8..b9becf32a5 100644 --- a/packages/modules/devices/sungrow/sungrow/device.py +++ b/packages/modules/devices/sungrow/sungrow/device.py @@ -4,7 +4,6 @@ from modules.common import modbus from modules.common.abstract_device import DeviceDescriptor -from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.devices.sungrow.sungrow.bat import SungrowBat from modules.devices.sungrow.sungrow.config import Sungrow, SungrowBatSetup, SungrowCounterSetup, SungrowInverterSetup @@ -15,36 +14,40 @@ def create_device(device_config: Sungrow): + client = None + def create_bat_component(component_config: SungrowBatSetup): - return SungrowBat(device_config, component_config, client) + nonlocal client + return SungrowBat(component_config, device_config=device_config, client=client) def create_counter_component(component_config: SungrowCounterSetup): - return SungrowCounter(device_config, component_config, client) + nonlocal client + return SungrowCounter(component_config, device_config=device_config, client=client) def create_inverter_component(component_config: SungrowInverterSetup): - return SungrowInverter(device_config, component_config, client) + nonlocal client + return SungrowInverter(component_config, device_config=device_config, client=client) def update_components(components: Iterable[Union[SungrowBat, SungrowCounter, SungrowInverter]]): + nonlocal client with client: for component in components: if isinstance(component, SungrowInverter): - with SingleComponentUpdateContext(component.fault_state): - pv_power = component.update() + pv_power = component.update() for component in components: if isinstance(component, SungrowCounter): - with SingleComponentUpdateContext(component.fault_state): - component.update(pv_power) + component.update(pv_power) for component in components: if isinstance(component, SungrowBat): - with SingleComponentUpdateContext(component.fault_state): - component.update() + component.update() - try: + def initializer(): + nonlocal client client = modbus.ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/sungrow/sungrow/inverter.py b/packages/modules/devices/sungrow/sungrow/inverter.py index 827e39c353..791dc51744 100644 --- a/packages/modules/devices/sungrow/sungrow/inverter.py +++ b/packages/modules/devices/sungrow/sungrow/inverter.py @@ -1,27 +1,30 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict -from dataclass_utils import dataclass_from_dict -from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState -from modules.common.modbus import ModbusDataType, Endian +from modules.common.modbus import ModbusDataType, Endian, ModbusTcpClient_ from modules.common.simcount import SimCounter from modules.common.store import get_inverter_value_store from modules.devices.sungrow.sungrow.config import SungrowInverterSetup, Sungrow from modules.devices.sungrow.sungrow.version import Version +class KwargsDict(TypedDict): + client: ModbusTcpClient_ + device_config: Sungrow + + class SungrowInverter(AbstractInverter): - def __init__(self, - device_config: Union[Dict, Sungrow], - component_config: Union[Dict, SungrowInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.device_config = device_config - self.component_config = dataclass_from_dict(SungrowInverterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: SungrowInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.device_config: Sungrow = self.kwargs['device_config'] + self.__tcp_client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.device_config.id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -34,17 +37,29 @@ def update(self) -> float: wordorder=Endian.Little, unit=unit) * -1 dc_power = self.__tcp_client.read_input_registers(5016, ModbusDataType.UINT_32, wordorder=Endian.Little, unit=unit) * -1 + + current_L1 = self.__tcp_client.read_input_registers(13030, ModbusDataType.INT_16, unit=unit) * -0.1 + current_L2 = self.__tcp_client.read_input_registers(13031, ModbusDataType.INT_16, unit=unit) * -0.1 + current_L3 = self.__tcp_client.read_input_registers(13032, ModbusDataType.INT_16, unit=unit) * -0.1 + currents = [current_L1, current_L2, current_L3] else: power = self.__tcp_client.read_input_registers(5030, ModbusDataType.INT_32, wordorder=Endian.Little, unit=unit) * -1 dc_power = self.__tcp_client.read_input_registers(5016, ModbusDataType.UINT_32, wordorder=Endian.Little, unit=unit) * -1 - _, exported = self.sim_counter.sim_count(power) + current_L1 = self.__tcp_client.read_input_registers(5021, ModbusDataType.UINT_16, unit=unit) * -0.1 + current_L2 = self.__tcp_client.read_input_registers(5022, ModbusDataType.UINT_16, unit=unit) * -0.1 + current_L3 = self.__tcp_client.read_input_registers(5023, ModbusDataType.UINT_16, unit=unit) * -0.1 + currents = [current_L1, current_L2, current_L3] + + imported, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( power=power, dc_power=dc_power, + currents=currents, + imported=imported, exported=exported ) self.store.set(inverter_state) diff --git a/packages/modules/devices/sungrow/sungrow/modbus.md b/packages/modules/devices/sungrow/sungrow/modbus.md index d8bf7b210a..04a50389d2 100644 --- a/packages/modules/devices/sungrow/sungrow/modbus.md +++ b/packages/modules/devices/sungrow/sungrow/modbus.md @@ -1,13 +1,15 @@ -# Modbus Adressen für Sungrow SH* und SG* Wechselrichter +# Modbus Adressen für Sungrow SH\* und SG\* Wechselrichter -## Datengrundlage: -* TI_20230918_Communication Protocol of Residential and Commerical PV Grid-connected Inverter_V1.1.58_EN.pdf +## Datengrundlage + +* TI_20230918_Communication Protocol of Residential and Commercial PV Grid-connected Inverter_V1.1.58_EN.pdf * TI_20231019_Communication Protocol of Residential Hybrid Inverter_V1.1.2_EN.pdf * modbus_finder.py an SH10RT-V112 (LAN) Firmware SAPPHIRE-H_B001.V000.P005-20231027 * modbus_finder.py an SH10RT-V112 (WiNet-S) Firmware WINET-SV200.001.00.P023 * modbus_finder.py an SG10RT (WiNet-S) Firmware BERYL-S_B000.V000.P039-20230626 / WINET-SV200.001.00.P023 ## Werte + | Wert | SH_LAN | SH_WiNet | SG_WiNet | Einheit | Typ | Bemerkung | |-------------------------------------|--------|----------|---------------|---------|----------------|------------------------------------------------------------------| | WR: Zähler inkl. Batterieentladung | 5003 | 5003 | -- | 0.1 kWh | UINT_32 mixed | Delta zu 'WR: Zähler Gesamtertrag' ist entladene **Netz**energie | @@ -39,4 +41,4 @@ | Meter: AC Spannung Phase C | 5742 | -- | -- | 0.1 V | UINT_16 little | | | Meter: AC Strom Phase A | 5743 | -- | -- | 0.01 A | UINT_16 little | Immer positiv, auch bei Einspeisung | | Meter: AC Strom Phase B | 5744 | -- | -- | 0.01 A | UINT_16 little | Immer positiv, auch bei Einspeisung | -| Meter: AC Strom Phase C | 5745 | -- | -- | 0.01 A | UINT_16 little | Immer positiv, auch bei Einspeisung | \ No newline at end of file +| Meter: AC Strom Phase C | 5745 | -- | -- | 0.01 A | UINT_16 little | Immer positiv, auch bei Einspeisung | diff --git a/packages/modules/devices/sunways/sunways/device.py b/packages/modules/devices/sunways/sunways/device.py index 9844567d22..f4c1d1be0b 100644 --- a/packages/modules/devices/sunways/sunways/device.py +++ b/packages/modules/devices/sunways/sunways/device.py @@ -12,8 +12,8 @@ def create_device(device_config: Sunways): def create_inverter_component(component_config: SunwaysInverterSetup): return SunwaysInverter(component_config, - device_config.configuration.ip_address, - device_config.configuration.password) + ip_address=device_config.configuration.ip_address, + password=device_config.configuration.password) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/sunways/sunways/inverter.py b/packages/modules/devices/sunways/sunways/inverter.py index f1e3684d0e..5443b891cf 100644 --- a/packages/modules/devices/sunways/sunways/inverter.py +++ b/packages/modules/devices/sunways/sunways/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict from requests.auth import HTTPDigestAuth -from dataclass_utils import dataclass_from_dict from modules.common import req from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -18,15 +17,19 @@ """ +class KwargsDict(TypedDict): + ip_address: str + password: str + + class SunwaysInverter(AbstractInverter): - def __init__(self, - component_config: Union[Dict, SunwaysInverterSetup], - ip_address: str, - password: str) -> None: - - self.component_config = dataclass_from_dict(SunwaysInverterSetup, component_config) - self.ip_address = ip_address - self.password = password + def __init__(self, component_config: SunwaysInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.ip_address: str = self.kwargs['ip_address'] + self.password: str = self.kwargs['password'] self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/tasmota/tasmota/bat.py b/packages/modules/devices/tasmota/tasmota/bat.py new file mode 100644 index 0000000000..d38f1a4090 --- /dev/null +++ b/packages/modules/devices/tasmota/tasmota/bat.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict +import logging + +from modules.devices.tasmota.tasmota.config import TasmotaBatSetup +from modules.common.abstract_device import AbstractBat +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_bat_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import BatState + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + phase: int + + +class TasmotaBat(AbstractBat): + def __init__(self, component_config: TasmotaBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") + self.__phase: int = self.kwargs['phase'] + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + currents = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) + currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0 + imported = float(response['StatusSNS']['ENERGY']['Total']*1000) + _, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + currents=currents, + imported=imported, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) + imported = float(response['StatusSNS']['Itron']['E_in']*1000) + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + bat_state = BatState( + power=power, + imported=imported, + exported=exported + ) + + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=TasmotaBatSetup) diff --git a/packages/modules/devices/tasmota/tasmota/config.py b/packages/modules/devices/tasmota/tasmota/config.py index bd874e760d..8f6785b83c 100644 --- a/packages/modules/devices/tasmota/tasmota/config.py +++ b/packages/modules/devices/tasmota/tasmota/config.py @@ -35,3 +35,31 @@ def __init__(self, id: int = 0, configuration: TasmotaCounterConfiguration = None) -> None: super().__init__(name, type, id, configuration or TasmotaCounterConfiguration()) + + +class TasmotaInverterConfiguration: + def __init__(self): + pass + + +class TasmotaInverterSetup(ComponentSetup[TasmotaInverterConfiguration]): + def __init__(self, + name: str = "Tasmota Wechselrichterzähler", + type: str = "inverter", + id: int = 0, + configuration: TasmotaInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or TasmotaInverterConfiguration()) + + +class TasmotaBatConfiguration: + def __init__(self): + pass + + +class TasmotaBatSetup(ComponentSetup[TasmotaBatConfiguration]): + def __init__(self, + name: str = "Tasmota Speicherzähler", + type: str = "bat", + id: int = 0, + configuration: TasmotaBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or TasmotaBatConfiguration()) diff --git a/packages/modules/devices/tasmota/tasmota/counter.py b/packages/modules/devices/tasmota/tasmota/counter.py index cff9f68d39..b0330725ba 100644 --- a/packages/modules/devices/tasmota/tasmota/counter.py +++ b/packages/modules/devices/tasmota/tasmota/counter.py @@ -1,39 +1,76 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import Any, TypedDict import logging -from dataclass_utils import dataclass_from_dict from modules.devices.tasmota.tasmota.config import TasmotaCounterSetup from modules.common.abstract_device import AbstractCounter -from modules.common.tasmota import Tasmota from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState from modules.common.store import get_counter_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import CounterState log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + ip_address: str + phase: int + + class TasmotaCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, TasmotaCounterSetup], - ip_address: str, - phase: int) -> None: - self.__device_id = device_id - self.__ip_address = ip_address - if phase: - self.__phase = phase - else: - self.__phase = 1 - self.component_config = dataclass_from_dict(TasmotaCounterSetup, component_config) + def __init__(self, component_config: TasmotaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.__phase: int = self.kwargs['phase'] self.store = get_counter_value_store(self.component_config.id) - self.component_info = ComponentInfo.from_component_config(self.component_config) - self.__tasmota = Tasmota(self.__device_id, self.__ip_address, self.__phase) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) def update(self): - log.debug("tasmota.counter.update: " + self.__ip_address) - counter_state = self.__tasmota.get_CounterState() + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + voltages = [0.0, 0.0, 0.0] + powers = [0.0, 0.0, 0.0] + currents = [0.0, 0.0, 0.0] + power_factors = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) + voltages[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Voltage']) + powers[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Power']) + currents[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Current']) + power_factors[self.__phase-1] = float(response['StatusSNS']['ENERGY']['Factor']) + imported = float(response['StatusSNS']['ENERGY']['Total']*1000) + _, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + power=power, + voltages=voltages, + currents=currents, + powers=powers, + power_factors=power_factors, + imported=imported, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) + imported = float(response['StatusSNS']['Itron']['E_in']*1000) + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + counter_state = CounterState( + power=power, + imported=imported, + exported=exported + ) + self.store.set(counter_state) diff --git a/packages/modules/devices/tasmota/tasmota/device.py b/packages/modules/devices/tasmota/tasmota/device.py index d571ad3847..2a187ab957 100644 --- a/packages/modules/devices/tasmota/tasmota/device.py +++ b/packages/modules/devices/tasmota/tasmota/device.py @@ -2,24 +2,40 @@ import logging from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, IndependentComponentUpdater -from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup +from modules.devices.tasmota.tasmota.config import Tasmota, TasmotaCounterSetup, TasmotaInverterSetup, TasmotaBatSetup from modules.common.abstract_device import DeviceDescriptor from modules.devices.tasmota.tasmota.counter import TasmotaCounter +from modules.devices.tasmota.tasmota.inverter import TasmotaInverter +from modules.devices.tasmota.tasmota.bat import TasmotaBat log = logging.getLogger(__name__) def create_device(device_config: Tasmota): def create_counter_component(component_config: TasmotaCounterSetup): - return TasmotaCounter(device_config.id, - component_config, - device_config.configuration.ip_address, - int(device_config.configuration.phase)) + return TasmotaCounter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + phase=int(device_config.configuration.phase)) + + def create_inverter_component(component_config: TasmotaInverterSetup): + return TasmotaInverter(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + phase=int(device_config.configuration.phase)) + + def create_bat_component(component_config: TasmotaBatSetup): + return TasmotaBat(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address, + phase=int(device_config.configuration.phase)) return ConfigurableDevice( device_config=device_config, component_factory=ComponentFactoryByType( counter=create_counter_component, + inverter=create_inverter_component, + bat=create_bat_component ), component_updater=IndependentComponentUpdater(lambda component: component.update()) ) diff --git a/packages/modules/devices/tasmota/tasmota/inverter.py b/packages/modules/devices/tasmota/tasmota/inverter.py new file mode 100644 index 0000000000..c610831c28 --- /dev/null +++ b/packages/modules/devices/tasmota/tasmota/inverter.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +from typing import Any, TypedDict +import logging + +from modules.devices.tasmota.tasmota.config import TasmotaInverterSetup +from modules.common.abstract_device import AbstractInverter +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.store import get_inverter_value_store +from modules.common.simcount import SimCounter +from modules.common import req +from modules.common.component_state import InverterState + +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + ip_address: str + phase: int + + +class TasmotaInverter(AbstractInverter): + def __init__(self, component_config: TasmotaInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__ip_address: str = self.kwargs['ip_address'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") + self.__phase: int = self.kwargs['phase'] + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + url = "http://" + self.__ip_address + "/cm?cmnd=Status%208" + response = req.get_http_session().get(url, timeout=5).json() + + if 'ENERGY' in response['StatusSNS']: + currents = [0.0, 0.0, 0.0] + + power = float(response['StatusSNS']['ENERGY']['Power']) * -1 + currents[self.__phase-1] = (response['StatusSNS']['ENERGY']['Current']), 0.0, 0.0 + _, exported = self.sim_counter.sim_count(power) + + inverter_state = InverterState( + power=power, + currents=currents, + exported=exported + ) + else: + power = float(response['StatusSNS']['Itron']['Power']) * -1 + exported = float(response['StatusSNS']['Itron']['E_out']*1000) + + inverter_state = InverterState( + power=power, + exported=exported + ) + + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=TasmotaInverterSetup) diff --git a/packages/modules/devices/tesla/tesla/bat.py b/packages/modules/devices/tesla/tesla/bat.py index 8383a4aaad..01f32b5153 100644 --- a/packages/modules/devices/tesla/tesla/bat.py +++ b/packages/modules/devices/tesla/tesla/bat.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -from typing import Dict, Union -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -12,8 +10,10 @@ class TeslaBat(AbstractBat): - def __init__(self, component_config: Union[Dict, TeslaBatSetup]) -> None: - self.component_config = dataclass_from_dict(TeslaBatSetup, component_config) + def __init__(self, component_config: TeslaBatSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/tesla/tesla/counter.py b/packages/modules/devices/tesla/tesla/counter.py index f135afe439..277048c5ca 100644 --- a/packages/modules/devices/tesla/tesla/counter.py +++ b/packages/modules/devices/tesla/tesla/counter.py @@ -1,9 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union import logging from requests import HTTPError -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -16,8 +14,10 @@ class TeslaCounter(AbstractCounter): - def __init__(self, component_config: Union[Dict, TeslaCounterSetup]) -> None: - self.component_config = dataclass_from_dict(TeslaCounterSetup, component_config) + def __init__(self, component_config: TeslaCounterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/tesla/tesla/device.py b/packages/modules/devices/tesla/tesla/device.py index 0d6bbd2d61..1bc1703084 100644 --- a/packages/modules/devices/tesla/tesla/device.py +++ b/packages/modules/devices/tesla/tesla/device.py @@ -5,6 +5,7 @@ from typing import Iterable, Union from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common.req import get_http_session from modules.devices.tesla.tesla.bat import TeslaBat @@ -20,7 +21,8 @@ def __update_components(client: PowerwallHttpClient, components: Iterable[Union[TeslaBat, TeslaCounter, TeslaInverter]]): aggregate = client.get_json("/api/meters/aggregates") for component in components: - component.update(client, aggregate) + with SingleComponentUpdateContext(component.fault_state): + component.update(client, aggregate) def _authenticate(session: requests.Session, url: str, email: str, password: str): @@ -38,6 +40,9 @@ def _authenticate(session: requests.Session, url: str, email: str, password: str def create_device(device_config: Tesla): + http_client = None + session = None + def create_bat_component(component_config: TeslaBatSetup): return TeslaBat(component_config) @@ -49,7 +54,7 @@ def create_inverter_component(component_config: TeslaInverterSetup): def update_components(components: Iterable[Union[TeslaBat, TeslaCounter, TeslaInverter]]): log.debug("Beginning update") - nonlocal http_client + nonlocal http_client, session address = device_config.configuration.ip_address email = device_config.configuration.email password = device_config.configuration.password @@ -69,10 +74,14 @@ def update_components(components: Iterable[Union[TeslaBat, TeslaCounter, TeslaIn __update_components(http_client, components) log.debug("Update completed successfully") - session = get_http_session() - http_client = PowerwallHttpClient(device_config.configuration.ip_address, session, None) + def initializer(): + nonlocal http_client, session + session = get_http_session() + http_client = PowerwallHttpClient(device_config.configuration.ip_address, session, None) + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/tesla/tesla/inverter.py b/packages/modules/devices/tesla/tesla/inverter.py index 186cef65fa..7aea4b24b8 100644 --- a/packages/modules/devices/tesla/tesla/inverter.py +++ b/packages/modules/devices/tesla/tesla/inverter.py @@ -1,7 +1,4 @@ #!/usr/bin/env python3 -from typing import Dict, Union - -from dataclass_utils import dataclass_from_dict from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -12,8 +9,10 @@ class TeslaInverter(AbstractInverter): - def __init__(self, component_config: Union[Dict, TeslaInverterSetup]) -> None: - self.component_config = dataclass_from_dict(TeslaInverterSetup, component_config) + def __init__(self, component_config: TeslaInverterSetup) -> None: + self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/thermia/__init__ .py b/packages/modules/devices/thermia/__init__ .py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/thermia/thermia/__init__.py b/packages/modules/devices/thermia/thermia/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/thermia/thermia/config.py b/packages/modules/devices/thermia/thermia/config.py new file mode 100644 index 0000000000..e597e10826 --- /dev/null +++ b/packages/modules/devices/thermia/thermia/config.py @@ -0,0 +1,42 @@ +from typing import Optional +from helpermodules.auto_str import auto_str +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +@auto_str +class ThermiaConfiguration: + def __init__(self, ip_address: Optional[str] = None, port: int = 502, modbus_id: int = 1): + self.ip_address = ip_address + self.port = port + self.modbus_id = modbus_id + + +@auto_str +class Thermia: + def __init__(self, + name: str = "Thermia", + type: str = "thermia", + id: int = 0, + configuration: ThermiaConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or ThermiaConfiguration() + + +@auto_str +class ThermiaCounterConfiguration: + def __init__(self): + pass + + +@auto_str +class ThermiaCounterSetup(ComponentSetup[ThermiaCounterConfiguration]): + def __init__(self, + name: str = "Thermia Zähler", + type: str = "counter", + id: int = 0, + configuration: ThermiaCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or ThermiaCounterConfiguration()) diff --git a/packages/modules/devices/thermia/thermia/counter.py b/packages/modules/devices/thermia/thermia/counter.py new file mode 100644 index 0000000000..20d39d773e --- /dev/null +++ b/packages/modules/devices/thermia/thermia/counter.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +from typing import TypedDict, Any +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.thermia.thermia.config import ThermiaCounterSetup +from pymodbus.constants import Endian + + +class KwargsDict(TypedDict): + device_id: int + client: ModbusTcpClient_ + modbus_id: int + + +class ThermiaCounter(AbstractCounter): + def __init__(self, component_config: ThermiaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] + self.modbus_id: int = self.kwargs['modbus_id'] + self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self): + with self.client: + voltages = [val / 100 for val in self.client.read_input_registers( + 72, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + powers = [val / 1 for val in self.client.read_input_registers( + 78, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + power = sum(powers) + currents = [(val / 100) for val in self.client.read_input_registers( + 69, [ModbusDataType.INT_16] * 3, unit=self.modbus_id)] + imported = self.client.read_input_registers( + 83, ModbusDataType.INT_32, wordorder=Endian.Little, + unit=self.modbus_id) * 100 + exported = 0 + + counter_state = CounterState( + currents=currents, + imported=imported, + exported=exported, + power=power, + powers=powers, + voltages=voltages + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=ThermiaCounterSetup) diff --git a/packages/modules/devices/thermia/thermia/device.py b/packages/modules/devices/thermia/thermia/device.py new file mode 100644 index 0000000000..019d2966ab --- /dev/null +++ b/packages/modules/devices/thermia/thermia/device.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.thermia.thermia.config import Thermia, ThermiaCounterSetup +from modules.devices.thermia.thermia.counter import ThermiaCounter + +log = logging.getLogger(__name__) + + +def create_device(device_config: Thermia): + client = None + + def create_counter_component(component_config: ThermiaCounterSetup): + nonlocal client + return ThermiaCounter(component_config, device_id=device_config.id, + client=client, modbus_id=device_config.configuration.modbus_id) + + def update_components(components: Iterable[Union[ThermiaCounter]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + counter=create_counter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=Thermia) diff --git a/packages/modules/devices/thermia/vendor.py b/packages/modules/devices/thermia/vendor.py new file mode 100644 index 0000000000..911c58a237 --- /dev/null +++ b/packages/modules/devices/thermia/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "Thermia" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/upower/__init__.py b/packages/modules/devices/upower/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/upower/upower/__init__.py b/packages/modules/devices/upower/upower/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/modules/devices/upower/upower/bat.py b/packages/modules/devices/upower/upower/bat.py new file mode 100644 index 0000000000..ce1be62c90 --- /dev/null +++ b/packages/modules/devices/upower/upower/bat.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +from typing import Dict, Union + +from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractBat +from modules.common.component_state import BatState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_bat_value_store +from modules.devices.upower.upower.config import UPowerBatSetup +from modules.devices.upower.upower.version import UPowerVersion + + +class UPowerBat(AbstractBat): + def __init__(self, + component_config: Union[Dict, UPowerBatSetup], + version: UPowerVersion, + modbus_id: int, + client: ModbusTcpClient_, + device_id: int) -> None: + self.component_config = dataclass_from_dict(UPowerBatSetup, component_config) + self.__modbus_id = modbus_id + self.version = version + self.client = client + self.store = get_bat_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="speicher") + + def update(self) -> None: + if self.version == UPowerVersion.GEN_1: + power = self.client.read_input_registers(30258, ModbusDataType.INT_32, unit=self.__modbus_id) * -1 + soc = self.client.read_input_registers(33000, ModbusDataType.UINT_16, unit=self.__modbus_id) + imported = self.client.read_input_registers( + 31108, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + exported = self.client.read_input_registers( + 31110, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + else: + # 1221 Total Bat Power + # 1427 Battery 1 current power + # Bat 1 (additional batteries offset by 50) + power = self.client.read_input_registers(1427, ModbusDataType.INT_16, unit=self.__modbus_id) + soc = self.client.read_input_registers(1402, ModbusDataType.UINT_16, unit=self.__modbus_id) / 10 + imported, exported = self.sim_counter.sim_count(power) + + bat_state = BatState( + power=power, + soc=soc, + imported=imported, + exported=exported + ) + self.store.set(bat_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=UPowerBatSetup) diff --git a/packages/modules/devices/upower/upower/config.py b/packages/modules/devices/upower/upower/config.py new file mode 100644 index 0000000000..64d4148f1d --- /dev/null +++ b/packages/modules/devices/upower/upower/config.py @@ -0,0 +1,67 @@ +from typing import Optional + +from modules.common.component_setup import ComponentSetup +from ..vendor import vendor_descriptor + + +class UPowerConfiguration: + def __init__(self, modbus_id: int = 1, ip_address: Optional[str] = None, port: int = 502, version: int = 1): + self.modbus_id = modbus_id + self.ip_address = ip_address + self.port = port + self.version = version + + +class UPower: + def __init__(self, + name: str = "UPower", + type: str = "upower", + id: int = 0, + configuration: UPowerConfiguration = None) -> None: + self.name = name + self.type = type + self.vendor = vendor_descriptor.configuration_factory().type + self.id = id + self.configuration = configuration or UPowerConfiguration() + + +class UPowerBatConfiguration: + def __init__(self): + pass + + +class UPowerBatSetup(ComponentSetup[UPowerBatConfiguration]): + def __init__(self, + name: str = "UPower Speicher", + type: str = "bat", + id: int = 0, + configuration: UPowerBatConfiguration = None) -> None: + super().__init__(name, type, id, configuration or UPowerBatConfiguration()) + + +class UPowerCounterConfiguration: + def __init__(self): + pass + + +class UPowerCounterSetup(ComponentSetup[UPowerCounterConfiguration]): + def __init__(self, + name: str = "UPower Zähler", + type: str = "counter", + id: int = 0, + configuration: UPowerCounterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or UPowerCounterConfiguration()) + + +class UPowerInverterConfiguration: + def __init__(self): + pass + + +class UPowerInverterSetup(ComponentSetup[UPowerInverterConfiguration]): + def __init__(self, + name: str = "UPower Wechselrichter", + type: str = "inverter", + id: int = 0, + configuration: UPowerInverterConfiguration = None) -> None: + super().__init__(name, type, id, configuration or UPowerInverterConfiguration()) diff --git a/packages/modules/devices/upower/upower/counter.py b/packages/modules/devices/upower/upower/counter.py new file mode 100644 index 0000000000..85756a7d91 --- /dev/null +++ b/packages/modules/devices/upower/upower/counter.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +from typing import Dict, Union + +from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractCounter +from modules.common.component_state import CounterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.simcount import SimCounter +from modules.common.store import get_counter_value_store +from modules.devices.upower.upower.config import UPowerCounterSetup +from modules.devices.upower.upower.version import UPowerVersion + + +class UPowerCounter(AbstractCounter): + def __init__(self, + component_config: Union[Dict, UPowerCounterSetup], + version: UPowerVersion, + modbus_id: int, + client: ModbusTcpClient_, + device_id: int) -> None: + self.component_config = dataclass_from_dict(UPowerCounterSetup, component_config) + self.__modbus_id = modbus_id + self.version = version + self.client = client + self.store = get_counter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.sim_counter = SimCounter(device_id, self.component_config.id, prefix="bezug") + + def update(self): + if self.version == UPowerVersion.GEN_1: + power = self.client.read_holding_registers(1000, ModbusDataType.INT_32, unit=self.__modbus_id) * -1 + frequency = self.client.read_holding_registers(11015, ModbusDataType.UINT_16, unit=self.__modbus_id) + + powers = [-value for value in self.client.read_holding_registers( + 10994, [ModbusDataType.INT_32] * 3, unit=self.__modbus_id)] + exported = self.client.read_holding_registers(31102, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + imported = self.client.read_holding_registers(31104, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + else: + power = self.client.read_holding_registers(1219, ModbusDataType.INT_16, unit=self.__modbus_id) * -10 + frequency = self.client.read_holding_registers( + 1759, ModbusDataType.UINT_16, unit=self.__modbus_id) / 100 + + powers = [-10 * value for value in self.client.read_holding_registers( + 1750, [ModbusDataType.INT_16] * 3, unit=self.__modbus_id + )] + imported, exported = self.sim_counter.sim_count(power) + + counter_state = CounterState( + imported=imported, + exported=exported, + power=power, + powers=powers, + frequency=frequency + ) + self.store.set(counter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=UPowerCounterSetup) diff --git a/packages/modules/devices/upower/upower/device.py b/packages/modules/devices/upower/upower/device.py new file mode 100644 index 0000000000..4637d1922d --- /dev/null +++ b/packages/modules/devices/upower/upower/device.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +import logging +from typing import Iterable, Union + +from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext +from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater +from modules.common.modbus import ModbusTcpClient_ +from modules.devices.upower.upower.bat import UPowerBat +from modules.devices.upower.upower.config import UPower, UPowerBatSetup, UPowerCounterSetup, UPowerInverterSetup +from modules.devices.upower.upower.counter import UPowerCounter +from modules.devices.upower.upower.inverter import UPowerInverter +from modules.devices.upower.upower.version import UPowerVersion + +log = logging.getLogger(__name__) + + +def create_device(device_config: UPower): + client = None + + def create_bat_component(component_config: UPowerBatSetup): + nonlocal client + return UPowerBat(component_config, + UPowerVersion(device_config.configuration.version), + device_config.configuration.modbus_id, client, device_config.id) + + def create_counter_component(component_config: UPowerCounterSetup): + nonlocal client + return UPowerCounter(component_config, + UPowerVersion(device_config.configuration.version), + device_config.configuration.modbus_id, client, device_config.id) + + def create_inverter_component(component_config: UPowerInverterSetup): + nonlocal client + return UPowerInverter(component_config, + UPowerVersion(device_config.configuration.version), + device_config.configuration.modbus_id, client) + + def update_components(components: Iterable[Union[UPowerBat, UPowerCounter, UPowerInverter]]): + with client: + for component in components: + with SingleComponentUpdateContext(component.fault_state): + component.update() + + def initializer(): + nonlocal client + client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) + + return ConfigurableDevice( + device_config=device_config, + initializer=initializer, + component_factory=ComponentFactoryByType( + bat=create_bat_component, + counter=create_counter_component, + inverter=create_inverter_component, + ), + component_updater=MultiComponentUpdater(update_components) + ) + + +device_descriptor = DeviceDescriptor(configuration_factory=UPower) diff --git a/packages/modules/devices/upower/upower/inverter.py b/packages/modules/devices/upower/upower/inverter.py new file mode 100644 index 0000000000..6fd13770df --- /dev/null +++ b/packages/modules/devices/upower/upower/inverter.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +from typing import Dict, Union + +from dataclass_utils import dataclass_from_dict +from modules.common.abstract_device import AbstractInverter +from modules.common.component_state import InverterState +from modules.common.component_type import ComponentDescriptor +from modules.common.fault_state import ComponentInfo, FaultState +from modules.common.modbus import ModbusDataType, ModbusTcpClient_ +from modules.common.store import get_inverter_value_store +from modules.devices.upower.upower.config import UPowerInverterSetup +from modules.devices.upower.upower.version import UPowerVersion + + +class UPowerInverter(AbstractInverter): + def __init__(self, + component_config: Union[Dict, UPowerInverterSetup], + version: UPowerVersion, + modbus_id: int, + client: ModbusTcpClient_) -> None: + self.component_config = dataclass_from_dict(UPowerInverterSetup, component_config) + self.__modbus_id = modbus_id + self.version = version + self.client = client + self.store = get_inverter_value_store(self.component_config.id) + self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + + def update(self) -> None: + if self.version == UPowerVersion.GEN_1: + power = self.client.read_holding_registers(11028, ModbusDataType.UINT_32, unit=self.__modbus_id) * -1 + exported = self.client.read_holding_registers(11020, ModbusDataType.UINT_32, unit=self.__modbus_id) * 100 + else: + power = self.client.read_holding_registers(1220, ModbusDataType.UINT_16, unit=self.__modbus_id) * -1 + exported = self.client.read_holding_registers(1006, ModbusDataType.UINT_32, unit=self.__modbus_id) * 10 + + inverter_state = InverterState( + power=power, + exported=exported + ) + self.store.set(inverter_state) + + +component_descriptor = ComponentDescriptor(configuration_factory=UPowerInverterSetup) diff --git a/packages/modules/devices/upower/upower/version.py b/packages/modules/devices/upower/upower/version.py new file mode 100644 index 0000000000..a404e2901f --- /dev/null +++ b/packages/modules/devices/upower/upower/version.py @@ -0,0 +1,6 @@ +from enum import IntEnum + + +class UPowerVersion(IntEnum): + GEN_1 = 1 + GEN_2 = 2 diff --git a/packages/modules/devices/upower/vendor.py b/packages/modules/devices/upower/vendor.py new file mode 100644 index 0000000000..988c9192dc --- /dev/null +++ b/packages/modules/devices/upower/vendor.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from modules.common.abstract_device import DeviceDescriptor +from modules.devices.vendors import VendorGroup + + +class Vendor: + def __init__(self): + self.type = Path(__file__).parent.name + self.vendor = "UPower" + self.group = VendorGroup.VENDORS.value + + +vendor_descriptor = DeviceDescriptor(configuration_factory=Vendor) diff --git a/packages/modules/devices/varta/varta/bat_api.py b/packages/modules/devices/varta/varta/bat_api.py index 021c250ded..a7475d8bf1 100644 --- a/packages/modules/devices/varta/varta/bat_api.py +++ b/packages/modules/devices/varta/varta/bat_api.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import xml.etree.ElementTree as ET -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any from modules.common import req from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -12,11 +12,19 @@ from modules.devices.varta.varta.config import VartaBatApiSetup +class KwargsDict(TypedDict): + device_id: int + ip_address: str + + class VartaBatApi(AbstractBat): - def __init__(self, device_id: int, component_config: VartaBatApiSetup, device_address: str) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VartaBatApiSetup, component_config) - self.__device_address = device_address + def __init__(self, component_config: VartaBatApiSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.ip_address: str = self.kwargs['ip_address'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) @@ -33,7 +41,7 @@ def get_xml_text(attribute_value: str) -> float: # Wenn Speicher aus bzw. im Standby (keine Antwort), ersetze leeren Wert durch eine 0. return 0 - response = req.get_http_session().get('http://'+self.__device_address+'/cgi/ems_data.xml', + response = req.get_http_session().get('http://'+self.ip_address+'/cgi/ems_data.xml', timeout=5) response.encoding = 'utf-8' response = response.text.replace("\n", "") diff --git a/packages/modules/devices/varta/varta/bat_modbus.py b/packages/modules/devices/varta/varta/bat_modbus.py index 8267991145..f0ac3b5b31 100644 --- a/packages/modules/devices/varta/varta/bat_modbus.py +++ b/packages/modules/devices/varta/varta/bat_modbus.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState from modules.common.component_type import ComponentDescriptor @@ -10,23 +11,31 @@ from modules.devices.varta.varta.config import VartaBatModbusSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class VartaBatModbus(AbstractBat): - def __init__(self, device_id: int, - component_config: VartaBatModbusSetup, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VartaBatModbusSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: VartaBatModbusSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_) -> None: - self.set_state(self.get_state(client)) + def update(self) -> None: + self.set_state(self.get_state()) - def get_state(self, client: ModbusTcpClient_) -> BatState: - soc = client.read_holding_registers(1068, ModbusDataType.INT_16, unit=self.__modbus_id) - power = client.read_holding_registers(1066, ModbusDataType.INT_16, unit=self.__modbus_id) + def get_state(self) -> BatState: + soc = self.client.read_holding_registers(1068, ModbusDataType.INT_16, unit=self.__modbus_id) + power = self.client.read_holding_registers(1066, ModbusDataType.INT_16, unit=self.__modbus_id) return BatState( power=power, soc=soc, diff --git a/packages/modules/devices/varta/varta/counter.py b/packages/modules/devices/varta/varta/counter.py index 244069ef0c..0f84a0fb23 100644 --- a/packages/modules/devices/varta/varta/counter.py +++ b/packages/modules/devices/varta/varta/counter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -10,19 +11,27 @@ from modules.devices.varta.varta.config import VartaCounterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class VartaCounter(AbstractCounter): - def __init__(self, device_id: int, - component_config: VartaCounterSetup, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VartaCounterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: VartaCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): - power = client.read_holding_registers(1078, ModbusDataType.INT_16, unit=self.__modbus_id) * -1 + def update(self): + power = self.client.read_holding_registers(1078, ModbusDataType.INT_16, unit=self.__modbus_id) * -1 imported, exported = self.sim_counter.sim_count(power) counter_state = CounterState( diff --git a/packages/modules/devices/varta/varta/device.py b/packages/modules/devices/varta/varta/device.py index 0e34ee40f4..e22d64ac72 100644 --- a/packages/modules/devices/varta/varta/device.py +++ b/packages/modules/devices/varta/varta/device.py @@ -21,35 +21,53 @@ def create_device(device_config: Varta): + client = None + def create_bat_api_component(component_config: VartaBatApiSetup): - return VartaBatApi(device_config.id, component_config, device_config.configuration.ip_address) + return VartaBatApi(component_config, + device_id=device_config.id, + ip_address=device_config.configuration.ip_address) def create_bat_modbus_component(component_config: VartaBatModbusSetup): - return VartaBatModbus(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return VartaBatModbus(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_counter_component(component_config: VartaCounterSetup): - return VartaCounter(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return VartaCounter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def create_inverter_component(component_config: VartaInverterSetup): - return VartaInverter(device_config.id, component_config, device_config.configuration.modbus_id) + nonlocal client + return VartaInverter(component_config, + device_id=device_config.id, + modbus_id=device_config.configuration.modbus_id, + client=client) def update_components(components: Iterable[Union[VartaBatApi, VartaBatModbus, VartaCounter, VartaInverter]]): - with client as c: + nonlocal client + with client: for component in components: if isinstance(component, (VartaBatModbus, VartaCounter, VartaInverter)): with SingleComponentUpdateContext(component.fault_state): - component.update(c) + component.update() for component in components: if isinstance(component, (VartaBatApi)): with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat_api=create_bat_api_component, bat_modbus=create_bat_modbus_component, diff --git a/packages/modules/devices/varta/varta/inverter.py b/packages/modules/devices/varta/varta/inverter.py index 6be1b430fd..5c7627a819 100644 --- a/packages/modules/devices/varta/varta/inverter.py +++ b/packages/modules/devices/varta/varta/inverter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor from modules.common.fault_state import ComponentInfo, FaultState @@ -9,19 +10,27 @@ from modules.devices.varta.varta.config import VartaInverterSetup +class KwargsDict(TypedDict): + device_id: int + modbus_id: int + client: ModbusTcpClient_ + + class VartaInverter: - def __init__(self, device_id: int, - component_config: VartaInverterSetup, - modbus_id: int) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VartaInverterSetup, component_config) - self.__modbus_id = modbus_id + def __init__(self, component_config: VartaInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__modbus_id: int = self.kwargs['modbus_id'] + self.client: ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) - def update(self, client: ModbusTcpClient_): - power = client.read_holding_registers(1102, ModbusDataType.UINT_16, unit=self.__modbus_id) * -1 + def update(self): + power = self.client.read_holding_registers(1102, ModbusDataType.UINT_16, unit=self.__modbus_id) * -1 _, exported = self.sim_counter.sim_count(power) inverter_state = InverterState( diff --git a/packages/modules/devices/victron/victron/bat.py b/packages/modules/devices/victron/victron/bat.py index 0e58b5fd7f..d0fd546fb3 100644 --- a/packages/modules/devices/victron/victron/bat.py +++ b/packages/modules/devices/victron/victron/bat.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -from typing import Dict, Union +import logging +from typing import Any, Optional, TypedDict -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractBat from modules.common.component_state import BatState @@ -12,18 +12,26 @@ from modules.common.store import get_bat_value_store from modules.devices.victron.victron.config import VictronBatSetup +log = logging.getLogger(__name__) + + +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + class VictronBat(AbstractBat): - def __init__(self, - device_id: int, - component_config: Union[Dict, VictronBatSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VictronBatSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: VictronBatSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="speicher") self.store = get_bat_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) + self.last_mode = 'Undefined' def update(self) -> None: modbus_id = self.component_config.configuration.modbus_id @@ -40,5 +48,42 @@ def update(self) -> None: ) self.store.set(bat_state) + def set_power_limit(self, power_limit: Optional[int]) -> None: + modbus_id = self.component_config.configuration.modbus_id + + # Wenn Victron Dynamic ESS aktiv, erfolgt keine weitere Regelung in openWB + dynamic_ess_mode = self.__tcp_client.read_holding_registers(5400, ModbusDataType.UINT_16, unit=modbus_id) + if dynamic_ess_mode == 1: + log.debug("Dynamic ESS Mode ist aktiv, daher erfolgt keine Regelung des Speichers durch openWB") + return + + if power_limit is None: + log.debug("Keine Batteriesteuerung, Selbstregelung durch Wechselrichter") + if self.last_mode is not None: + # ESS Mode 1 für Selbstregelung mit Phasenkompensation setzen + self.__tcp_client.write_registers(39, [0], data_type=ModbusDataType.UINT_16, unit=228) + self.__tcp_client.write_registers(2902, [1], data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.last_mode = None + elif power_limit == 0: + log.debug("Aktive Batteriesteuerung. Batterie wird auf Stop gesetzt und nicht entladen") + if self.last_mode != 'stop': + # ESS Mode 3 für externe Steuerung und keine Entladung + self.__tcp_client.write_registers(2902, [3], data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_registers(39, [1], data_type=ModbusDataType.UINT_16, unit=228) + self.last_mode = 'stop' + elif power_limit < 0: + if self.last_mode != 'discharge': + # ESS Mode 3 für externe Steuerung und auf L1 wird entladen + self.__tcp_client.write_registers(2902, [3], data_type=ModbusDataType.UINT_16, unit=modbus_id) + self.__tcp_client.write_registers(39, [0], data_type=ModbusDataType.UINT_16, unit=228) + self.last_mode = 'discharge' + # Die maximale Entladeleistung begrenzen auf 5000W + power_value = int(min(power_limit, 5000)) + log.debug(f"Aktive Batteriesteuerung. Batterie wird mit {power_value} W entladen") + self.__tcp_client.write_registers(37, [power_value & 0xFFFF], data_type=ModbusDataType.INT_16, unit=228) + + def power_limit_controllable(self) -> bool: + return True + component_descriptor = ComponentDescriptor(configuration_factory=VictronBatSetup) diff --git a/packages/modules/devices/victron/victron/counter.py b/packages/modules/devices/victron/victron/counter.py index 00a992cfd7..138fa638a4 100644 --- a/packages/modules/devices/victron/victron/counter.py +++ b/packages/modules/devices/victron/victron/counter.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState @@ -13,14 +12,19 @@ from modules.devices.victron.victron.config import VictronCounterSetup +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class VictronCounter(AbstractCounter): - def __init__(self, - device_id: int, - component_config: Union[Dict, VictronCounterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VictronCounterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: VictronCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/victron/victron/device.py b/packages/modules/devices/victron/victron/device.py index c74fdda0f8..5f346f400e 100644 --- a/packages/modules/devices/victron/victron/device.py +++ b/packages/modules/devices/victron/victron/device.py @@ -4,7 +4,6 @@ from modules.common.abstract_device import DeviceDescriptor from modules.common.component_context import SingleComponentUpdateContext - from modules.common.configurable_device import ComponentFactoryByType, ConfigurableDevice, MultiComponentUpdater from modules.common.modbus import ModbusTcpClient_ from modules.devices.victron.victron.bat import VictronBat @@ -16,27 +15,34 @@ def create_device(device_config: Victron): + client = None + def create_bat_component(component_config: VictronBatSetup): - return VictronBat(device_config.id, component_config, client) + nonlocal client + return VictronBat(component_config, device_id=device_config.id, client=client) def create_counter_component(component_config: VictronCounterSetup): - return VictronCounter(device_config.id, component_config, client) + nonlocal client + return VictronCounter(component_config, device_id=device_config.id, client=client) def create_inverter_component(component_config: VictronInverterSetup): - return VictronInverter(device_config.id, component_config, client) + nonlocal client + return VictronInverter(component_config, device_id=device_config.id, client=client) def update_components(components: Iterable[Union[VictronBat, VictronCounter, VictronInverter]]): + nonlocal client with client: for component in components: with SingleComponentUpdateContext(component.fault_state): component.update() - try: + def initializer(): + nonlocal client client = ModbusTcpClient_(device_config.configuration.ip_address, device_config.configuration.port) - except Exception: - log.exception("Fehler in create_device") + return ConfigurableDevice( device_config=device_config, + initializer=initializer, component_factory=ComponentFactoryByType( bat=create_bat_component, counter=create_counter_component, diff --git a/packages/modules/devices/victron/victron/inverter.py b/packages/modules/devices/victron/victron/inverter.py index 91899b389b..84b677e13e 100644 --- a/packages/modules/devices/victron/victron/inverter.py +++ b/packages/modules/devices/victron/victron/inverter.py @@ -1,8 +1,7 @@ #!/usr/bin/env python3 import logging -from typing import Dict, Union +from typing import TypedDict, Any -from dataclass_utils import dataclass_from_dict from modules.common import modbus from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState @@ -16,14 +15,19 @@ log = logging.getLogger(__name__) +class KwargsDict(TypedDict): + device_id: int + client: modbus.ModbusTcpClient_ + + class VictronInverter(AbstractInverter): - def __init__(self, - device_id: int, - component_config: Union[Dict, VictronInverterSetup], - tcp_client: modbus.ModbusTcpClient_) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VictronInverterSetup, component_config) - self.__tcp_client = tcp_client + def __init__(self, component_config: VictronInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] + self.__tcp_client: modbus.ModbusTcpClient_ = self.kwargs['client'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/vzlogger/vzlogger/counter.py b/packages/modules/devices/vzlogger/vzlogger/counter.py index 04f4cde876..0927af64e7 100644 --- a/packages/modules/devices/vzlogger/vzlogger/counter.py +++ b/packages/modules/devices/vzlogger/vzlogger/counter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractCounter from modules.common.component_state import CounterState from modules.common.component_type import ComponentDescriptor @@ -10,10 +11,17 @@ from modules.devices.vzlogger.vzlogger.utils import parse_line +class KwargsDict(TypedDict): + device_id: int + + class VZLoggerCounter(AbstractCounter): - def __init__(self, device_id: int, component_config: VZLoggerCounterSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VZLoggerCounterSetup, component_config) + def __init__(self, component_config: VZLoggerCounterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="bezug") self.store = get_counter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/vzlogger/vzlogger/device.py b/packages/modules/devices/vzlogger/vzlogger/device.py index 30a948d68c..b292fac278 100644 --- a/packages/modules/devices/vzlogger/vzlogger/device.py +++ b/packages/modules/devices/vzlogger/vzlogger/device.py @@ -4,6 +4,7 @@ from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.devices.vzlogger.vzlogger.config import VZLogger, VZLoggerCounterSetup, VZLoggerInverterSetup from modules.devices.vzlogger.vzlogger.counter import VZLoggerCounter @@ -14,15 +15,16 @@ def create_device(device_config: VZLogger): def create_counter_component(component_config: VZLoggerCounterSetup): - return VZLoggerCounter(device_config.id, component_config) + return VZLoggerCounter(component_config, device_id=device_config.id) def create_inverter_component(component_config: VZLoggerInverterSetup): - return VZLoggerInverter(device_config.id, component_config) + return VZLoggerInverter(component_config, device_id=device_config.id) def update_components(components: Iterable[Union[VZLoggerCounter, VZLoggerInverter]]): response = req.get_http_session().get(device_config.configuration.ip_address, timeout=5).json() for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/vzlogger/vzlogger/inverter.py b/packages/modules/devices/vzlogger/vzlogger/inverter.py index ac953309b2..fda1d30dc2 100644 --- a/packages/modules/devices/vzlogger/vzlogger/inverter.py +++ b/packages/modules/devices/vzlogger/vzlogger/inverter.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 -from dataclass_utils import dataclass_from_dict +from typing import TypedDict, Any + from modules.common.abstract_device import AbstractInverter from modules.common.component_state import InverterState from modules.common.component_type import ComponentDescriptor @@ -10,10 +11,17 @@ from modules.devices.vzlogger.vzlogger.utils import parse_line +class KwargsDict(TypedDict): + device_id: int + + class VZLoggerInverter(AbstractInverter): - def __init__(self, device_id: int, component_config: VZLoggerInverterSetup) -> None: - self.__device_id = device_id - self.component_config = dataclass_from_dict(VZLoggerInverterSetup, component_config) + def __init__(self, component_config: VZLoggerInverterSetup, **kwargs: Any) -> None: + self.component_config = component_config + self.kwargs: KwargsDict = kwargs + + def initialize(self) -> None: + self.__device_id: int = self.kwargs['device_id'] self.sim_counter = SimCounter(self.__device_id, self.component_config.id, prefix="pv") self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/devices/youless/youless/device.py b/packages/modules/devices/youless/youless/device.py index 684ff14c42..026b4c8026 100644 --- a/packages/modules/devices/youless/youless/device.py +++ b/packages/modules/devices/youless/youless/device.py @@ -5,6 +5,7 @@ from helpermodules.cli import run_using_positional_cli_args from modules.common import req from modules.common.abstract_device import DeviceDescriptor +from modules.common.component_context import SingleComponentUpdateContext from modules.common.configurable_device import ConfigurableDevice, ComponentFactoryByType, MultiComponentUpdater from modules.devices.youless.youless import inverter from modules.devices.youless.youless.config import Youless, YoulessConfiguration, YoulessInverterSetup @@ -22,7 +23,8 @@ def update_components(components: Iterable[YoulessInverter]): params=(('f', 'j'),), timeout=5).json() for component in components: - component.update(response) + with SingleComponentUpdateContext(component.fault_state): + component.update(response) return ConfigurableDevice( device_config=device_config, diff --git a/packages/modules/devices/youless/youless/inverter.py b/packages/modules/devices/youless/youless/inverter.py index 63bdf3bda7..a26da3a205 100644 --- a/packages/modules/devices/youless/youless/inverter.py +++ b/packages/modules/devices/youless/youless/inverter.py @@ -10,6 +10,8 @@ class YoulessInverter(AbstractInverter): def __init__(self, component_config: YoulessInverterSetup) -> None: self.component_config = component_config + + def initialize(self) -> None: self.store = get_inverter_value_store(self.component_config.id) self.fault_state = FaultState(ComponentInfo.from_component_config(self.component_config)) diff --git a/packages/modules/display_themes/cards/config.py b/packages/modules/display_themes/cards/config.py index 0d4e528838..7cf8475142 100644 --- a/packages/modules/display_themes/cards/config.py +++ b/packages/modules/display_themes/cards/config.py @@ -9,6 +9,8 @@ class CardsDisplayThemeConfiguration: def __init__(self, lock_changes: bool = False, lock_changes_code: Optional[str] = None, + default_view: str = "dashboard", + default_view_timeout: int = 0, enable_dashboard_view: bool = True, enable_dashboard_card_grid: bool = True, enable_dashboard_card_home_consumption: bool = True, @@ -24,6 +26,8 @@ def __init__(self, self.lock_changes = lock_changes self.lock_changes_code = lock_changes_code # dashboard settings + self.default_view = default_view + self.default_view_timeout = default_view_timeout self.enable_dashboard_view = enable_dashboard_view self.enable_dashboard_card_grid = enable_dashboard_card_grid self.enable_dashboard_card_home_consumption = enable_dashboard_card_home_consumption diff --git a/packages/modules/display_themes/cards/source/eslint.config.js b/packages/modules/display_themes/cards/source/eslint.config.js index 75e73d5160..5be329dbb1 100644 --- a/packages/modules/display_themes/cards/source/eslint.config.js +++ b/packages/modules/display_themes/cards/source/eslint.config.js @@ -1,5 +1,6 @@ import js from "@eslint/js" import pluginVue from "eslint-plugin-vue" +import globals from "globals" export default [ js.configs.recommended, @@ -8,6 +9,9 @@ export default [ files: ["**/*.{vue,js,jsx,cjs,mjs}"], languageOptions: { ecmaVersion: "latest", + globals: { + ...globals.browser, + }, }, } ] diff --git a/packages/modules/display_themes/cards/source/package-lock.json b/packages/modules/display_themes/cards/source/package-lock.json index 90340078b8..18811305d7 100644 --- a/packages/modules/display_themes/cards/source/package-lock.json +++ b/packages/modules/display_themes/cards/source/package-lock.json @@ -8,77 +8,82 @@ "name": "openwb-display-cards", "version": "0.0.0", "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-regular-svg-icons": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "@fortawesome/vue-fontawesome": "^3.0.8", + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-regular-svg-icons": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@fortawesome/vue-fontawesome": "^3.1.1", "@inkline/inkline": "^3.2.2", "buffer": "^6.0.3", + "chart.js": "^4.5.0", + "chartjs-adapter-luxon": "^1.3.1", + "chartjs-plugin-annotation": "^3.1.0", "events": "^3.3.0", - "mqtt": "^5.10.3", + "luxon": "^3.7.1", + "mqtt": "^5.14.0", "node-stdlib-browser": "^1.3.1", - "pinia": "^2.3.1", - "vue": "^3.5.13", - "vue-router": "^4.5.0" + "pinia": "^3.0.3", + "vue": "^3.5.18", + "vue-chartjs": "^5.3.2", + "vue-router": "^4.5.1" }, "devDependencies": { "@rollup/plugin-terser": "^0.4.4", - "@rushstack/eslint-patch": "^1.10.5", - "@vitejs/plugin-vue": "^5.2.1", + "@rushstack/eslint-patch": "^1.12.0", + "@vitejs/plugin-vue": "^5.2.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/test-utils": "^2.4.6", - "eslint": "^9.19.0", - "eslint-plugin-vue": "^9.32.0", - "jsdom": "^26.0.0", - "postcss": "^8.5.1", - "postcss-preset-env": "^10.1.3", - "prettier": "^3.4.2", + "eslint": "^9.32.0", + "eslint-plugin-vue": "^10.4.0", + "jsdom": "^26.1.0", + "postcss": "^8.5.6", + "postcss-preset-env": "^10.2.4", + "prettier": "^3.6.2", "rollup-plugin-polyfill-node": "^0.13.0", - "sass": "^1.84.0", - "vite": "^5.4.14", - "vite-plugin-node-polyfills": "^0.23.0", - "vitest": "^2.1.9" + "sass": "^1.89.2", + "vite": "^5.4.19", + "vite-plugin-node-polyfills": "^0.24.0", + "vitest": "^3.2.4" } }, "node_modules/@asamuzakjp/css-color": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", - "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "dev": true, "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", - "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.7" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -88,34 +93,31 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", - "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", - "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@csstools/cascade-layer-name-parser": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.4.tgz", - "integrity": "sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-2.0.5.tgz", + "integrity": "sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==", "dev": true, "funding": [ { @@ -132,14 +134,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/color-helpers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", - "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", "dev": true, "funding": [ { @@ -157,9 +159,9 @@ } }, "node_modules/@csstools/css-calc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", - "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", "dev": true, "funding": [ { @@ -176,14 +178,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-color-parser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", - "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", "dev": true, "funding": [ { @@ -197,21 +199,21 @@ ], "license": "MIT", "dependencies": { - "@csstools/color-helpers": "^5.0.1", - "@csstools/css-calc": "^2.1.1" + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.4" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", "dev": true, "funding": [ { @@ -228,13 +230,13 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", "dev": true, "funding": [ { @@ -252,9 +254,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.2.tgz", - "integrity": "sha512-EUos465uvVvMJehckATTlNqGj4UJWkTmdWuDMjqvSUkjGpmOyFZBVwb4knxCm/k2GMTXY+c/5RkdndzFYWeX5A==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-4.0.3.tgz", + "integrity": "sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==", "dev": true, "funding": [ { @@ -271,14 +273,14 @@ "node": ">=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, "node_modules/@csstools/postcss-cascade-layers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.1.tgz", - "integrity": "sha512-XOfhI7GShVcKiKwmPAnWSqd2tBR0uxt+runAxttbSp/LY2U16yAVPmAf7e9q4JJ0d+xMNmpwNDLBXnmRCl3HMQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-5.0.2.tgz", + "integrity": "sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==", "dev": true, "funding": [ { @@ -326,9 +328,9 @@ } }, "node_modules/@csstools/postcss-cascade-layers/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -340,9 +342,9 @@ } }, "node_modules/@csstools/postcss-color-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.7.tgz", - "integrity": "sha512-aDHYmhNIHR6iLw4ElWhf+tRqqaXwKnMl0YsQ/X105Zc4dQwe6yJpMrTN6BwOoESrkDjOYMOfORviSSLeDTJkdQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-4.0.10.tgz", + "integrity": "sha512-4dY0NBu7NVIpzxZRgh/Q/0GPSz/jLSw0i/u3LTUor0BkQcz/fNhN10mSWBDsL0p9nDb0Ky1PD6/dcGbhACuFTQ==", "dev": true, "funding": [ { @@ -356,10 +358,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -370,9 +372,9 @@ } }, "node_modules/@csstools/postcss-color-mix-function": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.7.tgz", - "integrity": "sha512-e68Nev4CxZYCLcrfWhHH4u/N1YocOfTmw67/kVX5Rb7rnguqqLyxPjhHWjSBX8o4bmyuukmNf3wrUSU3//kT7g==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-function/-/postcss-color-mix-function-3.0.10.tgz", + "integrity": "sha512-P0lIbQW9I4ShE7uBgZRib/lMTf9XMjJkFl/d6w4EMNHu2qvQ6zljJGEcBkw/NsBtq/6q3WrmgxSS8kHtPMkK4Q==", "dev": true, "funding": [ { @@ -386,10 +388,40 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/utilities": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-color-mix-variadic-function-arguments": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-mix-variadic-function-arguments/-/postcss-color-mix-variadic-function-arguments-1.0.0.tgz", + "integrity": "sha512-Z5WhouTyD74dPFPrVE7KydgNS9VvnjB8qcdes9ARpCOItb4jTnm7cHp4FhxCRUoyhabD0WVv43wbkJ4p8hLAlQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "dependencies": { + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -400,9 +432,9 @@ } }, "node_modules/@csstools/postcss-content-alt-text": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.4.tgz", - "integrity": "sha512-YItlZUOuZJCBlRaCf8Aucc1lgN41qYGALMly0qQllrxYJhiyzlI6RxOTMUvtWk+KhS8GphMDsDhKQ7KTPfEMSw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-content-alt-text/-/postcss-content-alt-text-2.0.6.tgz", + "integrity": "sha512-eRjLbOjblXq+byyaedQRSrAejKGNAFued+LcbzT+LCL78fabxHkxYjBbxkroONxHHYu2qxhFK2dBStTLPG3jpQ==", "dev": true, "funding": [ { @@ -416,9 +448,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -429,9 +461,9 @@ } }, "node_modules/@csstools/postcss-exponential-functions": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.6.tgz", - "integrity": "sha512-IgJA5DQsQLu/upA3HcdvC6xEMR051ufebBTIXZ5E9/9iiaA7juXWz1ceYj814lnDYP/7eWjZnw0grRJlX4eI6g==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-exponential-functions/-/postcss-exponential-functions-2.0.9.tgz", + "integrity": "sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==", "dev": true, "funding": [ { @@ -445,9 +477,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -484,9 +516,9 @@ } }, "node_modules/@csstools/postcss-gamut-mapping": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.7.tgz", - "integrity": "sha512-gzFEZPoOkY0HqGdyeBXR3JP218Owr683u7KOZazTK7tQZBE8s2yhg06W1tshOqk7R7SWvw9gkw2TQogKpIW8Xw==", + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gamut-mapping/-/postcss-gamut-mapping-2.0.10.tgz", + "integrity": "sha512-QDGqhJlvFnDlaPAfCYPsnwVA6ze+8hhrwevYWlnUeSjkkZfBpcCO42SaUD8jiLlq7niouyLgvup5lh+f1qessg==", "dev": true, "funding": [ { @@ -500,9 +532,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -512,9 +544,9 @@ } }, "node_modules/@csstools/postcss-gradients-interpolation-method": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.7.tgz", - "integrity": "sha512-WgEyBeg6glUeTdS2XT7qeTFBthTJuXlS9GFro/DVomj7W7WMTamAwpoP4oQCq/0Ki2gvfRYFi/uZtmRE14/DFA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-gradients-interpolation-method/-/postcss-gradients-interpolation-method-5.0.10.tgz", + "integrity": "sha512-HHPauB2k7Oits02tKFUeVFEU2ox/H3OQVrP3fSOKDxvloOikSal+3dzlyTZmYsb9FlY9p5EUpBtz0//XBmy+aw==", "dev": true, "funding": [ { @@ -528,10 +560,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -542,9 +574,9 @@ } }, "node_modules/@csstools/postcss-hwb-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.7.tgz", - "integrity": "sha512-LKYqjO+wGwDCfNIEllessCBWfR4MS/sS1WXO+j00KKyOjm7jDW2L6jzUmqASEiv/kkJO39GcoIOvTTfB3yeBUA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-4.0.10.tgz", + "integrity": "sha512-nOKKfp14SWcdEQ++S9/4TgRKchooLZL0TUFdun3nI4KPwCjETmhjta1QT4ICQcGVWQTvrsgMM/aLB5We+kMHhQ==", "dev": true, "funding": [ { @@ -558,10 +590,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -572,9 +604,9 @@ } }, "node_modules/@csstools/postcss-ic-unit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.0.tgz", - "integrity": "sha512-9QT5TDGgx7wD3EEMN3BSUG6ckb6Eh5gSPT5kZoVtUuAonfPmLDJyPhqR4ntPpMYhUKAMVKAg3I/AgzqHMSeLhA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-4.0.2.tgz", + "integrity": "sha512-lrK2jjyZwh7DbxaNnIUjkeDmU8Y6KyzRBk91ZkI5h8nb1ykEfZrtIVArdIjX4DHMIBGpdHrgP0n4qXDr7OHaKA==", "dev": true, "funding": [ { @@ -588,7 +620,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -600,9 +632,9 @@ } }, "node_modules/@csstools/postcss-initial": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.0.tgz", - "integrity": "sha512-dv2lNUKR+JV+OOhZm9paWzYBXOCi+rJPqJ2cJuhh9xd8USVrd0cBEPczla81HNOyThMQWeCcdln3gZkQV2kYxA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-initial/-/postcss-initial-2.0.1.tgz", + "integrity": "sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==", "dev": true, "funding": [ { @@ -623,9 +655,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.1.tgz", - "integrity": "sha512-JLp3POui4S1auhDR0n8wHd/zTOWmMsmK3nQd3hhL6FhWPaox5W7j1se6zXOG/aP07wV2ww0lxbKYGwbBszOtfQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-5.0.3.tgz", + "integrity": "sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==", "dev": true, "funding": [ { @@ -673,9 +705,9 @@ } }, "node_modules/@csstools/postcss-is-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -687,9 +719,9 @@ } }, "node_modules/@csstools/postcss-light-dark-function": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.7.tgz", - "integrity": "sha512-ZZ0rwlanYKOHekyIPaU+sVm3BEHCe+Ha0/px+bmHe62n0Uc1lL34vbwrLYn6ote8PHlsqzKeTQdIejQCJ05tfw==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-light-dark-function/-/postcss-light-dark-function-2.0.9.tgz", + "integrity": "sha512-1tCZH5bla0EAkFAI2r0H33CDnIBeLUaJh1p+hvvsylJ4svsv2wOmJjJn+OXwUZLXef37GYbRIVKX+X+g6m+3CQ==", "dev": true, "funding": [ { @@ -703,9 +735,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -811,9 +843,9 @@ } }, "node_modules/@csstools/postcss-logical-viewport-units": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.3.tgz", - "integrity": "sha512-OC1IlG/yoGJdi0Y+7duz/kU/beCwO+Gua01sD6GtOtLi7ByQUpcIqs7UE/xuRPay4cHgOMatWdnDdsIDjnWpPw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-3.0.4.tgz", + "integrity": "sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==", "dev": true, "funding": [ { @@ -827,7 +859,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -838,9 +870,9 @@ } }, "node_modules/@csstools/postcss-media-minmax": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.6.tgz", - "integrity": "sha512-J1+4Fr2W3pLZsfxkFazK+9kr96LhEYqoeBszLmFjb6AjYs+g9oDAw3J5oQignLKk3rC9XHW+ebPTZ9FaW5u5pg==", + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-minmax/-/postcss-media-minmax-2.0.9.tgz", + "integrity": "sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==", "dev": true, "funding": [ { @@ -854,10 +886,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -867,9 +899,9 @@ } }, "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.4.tgz", - "integrity": "sha512-AnGjVslHMm5xw9keusQYvjVWvuS7KWK+OJagaG0+m9QnIjZsrysD2kJP/tr/UJIyYtMCtu8OkUd+Rajb4DqtIQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-3.0.5.tgz", + "integrity": "sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==", "dev": true, "funding": [ { @@ -883,9 +915,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -948,9 +980,9 @@ } }, "node_modules/@csstools/postcss-oklab-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.7.tgz", - "integrity": "sha512-I6WFQIbEKG2IO3vhaMGZDkucbCaUSXMxvHNzDdnfsTCF5tc0UlV3Oe2AhamatQoKFjBi75dSEMrgWq3+RegsOQ==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-4.0.10.tgz", + "integrity": "sha512-ZzZUTDd0fgNdhv8UUjGCtObPD8LYxMH+MJsW9xlZaWTV8Ppr4PtxlHYNMmF4vVWGl0T6f8tyWAKjoI6vePSgAg==", "dev": true, "funding": [ { @@ -964,10 +996,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -978,9 +1010,9 @@ } }, "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.0.0.tgz", - "integrity": "sha512-XQPtROaQjomnvLUSy/bALTR5VCtTVUFwYs1SblvYgLSeTo2a/bMNwUwo2piXw5rTv/FEYiy5yPSXBqg9OKUx7Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-4.1.0.tgz", + "integrity": "sha512-YrkI9dx8U4R8Sz2EJaoeD9fI7s7kmeEBfmO+UURNeL6lQI7VxF6sBE+rSqdCBn4onwqmxFdBU3lTwyYb/lCmxA==", "dev": true, "funding": [ { @@ -1004,9 +1036,9 @@ } }, "node_modules/@csstools/postcss-random-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-1.0.2.tgz", - "integrity": "sha512-vBCT6JvgdEkvRc91NFoNrLjgGtkLWt47GKT6E2UDn3nd8ZkMBiziQ1Md1OiKoSsgzxsSnGKG3RVdhlbdZEkHjA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-random-function/-/postcss-random-function-2.0.1.tgz", + "integrity": "sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==", "dev": true, "funding": [ { @@ -1020,9 +1052,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -1032,9 +1064,9 @@ } }, "node_modules/@csstools/postcss-relative-color-syntax": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.7.tgz", - "integrity": "sha512-apbT31vsJVd18MabfPOnE977xgct5B1I+Jpf+Munw3n6kKb1MMuUmGGH+PT9Hm/fFs6fe61Q/EWnkrb4bNoNQw==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/postcss-relative-color-syntax/-/postcss-relative-color-syntax-3.0.10.tgz", + "integrity": "sha512-8+0kQbQGg9yYG8hv0dtEpOMLwB9M+P7PhacgIzVzJpixxV4Eq9AUQtQw8adMmAJU1RBBmIlpmtmm3XTRd/T00g==", "dev": true, "funding": [ { @@ -1048,10 +1080,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -1088,9 +1120,9 @@ } }, "node_modules/@csstools/postcss-scope-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -1102,9 +1134,9 @@ } }, "node_modules/@csstools/postcss-sign-functions": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.1.tgz", - "integrity": "sha512-MslYkZCeMQDxetNkfmmQYgKCy4c+w9pPDfgOBCJOo/RI1RveEUdZQYtOfrC6cIZB7sD7/PHr2VGOcMXlZawrnA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/postcss-sign-functions/-/postcss-sign-functions-1.1.4.tgz", + "integrity": "sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==", "dev": true, "funding": [ { @@ -1118,9 +1150,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -1130,9 +1162,9 @@ } }, "node_modules/@csstools/postcss-stepped-value-functions": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.6.tgz", - "integrity": "sha512-/dwlO9w8vfKgiADxpxUbZOWlL5zKoRIsCymYoh1IPuBsXODKanKnfuZRr32DEqT0//3Av1VjfNZU9yhxtEfIeA==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-4.0.9.tgz", + "integrity": "sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==", "dev": true, "funding": [ { @@ -1146,9 +1178,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -1158,9 +1190,9 @@ } }, "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.1.tgz", - "integrity": "sha512-xPZIikbx6jyzWvhms27uugIc0I4ykH4keRvoa3rxX5K7lEhkbd54rjj/dv60qOCTisoS+3bmwJTeyV1VNBrXaw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-4.0.2.tgz", + "integrity": "sha512-8XvCRrFNseBSAGxeaVTaNijAu+FzUvjwFXtcrynmazGb/9WUdsPCpBX+mHEHShVRq47Gy4peYAoxYs8ltUnmzA==", "dev": true, "funding": [ { @@ -1174,7 +1206,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/color-helpers": "^5.0.1", + "@csstools/color-helpers": "^5.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -1185,9 +1217,9 @@ } }, "node_modules/@csstools/postcss-trigonometric-functions": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.6.tgz", - "integrity": "sha512-c4Y1D2Why/PeccaSouXnTt6WcNHJkoJRidV2VW9s5gJ97cNxnLgQ4Qj8qOqkIR9VmTQKJyNcbF4hy79ZQnWD7A==", + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-4.0.9.tgz", + "integrity": "sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==", "dev": true, "funding": [ { @@ -1201,9 +1233,9 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-calc": "^2.1.1", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "@csstools/css-calc": "^2.1.4", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" }, "engines": { "node": ">=18" @@ -1650,9 +1682,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", - "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -1692,9 +1724,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1706,10 +1738,20 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1720,9 +1762,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1744,13 +1786,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.19.0.tgz", - "integrity": "sha512-rbq9/g38qjfqFLOVPvwjIvFFdNziEC5S65jmjPw5r6A//QH+W91akh9irMwjDN8zKUTak6W9EsAv4m/7Wnw0UQ==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -1764,13 +1809,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -1778,57 +1823,57 @@ } }, "node_modules/@fortawesome/fontawesome-common-types": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.7.2.tgz", - "integrity": "sha512-Zs+YeHUC5fkt7Mg1l6XTniei3k4bwG/yo3iFUtZWd/pMx9g3fdvkSK9E0FOC+++phXOka78uJcYb8JaFkW52Xg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.0.0.tgz", + "integrity": "sha512-PGMrIYXLGA5K8RWy8zwBkd4vFi4z7ubxtet6Yn13Plf6krRTwPbdlCwlcfmoX0R7B4Z643QvrtHmdQ5fNtfFCg==", "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@fortawesome/fontawesome-svg-core": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz", - "integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.0.0.tgz", + "integrity": "sha512-obBEF+zd98r/KtKVW6A+8UGWeaOoyMpl6Q9P3FzHsOnsg742aXsl8v+H/zp09qSSu/a/Hxe9LNKzbBaQq1CEbA==", "license": "MIT", "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" + "@fortawesome/fontawesome-common-types": "7.0.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-regular-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.7.2.tgz", - "integrity": "sha512-7Z/ur0gvCMW8G93dXIQOkQqHo2M5HLhYrRVC0//fakJXxcF1VmMPsxnG6Ee8qEylA8b8Q3peQXWMNZ62lYF28g==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-7.0.0.tgz", + "integrity": "sha512-qAh0mTaCY22sQzMK2lKBrtn/aR4keUu5XmtdYR7d702laMe0h+Ab4Kj2pExR9HZkKhjKoq8pbwt8Td+mjW/ipQ==", "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" + "@fortawesome/fontawesome-common-types": "7.0.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/free-solid-svg-icons": { - "version": "6.7.2", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.7.2.tgz", - "integrity": "sha512-GsBrnOzU8uj0LECDfD5zomZJIjrPhIlWU82AHwa2s40FKH+kcxQaBvBo3Z4TxyZHIyX8XTDxsyA33/Vx9eFuQA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.0.0.tgz", + "integrity": "sha512-njSLAllkOddYDCXgTFboXn54Oe5FcvpkWq+FoetOHR64PbN0608kM02Lze0xtISGpXgP+i26VyXRQA0Irh3Obw==", "license": "(CC-BY-4.0 AND MIT)", "dependencies": { - "@fortawesome/fontawesome-common-types": "6.7.2" + "@fortawesome/fontawesome-common-types": "7.0.0" }, "engines": { "node": ">=6" } }, "node_modules/@fortawesome/vue-fontawesome": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.8.tgz", - "integrity": "sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.1.1.tgz", + "integrity": "sha512-U5azn4mcUVpjHe4JO0Wbe7Ih8e3VbN83EH7OTBtA5/QGw9qcPGffqcmwsLyZYgEkpVkYbq/6dX1Iyl5KUGMp6Q==", "license": "MIT", "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "@fortawesome/fontawesome-svg-core": "~1 || ~6 || ~7", "vue": ">= 3.0.0 < 4" } }, @@ -1885,9 +1930,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1933,18 +1978,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1957,20 +1998,10 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.10.tgz", + "integrity": "sha512-0pPkgz9dY+bijgistcTTJ5mR+ocqRXLuhXHYdzoMmmoJ2C9S46RCm2GMUbatPEUK9Yjy26IrAy8D/M00lLkv+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1979,15 +2010,15 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1995,6 +2026,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, "node_modules/@one-ini/wasm": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", @@ -2324,16 +2361,16 @@ } }, "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz", + "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==", "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/pkgr" } }, "node_modules/@popperjs/core": { @@ -2393,9 +2430,9 @@ } }, "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", + "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", "dev": true, "license": "MIT", "dependencies": { @@ -2416,9 +2453,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.4.tgz", - "integrity": "sha512-gGi5adZWvjtJU7Axs//CWaQbQd/vGy8KGcnEaCWiyCqxWYDxwIlAHFuSe6Guoxtd0SRvSfVTDMPd5H+4KE2kKA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "cpu": [ "arm" ], @@ -2430,9 +2467,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.4.tgz", - "integrity": "sha512-1aRlh1gqtF7vNPMnlf1vJKk72Yshw5zknR/ZAVh7zycRAGF2XBMVDAHmFQz/Zws5k++nux3LOq/Ejj1WrDR6xg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", "cpu": [ "arm64" ], @@ -2444,9 +2481,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.4.tgz", - "integrity": "sha512-drHl+4qhFj+PV/jrQ78p9ch6A0MfNVZScl/nBps5a7u01aGf/GuBRrHnRegA9bP222CBDfjYbFdjkIJ/FurvSQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], @@ -2458,9 +2495,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.4.tgz", - "integrity": "sha512-hQqq/8QALU6t1+fbNmm6dwYsa0PDD4L5r3TpHx9dNl+aSEMnIksHZkSO3AVH+hBMvZhpumIGrTFj8XCOGuIXjw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ "x64" ], @@ -2472,9 +2509,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.4.tgz", - "integrity": "sha512-/L0LixBmbefkec1JTeAQJP0ETzGjFtNml2gpQXA8rpLo7Md+iXQzo9kwEgzyat5Q+OG/C//2B9Fx52UxsOXbzw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ "arm64" ], @@ -2486,9 +2523,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.4.tgz", - "integrity": "sha512-6Rk3PLRK+b8L/M6m/x6Mfj60LhAUcLJ34oPaxufA+CfqkUrDoUPQYFdRrhqyOvtOKXLJZJwxlOLbQjNYQcRQfw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], @@ -2500,9 +2537,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.4.tgz", - "integrity": "sha512-kmT3x0IPRuXY/tNoABp2nDvI9EvdiS2JZsd4I9yOcLCCViKsP0gB38mVHOhluzx+SSVnM1KNn9k6osyXZhLoCA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], @@ -2514,9 +2551,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.4.tgz", - "integrity": "sha512-3iSA9tx+4PZcJH/Wnwsvx/BY4qHpit/u2YoZoXugWVfc36/4mRkgGEoRbRV7nzNBSCOgbWMeuQ27IQWgJ7tRzw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], @@ -2528,9 +2565,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.4.tgz", - "integrity": "sha512-7CwSJW+sEhM9sESEk+pEREF2JL0BmyCro8UyTq0Kyh0nu1v0QPNY3yfLPFKChzVoUmaKj8zbdgBxUhBRR+xGxg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], @@ -2542,9 +2579,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.4.tgz", - "integrity": "sha512-GZdafB41/4s12j8Ss2izofjeFXRAAM7sHCb+S4JsI9vaONX/zQ8cXd87B9MRU/igGAJkKvmFmJJBeeT9jJ5Cbw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "cpu": [ "arm64" ], @@ -2556,9 +2593,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.4.tgz", - "integrity": "sha512-uuphLuw1X6ur11675c2twC6YxbzyLSpWggvdawTUamlsoUv81aAXRMPBC1uvQllnBGls0Qt5Siw8reSIBnbdqQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", "cpu": [ "loong64" ], @@ -2569,10 +2606,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.4.tgz", - "integrity": "sha512-KvLEw1os2gSmD6k6QPCQMm2T9P2GYvsMZMRpMz78QpSoEevHbV/KOUbI/46/JRalhtSAYZBYLAnT9YE4i/l4vg==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ "ppc64" ], @@ -2584,9 +2621,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.4.tgz", - "integrity": "sha512-wcpCLHGM9yv+3Dql/CI4zrY2mpQ4WFergD3c9cpRowltEh5I84pRT/EuHZsG0In4eBPPYthXnuR++HrFkeqwkA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ "riscv64" ], @@ -2598,9 +2649,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.4.tgz", - "integrity": "sha512-nLbfQp2lbJYU8obhRQusXKbuiqm4jSJteLwfjnunDT5ugBKdxqw1X9KWwk8xp1OMC6P5d0WbzxzhWoznuVK6XA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ "s390x" ], @@ -2612,9 +2663,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.4.tgz", - "integrity": "sha512-JGejzEfVzqc/XNiCKZj14eb6s5w8DdWlnQ5tWUbs99kkdvfq9btxxVX97AaxiUX7xJTKFA0LwoS0KU8C2faZRg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "cpu": [ "x64" ], @@ -2626,9 +2677,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.4.tgz", - "integrity": "sha512-/iFIbhzeyZZy49ozAWJ1ZR2KW6ZdYUbQXLT4O5n1cRZRoTpwExnHLjlurDXXPKEGxiAg0ujaR9JDYKljpr2fDg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ "x64" ], @@ -2640,9 +2691,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.4.tgz", - "integrity": "sha512-qORc3UzoD5UUTneiP2Afg5n5Ti1GAW9Gp5vHPxzvAFFA3FBaum9WqGvYXGf+c7beFdOKNos31/41PRMUwh1tpA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "cpu": [ "arm64" ], @@ -2654,9 +2705,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.4.tgz", - "integrity": "sha512-5g7E2PHNK2uvoD5bASBD9aelm44nf1w4I5FEI7MPHLWcCSrR8JragXZWgKPXk5i2FU3JFfa6CGZLw2RrGBHs2Q==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "cpu": [ "ia32" ], @@ -2668,9 +2719,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.4.tgz", - "integrity": "sha512-p0scwGkR4kZ242xLPBuhSckrJ734frz6v9xZzD+kHVYRAkSUmdSLCIJRfql6H5//aF8Q10K+i7q8DiPfZp0b7A==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], @@ -2682,16 +2733,33 @@ ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.5.tgz", - "integrity": "sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.12.0.tgz", + "integrity": "sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, @@ -2703,37 +2771,36 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.13.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz", - "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==", + "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz", + "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~7.10.0" } }, "node_modules/@types/readable-stream": { - "version": "4.0.18", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.18.tgz", - "integrity": "sha512-21jK/1j+Wg+7jVw1xnSwy/2Q1VgVjWuFssbYGTREPUBeZ+rqVFl2udq0IkxzPC0ZhOzVceUbyIACFZKLqKEBlA==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.21.tgz", + "integrity": "sha512-19eKVv9tugr03IgfXlA9UVUVRbW6IuqRO5B92Dl4a6pT7K8uaGrNS0GkxiZD0BOk6PLuXl5FhWl//eX/pzYdTQ==", "license": "MIT", "dependencies": { - "@types/node": "*", - "safe-buffer": "~5.1.1" + "@types/node": "*" } }, "node_modules/@types/ws": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", - "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@vitejs/plugin-vue": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", - "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz", + "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==", "dev": true, "license": "MIT", "engines": { @@ -2745,38 +2812,39 @@ } }, "node_modules/@vitest/expect": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", - "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "tinyrainbow": "^1.2.0" + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", - "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.1.9", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", - "magic-string": "^0.30.12" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -2798,130 +2866,170 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", - "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "2.1.9", - "pathe": "^1.1.2" + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", - "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.2" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vue/compiler-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", - "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.18.tgz", + "integrity": "sha512-3slwjQrrV1TO8MoXgy3aynDQ7lslj5UqDxuHnrzHtpON5CBinhWjJETciPngpin/T3OuW3tXUf86tEurusnztw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.13", + "@babel/parser": "^7.28.0", + "@vue/shared": "3.5.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-core/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/@vue/compiler-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", - "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.18.tgz", + "integrity": "sha512-RMbU6NTU70++B1JyVJbNbeFkK+A+Q7y9XKE2EM4NLGm2WFR8x9MbAtWxPPLdm0wUkuZv9trpwfSlL6tjdIa1+A==", "license": "MIT", "dependencies": { - "@vue/compiler-core": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-core": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", - "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.18.tgz", + "integrity": "sha512-5aBjvGqsWs+MoxswZPoTB9nSDb3dhd1x30xrrltKujlCxo48j8HGDNj3QPhF4VIS0VQDUrA1xUfp2hEa+FNyXA==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.13", - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13", + "@babel/parser": "^7.28.0", + "@vue/compiler-core": "3.5.18", + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18", "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.48", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.17", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", - "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.18.tgz", + "integrity": "sha512-xM16Ak7rSWHkM3m22NlmcdIM+K4BMyFARAfV9hYFl+SFuRzrZ3uGMNW05kA5pmeMa0X9X963Kgou7ufdbpOP9g==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-dom": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/devtools-api": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "license": "MIT" + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz", + "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.7" + } + }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz", + "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.7", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz", + "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } }, "node_modules/@vue/eslint-config-prettier": { "version": "10.2.0", @@ -2939,53 +3047,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", - "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.18.tgz", + "integrity": "sha512-x0vPO5Imw+3sChLM5Y+B6G1zPjwdOri9e8V21NnTnlEvkxatHEH5B5KEAJcjuzQ7BsjGrKtfzuQ5eQwXh8HXBg==", "license": "MIT", "dependencies": { - "@vue/shared": "3.5.13" + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", - "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.18.tgz", + "integrity": "sha512-DUpHa1HpeOQEt6+3nheUfqVXRog2kivkXHUhoqJiKR33SO4x+a5uNOMkV487WPerQkL0vUuRvq/7JhRgLW3S+w==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/reactivity": "3.5.18", + "@vue/shared": "3.5.18" } }, "node_modules/@vue/runtime-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", - "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.18.tgz", + "integrity": "sha512-YwDj71iV05j4RnzZnZtGaXwPoUWeRsqinblgVJwR8XTXYZ9D5PbahHQgsbmzUvCWNF6x7siQ89HgnX5eWkr3mw==", "license": "MIT", "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/runtime-core": "3.5.13", - "@vue/shared": "3.5.13", + "@vue/reactivity": "3.5.18", + "@vue/runtime-core": "3.5.18", + "@vue/shared": "3.5.18", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", - "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.18.tgz", + "integrity": "sha512-PvIHLUoWgSbDG7zLHqSqaCoZvHi6NNmfVFOqO+OnwvqMz/tqQr3FuGWS8ufluNddk7ZLBJYMrjcw1c6XzR12mA==", "license": "MIT", "dependencies": { - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-ssr": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { - "vue": "3.5.13" + "vue": "3.5.18" } }, "node_modules/@vue/shared": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", - "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.18.tgz", + "integrity": "sha512-cZy8Dq+uuIXbxCZpuLd2GJdeSO/lIzIspC2WtkqIpje5QyFbvLaI5wZtdUjLHjGZrlVX6GilejatWwVYYRc8tA==", "license": "MIT" }, "node_modules/@vue/test-utils": { @@ -3022,9 +3130,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -3045,9 +3153,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", "engines": { @@ -3119,9 +3227,9 @@ } }, "node_modules/asn1.js/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/assert": { @@ -3147,17 +3255,10 @@ "node": ">=12" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "dev": true, "funding": [ { @@ -3175,11 +3276,11 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3234,10 +3335,19 @@ ], "license": "MIT" }, + "node_modules/birpc": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.5.0.tgz", + "integrity": "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/bl": { - "version": "6.0.19", - "resolved": "https://registry.npmjs.org/bl/-/bl-6.0.19.tgz", - "integrity": "sha512-4Ay3A3oDfGg3GGirhl4s62ebtnk0pJZA5mLp672MPKOQXsWvXjEF4dqdXySjJIs7b9OVr/O8aOo0Lm+xdjo2JA==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.1.tgz", + "integrity": "sha512-yYc8UIHrd1ZTLgNBIE7JjMzUPZH+dec3q7nWkrSHEbtvkQ3h6WKC63W9K5jthcL5EXFyMuWYq+2pq5WMSIgFHw==", "license": "MIT", "dependencies": { "@types/readable-stream": "^4.0.0", @@ -3247,9 +3357,9 @@ } }, "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", + "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", "license": "MIT" }, "node_modules/boolbase": { @@ -3260,9 +3370,9 @@ "license": "ISC" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3284,6 +3394,18 @@ "node": ">=8" } }, + "node_modules/broker-factory": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.8.tgz", + "integrity": "sha512-xmVnYN0FZtynhPUmAnN+/MFRdbDi3syCuxWV7o7s78FcIN0pjDtn9mUrVqEgdjQkbfojRhlPWbYbXJkMCyddrg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "fast-unique-numbers": "^9.0.22", + "tslib": "^2.8.1", + "worker-factory": "^7.0.44" + } + }, "node_modules/brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", @@ -3350,26 +3472,6 @@ "node": ">= 0.10" } }, - "node_modules/browserify-rsa/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/browserify-sign": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.3.tgz", @@ -3391,6 +3493,12 @@ "node": ">= 0.12" } }, + "node_modules/browserify-sign/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, "node_modules/browserify-sign/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -3412,26 +3520,6 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, - "node_modules/browserify-sign/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/browserify-sign/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3457,9 +3545,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -3477,10 +3565,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -3560,9 +3648,9 @@ } }, "node_modules/call-bind-apply-helpers": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", - "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -3573,13 +3661,13 @@ } }, "node_modules/call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -3599,9 +3687,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001697", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001697.tgz", - "integrity": "sha512-GwNPlWJin8E+d7Gxq96jxM6w0w+VFeyyXRsjU58emtkYqnbwHqXm5uT2uCmO0RQE9htWknOP4xtBlLmM/gWxvQ==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -3620,9 +3708,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", - "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz", + "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==", "dev": true, "license": "MIT", "dependencies": { @@ -3633,7 +3721,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -3653,6 +3741,37 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-adapter-luxon": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/chartjs-adapter-luxon/-/chartjs-adapter-luxon-1.3.1.tgz", + "integrity": "sha512-yxHov3X8y+reIibl1o+j18xzrcdddCLqsXhriV2+aQ4hCR66IYFchlRXUvrJVoxglJ380pgytU7YWtoqdIgqhg==", + "license": "MIT", + "peerDependencies": { + "chart.js": ">=3.0.0", + "luxon": ">=1.0.0" + } + }, + "node_modules/chartjs-plugin-annotation": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/chartjs-plugin-annotation/-/chartjs-plugin-annotation-3.1.0.tgz", + "integrity": "sha512-EkAed6/ycXD/7n0ShrlT1T2Hm3acnbFhgkIEJLa0X+M6S16x0zwj1Fv4suv/2bwayCT3jGPdAtI9uLcAMToaQQ==", + "license": "MIT", + "peerDependencies": { + "chart.js": ">=4.0.0" + } + }, "node_modules/check-error": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", @@ -3692,26 +3811,6 @@ "node": ">= 0.10" } }, - "node_modules/cipher-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3732,19 +3831,6 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -3819,6 +3905,21 @@ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", "license": "MIT" }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3836,9 +3937,9 @@ } }, "node_modules/create-ecdh/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/create-hash": { @@ -3942,9 +4043,9 @@ } }, "node_modules/css-blank-pseudo/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -4007,9 +4108,9 @@ } }, "node_modules/css-has-pseudo/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -4044,9 +4145,9 @@ } }, "node_modules/cssdb": { - "version": "8.2.3", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.2.3.tgz", - "integrity": "sha512-9BDG5XmJrJQQnJ51VFxXCAtpZ5ebDlAREmO8sxMOVU0aSxN/gocbctjIG5LMh3WBUq+xTlb/jw2LoljBEqraTA==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-8.3.1.tgz", + "integrity": "sha512-XnDRQMXucLueX92yDe0LPKupXetWoFOgawr4O4X41l5TltgK2NVbJJVDnnOywDYfW1sTJ28AcXGKOqdRKwCcmQ==", "dev": true, "funding": [ { @@ -4074,13 +4175,13 @@ } }, "node_modules/cssstyle": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", - "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", "dependencies": { - "@asamuzakjp/css-color": "^2.8.2", + "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" }, "engines": { @@ -4108,9 +4209,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4125,9 +4226,9 @@ } }, "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, "license": "MIT" }, @@ -4182,16 +4283,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/des.js": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", @@ -4228,9 +4319,9 @@ } }, "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/domain-browser": { @@ -4286,9 +4377,9 @@ } }, "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4312,9 +4403,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.93", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.93.tgz", - "integrity": "sha512-M+29jTcfNNoR9NV7la4SwUqzWAxEwnc7ThA5e1m6LRSotmpfpCpLcIfgtSCVL+MllNLgAyM/5ru86iMRemPzDQ==", + "version": "1.5.195", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.195.tgz", + "integrity": "sha512-URclP0iIaDUzqcAyV1v2PgduJ9N0IdXmWsnPzPfelvBmjmZzEy6xJcjb1cXj+TbYqXgtLrjHEoaSIdTYhw4ezg==", "dev": true, "license": "ISC" }, @@ -4334,9 +4425,9 @@ } }, "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/emoji-regex": { @@ -4347,9 +4438,10 @@ "license": "MIT" }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -4377,9 +4469,9 @@ } }, "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -4458,22 +4550,23 @@ } }, "node_modules/eslint": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", - "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.19.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -4481,9 +4574,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4518,27 +4611,30 @@ } }, "node_modules/eslint-config-prettier": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", - "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { - "eslint-config-prettier": "build/bin/cli.js" + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-prettier": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", - "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.3.tgz", + "integrity": "sha512-NAdMYww51ehKfDyDhv59/eIItUVzU0Io9H2E8nHNGKEeeqlnci+1gCvrHib6EmZdf6GxF+LCV5K7UC65Ezvw7w==", "dev": true, "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.9.1" + "synckit": "^0.11.7" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -4549,7 +4645,7 @@ "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", - "eslint-config-prettier": "*", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { @@ -4562,48 +4658,37 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.32.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", - "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-10.4.0.tgz", + "integrity": "sha512-K6tP0dW8FJVZLQxa2S7LcE1lLw3X8VvB3t887Q6CLrFVxHYBXGANbXvwNzYIu6Ughx1bSJ5BDT0YB3ybPT39lw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "globals": "^13.24.0", "natural-compare": "^1.4.0", "nth-check": "^2.1.1", "postcss-selector-parser": "^6.0.15", "semver": "^7.6.3", - "vue-eslint-parser": "^9.4.3", "xml-name-validator": "^4.0.0" }, "engines": { - "node": "^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-vue/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" + "@typescript-eslint/parser": "^7.0.0 || ^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "vue-eslint-parser": "^10.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "@typescript-eslint/parser": { + "optional": true + } } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -4618,9 +4703,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4631,15 +4716,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4729,9 +4814,9 @@ } }, "node_modules/expect-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", - "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4767,16 +4852,31 @@ "license": "MIT" }, "node_modules/fast-unique-numbers": { - "version": "8.0.13", - "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", - "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "version": "9.0.22", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.22.tgz", + "integrity": "sha512-dBR+30yHAqBGvOuxxQdnn2lTLHCO6r/9B+M4yF8mNrzr3u1yiF+YVJ6u3GTyPN/VRWqaE1FcscZDdBgVKmrmQQ==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.8", - "tslib": "^2.6.2" + "@babel/runtime": "^7.27.6", + "tslib": "^2.8.1" }, "engines": { - "node": ">=16.1.0" + "node": ">=18.2.0" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, "node_modules/file-entry-cache": { @@ -4837,16 +4937,16 @@ } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -4859,13 +4959,13 @@ } }, "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -4875,21 +4975,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4929,17 +5014,17 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", + "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "get-proto": "^1.0.0", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", @@ -5000,9 +5085,9 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5112,26 +5197,6 @@ "node": ">= 0.10" } }, - "node_modules/hash-base/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/hash.js": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", @@ -5171,6 +5236,12 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, "node_modules/html-encoding-sniffer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", @@ -5262,9 +5333,9 @@ } }, "node_modules/immutable": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.0.3.tgz", - "integrity": "sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", + "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", "dev": true, "license": "MIT" }, @@ -5308,6 +5379,19 @@ "dev": true, "license": "ISC" }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/is-arguments": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", @@ -5469,10 +5553,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, "node_modules/isexe": { @@ -5508,17 +5604,17 @@ } }, "node_modules/js-beautify": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", - "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "dev": true, "license": "MIT", "dependencies": { "config-chain": "^1.1.13", "editorconfig": "^1.0.4", - "glob": "^10.3.3", + "glob": "^10.4.2", "js-cookie": "^3.0.5", - "nopt": "^7.2.0" + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", @@ -5549,6 +5645,13 @@ "url": "https://opencollective.com/js-sdsl" } }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -5562,17 +5665,22 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT" + }, "node_modules/jsdom": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", - "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", - "decimal.js": "^10.4.3", - "form-data": "^4.0.1", + "decimal.js": "^10.5.0", "html-encoding-sniffer": "^4.0.0", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", @@ -5582,12 +5690,12 @@ "rrweb-cssom": "^0.8.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^5.0.0", + "tough-cookie": "^5.1.1", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.0", + "whatwg-url": "^14.1.1", "ws": "^8.18.0", "xml-name-validator": "^5.0.0" }, @@ -5673,13 +5781,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5688,9 +5789,9 @@ "license": "MIT" }, "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", "dev": true, "license": "MIT" }, @@ -5700,6 +5801,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/luxon": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", + "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -5772,34 +5882,11 @@ } }, "node_modules/miller-rabin/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -5844,28 +5931,34 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/mqtt": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.10.3.tgz", - "integrity": "sha512-hA/6YrUS4fywhBGCjH/XXUuLeueJiPqruVVWjK2A24Ma4KcWfZ/x8x07aoesBV+HXDWBC08tbT4IWfSXNW0Jtw==", + "version": "5.14.0", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.14.0.tgz", + "integrity": "sha512-H7EmeCJhbGblbWjm6APF5sAH3SkdI7lxHw/UkblZp8fjSNl8b2MsLcdAkIaQKxvZYmiORkdAjffvKjqQWPkd6w==", "license": "MIT", "dependencies": { - "@types/readable-stream": "^4.0.5", - "@types/ws": "^8.5.9", + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", "commist": "^3.2.0", "concat-stream": "^2.0.0", - "debug": "^4.3.4", + "debug": "^4.4.1", "help-me": "^5.0.0", - "lru-cache": "^10.0.1", + "lru-cache": "^10.4.3", "minimist": "^1.2.8", - "mqtt-packet": "^9.0.1", + "mqtt-packet": "^9.0.2", "number-allocator": "^1.0.14", - "readable-stream": "^4.4.2", - "reinterval": "^1.1.0", - "rfdc": "^1.3.0", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", "split2": "^4.2.0", - "worker-timers": "^7.1.4", - "ws": "^8.17.1" + "worker-timers": "^8.0.23", + "ws": "^8.18.3" }, "bin": { "mqtt": "build/bin/mqtt.js", @@ -5877,9 +5970,9 @@ } }, "node_modules/mqtt-packet": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.1.tgz", - "integrity": "sha512-koZF1V/X2RZUI6uD9wN5OK1JxxcG1ofAR4H3LjCw1FkeKzruZQ26aAA6v2m1lZyWONZIR5wMMJFrZJDRNzbiQw==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", "license": "MIT", "dependencies": { "bl": "^6.0.8", @@ -5894,9 +5987,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "funding": [ { "type": "github", @@ -6059,9 +6152,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", - "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "version": "2.2.21", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz", + "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==", "dev": true, "license": "MIT" }, @@ -6219,34 +6312,14 @@ "node": ">= 0.10" } }, - "node_modules/parse-asn1/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.5.0" + "entities": "^6.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -6301,16 +6374,16 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -6318,32 +6391,70 @@ } }, "node_modules/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.3.tgz", + "integrity": "sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==", "license": "MIT", "dependencies": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" + "create-hash": "~1.1.3", + "create-hmac": "^1.1.7", + "ripemd160": "=2.0.1", + "safe-buffer": "^5.2.1", + "sha.js": "^2.4.11", + "to-buffer": "^1.2.0" }, "engines": { "node": ">=0.12" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" + "node_modules/pbkdf2/node_modules/create-hash": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz", + "integrity": "sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==", + "license": "MIT", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "sha.js": "^2.4.0" + } }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, + "node_modules/pbkdf2/node_modules/hash-base": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", + "integrity": "sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1" + } + }, + "node_modules/pbkdf2/node_modules/ripemd160": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz", + "integrity": "sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==", + "license": "MIT", + "dependencies": { + "hash-base": "^2.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6353,13 +6464,12 @@ } }, "node_modules/pinia": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.3.1.tgz", - "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.3.tgz", + "integrity": "sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==", "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.6.3", - "vue-demi": "^0.14.10" + "@vue/devtools-api": "^7.7.2" }, "funding": { "url": "https://github.com/sponsors/posva" @@ -6387,18 +6497,18 @@ } }, "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "funding": [ { "type": "opencollective", @@ -6415,7 +6525,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6450,9 +6560,9 @@ } }, "node_modules/postcss-attribute-case-insensitive/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -6480,9 +6590,9 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.7.tgz", - "integrity": "sha512-EZvAHsvyASX63vXnyXOIynkxhaHRSsdb7z6yiXKIovGXAolW4cMZ3qoh7k3VdTsLBS6VGdksGfIo3r6+waLoOw==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-7.0.10.tgz", + "integrity": "sha512-k9qX+aXHBiLTRrWoCJuUFI6F1iF6QJQUXNVWJVSbqZgj57jDhBlOvD8gNUGl35tgqDivbGLhZeW3Ongz4feuKA==", "dev": true, "funding": [ { @@ -6496,10 +6606,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -6564,9 +6674,9 @@ } }, "node_modules/postcss-custom-media": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.5.tgz", - "integrity": "sha512-SQHhayVNgDvSAdX9NQ/ygcDQGEY+aSF4b/96z7QUX6mqL5yl/JgG/DywcF6fW9XbnCRE+aVYk+9/nqGuzOPWeQ==", + "version": "11.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-11.0.6.tgz", + "integrity": "sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==", "dev": true, "funding": [ { @@ -6580,10 +6690,10 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/media-query-list-parser": "^4.0.2" + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/media-query-list-parser": "^4.0.3" }, "engines": { "node": ">=18" @@ -6593,9 +6703,9 @@ } }, "node_modules/postcss-custom-properties": { - "version": "14.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.4.tgz", - "integrity": "sha512-QnW8FCCK6q+4ierwjnmXF9Y9KF8q0JkbgVfvQEMa93x1GT8FvOiUevWCN2YLaOWyByeDX8S6VFbZEeWoAoXs2A==", + "version": "14.0.6", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-14.0.6.tgz", + "integrity": "sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==", "dev": true, "funding": [ { @@ -6609,9 +6719,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -6623,9 +6733,9 @@ } }, "node_modules/postcss-custom-selectors": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.4.tgz", - "integrity": "sha512-ASOXqNvDCE0dAJ/5qixxPeL1aOVGHGW2JwSy7HyjWNbnWTQCl+fDc968HY1jCmZI0+BaYT5CxsOiUhavpG/7eg==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-8.0.5.tgz", + "integrity": "sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==", "dev": true, "funding": [ { @@ -6639,9 +6749,9 @@ ], "license": "MIT", "dependencies": { - "@csstools/cascade-layer-name-parser": "^2.0.4", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", + "@csstools/cascade-layer-name-parser": "^2.0.5", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", "postcss-selector-parser": "^7.0.0" }, "engines": { @@ -6652,9 +6762,9 @@ } }, "node_modules/postcss-custom-selectors/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -6692,9 +6802,9 @@ } }, "node_modules/postcss-dir-pseudo-class/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -6706,9 +6816,9 @@ } }, "node_modules/postcss-double-position-gradients": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.0.tgz", - "integrity": "sha512-JkIGah3RVbdSEIrcobqj4Gzq0h53GG4uqDPsho88SgY84WnpkTpI0k50MFK/sX7XqVisZ6OqUfFnoUO6m1WWdg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-6.0.2.tgz", + "integrity": "sha512-7qTqnL7nfLRyJK/AHSVrrXOuvDDzettC+wGoienURV8v2svNbu6zJC52ruZtHaO6mfcagFmuTGFdzRsJKB3k5Q==", "dev": true, "funding": [ { @@ -6722,7 +6832,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0", "postcss-value-parser": "^4.2.0" }, @@ -6760,9 +6870,9 @@ } }, "node_modules/postcss-focus-visible/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -6800,9 +6910,9 @@ } }, "node_modules/postcss-focus-within/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -6874,9 +6984,9 @@ } }, "node_modules/postcss-lab-function": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.7.tgz", - "integrity": "sha512-+ONj2bpOQfsCKZE2T9VGMyVVdGcGUpr7u3SVfvkJlvhTRmDCfY25k4Jc8fubB9DclAPR4+w8uVtDZmdRgdAHig==", + "version": "7.0.10", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-7.0.10.tgz", + "integrity": "sha512-tqs6TCEv9tC1Riq6fOzHuHcZyhg4k3gIAMB8GGY/zA1ssGdm6puHMVE7t75aOSoFg7UD2wyrFFhbldiCMyyFTQ==", "dev": true, "funding": [ { @@ -6890,10 +7000,10 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/css-color-parser": "^3.0.7", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", + "@csstools/css-color-parser": "^3.0.10", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", "@csstools/utilities": "^2.0.0" }, "engines": { @@ -6904,9 +7014,9 @@ } }, "node_modules/postcss-logical": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.0.0.tgz", - "integrity": "sha512-HpIdsdieClTjXLOyYdUPAX/XQASNIwdKt5hoZW08ZOAiI+tbV0ta1oclkpVkW5ANU+xJvk3KkA0FejkjGLXUkg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-8.1.0.tgz", + "integrity": "sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==", "dev": true, "funding": [ { @@ -6930,9 +7040,9 @@ } }, "node_modules/postcss-nesting": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.1.tgz", - "integrity": "sha512-VbqqHkOBOt4Uu3G8Dm8n6lU5+9cJFxiuty9+4rcoyRPO9zZS1JIs6td49VIoix3qYqELHlJIn46Oih9SAKo+yQ==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-13.0.2.tgz", + "integrity": "sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==", "dev": true, "funding": [ { @@ -6946,7 +7056,7 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/selector-resolve-nested": "^3.0.0", + "@csstools/selector-resolve-nested": "^3.1.0", "@csstools/selector-specificity": "^5.0.0", "postcss-selector-parser": "^7.0.0" }, @@ -6958,9 +7068,9 @@ } }, "node_modules/postcss-nesting/node_modules/@csstools/selector-resolve-nested": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.0.0.tgz", - "integrity": "sha512-ZoK24Yku6VJU1gS79a5PFmC8yn3wIapiKmPgun0hZgEI5AOqgH2kiPRsPz1qkGv4HL+wuDLH83yQyk6inMYrJQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-resolve-nested/-/selector-resolve-nested-3.1.0.tgz", + "integrity": "sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==", "dev": true, "funding": [ { @@ -7004,9 +7114,9 @@ } }, "node_modules/postcss-nesting/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -7103,9 +7213,9 @@ } }, "node_modules/postcss-preset-env": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.1.3.tgz", - "integrity": "sha512-9qzVhcMFU/MnwYHyYpJz4JhGku/4+xEiPTmhn0hj3IxnUYlEF9vbh7OC1KoLAnenS6Fgg43TKNp9xcuMeAi4Zw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-10.2.4.tgz", + "integrity": "sha512-q+lXgqmTMdB0Ty+EQ31SuodhdfZetUlwCA/F0zRcd/XdxjzI+Rl2JhZNz5US2n/7t9ePsvuhCnEN4Bmu86zXlA==", "dev": true, "funding": [ { @@ -7119,62 +7229,63 @@ ], "license": "MIT-0", "dependencies": { - "@csstools/postcss-cascade-layers": "^5.0.1", - "@csstools/postcss-color-function": "^4.0.7", - "@csstools/postcss-color-mix-function": "^3.0.7", - "@csstools/postcss-content-alt-text": "^2.0.4", - "@csstools/postcss-exponential-functions": "^2.0.6", + "@csstools/postcss-cascade-layers": "^5.0.2", + "@csstools/postcss-color-function": "^4.0.10", + "@csstools/postcss-color-mix-function": "^3.0.10", + "@csstools/postcss-color-mix-variadic-function-arguments": "^1.0.0", + "@csstools/postcss-content-alt-text": "^2.0.6", + "@csstools/postcss-exponential-functions": "^2.0.9", "@csstools/postcss-font-format-keywords": "^4.0.0", - "@csstools/postcss-gamut-mapping": "^2.0.7", - "@csstools/postcss-gradients-interpolation-method": "^5.0.7", - "@csstools/postcss-hwb-function": "^4.0.7", - "@csstools/postcss-ic-unit": "^4.0.0", - "@csstools/postcss-initial": "^2.0.0", - "@csstools/postcss-is-pseudo-class": "^5.0.1", - "@csstools/postcss-light-dark-function": "^2.0.7", + "@csstools/postcss-gamut-mapping": "^2.0.10", + "@csstools/postcss-gradients-interpolation-method": "^5.0.10", + "@csstools/postcss-hwb-function": "^4.0.10", + "@csstools/postcss-ic-unit": "^4.0.2", + "@csstools/postcss-initial": "^2.0.1", + "@csstools/postcss-is-pseudo-class": "^5.0.3", + "@csstools/postcss-light-dark-function": "^2.0.9", "@csstools/postcss-logical-float-and-clear": "^3.0.0", "@csstools/postcss-logical-overflow": "^2.0.0", "@csstools/postcss-logical-overscroll-behavior": "^2.0.0", "@csstools/postcss-logical-resize": "^3.0.0", - "@csstools/postcss-logical-viewport-units": "^3.0.3", - "@csstools/postcss-media-minmax": "^2.0.6", - "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.4", + "@csstools/postcss-logical-viewport-units": "^3.0.4", + "@csstools/postcss-media-minmax": "^2.0.9", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^3.0.5", "@csstools/postcss-nested-calc": "^4.0.0", "@csstools/postcss-normalize-display-values": "^4.0.0", - "@csstools/postcss-oklab-function": "^4.0.7", - "@csstools/postcss-progressive-custom-properties": "^4.0.0", - "@csstools/postcss-random-function": "^1.0.2", - "@csstools/postcss-relative-color-syntax": "^3.0.7", + "@csstools/postcss-oklab-function": "^4.0.10", + "@csstools/postcss-progressive-custom-properties": "^4.1.0", + "@csstools/postcss-random-function": "^2.0.1", + "@csstools/postcss-relative-color-syntax": "^3.0.10", "@csstools/postcss-scope-pseudo-class": "^4.0.1", - "@csstools/postcss-sign-functions": "^1.1.1", - "@csstools/postcss-stepped-value-functions": "^4.0.6", - "@csstools/postcss-text-decoration-shorthand": "^4.0.1", - "@csstools/postcss-trigonometric-functions": "^4.0.6", + "@csstools/postcss-sign-functions": "^1.1.4", + "@csstools/postcss-stepped-value-functions": "^4.0.9", + "@csstools/postcss-text-decoration-shorthand": "^4.0.2", + "@csstools/postcss-trigonometric-functions": "^4.0.9", "@csstools/postcss-unset-value": "^4.0.0", - "autoprefixer": "^10.4.19", - "browserslist": "^4.23.1", + "autoprefixer": "^10.4.21", + "browserslist": "^4.25.0", "css-blank-pseudo": "^7.0.1", "css-has-pseudo": "^7.0.2", "css-prefers-color-scheme": "^10.0.0", - "cssdb": "^8.2.3", + "cssdb": "^8.3.0", "postcss-attribute-case-insensitive": "^7.0.1", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^7.0.7", + "postcss-color-functional-notation": "^7.0.10", "postcss-color-hex-alpha": "^10.0.0", "postcss-color-rebeccapurple": "^10.0.0", - "postcss-custom-media": "^11.0.5", - "postcss-custom-properties": "^14.0.4", - "postcss-custom-selectors": "^8.0.4", + "postcss-custom-media": "^11.0.6", + "postcss-custom-properties": "^14.0.6", + "postcss-custom-selectors": "^8.0.5", "postcss-dir-pseudo-class": "^9.0.1", - "postcss-double-position-gradients": "^6.0.0", + "postcss-double-position-gradients": "^6.0.2", "postcss-focus-visible": "^10.0.1", "postcss-focus-within": "^9.0.1", "postcss-font-variant": "^5.0.0", "postcss-gap-properties": "^6.0.0", "postcss-image-set-function": "^7.0.0", - "postcss-lab-function": "^7.0.7", - "postcss-logical": "^8.0.0", - "postcss-nesting": "^13.0.1", + "postcss-lab-function": "^7.0.10", + "postcss-logical": "^8.1.0", + "postcss-nesting": "^13.0.2", "postcss-opacity-percentage": "^3.0.0", "postcss-overflow-shorthand": "^6.0.0", "postcss-page-break": "^3.0.4", @@ -7217,9 +7328,9 @@ } }, "node_modules/postcss-pseudo-class-any-link/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -7267,9 +7378,9 @@ } }, "node_modules/postcss-selector-not/node_modules/postcss-selector-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz", - "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", + "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "dev": true, "license": "MIT", "dependencies": { @@ -7312,9 +7423,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", "bin": { @@ -7377,9 +7488,9 @@ } }, "node_modules/public-encrypt/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", + "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, "node_modules/punycode": { @@ -7447,9 +7558,9 @@ } }, "node_modules/readdirp": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.1.tgz", - "integrity": "sha512-h80JrZu/MHUZCyHu5ciuoI0+WxsCxzxJTILn6Fs8rxSnFPh+UVHYfeIxK1nVGugMqkfC4vJcBOYbkfkwYK0+gw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, "license": "MIT", "engines": { @@ -7460,18 +7571,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, - "node_modules/reinterval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", - "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -7519,13 +7618,13 @@ } }, "node_modules/rollup": { - "version": "4.34.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.4.tgz", - "integrity": "sha512-spF66xoyD7rz3o08sHP7wogp1gZ6itSq22SGa/IZTcUDXDlOyrShwMwkVSB+BUxFRZZCUYqdb3KWDEOMVQZxuw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -7535,25 +7634,26 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.4", - "@rollup/rollup-android-arm64": "4.34.4", - "@rollup/rollup-darwin-arm64": "4.34.4", - "@rollup/rollup-darwin-x64": "4.34.4", - "@rollup/rollup-freebsd-arm64": "4.34.4", - "@rollup/rollup-freebsd-x64": "4.34.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.4", - "@rollup/rollup-linux-arm-musleabihf": "4.34.4", - "@rollup/rollup-linux-arm64-gnu": "4.34.4", - "@rollup/rollup-linux-arm64-musl": "4.34.4", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.4", - "@rollup/rollup-linux-riscv64-gnu": "4.34.4", - "@rollup/rollup-linux-s390x-gnu": "4.34.4", - "@rollup/rollup-linux-x64-gnu": "4.34.4", - "@rollup/rollup-linux-x64-musl": "4.34.4", - "@rollup/rollup-win32-arm64-msvc": "4.34.4", - "@rollup/rollup-win32-ia32-msvc": "4.34.4", - "@rollup/rollup-win32-x64-msvc": "4.34.4", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, @@ -7578,9 +7678,23 @@ "license": "MIT" }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/safe-regex-test": { @@ -7608,9 +7722,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.84.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.84.0.tgz", - "integrity": "sha512-XDAbhEPJRxi7H0SxrnOpiXFQoUJHwkR2u3Zc4el+fK/Tt5Hpzw5kkQ59qVDfvdaUq6gCrEZIbySFBM2T9DNKHg==", + "version": "1.89.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.89.2.tgz", + "integrity": "sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==", "dev": true, "license": "MIT", "dependencies": { @@ -7642,9 +7756,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -7688,16 +7802,23 @@ "license": "MIT" }, "node_modules/sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "license": "(MIT AND BSD-3-Clause)", "dependencies": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" }, "bin": { "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/shebang-command": { @@ -7815,6 +7936,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/smob": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/smob/-/smob-1.5.0.tgz", @@ -7822,6 +7953,20 @@ "dev": true, "license": "MIT" }, + "node_modules/socks": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.6.tgz", + "integrity": "sha512-pe4Y2yzru68lXCb38aAqRf5gvN8YdjP1lok5o0J7BOHljkyCGKVz7H3vpVIXKD27rj2giOJ7DwVyk/GWrPHDWA==", + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7852,6 +7997,15 @@ "source-map": "^0.6.0" } }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -7861,6 +8015,12 @@ "node": ">= 10.x" } }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -7869,9 +8029,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", - "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", "dev": true, "license": "MIT" }, @@ -7934,26 +8094,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -8071,6 +8211,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -8104,31 +8269,30 @@ "license": "MIT" }, "node_modules/synckit": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", - "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", + "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", "dev": true, "license": "MIT", "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" + "@pkgr/core": "^0.2.9" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/unts" + "url": "https://opencollective.com/synckit" } }, "node_modules/terser": { - "version": "5.38.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.38.0.tgz", - "integrity": "sha512-a4GD5R1TjEeuCT6ZRiYMHmIf7okbCPEuhQET8bczV6FrQMMlFXA1n+G0KKjdlFCm3TEHV77GxfZB3vZSUQGFpg==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8172,10 +8336,27 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", "dev": true, "license": "MIT", "engines": { @@ -8183,9 +8364,9 @@ } }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, "license": "MIT", "engines": { @@ -8193,9 +8374,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", "dev": true, "license": "MIT", "engines": { @@ -8203,25 +8384,39 @@ } }, "node_modules/tldts": { - "version": "6.1.76", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.76.tgz", - "integrity": "sha512-6U2ti64/nppsDxQs9hw8ephA3nO6nSQvVVfxwRw8wLQPFtLI1cFI1a1eP22g+LUP+1TA2pKKjUTwWB+K2coqmQ==", + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.76" + "tldts-core": "^6.1.86" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.76", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.76.tgz", - "integrity": "sha512-uzhJ02RaMzgQR3yPoeE65DrcHI6LoM4saUqXOt/b5hmb3+mc4YWpdSeAQqVqRUlQ14q8ZuLRWyBR1ictK1dzzg==", + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, "license": "MIT" }, + "node_modules/to-buffer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.1.tgz", + "integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8237,9 +8432,9 @@ } }, "node_modules/tough-cookie": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", - "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -8250,9 +8445,9 @@ } }, "node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", "dependencies": { @@ -8297,17 +8492,18 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 0.4" } }, "node_modules/typedarray": { @@ -8317,15 +8513,15 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", - "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { @@ -8406,9 +8602,9 @@ "license": "MIT" }, "node_modules/vite": { - "version": "5.4.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", - "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", "dependencies": { @@ -8466,32 +8662,32 @@ } }, "node_modules/vite-node": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", - "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, "license": "MIT", "dependencies": { "cac": "^6.7.14", - "debug": "^4.3.7", - "es-module-lexer": "^1.5.4", - "pathe": "^1.1.2", - "vite": "^5.0.0" + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/vite-plugin-node-polyfills": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.23.0.tgz", - "integrity": "sha512-4n+Ys+2bKHQohPBKigFlndwWQ5fFKwaGY6muNDMTb0fSQLyBzS+jjUNRZG9sKF0S/Go4ApG6LFnUGopjkILg3w==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/vite-plugin-node-polyfills/-/vite-plugin-node-polyfills-0.24.0.tgz", + "integrity": "sha512-GA9QKLH+vIM8NPaGA+o2t8PDfFUl32J8rUp1zQfMKVJQiNkOX4unE51tR6ppl6iKw5yOrDAdSH7r/UIFLCVhLw==", "dev": true, "license": "MIT", "dependencies": { @@ -8502,51 +8698,55 @@ "url": "https://github.com/sponsors/davidmyersdev" }, "peerDependencies": { - "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/vitest": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", - "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "2.1.9", - "@vitest/mocker": "2.1.9", - "@vitest/pretty-format": "^2.1.9", - "@vitest/runner": "2.1.9", - "@vitest/snapshot": "2.1.9", - "@vitest/spy": "2.1.9", - "@vitest/utils": "2.1.9", - "chai": "^5.1.2", - "debug": "^4.3.7", - "expect-type": "^1.1.0", - "magic-string": "^0.30.12", - "pathe": "^1.1.2", - "std-env": "^3.8.0", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.1", - "tinypool": "^1.0.1", - "tinyrainbow": "^1.2.0", - "vite": "^5.0.0", - "vite-node": "2.1.9", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "2.1.9", - "@vitest/ui": "2.1.9", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, @@ -8554,6 +8754,9 @@ "@edge-runtime/vm": { "optional": true }, + "@types/debug": { + "optional": true + }, "@types/node": { "optional": true }, @@ -8578,16 +8781,16 @@ "license": "MIT" }, "node_modules/vue": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", - "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.18.tgz", + "integrity": "sha512-7W4Y4ZbMiQ3SEo+m9lnoNpV9xG7QVMLa+/0RFwwiAVkeYoyGXqWE85jabU4pllJNUzqfLShJ5YLptewhCWUgNA==", "license": "MIT", "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-sfc": "3.5.13", - "@vue/runtime-dom": "3.5.13", - "@vue/server-renderer": "3.5.13", - "@vue/shared": "3.5.13" + "@vue/compiler-dom": "3.5.18", + "@vue/compiler-sfc": "3.5.18", + "@vue/runtime-dom": "3.5.18", + "@vue/server-renderer": "3.5.18", + "@vue/shared": "3.5.18" }, "peerDependencies": { "typescript": "*" @@ -8598,116 +8801,52 @@ } } }, - "node_modules/vue-component-type-helpers": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.0.tgz", - "integrity": "sha512-cYrAnv2me7bPDcg9kIcGwjJiSB6Qyi08+jLDo9yuvoFQjzHiPTzML7RnkJB1+3P6KMsX/KbCD4QE3Tv/knEllw==", - "dev": true, - "license": "MIT" - }, - "node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, + "node_modules/vue-chartjs": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.2.tgz", + "integrity": "sha512-NrkbRRoYshbXbWqJkTN6InoDVwVb90C0R7eAVgMWcB9dPikbruaOoTFjFYHE/+tNPdIe6qdLCDjfjPHQ0fw4jw==", "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } + "chart.js": "^4.1.1", + "vue": "^3.0.0-0 || ^2.7.0" } }, + "node_modules/vue-component-type-helpers": { + "version": "2.2.12", + "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz", + "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==", + "dev": true, + "license": "MIT" + }, "node_modules/vue-eslint-parser": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz", + "integrity": "sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" + "debug": "^4.4.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "semver": "^7.6.3" }, "engines": { - "node": "^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/vue-eslint-parser/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/vue-router": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", - "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz", + "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==", "license": "MIT", "dependencies": { "@vue/devtools-api": "^6.6.4" @@ -8719,6 +8858,12 @@ "vue": "^3.2.0" } }, + "node_modules/vue-router/node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" + }, "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -8776,13 +8921,13 @@ } }, "node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", "dependencies": { - "tr46": "^5.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { @@ -8806,15 +8951,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.18", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.18.tgz", - "integrity": "sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==", + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "for-each": "^0.3.3", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" }, @@ -8852,38 +8998,51 @@ "node": ">=0.10.0" } }, + "node_modules/worker-factory": { + "version": "7.0.44", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.44.tgz", + "integrity": "sha512-08AuUfWi+KeZI+KC7nU4pU/9tDeAFvE5NSWk+K9nIfuQc6UlOsZtjjeGVYVEn+DEchyXNJ5i10HCn0xRzFXEQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "fast-unique-numbers": "^9.0.22", + "tslib": "^2.8.1" + } + }, "node_modules/worker-timers": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", - "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "version": "8.0.23", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.23.tgz", + "integrity": "sha512-1BnWHNNiu5YEutgF7eVZEqNntAsij2oG0r66xDdScoY3fKGFrok2y0xA8OgG6FA+3srrmAplSY6JN5h9jV5D0w==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.24.5", - "tslib": "^2.6.2", - "worker-timers-broker": "^6.1.8", - "worker-timers-worker": "^7.0.71" + "@babel/runtime": "^7.27.6", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.9", + "worker-timers-worker": "^9.0.9" } }, "node_modules/worker-timers-broker": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", - "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.9.tgz", + "integrity": "sha512-WJsd7aIvu2GBTXp7IBGT1NKnt3ZbiJ2wqb7Pl4nFJXC8pek84+X68TJGVvvrqwHgHPNxSlzpU1nadhcW4PDD7A==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.24.5", - "fast-unique-numbers": "^8.0.13", - "tslib": "^2.6.2", - "worker-timers-worker": "^7.0.71" + "@babel/runtime": "^7.27.6", + "broker-factory": "^3.1.8", + "fast-unique-numbers": "^9.0.22", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.9" } }, "node_modules/worker-timers-worker": { - "version": "7.0.71", - "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", - "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.9.tgz", + "integrity": "sha512-OOKTMdHbzx7FaXCW40RS8RxAqLF/R8xU5/YA7CFasDy+jBA5yQWUusSQJUFFTV2Z9ZOpnR+ZWgte/IuAqOAEVw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.24.5", - "tslib": "^2.6.2" + "@babel/runtime": "^7.27.6", + "tslib": "^2.8.1", + "worker-factory": "^7.0.44" } }, "node_modules/wrap-ansi": { @@ -8982,9 +9141,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/packages/modules/display_themes/cards/source/package.json b/packages/modules/display_themes/cards/source/package.json index 503e83cdf5..4175da6ca0 100644 --- a/packages/modules/display_themes/cards/source/package.json +++ b/packages/modules/display_themes/cards/source/package.json @@ -12,35 +12,40 @@ "build-dev": "vite build --mode=development --out-dir=../web/ --emptyOutDir" }, "dependencies": { - "@fortawesome/fontawesome-svg-core": "^6.7.2", - "@fortawesome/free-regular-svg-icons": "^6.7.2", - "@fortawesome/free-solid-svg-icons": "^6.7.2", - "@fortawesome/vue-fontawesome": "^3.0.8", + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-regular-svg-icons": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@fortawesome/vue-fontawesome": "^3.1.1", "@inkline/inkline": "^3.2.2", "buffer": "^6.0.3", + "chart.js": "^4.5.0", + "chartjs-adapter-luxon": "^1.3.1", + "chartjs-plugin-annotation": "^3.1.0", "events": "^3.3.0", - "mqtt": "^5.10.3", + "luxon": "^3.7.1", + "mqtt": "^5.14.0", "node-stdlib-browser": "^1.3.1", - "pinia": "^2.3.1", - "vue": "^3.5.13", - "vue-router": "^4.5.0" + "pinia": "^3.0.3", + "vue": "^3.5.18", + "vue-chartjs": "^5.3.2", + "vue-router": "^4.5.1" }, "devDependencies": { "@rollup/plugin-terser": "^0.4.4", - "@rushstack/eslint-patch": "^1.10.5", - "@vitejs/plugin-vue": "^5.2.1", + "@rushstack/eslint-patch": "^1.12.0", + "@vitejs/plugin-vue": "^5.2.4", "@vue/eslint-config-prettier": "^10.2.0", "@vue/test-utils": "^2.4.6", - "eslint": "^9.19.0", - "eslint-plugin-vue": "^9.32.0", - "jsdom": "^26.0.0", - "postcss": "^8.5.1", - "postcss-preset-env": "^10.1.3", - "prettier": "^3.4.2", + "eslint": "^9.32.0", + "eslint-plugin-vue": "^10.4.0", + "jsdom": "^26.1.0", + "postcss": "^8.5.6", + "postcss-preset-env": "^10.2.4", + "prettier": "^3.6.2", "rollup-plugin-polyfill-node": "^0.13.0", - "sass": "^1.84.0", - "vite": "^5.4.14", - "vite-plugin-node-polyfills": "^0.23.0", - "vitest": "^2.1.9" + "sass": "^1.89.2", + "vite": "^5.4.19", + "vite-plugin-node-polyfills": "^0.24.0", + "vitest": "^3.2.4" } } diff --git a/packages/modules/display_themes/cards/source/src/App.vue b/packages/modules/display_themes/cards/source/src/App.vue index ef178a6be1..3a1efb2390 100644 --- a/packages/modules/display_themes/cards/source/src/App.vue +++ b/packages/modules/display_themes/cards/source/src/App.vue @@ -39,6 +39,9 @@ export default { "openWB/chargepoint/+/config", "openWB/chargepoint/+/get/charge_state", "openWB/chargepoint/+/get/connected_vehicle/+", + "openWB/chargepoint/+/set/charge_template", + "openWB/chargepoint/+/set/charge_template/chargemode/scheduled_charging/plans/+", + "openWB/chargepoint/+/set/charge_template/time_charging/plans/+", "openWB/chargepoint/+/get/phases_in_use", "openWB/chargepoint/+/get/plug_state", "openWB/chargepoint/+/get/power", @@ -51,6 +54,8 @@ export default { "openWB/counter/+/get/power", "openWB/counter/get/hierarchy", "openWB/counter/set/home_consumption", + "openWB/optional/et/provider", + "openWB/optional/et/get/prices", "openWB/optional/int_display/theme", "openWB/optional/int_display/standby", "openWB/optional/rfid/active", @@ -64,7 +69,6 @@ export default { "openWB/vehicle/+/get/fault_state", "openWB/vehicle/+/name", "openWB/vehicle/+/soc_module/config", - "openWB/vehicle/template/charge_template/#", ], mqttStore: useMqttStore(), chartInterval: "", diff --git a/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargeModeModal.vue b/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargeModeModal.vue new file mode 100644 index 0000000000..0035905721 --- /dev/null +++ b/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargeModeModal.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargePointCard.vue b/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargePointCard.vue index b39692b01b..eb5247058d 100644 --- a/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargePointCard.vue +++ b/packages/modules/display_themes/cards/source/src/components/ChargePoints/ChargePointCard.vue @@ -1,6 +1,6 @@ diff --git a/packages/modules/display_themes/cards/source/src/components/DashBoard/BatteryCard.vue b/packages/modules/display_themes/cards/source/src/components/DashBoard/BatteryCard.vue deleted file mode 100644 index bf52149aca..0000000000 --- a/packages/modules/display_themes/cards/source/src/components/DashBoard/BatteryCard.vue +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/packages/modules/display_themes/cards/source/src/components/DashBoardCard.vue b/packages/modules/display_themes/cards/source/src/components/DashBoardCard.vue deleted file mode 100644 index 04ce78fb29..0000000000 --- a/packages/modules/display_themes/cards/source/src/components/DashBoardCard.vue +++ /dev/null @@ -1,44 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/cards/source/src/components/Dashboard/BatteryCard.vue b/packages/modules/display_themes/cards/source/src/components/Dashboard/BatteryCard.vue new file mode 100644 index 0000000000..d9d9469d43 --- /dev/null +++ b/packages/modules/display_themes/cards/source/src/components/Dashboard/BatteryCard.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/modules/display_themes/cards/source/src/components/DashBoard/ChargePointsCard.vue b/packages/modules/display_themes/cards/source/src/components/Dashboard/ChargePointsCard.vue similarity index 93% rename from packages/modules/display_themes/cards/source/src/components/DashBoard/ChargePointsCard.vue rename to packages/modules/display_themes/cards/source/src/components/Dashboard/ChargePointsCard.vue index 8054750f16..fbc5c08679 100644 --- a/packages/modules/display_themes/cards/source/src/components/DashBoard/ChargePointsCard.vue +++ b/packages/modules/display_themes/cards/source/src/components/Dashboard/ChargePointsCard.vue @@ -1,6 +1,6 @@ diff --git a/packages/modules/display_themes/cards/source/src/components/DashBoard/FlowCard.vue b/packages/modules/display_themes/cards/source/src/components/Dashboard/FlowCard.vue similarity index 87% rename from packages/modules/display_themes/cards/source/src/components/DashBoard/FlowCard.vue rename to packages/modules/display_themes/cards/source/src/components/Dashboard/FlowCard.vue index 1df7f1492d..7114e3fc40 100644 --- a/packages/modules/display_themes/cards/source/src/components/DashBoard/FlowCard.vue +++ b/packages/modules/display_themes/cards/source/src/components/Dashboard/FlowCard.vue @@ -1,11 +1,13 @@ diff --git a/packages/modules/display_themes/cards/source/src/components/ElectricityTariffChart.vue b/packages/modules/display_themes/cards/source/src/components/ElectricityTariffChart.vue new file mode 100644 index 0000000000..71f503aa72 --- /dev/null +++ b/packages/modules/display_themes/cards/source/src/components/ElectricityTariffChart.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/packages/modules/display_themes/cards/source/src/components/NavBar.vue b/packages/modules/display_themes/cards/source/src/components/NavBar.vue index aecf8ed47a..a569e02dcd 100644 --- a/packages/modules/display_themes/cards/source/src/components/NavBar.vue +++ b/packages/modules/display_themes/cards/source/src/components/NavBar.vue @@ -20,8 +20,8 @@ export default { size="lg" > Übersicht diff --git a/packages/modules/display_themes/cards/source/src/components/TouchBlocker.vue b/packages/modules/display_themes/cards/source/src/components/TouchBlocker.vue index 66ebfa2ad6..f65213e46d 100644 --- a/packages/modules/display_themes/cards/source/src/components/TouchBlocker.vue +++ b/packages/modules/display_themes/cards/source/src/components/TouchBlocker.vue @@ -7,8 +7,10 @@ export default { return { mqttStore: useMqttStore(), show: false, - countdown: undefined, - countdownInterval: undefined, + touchBlockerCountdown: undefined, + touchBlockerCountdownInterval: undefined, + defaultViewCountdown: undefined, + defaultViewCountdownInterval: undefined, events: ["mousemove", "touchmove", "wheel", "click"], eventHandlerSetup: false, }; @@ -20,25 +22,38 @@ export default { } return this.mqttStore.getDisplayStandby; }, + configuredDefaultViewTimeout() { + if (this.mqttStore.getDefaultViewTimeout === 0 || this.mqttStore.getDefaultViewTimeout === undefined) { + return undefined; + } + return this.mqttStore.getDefaultViewTimeout; + }, touchBlockerTimeout() { // show touch blocker right before the configured standby time return Math.max(this.configuredDisplayStandby - 3, 1); }, + defaultViewTimeout() { + // switch to default view after the configured timeout + return this.configuredDefaultViewTimeout; + }, }, mounted() { this.setupEventHandler(); - this.setupTimeout(); + this.setupTouchBlockerTimeout(); + this.setupDefaultViewTimeout(); }, unmounted() { this.clearEventHandler(); - this.clearTimeout(); + this.clearTouchBlockerTimeout(); + this.clearDefaultViewTimeout(); }, methods: { handleTouchBlockerClick(event) { if (event === false) { this.show = false; this.setupEventHandler(); - this.setupTimeout(); + this.setupTouchBlockerTimeout(); + this.setupDefaultViewTimeout(); } }, setupEventHandler() { @@ -57,34 +72,72 @@ export default { this.eventHandlerSetup = false; } }, - setupTimeout() { - if (this.countdownInterval === undefined) { - this.countdownInterval = setInterval(this.updateCountdown, 1000); + setupTouchBlockerTimeout() { + if (this.touchBlockerCountdownCountdownInterval === undefined) { + this.touchBlockerCountdownCountdownInterval = setInterval(this.updateTouchBlockerCountdown, 1000); } }, - clearTimeout() { - if (this.countdownInterval !== undefined) { - clearInterval(this.countdownInterval); - this.countdownInterval = undefined; + clearTouchBlockerTimeout() { + if (this.touchBlockerCountdownCountdownInterval !== undefined) { + clearInterval(this.touchBlockerCountdownCountdownInterval); + this.touchBlockerCountdownCountdownInterval = undefined; } }, - updateCountdown() { - if (this.countdown === undefined) { - this.countdown = this.touchBlockerTimeout; + updateTouchBlockerCountdown() { + if (this.touchBlockerCountdown === undefined) { + this.touchBlockerCountdown = this.touchBlockerTimeout; } else { - this.countdown -= 1; - if (this.countdown < 1) { + this.touchBlockerCountdown -= 1; + if (this.touchBlockerCountdown < 1) { this.showTouchBlocker(); } } }, + setupDefaultViewTimeout() { + if ( + this.defaultViewCountdownInterval === undefined + && this.mqttStore.getDefaultView !== this.$route.name + && this.defaultViewTimeout !== undefined + ) { + this.defaultViewCountdownInterval = setInterval(this.updateDefaultViewCountdown, 1000); + } + }, + clearDefaultViewTimeout() { + if (this.defaultViewCountdownInterval !== undefined) { + clearInterval(this.defaultViewCountdownInterval); + this.defaultViewCountdownInterval = undefined; + } + }, + updateDefaultViewCountdown() { + if (this.defaultViewCountdown === undefined && this.defaultViewTimeout !== undefined) { + this.defaultViewCountdown = this.defaultViewTimeout; + } else { + if (this.$route.name === this.mqttStore.getDefaultView) { + this.clearDefaultViewTimeout(); + } else { + this.defaultViewCountdown -= 1; + if (this.defaultViewCountdown < 1) { + this.showDefaultView(); + } + } + } + }, handleDocumentEvent() { - this.countdown = this.touchBlockerTimeout; + this.touchBlockerCountdown = this.touchBlockerTimeout; + this.defaultViewCountdown = this.defaultViewTimeout; + this.setupDefaultViewTimeout(); this.show = false; }, showTouchBlocker() { this.show = true; - this.clearTimeout(); + this.clearTouchBlockerTimeout(); + }, + showDefaultView() { + console.log("switching to default view:", this.mqttStore.getDefaultView); + this.clearDefaultViewTimeout(); + if (this.$route.name !== this.mqttStore.getDefaultView) { + this.$router.push({ name: this.mqttStore.getDefaultView }); + } }, }, }; @@ -112,7 +165,7 @@ export default { diff --git a/packages/modules/display_themes/cards/source/src/views/DashBoardView.vue b/packages/modules/display_themes/cards/source/src/views/DashBoardView.vue deleted file mode 100644 index 0f4ef13d4a..0000000000 --- a/packages/modules/display_themes/cards/source/src/views/DashBoardView.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/cards/source/src/views/DashboardView.vue b/packages/modules/display_themes/cards/source/src/views/DashboardView.vue new file mode 100644 index 0000000000..442c501de0 --- /dev/null +++ b/packages/modules/display_themes/cards/source/src/views/DashboardView.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/packages/modules/display_themes/cards/source/src/views/EnergyFlowView.vue b/packages/modules/display_themes/cards/source/src/views/EnergyFlowView.vue index fc7c374ba6..e918b0f75d 100644 --- a/packages/modules/display_themes/cards/source/src/views/EnergyFlowView.vue +++ b/packages/modules/display_themes/cards/source/src/views/EnergyFlowView.vue @@ -1,5 +1,5 @@ diff --git a/packages/modules/display_themes/cards/source/src/views/StatusView.vue b/packages/modules/display_themes/cards/source/src/views/StatusView.vue index 9ceb05c011..7190d61b2a 100644 --- a/packages/modules/display_themes/cards/source/src/views/StatusView.vue +++ b/packages/modules/display_themes/cards/source/src/views/StatusView.vue @@ -3,7 +3,7 @@ import { useMqttStore } from "@/stores/mqtt.js"; import ReloadButton from "@/components/Status/ReloadButton.vue"; import RebootButton from "@/components/Status/RebootButton.vue"; import ShutdownButton from "@/components/Status/ShutdownButton.vue"; -import DashBoardCard from "@/components/DashBoardCard.vue"; +import DashboardCard from "@/components/DashboardCard.vue"; export default { name: "StatusView", @@ -11,7 +11,7 @@ export default { ReloadButton, RebootButton, ShutdownButton, - DashBoardCard, + DashboardCard, }, props: { changesLocked: { required: false, type: Boolean, default: false }, @@ -25,7 +25,7 @@ export default { diff --git a/packages/modules/display_themes/cards/source/src/views/WelcomeView.vue b/packages/modules/display_themes/cards/source/src/views/WelcomeView.vue index 39d662d3f0..4602a676a0 100644 --- a/packages/modules/display_themes/cards/source/src/views/WelcomeView.vue +++ b/packages/modules/display_themes/cards/source/src/views/WelcomeView.vue @@ -8,32 +8,13 @@ export default { mqttStore: useMqttStore(), }; }, - computed: { - firstView() { - if (this.mqttStore.getThemeConfiguration) { - if (this.mqttStore.getThemeConfiguration.enable_dashboard_view) { - return "dash-board"; - } - if (this.mqttStore.getThemeConfiguration.enable_energy_flow_view) { - return "energy-flow"; - } - if (this.mqttStore.getThemeConfiguration.enable_charge_points_view) { - return "charge-points"; - } - if (this.mqttStore.getThemeConfiguration.enable_status_view) { - return "status"; - } - } - return undefined; - }, - }, mounted() { setTimeout(this.selectFirstRoute, 3000); }, methods: { selectFirstRoute() { - if (this.firstView) { - this.$router.push({ name: this.firstView }); + if (this.mqttStore.getDefaultView) { + this.$router.push({ name: this.mqttStore.getDefaultView }); } else { console.warn("no router view enabled, check your configuration!"); } diff --git a/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CGs_z8ao.css b/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CGs_z8ao.css new file mode 100644 index 0000000000..587ece271e --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CGs_z8ao.css @@ -0,0 +1 @@ +.large-button[data-v-bd48ea1b]{height:3.5rem;font-size:1.5rem;padding:.75rem 1.5rem} diff --git a/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CNWdoEpw.js b/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CNWdoEpw.js new file mode 100644 index 0000000000..e503872c3b --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/ChargeModeModal-CNWdoEpw.js @@ -0,0 +1 @@ +import{u as V}from"./index-C0J-ZJ88.js";import{_ as q}from"./vendor-inkline-S9CBmrTS.js";import{l as C,q as o,p as l,o as s,s as i,e as S,F as M,A as b,k as d,x as p}from"./vendor-Bzn5cd2Y.js";const T=q({name:"ChargeModeModal",props:{modelValue:{required:!0,type:Boolean,default:!1},chargePointId:{type:Number,required:!0}},emits:["update:modelValue"],data:()=>({mqttStore:V(),simpleChargeModes:["instant_charging","pv_charging","stop"]}),computed:{filteredChargeModes(){return this.mqttStore.getSimpleChargePointView?this.mqttStore.chargeModeList().filter(t=>this.simpleChargeModes.includes(t.id)):this.mqttStore.chargeModeList()}},methods:{updateChargePointChargeTemplate(t,e,r=void 0){const g=this.mqttStore.updateState(`openWB/chargepoint/${t}/set/charge_template`,e,r);this.$root.sendTopicToBroker(`openWB/chargepoint/${t}/set/charge_template`,g)},setChargePointConnectedVehicleChargeMode(t){t!=this.mqttStore.getChargePointConnectedVehicleChargeMode(this.chargePointId)&&this.updateChargePointChargeTemplate(this.chargePointId,t,"chargemode.selected")},setChargePointConnectedVehiclePriority(t){t!=this.mqttStore.getChargePointConnectedVehiclePriority(this.chargePointId)&&this.updateChargePointChargeTemplate(this.chargePointId,t,"prio")}}},[["render",function(t,e,r,g,n,h){const c=l("i-button"),u=l("i-button-group"),m=l("i-form-group"),P=l("i-form-label"),_=l("i-form"),f=l("i-modal");return s(),C(f,{"model-value":r.modelValue,size:"lg","onUpdate:modelValue":e[2]||(e[2]=a=>t.$emit("update:modelValue",a))},{header:o(()=>[d(' Lademodus für "'+p(n.mqttStore.getChargePointConnectedVehicleName(r.chargePointId))+'" auswählen ',1)]),default:o(()=>[i(_,null,{default:o(()=>[i(m,null,{default:o(()=>[i(u,{block:"",vertical:""},{default:o(()=>[(s(!0),S(M,null,b(h.filteredChargeModes,a=>(s(),C(c,{key:a.id,size:"lg",class:"large-button",outline:"",color:a.class!="dark"?a.class:"light",active:n.mqttStore.getChargePointConnectedVehicleChargeMode(r.chargePointId)!=null&&a.id==n.mqttStore.getChargePointConnectedVehicleChargeMode(r.chargePointId).mode,onClick:I=>h.setChargePointConnectedVehicleChargeMode(a.id)},{default:o(()=>[d(p(a.label),1)]),_:2},1032,["color","active","onClick"]))),128))]),_:1})]),_:1}),i(m,null,{default:o(()=>[i(P,null,{default:o(()=>e[3]||(e[3]=[d("Priorität",-1)])),_:1,__:[3]}),i(u,{block:""},{default:o(()=>[i(c,{size:"lg",class:"large-button",color:n.mqttStore.getChargePointConnectedVehiclePriority(r.chargePointId)!==!0?"danger":"",onClick:e[0]||(e[0]=a=>h.setChargePointConnectedVehiclePriority(!1))},{default:o(()=>e[4]||(e[4]=[d(" Nein ",-1)])),_:1,__:[4]},8,["color"]),i(c,{color:n.mqttStore.getChargePointConnectedVehiclePriority(r.chargePointId)===!0?"success":"",onClick:e[1]||(e[1]=a=>h.setChargePointConnectedVehiclePriority(!0))},{default:o(()=>e[5]||(e[5]=[d(" Ja ",-1)])),_:1,__:[5]},8,["color"])]),_:1})]),_:1})]),_:1})]),_:1},8,["model-value"])}],["__scopeId","data-v-bd48ea1b"]]);export{T as C}; diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-Dsdv-7Vn.js b/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-Dsdv-7Vn.js new file mode 100644 index 0000000000..bc1e9b8061 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-Dsdv-7Vn.js @@ -0,0 +1 @@ +import{e as c,f,i as x,F as P,A as w,o as h,z as m,O as S,l as C,q as v,p as d,s as b,x as g}from"./vendor-Bzn5cd2Y.js";import{_ as y}from"./vendor-inkline-S9CBmrTS.js";import{u as k}from"./index-C0J-ZJ88.js";import{F as D,l as I,C as q,D as $,E as z}from"./vendor-fortawesome-C1Wk2aFl.js";const l={props:{data:{required:!1,type:Array,default:void 0},socData:{required:!1,type:Array,default:void 0},width:{type:Number,default:250},height:{type:Number,default:70},gap:{type:Number,default:3},stroke:{type:Number,default:3},min:{type:Number,default:0},max:{type:Number,default:1},color:{type:String,default:"var(--color--primary)"},colorNegative:{type:String,default:void 0}},computed:{highestPoint(){return Math.max(1,this.max,...this.slicedData)},lowestPoint(){return Math.min(0,this.min,...this.slicedData)},maxPoints(){return Math.floor(this.width/(this.stroke+this.gap))},slicedData(){if(this.data)return this.data.slice(-this.maxPoints)},slicedSocData(){if(this.socData)return this.socData.slice(-this.maxPoints)},zeroHeight(){return this.height-(0-this.lowestPoint)/(this.highestPoint-this.lowestPoint)*this.height},coordinates(){if(this.data)return this.calculateCoordinates(this.slicedData,this.lowestPoint,this.highestPoint)},socCoordinates(){if(this.socData)return this.calculateCoordinates(this.slicedSocData,0,100)},bars(){if(this.coordinates){const t=[];return this.coordinates.forEach(s=>{const e=s.x,o=s.y,r=this.stroke,a=Math.min(o,this.zeroHeight),i=Math.abs(o-this.zeroHeight),n=o>this.zeroHeight;t.push({x:e,y:a,width:r,height:i,negative:n})}),t}},socPath(){if(this.socCoordinates&&this.socCoordinates.length>0){let s=this.socCoordinates.slice(0,1)[0],e=this.socCoordinates.slice(-1)[0];var t=`M 0,${this.height}`;return t+=` L 0,${s.y}`,this.socCoordinates.forEach(o=>{t+=` L ${o.x+this.stroke/2},${o.y}`}),t+=` L ${e.x+this.stroke},${e.y} L ${e.x+this.stroke},${this.height} Z`}}},methods:{calculateCoordinates(t,s,e){const o=[];return t.forEach((r,a)=>{const i=a*this.width/this.maxPoints+1,n=this.height-(r-s)/(e-s)*this.height;o.push({x:i,y:n})}),o}}},u=()=>{S(t=>({"3f7db06a":t.color,"1de516bf":t.colorNegative}))},p=l.setup;l.setup=p?(t,s)=>(u(),p(t,s)):u;const N=["viewBox"],E=["d"],M=["x","y","width","height"],_=["y1","x2","y2"],R=y(l,[["render",function(t,s,e,o,r,a){return h(),c("svg",{class:"spark-line",viewBox:`0 0 ${e.width} ${e.height}`,width:"100%",preserveAspectRatio:"xMinYMin"},[e.socData?(h(),c("path",{key:0,class:"soc-path",d:a.socPath},null,8,E)):f("",!0),(h(!0),c(P,null,w(a.bars,i=>(h(),c("rect",{key:i.x,x:i.x,y:i.y,width:i.width,height:i.height,class:m(e.colorNegative&&i.negative?"negative":"")},null,10,M))),128)),x("line",{class:"zero-line",x1:0,y1:a.zeroHeight,x2:e.width,y2:a.zeroHeight},null,8,_)],8,N)}],["__scopeId","data-v-22cdf82b"]]);I.add(q,$,z);const A={name:"ChargePointStateBadge",components:{FontAwesomeIcon:D},props:{chargePointId:{required:!0,type:Array},showEnergyCharged:{required:!1,type:Boolean,default:!0}},data:()=>({mqttStore:k()}),computed:{plugState(){var t=!1;return this.chargePointId.forEach(s=>{t|=this.mqttStore.getChargePointPlugState(s)}),t},chargeState(){var t=!1;return this.chargePointId.forEach(s=>{t|=this.mqttStore.getChargePointChargeState(s)}),t},stateIcon(){return this.plugState?this.chargeState?["fas","fa-plug-circle-bolt"]:["fas","fa-plug-circle-check"]:["fas","fa-plug-circle-xmark"]},stateClass(){return this.plugState?this.chargeState?"_color:success":"_color:warning":"_color:gray"}}},H={key:0,class:"_padding-left:1"},Y=y(A,[["render",function(t,s,e,o,r,a){const i=d("font-awesome-icon"),n=d("i-badge");return h(),C(n,{size:"lg"},{default:v(()=>[b(i,{"fixed-width":"",icon:a.stateIcon,class:m(a.stateClass)},null,8,["icon","class"]),a.plugState&&e.showEnergyCharged?(h(),c("span",H,g(r.mqttStore.getChargePointImportedSincePlugged(e.chargePointId).energy)+" / "+g(r.mqttStore.getChargePointImportedSincePlugged(e.chargePointId).range),1)):f("",!0)]),_:1})}]]);export{Y as C,R as S}; diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-bP9BlN1o.js b/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-bP9BlN1o.js deleted file mode 100644 index 80c89ae38b..0000000000 --- a/packages/modules/display_themes/cards/web/assets/ChargePointPlugBadge-bP9BlN1o.js +++ /dev/null @@ -1 +0,0 @@ -import{e as c,f,i as x,F as P,A as w,o as h,z as m,O as S,l as C,q as v,p as d,s as b,x as g}from"./vendor-BMrK3KHF.js";import{_ as y}from"./vendor-inkline-C_NPDnDu.js";import{u as k}from"./index-nI_NVV5B.js";import{F as D,l as I,z as q,A as z,B as $}from"./vendor-fortawesome-CpQlJZ13.js";const l={props:{data:{required:!1,type:Array,default:void 0},socData:{required:!1,type:Array,default:void 0},width:{type:Number,default:250},height:{type:Number,default:70},gap:{type:Number,default:3},stroke:{type:Number,default:3},min:{type:Number,default:0},max:{type:Number,default:1},color:{type:String,default:"var(--color--primary)"},colorNegative:{type:String,default:void 0}},computed:{highestPoint(){return Math.max(1,this.max,...this.slicedData)},lowestPoint(){return Math.min(0,this.min,...this.slicedData)},maxPoints(){return Math.floor(this.width/(this.stroke+this.gap))},slicedData(){if(this.data)return this.data.slice(-this.maxPoints)},slicedSocData(){if(this.socData)return this.socData.slice(-this.maxPoints)},zeroHeight(){return this.height-(0-this.lowestPoint)/(this.highestPoint-this.lowestPoint)*this.height},coordinates(){if(this.data)return this.calculateCoordinates(this.slicedData,this.lowestPoint,this.highestPoint)},socCoordinates(){if(this.socData)return this.calculateCoordinates(this.slicedSocData,0,100)},bars(){if(this.coordinates){const t=[];return this.coordinates.forEach(s=>{const e=s.x,o=s.y,r=this.stroke,a=Math.min(o,this.zeroHeight),i=Math.abs(o-this.zeroHeight),n=o>this.zeroHeight;t.push({x:e,y:a,width:r,height:i,negative:n})}),t}},socPath(){if(this.socCoordinates&&this.socCoordinates.length>0){let s=this.socCoordinates.slice(0,1)[0],e=this.socCoordinates.slice(-1)[0];var t=`M 0,${this.height}`;return t+=` L 0,${s.y}`,this.socCoordinates.forEach(o=>{t+=` L ${o.x+this.stroke/2},${o.y}`}),t+=` L ${e.x+this.stroke},${e.y} L ${e.x+this.stroke},${this.height} Z`}}},methods:{calculateCoordinates(t,s,e){const o=[];return t.forEach((r,a)=>{const i=a*this.width/this.maxPoints+1,n=this.height-(r-s)/(e-s)*this.height;o.push({x:i,y:n})}),o}}},u=()=>{S(t=>({"3f7db06a":t.color,"1de516bf":t.colorNegative}))},p=l.setup;l.setup=p?(t,s)=>(u(),p(t,s)):u;const N=["viewBox"],M=["d"],_=["x","y","width","height"],A=["y1","x2","y2"],R=y(l,[["render",function(t,s,e,o,r,a){return h(),c("svg",{class:"spark-line",viewBox:`0 0 ${e.width} ${e.height}`,width:"100%",preserveAspectRatio:"xMinYMin"},[e.socData?(h(),c("path",{key:0,class:"soc-path",d:a.socPath},null,8,M)):f("",!0),(h(!0),c(P,null,w(a.bars,i=>(h(),c("rect",{key:i.x,x:i.x,y:i.y,width:i.width,height:i.height,class:m(e.colorNegative&&i.negative?"negative":"")},null,10,_))),128)),x("line",{class:"zero-line",x1:0,y1:a.zeroHeight,x2:e.width,y2:a.zeroHeight},null,8,A)],8,N)}],["__scopeId","data-v-22cdf82b"]]);I.add(q,z,$);const E={name:"ChargePointStateBadge",components:{FontAwesomeIcon:D},props:{chargePointId:{required:!0,type:Array},showEnergyCharged:{required:!1,type:Boolean,default:!0}},data:()=>({mqttStore:k()}),computed:{plugState(){var t=!1;return this.chargePointId.forEach(s=>{t|=this.mqttStore.getChargePointPlugState(s)}),t},chargeState(){var t=!1;return this.chargePointId.forEach(s=>{t|=this.mqttStore.getChargePointChargeState(s)}),t},stateIcon(){return this.plugState?this.chargeState?["fas","fa-plug-circle-bolt"]:["fas","fa-plug-circle-check"]:["fas","fa-plug-circle-xmark"]},stateClass(){return this.plugState?this.chargeState?"_color:success":"_color:warning":"_color:gray"}}},H={key:0,class:"_padding-left:1"},Y=y(E,[["render",function(t,s,e,o,r,a){const i=d("font-awesome-icon"),n=d("i-badge");return h(),C(n,{size:"lg"},{default:v(()=>[b(i,{"fixed-width":"",icon:a.stateIcon,class:m(a.stateClass)},null,8,["icon","class"]),a.plugState&&e.showEnergyCharged?(h(),c("span",H,g(r.mqttStore.getChargePointImportedSincePlugged(e.chargePointId).energy)+" / "+g(r.mqttStore.getChargePointImportedSincePlugged(e.chargePointId).range),1)):f("",!0)]),_:1})}]]);export{Y as C,R as S}; diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointsView-ALIhGmhe.css b/packages/modules/display_themes/cards/web/assets/ChargePointsView-ALIhGmhe.css deleted file mode 100644 index 58022c3ede..0000000000 --- a/packages/modules/display_themes/cards/web/assets/ChargePointsView-ALIhGmhe.css +++ /dev/null @@ -1 +0,0 @@ -.card[data-v-3c6489c4]{----background: inherit !important;----body--color: var(----color) !important}.clickable[data-v-3c6489c4]{cursor:pointer}.card[data-v-5e676b1f]{----background: inherit !important;----body--color: var(----color) !important}.clickable[data-v-5e676b1f]{cursor:pointer}.large-button[data-v-5e676b1f]{height:3.75rem;font-size:1.5rem;padding:.75rem 1.5rem}.button-group-wrapper[data-v-5e676b1f]{display:flex;flex-direction:column;padding-right:0}.main-button-group[data-v-5e676b1f]{display:flex;flex-wrap:wrap;width:100%}.button.-outline:disabled.-disabled.-active[data-v-5e676b1f]{----border-color: var(----border-color--hover);background:var(----background);color:var(----color)}.charge-points-card-wrapper[data-v-76699ceb]{display:grid;grid-template-columns:repeat(auto-fill,minmax(36rem,1fr));grid-gap:var(--spacing)}.large-button[data-v-76699ceb]{height:3.5rem;font-size:1.5rem;padding:.75rem 1.5rem}[data-v-76699ceb] .toggle .toggle-label:before{border-color:var(--color--dark-45)}[data-v-76699ceb] .tab{min-height:72vh;max-height:72vh;overflow-y:scroll}.modal-vehicle-select[data-v-76699ceb] .modal-body{max-height:72vh;overflow-y:scroll}[data-v-76699ceb] .input-prepend,[data-v-76699ceb] .input-append{min-width:3em} diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointsView-BF43Lifc.css b/packages/modules/display_themes/cards/web/assets/ChargePointsView-BF43Lifc.css new file mode 100644 index 0000000000..f456d790fd --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/ChargePointsView-BF43Lifc.css @@ -0,0 +1 @@ +.card[data-v-45dbe31a]{----background: inherit !important;----body--color: var(----color) !important}.clickable[data-v-45dbe31a]{cursor:pointer}.card[data-v-cb556d40]{----background: inherit !important;----body--color: var(----color) !important}.clickable[data-v-cb556d40]{cursor:pointer}.large-button[data-v-cb556d40]{height:3.75rem;font-size:1.5rem;padding:.75rem 1.5rem}.button-group-wrapper[data-v-cb556d40]{display:flex;flex-direction:column;padding-right:0}.main-button-group[data-v-cb556d40]{display:flex;flex-wrap:wrap;width:100%}.button.-outline:disabled.-disabled.-active[data-v-cb556d40]{----border-color: var(----border-color--hover);background:var(----background);color:var(----color)}.modal-vehicle-select[data-v-b89baba5] .modal-body{max-height:72vh;overflow-y:scroll}.large-button[data-v-b89baba5]{height:3.5rem;font-size:1.5rem;padding:.75rem 1.5rem}.chartContainer[data-v-30ed2f35]{width:100%;min-height:200px;height:min(50vh,300px)}.charge-points-card-wrapper[data-v-2850e23a]{display:grid;grid-template-columns:repeat(auto-fill,minmax(36rem,1fr));grid-gap:var(--spacing)}[data-v-2850e23a] .toggle .toggle-label:before{border-color:var(--color--dark-45)}[data-v-2850e23a] .tab{min-height:72vh;max-height:72vh;overflow-y:scroll}[data-v-2850e23a] .input-prepend,[data-v-2850e23a] .input-append{min-width:3em}.plan-name[data-v-2850e23a]{font-weight:700}.plan-details[data-v-2850e23a]{display:flex;flex-wrap:nowrap;justify-content:center}.plan-details>div[data-v-2850e23a]:not(:last-child){margin-right:.5em} diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointsView-DAOuhP6h.js b/packages/modules/display_themes/cards/web/assets/ChargePointsView-DAOuhP6h.js deleted file mode 100644 index b7adceab39..0000000000 --- a/packages/modules/display_themes/cards/web/assets/ChargePointsView-DAOuhP6h.js +++ /dev/null @@ -1 +0,0 @@ -import{u as B,C as ae,N as ie}from"./index-nI_NVV5B.js";import{D as F}from"./DashBoardCard-DJZ2GsC-.js";import{C as z,S as le}from"./ChargePointPlugBadge-bP9BlN1o.js";import{F as $,l as A,b as ne,c as oe,j as re,k as W,m as E,e as U,n as D,o as Z,p as R,q as j,r as K,s as O,t as J,u as G,v as ce,w as de,x as he,y as ge}from"./vendor-fortawesome-CpQlJZ13.js";import{_ as x}from"./vendor-inkline-C_NPDnDu.js";import{l as u,q as i,p as h,o as d,s as a,z as q,e as S,F as M,k as r,f as m,x as s,A as L,I as se,i as ue,n as me}from"./vendor-BMrK3KHF.js";A.add(ne,oe);const H=x({name:"ChargePointLockButton",components:{FontAwesomeIcon:$},props:{chargePointId:{required:!0,type:Number},changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:B()}),computed:{locked(){return this.mqttStore.getChargePointManualLock(this.chargePointId)},stateIcon(){return this.locked?["fas","fa-lock"]:["fas","fa-lock-open"]},stateClass(){return this.locked?["_color:danger"]:"_color:success"}},methods:{toggleChargePointManualLock(){this.changesLocked||this.$root.sendTopicToBroker(`openWB/chargepoint/${this.chargePointId}/set/manual_lock`,!this.mqttStore.getValueBool(`openWB/chargepoint/${this.chargePointId}/set/manual_lock`))}}},[["render",function(l,e,n,p,t,c){const P=h("font-awesome-icon"),V=h("i-button");return d(),u(V,{size:"lg",disabled:n.changesLocked,outline:n.changesLocked},{default:i(()=>[a(P,{"fixed-width":"",icon:c.stateIcon,class:q(c.stateClass),onClick:e[0]||(e[0]=C=>c.toggleChargePointManualLock())},null,8,["icon","class"])]),_:1},8,["disabled","outline"])}]]);A.add(re);const Q=x({name:"ChargePointCodeButton",components:{FontAwesomeIcon:$,CodeInputModal:ae},props:{chargePointId:{type:Number,required:!0}},data:()=>({mqttStore:B(),modalIdTagEntryVisible:!1,modalIdTagEntryColor:"warning",code:""}),computed:{tagState(){return this.mqttStore.getChargepointTagState(this.chargePointId)},tagButtonColor(){switch(this.tagState){case 2:return"success";case 1:return"warning";default:return""}},tagClass(){switch(this.tagState){case 2:return"_color:success-80";case 1:return"_color:warning-80";default:return""}}},methods:{toggleIdTagModal(){this.modalIdTagEntryVisible=!this.modalIdTagEntryVisible},sendIdTag(l){this.$root.sendTopicToBroker(`openWB/chargepoint/${this.chargePointId}/get/rfid`,l),this.modalIdTagEntryVisible=!1}}},[["render",function(l,e,n,p,t,c){const P=h("FontAwesomeIcon"),V=h("i-button"),C=h("CodeInputModal");return d(),S(M,null,[a(V,{class:"_margin-right:1",size:"lg",color:c.tagButtonColor,disabled:c.tagState==2,onClick:e[0]||(e[0]=g=>c.toggleIdTagModal())},{default:i(()=>[a(P,{"fixed-width":"",icon:["fas","fa-calculator"],class:q(c.tagClass)},null,8,["class"])]),_:1},8,["color","disabled"]),a(C,{ref:"lockInput",modelValue:t.modalIdTagEntryVisible,"onUpdate:modelValue":e[1]||(e[1]=g=>t.modalIdTagEntryVisible=g),"min-length":4,"max-length":20,"onUpdate:inputValue":c.sendIdTag},{header:i(()=>e[2]||(e[2]=[r(" Bitte einen ID-Tag eingeben. ")])),_:1},8,["modelValue","onUpdate:inputValue"])],64)}]]);A.add(W,E,U,D,Z,R,j,K,O,J,G);const Ce={name:"ChargePointCard",components:{DashBoardCard:F,SparkLine:le,ChargePointPlugBadge:z,ChargePointLockButton:H,ChargePointCodeButton:Q,FontAwesomeIcon:$},props:{chargePointId:{type:Number,required:!0},changesLocked:{type:Boolean,required:!0}},emits:["vehicle-click","soc-click","charge-mode-click","toggle-charge-point-settings"],data:()=>({mqttStore:B()}),methods:{handleVehicleClick(l){this.$emit("vehicle-click",l)},handleSocClick(l){this.$emit("soc-click",l)},handleChargeModeClick(l){this.$emit("charge-mode-click",l)},toggleChargePointSettings(l){this.$emit("toggle-charge-point-settings",l)}}},Pe={key:0},pe=x(Ce,[["render",function(l,e,n,p,t,c){const P=h("charge-point-plug-badge"),V=h("charge-point-code-button"),C=h("charge-point-lock-button"),g=h("i-column"),I=h("i-row"),k=h("spark-line"),_=h("font-awesome-icon"),f=h("i-badge"),y=h("i-button"),b=h("i-container"),v=h("dash-board-card");return d(),u(v,{color:"primary"},{headerLeft:i(()=>[r(s(t.mqttStore.getChargePointName(n.chargePointId)),1)]),headerRight:i(()=>[a(P,{"charge-point-id":[n.chargePointId]},null,8,["charge-point-id"])]),default:i(()=>[a(b,null,{default:i(()=>[a(I,null,{default:i(()=>[a(g,null,{default:i(()=>[a(I,null,{default:i(()=>[a(g,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[t.mqttStore.getRfidEnabled?(d(),u(V,{key:0,"charge-point-id":n.chargePointId},null,8,["charge-point-id"])):m("",!0),a(C,{"charge-point-id":n.chargePointId,"changes-locked":n.changesLocked},null,8,["charge-point-id","changes-locked"])]),_:1}),a(g,{class:"_text-align:right _padding-left:0"},{default:i(()=>[r(s(t.mqttStore.getChargePointPower(n.chargePointId))+" "+s(t.mqttStore.getChargePointPhasesInUse(n.chargePointId))+" "+s(t.mqttStore.getChargePointSetCurrent(n.chargePointId)),1)]),_:1})]),_:1}),a(I,{class:"_padding-top:1"},{default:i(()=>[a(g,{class:"_padding-left:0"},{default:i(()=>[a(k,{color:"var(--color--primary)",data:t.mqttStore.getChargePointPowerChartData(n.chargePointId)},null,8,["data"])]),_:1})]),_:1})]),_:1}),a(g,{md:"6"},{default:i(()=>[a(I,{class:"_display:flex"},{default:i(()=>[a(g,{class:"_padding-left:0 _padding-right:0 _flex-grow:1"},{default:i(()=>[a(f,{size:"lg",class:q(["_width:100%",n.changesLocked?"":"clickable"]),onClick:e[0]||(e[0]=w=>c.handleVehicleClick(n.chargePointId))},{default:i(()=>[a(_,{"fixed-width":"",icon:["fas","fa-car"]}),r(" "+s(t.mqttStore.getChargePointConnectedVehicleName(n.chargePointId)),1)]),_:1},8,["class"])]),_:1}),t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))||t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))!=0?(d(),u(g,{key:0,class:"_flex-grow:0 _padding-right:0 _padding-left:1"},{default:i(()=>[a(y,{size:"sm",disabled:n.changesLocked,class:q(n.changesLocked?"":"clickable"),onClick:e[1]||(e[1]=w=>c.handleSocClick(n.chargePointId))},{default:i(()=>[t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))?(d(),S("span",Pe,[a(_,{"fixed-width":"",icon:t.mqttStore.getVehicleSocIsManual(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))?["fas","fa-edit"]:["fas","fa-car-battery"]},null,8,["icon"]),r(" "+s(t.mqttStore.getChargePointConnectedVehicleSoc(n.chargePointId).soc)+"% ",1)])):m("",!0),t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))!=0?(d(),u(_,{key:1,"fixed-width":"",icon:t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>1?["fas","times-circle"]:["fas","exclamation-triangle"]:[],class:q(t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>1?"_color:danger":"_color:warning":"")},null,8,["icon","class"])):m("",!0)]),_:1},8,["disabled","class"])]),_:1})):m("",!0)]),_:1}),a(I,{class:"_padding-top:1 _display:flex"},{default:i(()=>[a(g,{class:"_padding-left:0 _padding-right:0 _flex-grow:1"},{default:i(()=>[a(f,{size:"lg",class:q(["_width:100%",n.changesLocked?"":"clickable"]),color:t.mqttStore.getChargePointConnectedVehicleChargeMode(n.chargePointId).class,onClick:e[2]||(e[2]=w=>c.handleChargeModeClick(n.chargePointId))},{default:i(()=>[r(s(t.mqttStore.getChargePointConnectedVehicleChargeMode(n.chargePointId).label)+" ",1),a(_,{"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehiclePriority(n.chargePointId)?["fas","fa-star"]:["far","fa-star"],class:q(t.mqttStore.getChargePointConnectedVehiclePriority(n.chargePointId)?"_color:warning":"")},null,8,["icon","class"])]),_:1},8,["class","color"])]),_:1}),t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(n.chargePointId)?(d(),u(g,{key:0,class:"_flex-grow:0 _padding-right:0 _padding-left:1"},{default:i(()=>[a(f,{size:"lg"},{default:i(()=>[t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(n.chargePointId)?(d(),u(_,{key:0,"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehicleTimeChargingRunning(n.chargePointId)?["fas","fa-clock"]:["far","fa-clock"],class:q(t.mqttStore.getChargePointConnectedVehicleTimeChargingRunning(n.chargePointId)?"_color:success":"")},null,8,["icon","class"])):m("",!0)]),_:1})]),_:1})):m("",!0)]),_:1}),n.changesLocked?m("",!0):(d(),u(I,{key:0,class:"_padding-top:1"},{default:i(()=>[a(g,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[a(y,{block:"",onClick:e[3]||(e[3]=w=>c.toggleChargePointSettings(n.chargePointId))},{default:i(()=>[a(_,{"fixed-width":"",icon:["fas","fa-wrench"]})]),_:1})]),_:1})]),_:1}))]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-3c6489c4"]]);A.add(W,E,U,D,Z,R,j,K,O,J,G);const fe={name:"ChargePointCard",components:{DashBoardCard:F,ChargePointPlugBadge:z,ChargePointLockButton:H,ChargePointCodeButton:Q,FontAwesomeIcon:$},props:{chargePointId:{type:Number,required:!0},changesLocked:{type:Boolean,required:!0}},emits:["vehicle-click","soc-click","charge-mode-click","toggle-charge-point-settings","set-charge-point-connected-vehicle-charge-mode"],data:()=>({mqttStore:B(),simpleChargeModes:["instant_charging","pv_charging","stop"]}),computed:{filteredChargeModes(){return this.mqttStore.getSimpleChargePointView?this.mqttStore.chargeModeList().filter(l=>this.simpleChargeModes.includes(l.id)):this.mqttStore.chargeModeList()}},methods:{handleVehicleClick(l){this.$emit("vehicle-click",l)},handleSocClick(l){this.$emit("soc-click",l)},handleChargeModeClick(l){this.$emit("charge-mode-click",l)},toggleChargePointSettings(l){this.$emit("toggle-charge-point-settings",l)},setChargePointConnectedVehicleChargeMode(l,e){this.$emit("set-charge-point-connected-vehicle-charge-mode",l,e)}}},Se={key:0},Ve=x(fe,[["render",function(l,e,n,p,t,c){const P=h("charge-point-plug-badge"),V=h("charge-point-code-button"),C=h("charge-point-lock-button"),g=h("i-column"),I=h("i-row"),k=h("font-awesome-icon"),_=h("i-button"),f=h("i-button-group"),y=h("i-container"),b=h("dash-board-card");return d(),u(b,{color:"primary"},{headerLeft:i(()=>[r(s(t.mqttStore.getChargePointName(n.chargePointId)),1)]),headerRight:i(()=>[a(P,{"charge-point-id":[n.chargePointId]},null,8,["charge-point-id"])]),default:i(()=>[a(y,null,{default:i(()=>[a(I,null,{default:i(()=>[a(g,null,{default:i(()=>[a(I,null,{default:i(()=>[a(g,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[t.mqttStore.getRfidEnabled?(d(),u(V,{key:0,"charge-point-id":n.chargePointId},null,8,["charge-point-id"])):m("",!0),a(C,{"charge-point-id":n.chargePointId,"changes-locked":n.changesLocked},null,8,["charge-point-id","changes-locked"])]),_:1}),a(g,{class:"_text-align:right _padding-left:0"},{default:i(()=>[r(s(t.mqttStore.getChargePointPower(n.chargePointId))+" "+s(t.mqttStore.getChargePointPhasesInUse(n.chargePointId))+" "+s(t.mqttStore.getChargePointSetCurrent(n.chargePointId)),1)]),_:1})]),_:1}),a(I,{class:"_padding-top:1"},{default:i(()=>[a(g,{class:"_padding-left:0 button-group-wrapper"},{default:i(()=>[a(f,{class:"button-group main-button-group"},{default:i(()=>[a(_,{class:q(["large-button _flex-grow:1",n.changesLocked?"":"clickable"]),disabled:n.changesLocked,onClick:e[0]||(e[0]=v=>c.handleVehicleClick(n.chargePointId))},{default:i(()=>[a(k,{"fixed-width":"",icon:["fas","fa-car"]}),r(" "+s(t.mqttStore.getChargePointConnectedVehicleName(n.chargePointId))+" ",1),a(k,{class:q(["_padding-left:1",t.mqttStore.getChargePointConnectedVehiclePriority(n.chargePointId)?"_color:warning":""]),"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehiclePriority(n.chargePointId)?["fas","fa-star"]:["far","fa-star"]},null,8,["icon","class"])]),_:1},8,["class","disabled"]),t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))||t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))!=0?(d(),u(_,{key:0,class:q(["large-button _flex-grow:0",n.changesLocked?"":"clickable"]),disabled:n.changesLocked,onClick:e[1]||(e[1]=v=>c.handleSocClick(n.chargePointId))},{default:i(()=>[t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))?(d(),S("span",Se,[a(k,{"fixed-width":"",icon:t.mqttStore.getVehicleSocIsManual(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))?["fas","fa-edit"]:["fas","fa-car-battery"]},null,8,["icon"]),r(" "+s(t.mqttStore.getChargePointConnectedVehicleSoc(n.chargePointId).soc)+"% ",1)])):m("",!0),t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))!=0?(d(),u(k,{key:1,"fixed-width":"",icon:t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>1?["fas","times-circle"]:["fas","exclamation-triangle"]:[],class:q(t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(n.chargePointId))>1?"_color:danger":"_color:warning":"")},null,8,["icon","class"])):m("",!0)]),_:1},8,["disabled","class"])):m("",!0),a(_,{class:q(["large-button _flex-grow:0",n.changesLocked?"":"clickable"]),disabled:n.changesLocked,onClick:e[2]||(e[2]=v=>c.toggleChargePointSettings(n.chargePointId))},{default:i(()=>[a(k,{"fixed-width":"",icon:["fas","fa-wrench"]})]),_:1},8,["class","disabled"])]),_:1}),a(f,{class:"button-group _margin-top:1",disabled:n.changesLocked},{default:i(()=>[(d(!0),S(M,null,L(c.filteredChargeModes,v=>(d(),u(_,{key:v.id,outline:"",class:q(["large-button _flex-grow:1",n.changesLocked?"":"clickable"]),color:v.class!="dark"?v.class:"light",active:t.mqttStore.getChargePointConnectedVehicleChargeMode(n.chargePointId)!=null&&v.id==t.mqttStore.getChargePointConnectedVehicleChargeMode(n.chargePointId).mode,onClick:w=>c.setChargePointConnectedVehicleChargeMode(n.chargePointId,v.id)},{default:i(()=>[r(s(v.label),1)]),_:2},1032,["class","color","active","onClick"]))),128))]),_:1},8,["disabled"])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-5e676b1f"]]),X=x({name:"ExtendedNumberInput",inheritAttrs:!1,props:{modelValue:{type:Number,required:!0,default:NaN},unit:{type:String,default:""},min:{type:Number,default:0},max:{type:Number,default:100},step:{type:Number,default:1},labels:{type:Array,default:void 0}},emits:["update:modelValue"],data(){return{minimum:this.labels?0:this.min,maximum:this.labels?this.labels.length-1:this.max,stepSize:this.labels?1:this.step}},computed:{label(){var l;return this.labels&&this.inputValue!=null?this.inputValuec.label=C),plaintext:"",class:"_text-align:right",size:"lg"},{prepend:i(()=>[a(P,{onClick:c.decrement},{default:i(()=>e[1]||(e[1]=[r(" - ")])),_:1},8,["onClick"])]),suffix:i(()=>[r(s(n.unit),1)]),append:i(()=>[a(P,{onClick:c.increment},{default:i(()=>e[2]||(e[2]=[r(" + ")])),_:1},8,["onClick"])]),_:1},8,["modelValue"])}]]),Ie=x({name:"ManualSocInput",components:{ExtendedNumberInput:X,NumberPad:ie},props:{modelValue:{required:!0,type:Boolean,default:!1},vehicleId:{required:!0,type:Number,default:0}},emits:["update:modelValue"],data:()=>({mqttStore:B(),newSoc:0}),methods:{enter(l){let e=10*this.newSoc+parseInt(l);e>=0&&e<=100&&(this.newSoc=e)},removeDigit(){this.newSoc=Math.trunc(this.newSoc/10)},clear(){this.newSoc=0},close(){this.$emit("update:modelValue",!1),this.newSoc=0},updateManualSoc(){this.$root.sendTopicToBroker(`openWB/vehicle/${this.vehicleId}/soc_module/calculated_soc_state/manual_soc`,this.newSoc),this.close()}}},[["render",function(l,e,n,p,t,c){const P=h("extended-number-input"),V=h("i-column"),C=h("i-row"),g=h("NumberPad"),I=h("i-container"),k=h("i-button"),_=h("i-modal");return d(),u(se,{to:"body"},[a(_,{"model-value":n.modelValue,size:"sm","onUpdate:modelValue":e[6]||(e[6]=f=>l.$emit("update:modelValue",f))},{header:i(()=>[r(' SoC für Fahrzeug "'+s(t.mqttStore.getVehicleName(n.vehicleId))+'" ',1)]),footer:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(V,null,{default:i(()=>[a(k,{color:"danger",onClick:e[4]||(e[4]=f=>c.close())},{default:i(()=>e[7]||(e[7]=[r(" Zurück ")])),_:1})]),_:1}),a(V,{class:"_text-align:right"},{default:i(()=>[a(k,{color:"success",onClick:e[5]||(e[5]=f=>c.updateManualSoc())},{default:i(()=>e[8]||(e[8]=[r(" OK ")])),_:1})]),_:1})]),_:1})]),_:1})]),default:i(()=>[a(I,null,{default:i(()=>[a(C,{center:"",class:"_padding-bottom:1"},{default:i(()=>[a(V,null,{default:i(()=>[a(P,{modelValue:t.newSoc,"onUpdate:modelValue":e[0]||(e[0]=f=>t.newSoc=f),unit:"%",min:0,max:100,step:1,size:"lg",class:"_text-align:center"},null,8,["modelValue"])]),_:1})]),_:1}),a(g,{"onKey:digit":e[1]||(e[1]=f=>c.enter(f)),"onKey:clear":e[2]||(e[2]=f=>c.clear()),"onKey:delete":e[3]||(e[3]=f=>c.removeDigit())})]),_:1})]),_:1},8,["model-value"])])}]]);A.add(ce,de,he,ge);const _e={name:"ChargePointsView",components:{ChargePointCard:pe,SimpleChargePointCard:Ve,ExtendedNumberInput:X,ManualSocInput:Ie,FontAwesomeIcon:$},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:B(),modalChargeModeSettingVisible:!1,modalVehicleSelectVisible:!1,modalChargePointSettingsVisible:!1,modalChargePointId:0,modalVehicleId:0,modalActiveTab:"tab-general",modalManualSocInputVisible:!1,simpleChargeModes:["instant_charging","pv_charging","stop"]}),computed:{vehicleList(){let l=this.mqttStore.getVehicleList;var e=[];return Object.keys(l).forEach(n=>{let p=parseInt(n.match(/(?:\/)([0-9]+)(?=\/)*/g)[0].replace(/[^0-9]+/g,""));e.push({id:p,name:l[n]})}),e},filteredChargeModes(){return this.mqttStore.getSimpleChargePointView?this.mqttStore.chargeModeList().filter(l=>this.simpleChargeModes.includes(l.id)):this.mqttStore.chargeModeList()}},watch:{changesLocked(l,e){e!==!0&&l===!0&&(this.modalChargeModeSettingVisible=!1,this.modalVehicleSelectVisible=!1,this.modalChargePointSettingsVisible=!1,this.modalManualSocInputVisible=!1)}},methods:{toggleChargePointSettings(l){switch(this.mqttStore.getChargePointConnectedVehicleChargeMode(l).mode){case"pv_charging":this.modalActiveTab="tab-pv-charging";break;case"scheduled_charging":this.modalActiveTab="tab-scheduled-charging";break;default:this.modalActiveTab="tab-instant-charging"}this.modalChargePointId=l,this.modalChargePointSettingsVisible=!0},handleChargeModeClick(l){this.changesLocked||(this.modalChargePointId=l,this.modalChargeModeSettingVisible=!0)},handleVehicleClick(l){this.changesLocked||(this.modalChargePointId=l,this.modalVehicleSelectVisible=!0)},handleSocClick(l){let e=this.mqttStore.getChargePointConnectedVehicleId(l);if(this.mqttStore.getVehicleSocIsManual(e))return this.modalVehicleId=e,void(this.modalManualSocInputVisible=!0);this.$root.sendTopicToBroker(`openWB/set/vehicle/${e}/get/force_soc_update`,1)},setChargePointConnectedVehicle(l,e){e.id!=this.mqttStore.getChargePointConnectedVehicleId(l)&&this.$root.sendTopicToBroker(`openWB/chargepoint/${l}/config/ev`,e.id),this.modalVehicleSelectVisible&&(this.modalVehicleSelectVisible=!1)},setChargePointConnectedVehicleChargeMode(l,e){if(e.id!=this.mqttStore.getChargePointConnectedVehicleChargeMode(l)){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/selected`,e)}},setChargePointConnectedVehiclePriority(l,e){if(e!=this.mqttStore.getChargePointConnectedVehiclePriority(l)){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/prio`,e)}},setChargePointConnectedVehicleTimeChargingActive(l,e){if(e!=this.mqttStore.getChargePointConnectedVehicleTimeChargingActive(l)){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/time_charging/active`,e)}},setChargePointConnectedVehicleInstantChargingCurrent(l,e){if(e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingCurrent(l)){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/instant_charging/current`,parseFloat(e))}},setChargePointConnectedVehicleInstantChargingLimit(l,e){if(e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(l).selected){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/instant_charging/limit/selected`,e)}},setChargePointConnectedVehicleInstantChargingLimitSoc(l,e){if(e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(l).soc){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/instant_charging/limit/soc`,parseInt(e))}},setChargePointConnectedVehicleInstantChargingLimitAmount(l,e){if(e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(l).amount){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/instant_charging/limit/amount`,e)}},setChargePointConnectedVehiclePvChargingFeedInLimit(l,e){if(e!=this.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(l)){var n=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${n}/chargemode/pv_charging/feed_in_limit`,e)}},setChargePointConnectedVehiclePvChargingMinCurrent(l,e){let n=this.mqttStore.getChargePointConnectedVehiclePvChargingMinCurrent(l),p=parseInt(e);if(p!=n&&!isNaN(p)){var t=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${t}/chargemode/pv_charging/min_current`,p)}},setChargePointConnectedVehiclePvChargingMinSoc(l,e){let n=this.mqttStore.getChargePointConnectedVehiclePvChargingMinSoc(l),p=parseInt(e);if(p!=n&&!isNaN(p)){var t=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${t}/chargemode/pv_charging/min_soc`,p)}},setChargePointConnectedVehiclePvChargingMinSocCurrent(l,e){let n=this.mqttStore.getChargePointConnectedVehiclePvChargingMinSocCurrent(l),p=parseInt(e);if(p!=n&&!isNaN(p)){var t=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${t}/chargemode/pv_charging/min_soc_current`,p)}},setChargePointConnectedVehiclePvChargingMaxSoc(l,e){let n=this.mqttStore.getChargePointConnectedVehiclePvChargingMaxSoc(l),p=parseInt(e);if(p!=n&&!isNaN(p)){var t=this.mqttStore.getChargePointConnectedVehicleChargeTemplateIndex(l);this.$root.sendTopicToBroker(`openWB/vehicle/template/charge_template/${t}/chargemode/pv_charging/max_soc`,p)}},setChargePointConnectedVehicleScheduledChargingPlanActive(l,e){this.$root.sendTopicToBroker(`${l}/active`,e)},setChargePointConnectedVehicleTimeChargingPlanActive(l,e){this.$root.sendTopicToBroker(`${l}/active`,e)}}},be={class:"charge-points-card-wrapper"},ke={key:0},ve={key:1},qe={key:2},ye={key:3},we={key:4},Me={key:0},Te={key:1},xe={key:0},Le={key:1},Be={key:2},$e={key:3},Ae={key:4},De=x(_e,[["render",function(l,e,n,p,t,c){const P=h("i-button"),V=h("i-button-group"),C=h("i-form-group"),g=h("i-form-label"),I=h("i-form"),k=h("i-modal"),_=h("i-tab-title"),f=h("extended-number-input"),y=h("i-tab"),b=h("font-awesome-icon"),v=h("i-alert"),w=h("i-row"),N=h("i-container"),Y=h("i-tabs"),ee=h("manual-soc-input");return d(),S(M,null,[ue("div",be,[(d(!0),S(M,null,L(t.mqttStore.getChargePointIds,o=>(d(),u(me(t.mqttStore.getSimpleChargePointView?"SimpleChargePointCard":"ChargePointCard"),{key:o,"charge-point-id":o,"changes-locked":n.changesLocked,onVehicleClick:c.handleVehicleClick,onSocClick:c.handleSocClick,onChargeModeClick:c.handleChargeModeClick,onToggleChargePointSettings:c.toggleChargePointSettings,onSetChargePointConnectedVehicleChargeMode:c.setChargePointConnectedVehicleChargeMode},null,40,["charge-point-id","changes-locked","onVehicleClick","onSocClick","onChargeModeClick","onToggleChargePointSettings","onSetChargePointConnectedVehicleChargeMode"]))),128))]),a(k,{modelValue:t.modalChargeModeSettingVisible,"onUpdate:modelValue":e[2]||(e[2]=o=>t.modalChargeModeSettingVisible=o),size:"lg"},{header:i(()=>[r(' Lademodus für "'+s(t.mqttStore.getChargePointConnectedVehicleName(t.modalChargePointId))+'" auswählen ',1)]),default:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(V,{block:"",vertical:""},{default:i(()=>[(d(!0),S(M,null,L(c.filteredChargeModes,o=>(d(),u(P,{key:o.id,size:"lg",class:"large-button",outline:"",color:o.class!="dark"?o.class:"light",active:t.mqttStore.getChargePointConnectedVehicleChargeMode(t.modalChargePointId)!=null&&o.id==t.mqttStore.getChargePointConnectedVehicleChargeMode(t.modalChargePointId).mode,onClick:T=>c.setChargePointConnectedVehicleChargeMode(t.modalChargePointId,o.id)},{default:i(()=>[r(s(o.label),1)]),_:2},1032,["color","active","onClick"]))),128))]),_:1})]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[21]||(e[21]=[r("Priorität")])),_:1}),a(V,{block:""},{default:i(()=>[a(P,{size:"lg",class:"large-button",color:t.mqttStore.getChargePointConnectedVehiclePriority(t.modalChargePointId)!==!0?"danger":"",onClick:e[0]||(e[0]=o=>c.setChargePointConnectedVehiclePriority(t.modalChargePointId,!1))},{default:i(()=>e[22]||(e[22]=[r(" Nein ")])),_:1},8,["color"]),a(P,{color:t.mqttStore.getChargePointConnectedVehiclePriority(t.modalChargePointId)===!0?"success":"",onClick:e[1]||(e[1]=o=>c.setChargePointConnectedVehiclePriority(t.modalChargePointId,!0))},{default:i(()=>e[23]||(e[23]=[r(" Ja ")])),_:1},8,["color"])]),_:1})]),_:1})]),_:1})]),_:1},8,["modelValue"]),a(k,{modelValue:t.modalVehicleSelectVisible,"onUpdate:modelValue":e[3]||(e[3]=o=>t.modalVehicleSelectVisible=o),class:"modal-vehicle-select",size:"lg"},{header:i(()=>[r(' Fahrzeug an "'+s(t.mqttStore.getChargePointName(t.modalChargePointId))+'" auswählen ',1)]),default:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(V,{vertical:"",block:""},{default:i(()=>[(d(!0),S(M,null,L(c.vehicleList,o=>(d(),u(P,{key:o.id,size:"lg",class:"large-button",active:t.mqttStore.getChargePointConnectedVehicleId(t.modalChargePointId)==o.id,color:t.mqttStore.getChargePointConnectedVehicleId(t.modalChargePointId)==o.id?"primary":"",onClick:T=>c.setChargePointConnectedVehicle(t.modalChargePointId,o)},{default:i(()=>[r(s(o.name),1)]),_:2},1032,["active","color","onClick"]))),128))]),_:1})]),_:1})]),_:1})]),_:1},8,["modelValue"]),a(k,{modelValue:t.modalChargePointSettingsVisible,"onUpdate:modelValue":e[19]||(e[19]=o=>t.modalChargePointSettingsVisible=o),size:"lg"},{header:i(()=>[r(' Einstellungen für Fahrzeug "'+s(t.mqttStore.getChargePointConnectedVehicleName(t.modalChargePointId))+'" ',1)]),default:i(()=>[a(Y,{modelValue:t.modalActiveTab,"onUpdate:modelValue":e[18]||(e[18]=o=>t.modalActiveTab=o),stretch:""},{header:i(()=>[a(_,{for:"tab-instant-charging"},{default:i(()=>e[24]||(e[24]=[r(" Sofort ")])),_:1}),a(_,{for:"tab-pv-charging"},{default:i(()=>e[25]||(e[25]=[r(" PV ")])),_:1}),t.mqttStore.getSimpleChargePointView?m("",!0):(d(),u(_,{key:0,for:"tab-scheduled-charging"},{default:i(()=>e[26]||(e[26]=[r(" Zielladen ")])),_:1})),t.mqttStore.getSimpleChargePointView?m("",!0):(d(),u(_,{key:1,for:"tab-time-charging"},{default:i(()=>e[27]||(e[27]=[r(" Zeitladen ")])),_:1}))]),default:i(()=>[a(y,{name:"tab-instant-charging"},{default:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[28]||(e[28]=[r("Stromstärke")])),_:1}),a(f,{unit:"A",min:6,max:32,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingCurrent(t.modalChargePointId),"onUpdate:modelValue":e[4]||(e[4]=o=>c.setChargePointConnectedVehicleInstantChargingCurrent(t.modalChargePointId,o))},null,8,["model-value"])]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[29]||(e[29]=[r("Begrenzung")])),_:1}),a(V,{block:""},{default:i(()=>[a(P,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="none"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="none",onClick:e[5]||(e[5]=o=>c.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"none"))},{default:i(()=>e[30]||(e[30]=[r(" Keine ")])),_:1},8,["color","active"]),a(P,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc",onClick:e[6]||(e[6]=o=>c.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"soc"))},{default:i(()=>e[31]||(e[31]=[r(" EV-SoC ")])),_:1},8,["color","active"]),a(P,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount",onClick:e[7]||(e[7]=o=>c.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"amount"))},{default:i(()=>e[32]||(e[32]=[r(" Energie ")])),_:1},8,["color","active"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc"?(d(),u(C,{key:0},{default:i(()=>[a(g,null,{default:i(()=>e[33]||(e[33]=[r("Max. SoC")])),_:1}),a(f,{unit:"%",min:5,max:100,step:5,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).soc,"onUpdate:modelValue":e[8]||(e[8]=o=>c.setChargePointConnectedVehicleInstantChargingLimitSoc(t.modalChargePointId,o))},null,8,["model-value"])]),_:1})):m("",!0),t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount"?(d(),u(C,{key:1},{default:i(()=>[a(g,null,{default:i(()=>e[34]||(e[34]=[r("Max. Energie")])),_:1}),a(f,{unit:"kWh",min:1,max:100,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).amount/1e3,"onUpdate:modelValue":e[9]||(e[9]=o=>c.setChargePointConnectedVehicleInstantChargingLimitAmount(t.modalChargePointId,1e3*o))},null,8,["model-value"])]),_:1})):m("",!0)]),_:1})]),_:1}),a(y,{name:"tab-pv-charging"},{default:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[35]||(e[35]=[r("Einspeisegrenze beachten")])),_:1}),a(V,{block:""},{default:i(()=>[a(P,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId)!==!0?"danger":"",onClick:e[10]||(e[10]=o=>c.setChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId,!1))},{default:i(()=>e[36]||(e[36]=[r(" Nein ")])),_:1},8,["color"]),a(P,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId)===!0?"success":"",onClick:e[11]||(e[11]=o=>c.setChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId,!0))},{default:i(()=>e[37]||(e[37]=[r(" Ja ")])),_:1},8,["color"])]),_:1})]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[38]||(e[38]=[r("Minimaler Dauerstrom")])),_:1}),a(f,{unit:"A",labels:[{label:"Aus",value:0},{label:6,value:6},{label:7,value:7},{label:8,value:8},{label:9,value:9},{label:10,value:10},{label:11,value:11},{label:12,value:12},{label:13,value:13},{label:14,value:14},{label:15,value:15},{label:16,value:16}],"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinCurrent(t.modalChargePointId),"onUpdate:modelValue":e[12]||(e[12]=o=>c.setChargePointConnectedVehiclePvChargingMinCurrent(t.modalChargePointId,o))},null,8,["model-value"])]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[39]||(e[39]=[r("Mindest-SoC")])),_:1}),a(f,{unit:"%",labels:[{label:"Aus",value:0},{label:5,value:5},{label:10,value:10},{label:15,value:15},{label:20,value:20},{label:25,value:25},{label:30,value:30},{label:35,value:35},{label:40,value:40},{label:45,value:45},{label:50,value:50},{label:55,value:55},{label:60,value:60},{label:65,value:65},{label:70,value:70},{label:75,value:75},{label:80,value:80},{label:85,value:85},{label:90,value:90},{label:95,value:95}],"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinSoc(t.modalChargePointId),"onUpdate:modelValue":e[13]||(e[13]=o=>c.setChargePointConnectedVehiclePvChargingMinSoc(t.modalChargePointId,o))},null,8,["model-value"])]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[40]||(e[40]=[r("Mindest-SoC Strom")])),_:1}),a(f,{min:6,max:32,unit:"A","model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocCurrent(t.modalChargePointId),"onUpdate:modelValue":e[14]||(e[14]=o=>c.setChargePointConnectedVehiclePvChargingMinSocCurrent(t.modalChargePointId,o))},null,8,["model-value"])]),_:1}),a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[41]||(e[41]=[r("SoC-Limit")])),_:1}),a(f,{unit:"%",labels:[{label:5,value:5},{label:10,value:10},{label:15,value:15},{label:20,value:20},{label:25,value:25},{label:30,value:30},{label:35,value:35},{label:40,value:40},{label:45,value:45},{label:50,value:50},{label:55,value:55},{label:60,value:60},{label:65,value:65},{label:70,value:70},{label:75,value:75},{label:80,value:80},{label:85,value:85},{label:90,value:90},{label:95,value:95},{label:100,value:100},{label:"Aus",value:101}],"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMaxSoc(t.modalChargePointId),"onUpdate:modelValue":e[15]||(e[15]=o=>c.setChargePointConnectedVehiclePvChargingMaxSoc(t.modalChargePointId,o))},null,8,["model-value"])]),_:1})]),_:1})]),_:1}),t.mqttStore.getSimpleChargePointView?m("",!0):(d(),u(y,{key:0,name:"tab-scheduled-charging"},{default:i(()=>[Object.keys(t.mqttStore.getChargePointConnectedVehicleScheduledChargingPlans(t.modalChargePointId)).length===0?(d(),u(v,{key:0},{icon:i(()=>[a(b,{"fixed-width":"",icon:["fas","fa-info-circle"]})]),default:i(()=>[e[42]||(e[42]=r(" Es wurden noch keine Zeitpläne für das Zielladen eingerichtet. "))]),_:1})):(d(),u(I,{key:1},{default:i(()=>[(d(!0),S(M,null,L(t.mqttStore.getChargePointConnectedVehicleScheduledChargingPlans(t.modalChargePointId),(o,T)=>(d(),u(C,{key:T},{default:i(()=>[a(N,null,{default:i(()=>[a(w,null,{default:i(()=>[a(g,null,{default:i(()=>[r(s(o.name),1)]),_:2},1024)]),_:2},1024),a(w,null,{default:i(()=>[a(P,{size:"lg",block:"",color:o.active?"success":"danger",onClick:te=>c.setChargePointConnectedVehicleScheduledChargingPlanActive(T,!o.active)},{default:i(()=>[o.frequency.selected=="once"?(d(),S("span",ke,[a(b,{"fixed-width":"",icon:["fas","calendar-day"]}),r(" "+s(t.mqttStore.formatDate(o.frequency.once)),1)])):m("",!0),o.frequency.selected=="daily"?(d(),S("span",ve,[a(b,{"fixed-width":"",icon:["fas","calendar-week"]}),e[43]||(e[43]=r(" täglich "))])):m("",!0),o.frequency.selected=="weekly"?(d(),S("span",qe,[a(b,{"fixed-width":"",icon:["fas","calendar-alt"]}),r(" "+s(t.mqttStore.formatWeeklyScheduleDays(o.frequency.weekly)),1)])):m("",!0),a(b,{"fixed-width":"",icon:["fas","clock"]}),r(" "+s(o.time)+" ",1),o.limit.selected=="soc"?(d(),S("span",ye,[a(b,{"fixed-width":"",icon:["fas","car-battery"]}),r(" "+s(o.limit.soc_scheduled)+" % ",1)])):m("",!0),o.limit.selected=="amount"?(d(),S("span",we,[a(b,{"fixed-width":"",icon:["fas","bolt"]}),r(" "+s(o.limit.amount/1e3)+" kWh ",1)])):m("",!0)]),_:2},1032,["color","onClick"])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1}))]),_:1})),t.mqttStore.getSimpleChargePointView?m("",!0):(d(),u(y,{key:1,name:"tab-time-charging"},{default:i(()=>[a(I,null,{default:i(()=>[a(C,null,{default:i(()=>[a(g,null,{default:i(()=>e[44]||(e[44]=[r("Zeitladen aktivieren")])),_:1}),a(V,{block:""},{default:i(()=>[a(P,{color:t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId)!==!0?"danger":"",onClick:e[16]||(e[16]=o=>c.setChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId,!1))},{default:i(()=>e[45]||(e[45]=[r(" Nein ")])),_:1},8,["color"]),a(P,{color:t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId)===!0?"success":"",onClick:e[17]||(e[17]=o=>c.setChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId,!0))},{default:i(()=>e[46]||(e[46]=[r(" Ja ")])),_:1},8,["color"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId)===!0?(d(),S("div",Me,[Object.keys(t.mqttStore.getChargePointConnectedVehicleTimeChargingPlans(t.modalChargePointId)).length===0?(d(),u(v,{key:0,color:"warning",class:"_margin-top:2"},{icon:i(()=>[a(b,{"fixed-width":"",icon:["fas","fa-circle-info"]})]),default:i(()=>[e[47]||(e[47]=r(" Es wurden noch keine Zeitpläne für das Zeitladen eingerichtet. "))]),_:1})):(d(),S("div",Te,[(d(!0),S(M,null,L(t.mqttStore.getChargePointConnectedVehicleTimeChargingPlans(t.modalChargePointId),(o,T)=>(d(),u(C,{key:T},{default:i(()=>[a(N,null,{default:i(()=>[a(w,null,{default:i(()=>[a(g,null,{default:i(()=>[r(s(o.name),1)]),_:2},1024)]),_:2},1024),a(w,null,{default:i(()=>[a(P,{size:"lg",block:"",color:o.active?"success":"danger",onClick:te=>c.setChargePointConnectedVehicleTimeChargingPlanActive(T,!o.active)},{default:i(()=>[o.frequency.selected=="once"?(d(),S("span",xe,[a(b,{"fixed-width":"",icon:["fas","calendar-day"]}),r(" "+s(t.mqttStore.formatDateRange(o.frequency.once)),1)])):m("",!0),o.frequency.selected=="daily"?(d(),S("span",Le,[a(b,{"fixed-width":"",icon:["fas","calendar-week"]}),e[48]||(e[48]=r(" täglich "))])):m("",!0),o.frequency.selected=="weekly"?(d(),S("span",Be,[a(b,{"fixed-width":"",icon:["fas","calendar-alt"]}),r(" "+s(t.mqttStore.formatWeeklyScheduleDays(o.frequency.weekly)),1)])):m("",!0),a(b,{"fixed-width":"",icon:["fas","clock"]}),r(" "+s(o.time.join("-"))+" ",1),o.limit.selected=="soc"?(d(),S("span",$e,[a(b,{"fixed-width":"",icon:["fas","car-battery"]}),r(" "+s(o.limit.soc)+" % ",1)])):m("",!0),o.limit.selected=="amount"?(d(),S("span",Ae,[a(b,{"fixed-width":"",icon:["fas","bolt"]}),r(" "+s(o.limit.amount/1e3)+" kWh ",1)])):m("",!0)]),_:2},1032,["color","onClick"])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]))])):m("",!0)]),_:1})]),_:1}))]),_:1},8,["modelValue"])]),_:1},8,["modelValue"]),a(ee,{modelValue:t.modalManualSocInputVisible,"onUpdate:modelValue":e[20]||(e[20]=o=>t.modalManualSocInputVisible=o),"vehicle-id":t.modalVehicleId},null,8,["modelValue","vehicle-id"])],64)}],["__scopeId","data-v-76699ceb"]]);export{De as default}; diff --git a/packages/modules/display_themes/cards/web/assets/ChargePointsView-InJz5_Tj.js b/packages/modules/display_themes/cards/web/assets/ChargePointsView-InJz5_Tj.js new file mode 100644 index 0000000000..d1229dcf85 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/ChargePointsView-InJz5_Tj.js @@ -0,0 +1 @@ +var re=Object.defineProperty;var ce=(n,e,o)=>e in n?re(n,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):n[e]=o;var w=(n,e,o)=>ce(n,typeof e!="symbol"?e+"":e,o);import{u as x,C as de,N as he}from"./index-C0J-ZJ88.js";import{D}from"./DashboardCard-DS7AgUYm.js";import{C as W,S as ge}from"./ChargePointPlugBadge-Dsdv-7Vn.js";import{F as B,l as F,b as se,c as me,j as Ce,k as R,m as O,e as j,n as Z,o as K,p as J,q as H,r as Q,s as X,t as Y,u as G,v as ue,w as Pe,x as pe,y as fe,z as _e,A as Ve,B as Se}from"./vendor-fortawesome-C1Wk2aFl.js";import{_ as M}from"./vendor-inkline-S9CBmrTS.js";import{l as u,q as i,p as h,o as d,s as a,z as b,e as I,F as E,k as c,f as P,x as p,A as z,I as Ie,P as ve,Q as ke,S as be,U as qe,V as ye,W as Le,X as Me,Y as xe,Z as we,_ as Te,i as T,n as Ee}from"./vendor-Bzn5cd2Y.js";import{C as Ae}from"./ChargeModeModal-CNWdoEpw.js";F.add(se,me);const ee=M({name:"ChargePointLockButton",components:{FontAwesomeIcon:B},props:{chargePointId:{required:!0,type:Number},changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:x()}),computed:{locked(){return this.mqttStore.getChargePointManualLock(this.chargePointId)},stateIcon(){return this.locked?["fas","fa-lock"]:["fas","fa-lock-open"]},stateClass(){return this.locked?["_color:danger"]:"_color:success"}},methods:{toggleChargePointManualLock(){this.changesLocked||this.$root.sendTopicToBroker(`openWB/chargepoint/${this.chargePointId}/set/manual_lock`,!this.mqttStore.getValueBool(`openWB/chargepoint/${this.chargePointId}/set/manual_lock`))}}},[["render",function(n,e,o,g,t,l){const f=h("font-awesome-icon"),k=h("i-button");return d(),u(k,{size:"lg",disabled:o.changesLocked,outline:o.changesLocked},{default:i(()=>[a(f,{"fixed-width":"",icon:l.stateIcon,class:b(l.stateClass),onClick:e[0]||(e[0]=v=>l.toggleChargePointManualLock())},null,8,["icon","class"])]),_:1},8,["disabled","outline"])}]]);F.add(Ce);const te=M({name:"ChargePointCodeButton",components:{FontAwesomeIcon:B,CodeInputModal:de},props:{chargePointId:{type:Number,required:!0}},data:()=>({mqttStore:x(),modalIdTagEntryVisible:!1,modalIdTagEntryColor:"warning",code:""}),computed:{tagState(){return this.mqttStore.getChargepointTagState(this.chargePointId)},tagButtonColor(){switch(this.tagState){case 2:return"success";case 1:return"warning";default:return""}},tagClass(){switch(this.tagState){case 2:return"_color:success-80";case 1:return"_color:warning-80";default:return""}}},methods:{toggleIdTagModal(){this.modalIdTagEntryVisible=!this.modalIdTagEntryVisible},sendIdTag(n){this.$root.sendTopicToBroker(`openWB/chargepoint/${this.chargePointId}/get/rfid`,n),this.modalIdTagEntryVisible=!1}}},[["render",function(n,e,o,g,t,l){const f=h("FontAwesomeIcon"),k=h("i-button"),v=h("CodeInputModal");return d(),I(E,null,[a(k,{class:"_margin-right:1",size:"lg",color:l.tagButtonColor,disabled:l.tagState==2,onClick:e[0]||(e[0]=s=>l.toggleIdTagModal())},{default:i(()=>[a(f,{"fixed-width":"",icon:["fas","fa-calculator"],class:b(l.tagClass)},null,8,["class"])]),_:1},8,["color","disabled"]),a(v,{ref:"lockInput",modelValue:t.modalIdTagEntryVisible,"onUpdate:modelValue":e[1]||(e[1]=s=>t.modalIdTagEntryVisible=s),"min-length":4,"max-length":20,"onUpdate:inputValue":l.sendIdTag},{header:i(()=>e[2]||(e[2]=[c(" Bitte einen ID-Tag eingeben. ",-1)])),_:1},8,["modelValue","onUpdate:inputValue"])],64)}]]);F.add(R,O,j,Z,K,J,H,Q,X,Y,G);const ze={name:"ChargePointCard",components:{DashboardCard:D,SparkLine:ge,ChargePointPlugBadge:W,ChargePointLockButton:ee,ChargePointCodeButton:te,FontAwesomeIcon:B},props:{chargePointId:{type:Number,required:!0},changesLocked:{type:Boolean,required:!0}},emits:["vehicle-click","soc-click","charge-mode-click","toggle-charge-point-settings"],data:()=>({mqttStore:x()}),methods:{handleVehicleClick(n){this.$emit("vehicle-click",n)},handleSocClick(n){this.$emit("soc-click",n)},handleChargeModeClick(n){this.$emit("charge-mode-click",n)},toggleChargePointSettings(n){this.$emit("toggle-charge-point-settings",n)}}},Be={key:0},Fe=M(ze,[["render",function(n,e,o,g,t,l){const f=h("charge-point-plug-badge"),k=h("charge-point-code-button"),v=h("charge-point-lock-button"),s=h("i-column"),_=h("i-row"),m=h("spark-line"),C=h("font-awesome-icon"),V=h("i-badge"),y=h("i-button"),L=h("i-container"),q=h("dashboard-card");return d(),u(q,{color:"primary"},{headerLeft:i(()=>[c(p(t.mqttStore.getChargePointName(o.chargePointId)),1)]),headerRight:i(()=>[a(f,{"charge-point-id":[o.chargePointId]},null,8,["charge-point-id"])]),default:i(()=>[a(L,null,{default:i(()=>[a(_,null,{default:i(()=>[a(s,null,{default:i(()=>[a(_,null,{default:i(()=>[a(s,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[t.mqttStore.getRfidEnabled?(d(),u(k,{key:0,"charge-point-id":o.chargePointId},null,8,["charge-point-id"])):P("",!0),a(v,{"charge-point-id":o.chargePointId,"changes-locked":o.changesLocked},null,8,["charge-point-id","changes-locked"])]),_:1}),a(s,{class:"_text-align:right _padding-left:0"},{default:i(()=>[c(p(t.mqttStore.getChargePointPower(o.chargePointId))+" "+p(t.mqttStore.getChargePointPhasesInUse(o.chargePointId))+" "+p(t.mqttStore.getChargePointSetCurrent(o.chargePointId)),1)]),_:1})]),_:1}),a(_,{class:"_padding-top:1"},{default:i(()=>[a(s,{class:"_padding-left:0"},{default:i(()=>[a(m,{color:"var(--color--primary)",data:t.mqttStore.getChargePointPowerChartData(o.chargePointId)},null,8,["data"])]),_:1})]),_:1})]),_:1}),a(s,{md:"6"},{default:i(()=>[a(_,{class:"_display:flex"},{default:i(()=>[a(s,{class:"_padding-left:0 _padding-right:0 _flex-grow:1"},{default:i(()=>[a(V,{size:"lg",class:b(["_width:100%",o.changesLocked?"":"clickable"]),onClick:e[0]||(e[0]=S=>l.handleVehicleClick(o.chargePointId))},{default:i(()=>[a(C,{"fixed-width":"",icon:["fas","fa-car"]}),c(" "+p(t.mqttStore.getChargePointConnectedVehicleName(o.chargePointId)),1)]),_:1},8,["class"])]),_:1}),t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))||t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))!=0?(d(),u(s,{key:0,class:"_flex-grow:0 _padding-right:0 _padding-left:1"},{default:i(()=>[a(y,{size:"sm",disabled:o.changesLocked,class:b(o.changesLocked?"":"clickable"),onClick:e[1]||(e[1]=S=>l.handleSocClick(o.chargePointId))},{default:i(()=>[t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))?(d(),I("span",Be,[a(C,{"fixed-width":"",icon:t.mqttStore.getVehicleSocIsManual(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))?["fas","fa-edit"]:["fas","fa-car-battery"]},null,8,["icon"]),c(" "+p(t.mqttStore.getChargePointConnectedVehicleSoc(o.chargePointId).soc)+"% ",1)])):P("",!0),t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))!=0?(d(),u(C,{key:1,"fixed-width":"",icon:t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>1?["fas","times-circle"]:["fas","exclamation-triangle"]:[],class:b(t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>1?"_color:danger":"_color:warning":"")},null,8,["icon","class"])):P("",!0)]),_:1},8,["disabled","class"])]),_:1})):P("",!0)]),_:1}),a(_,{class:"_padding-top:1 _display:flex"},{default:i(()=>[a(s,{class:"_padding-left:0 _padding-right:0 _flex-grow:1"},{default:i(()=>{var S;return[a(V,{size:"lg",class:b(["_width:100%",o.changesLocked?"":"clickable"]),color:(S=t.mqttStore.getChargePointConnectedVehicleChargeMode(o.chargePointId))==null?void 0:S.class,onClick:e[2]||(e[2]=A=>l.handleChargeModeClick(o.chargePointId))},{default:i(()=>{var A;return[c(p((A=t.mqttStore.getChargePointConnectedVehicleChargeMode(o.chargePointId))==null?void 0:A.label)+" ",1),a(C,{"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehiclePriority(o.chargePointId)?["fas","fa-star"]:["far","fa-star"],class:b(t.mqttStore.getChargePointConnectedVehiclePriority(o.chargePointId)?"_color:warning":"")},null,8,["icon","class"])]}),_:1},8,["class","color"])]}),_:1}),t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(o.chargePointId)?(d(),u(s,{key:0,class:"_flex-grow:0 _padding-right:0 _padding-left:1"},{default:i(()=>[a(V,{size:"lg"},{default:i(()=>[t.mqttStore.getChargePointConnectedVehicleTimeChargingActive(o.chargePointId)?(d(),u(C,{key:0,"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehicleTimeChargingRunning(o.chargePointId)?["fas","fa-clock"]:["far","fa-clock"],class:b(t.mqttStore.getChargePointConnectedVehicleTimeChargingRunning(o.chargePointId)?"_color:success":"")},null,8,["icon","class"])):P("",!0)]),_:1})]),_:1})):P("",!0)]),_:1}),o.changesLocked?P("",!0):(d(),u(_,{key:0,class:"_padding-top:1"},{default:i(()=>[a(s,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[a(y,{block:"",onClick:e[3]||(e[3]=S=>l.toggleChargePointSettings(o.chargePointId))},{default:i(()=>[a(C,{"fixed-width":"",icon:["fas","fa-wrench"]})]),_:1})]),_:1})]),_:1}))]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-45dbe31a"]]);F.add(R,O,j,Z,K,J,H,Q,X,Y,G);const Ne={name:"ChargePointCard",components:{DashboardCard:D,ChargePointPlugBadge:W,ChargePointLockButton:ee,ChargePointCodeButton:te,FontAwesomeIcon:B},props:{chargePointId:{type:Number,required:!0},changesLocked:{type:Boolean,required:!0}},emits:["vehicle-click","soc-click","charge-mode-click","toggle-charge-point-settings","set-charge-point-connected-vehicle-charge-mode"],data:()=>({mqttStore:x(),simpleChargeModes:["instant_charging","pv_charging","stop"]}),computed:{filteredChargeModes(){return this.mqttStore.getSimpleChargePointView?this.mqttStore.chargeModeList().filter(n=>this.simpleChargeModes.includes(n.id)):this.mqttStore.chargeModeList()}},methods:{handleVehicleClick(n){this.$emit("vehicle-click",n)},handleSocClick(n){this.$emit("soc-click",n)},handleChargeModeClick(n){this.$emit("charge-mode-click",n)},toggleChargePointSettings(n){this.$emit("toggle-charge-point-settings",n)},setChargePointConnectedVehicleChargeMode(n,e){this.$emit("set-charge-point-connected-vehicle-charge-mode",n,e)}}},$e={key:0},Ue=M(Ne,[["render",function(n,e,o,g,t,l){const f=h("charge-point-plug-badge"),k=h("charge-point-code-button"),v=h("charge-point-lock-button"),s=h("i-column"),_=h("i-row"),m=h("font-awesome-icon"),C=h("i-button"),V=h("i-button-group"),y=h("i-container"),L=h("dashboard-card");return d(),u(L,{color:"primary"},{headerLeft:i(()=>[c(p(t.mqttStore.getChargePointName(o.chargePointId)),1)]),headerRight:i(()=>[a(f,{"charge-point-id":[o.chargePointId]},null,8,["charge-point-id"])]),default:i(()=>[a(y,null,{default:i(()=>[a(_,null,{default:i(()=>[a(s,null,{default:i(()=>[a(_,null,{default:i(()=>[a(s,{class:"_padding-left:0 _padding-right:0"},{default:i(()=>[t.mqttStore.getRfidEnabled?(d(),u(k,{key:0,"charge-point-id":o.chargePointId},null,8,["charge-point-id"])):P("",!0),a(v,{"charge-point-id":o.chargePointId,"changes-locked":o.changesLocked},null,8,["charge-point-id","changes-locked"])]),_:1}),a(s,{class:"_text-align:right _padding-left:0"},{default:i(()=>[c(p(t.mqttStore.getChargePointPower(o.chargePointId))+" "+p(t.mqttStore.getChargePointPhasesInUse(o.chargePointId))+" "+p(t.mqttStore.getChargePointSetCurrent(o.chargePointId)),1)]),_:1})]),_:1}),a(_,{class:"_padding-top:1"},{default:i(()=>[a(s,{class:"_padding-left:0 button-group-wrapper"},{default:i(()=>[a(V,{class:"button-group main-button-group"},{default:i(()=>[a(C,{class:b(["large-button _flex-grow:1",o.changesLocked?"":"clickable"]),disabled:o.changesLocked,onClick:e[0]||(e[0]=q=>l.handleVehicleClick(o.chargePointId))},{default:i(()=>[a(m,{"fixed-width":"",icon:["fas","fa-car"]}),c(" "+p(t.mqttStore.getChargePointConnectedVehicleName(o.chargePointId))+" ",1),a(m,{class:b(["_padding-left:1",t.mqttStore.getChargePointConnectedVehiclePriority(o.chargePointId)?"_color:warning":""]),"fixed-width":"",icon:t.mqttStore.getChargePointConnectedVehiclePriority(o.chargePointId)?["fas","fa-star"]:["far","fa-star"]},null,8,["icon","class"])]),_:1},8,["class","disabled"]),t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))||t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))!=0?(d(),u(C,{key:0,class:b(["large-button _flex-grow:0",o.changesLocked?"":"clickable"]),disabled:o.changesLocked,onClick:e[1]||(e[1]=q=>l.handleSocClick(o.chargePointId))},{default:i(()=>[t.mqttStore.getVehicleSocConfigured(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))?(d(),I("span",$e,[a(m,{"fixed-width":"",icon:t.mqttStore.getVehicleSocIsManual(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))?["fas","fa-edit"]:["fas","fa-car-battery"]},null,8,["icon"]),c(" "+p(t.mqttStore.getChargePointConnectedVehicleSoc(o.chargePointId).soc)+"% ",1)])):P("",!0),t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))!=0?(d(),u(m,{key:1,"fixed-width":"",icon:t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>1?["fas","times-circle"]:["fas","exclamation-triangle"]:[],class:b(t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>0?t.mqttStore.getVehicleFaultState(t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId))>1?"_color:danger":"_color:warning":"")},null,8,["icon","class"])):P("",!0)]),_:1},8,["disabled","class"])):P("",!0),a(C,{class:b(["large-button _flex-grow:0",o.changesLocked?"":"clickable"]),disabled:o.changesLocked,onClick:e[2]||(e[2]=q=>l.toggleChargePointSettings(o.chargePointId))},{default:i(()=>[a(m,{"fixed-width":"",icon:["fas","fa-wrench"]})]),_:1},8,["class","disabled"])]),_:1}),a(V,{class:"button-group _margin-top:1",disabled:o.changesLocked},{default:i(()=>[(d(!0),I(E,null,z(l.filteredChargeModes,q=>(d(),u(C,{key:q.id,outline:"",class:b(["large-button _flex-grow:1",o.changesLocked?"":"clickable"]),color:q.class!="dark"?q.class:"light",active:t.mqttStore.getChargePointConnectedVehicleChargeMode(o.chargePointId)!=null&&q.id==t.mqttStore.getChargePointConnectedVehicleChargeMode(o.chargePointId).mode,onClick:S=>l.setChargePointConnectedVehicleChargeMode(o.chargePointId,q.id)},{default:i(()=>[c(p(q.label),1)]),_:2},1032,["class","color","active","onClick"]))),128))]),_:1},8,["disabled"])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-cb556d40"]]),De={name:"ExtendedNumberInput",inheritAttrs:!1,props:{modelValue:{type:Number,required:!0,default:NaN},unit:{type:String,default:""},min:{type:Number,default:0},max:{type:Number,default:100},step:{type:Number,default:1},labels:{type:Array,default:void 0}},emits:["update:modelValue"],data(){return{minimum:this.labels?0:this.min,maximum:this.labels?this.labels.length-1:this.max,stepSize:this.labels?1:this.step}},computed:{label(){var n;return this.labels&&this.inputValue!=null?this.inputValuel.label=v),plaintext:"",class:"_text-align:right",size:"lg"},{prepend:i(()=>[a(f,{onClick:l.decrement},{default:i(()=>e[1]||(e[1]=[c(" - ",-1)])),_:1,__:[1]},8,["onClick"])]),suffix:i(()=>[c(p(o.unit),1)]),append:i(()=>[a(f,{onClick:l.increment},{default:i(()=>e[2]||(e[2]=[c(" + ",-1)])),_:1,__:[2]},8,["onClick"])]),_:1},8,["modelValue"])}]]),We=M({name:"ManualSocInput",components:{ExtendedNumberInput:ae,NumberPad:he},props:{modelValue:{required:!0,type:Boolean,default:!1},vehicleId:{required:!0,type:Number,default:0}},emits:["update:modelValue"],data:()=>({mqttStore:x(),newSoc:0}),methods:{enter(n){let e=10*this.newSoc+parseInt(n);e>=0&&e<=100&&(this.newSoc=e)},removeDigit(){this.newSoc=Math.trunc(this.newSoc/10)},clear(){this.newSoc=0},close(){this.$emit("update:modelValue",!1),this.newSoc=0},updateManualSoc(){this.$root.sendTopicToBroker(`openWB/vehicle/${this.vehicleId}/soc_module/calculated_soc_state/manual_soc`,this.newSoc),this.close()}}},[["render",function(n,e,o,g,t,l){const f=h("extended-number-input"),k=h("i-column"),v=h("i-row"),s=h("NumberPad"),_=h("i-container"),m=h("i-button"),C=h("i-modal");return d(),u(Ie,{to:"body"},[a(C,{"model-value":o.modelValue,size:"sm","onUpdate:modelValue":e[6]||(e[6]=V=>n.$emit("update:modelValue",V))},{header:i(()=>[c(' SoC für Fahrzeug "'+p(t.mqttStore.getVehicleName(o.vehicleId))+'" ',1)]),footer:i(()=>[a(_,null,{default:i(()=>[a(v,null,{default:i(()=>[a(k,null,{default:i(()=>[a(m,{color:"danger",onClick:e[4]||(e[4]=V=>l.close())},{default:i(()=>e[7]||(e[7]=[c(" Zurück ",-1)])),_:1,__:[7]})]),_:1}),a(k,{class:"_text-align:right"},{default:i(()=>[a(m,{color:"success",onClick:e[5]||(e[5]=V=>l.updateManualSoc())},{default:i(()=>e[8]||(e[8]=[c(" OK ",-1)])),_:1,__:[8]})]),_:1})]),_:1})]),_:1})]),default:i(()=>[a(_,null,{default:i(()=>[a(v,{center:"",class:"_padding-bottom:1"},{default:i(()=>[a(k,null,{default:i(()=>[a(f,{modelValue:t.newSoc,"onUpdate:modelValue":e[0]||(e[0]=V=>t.newSoc=V),unit:"%",min:0,max:100,step:1,size:"lg",class:"_text-align:center"},null,8,["modelValue"])]),_:1})]),_:1}),a(s,{"onKey:digit":e[1]||(e[1]=V=>l.enter(V)),"onKey:clear":e[2]||(e[2]=V=>l.clear()),"onKey:delete":e[3]||(e[3]=V=>l.removeDigit())})]),_:1})]),_:1},8,["model-value"])])}]]),Re=M({name:"VehicleSelectModal",props:{modelValue:{required:!0,type:Boolean,default:!1},chargePointId:{type:Number,required:!0}},emits:["update:modelValue"],data:()=>({mqttStore:x()}),computed:{vehicleList(){let n=this.mqttStore.getVehicleList;var e=[];return Object.keys(n).forEach(o=>{let g=parseInt(o.match(/(?:\/)([0-9]+)(?=\/)*/g)[0].replace(/[^0-9]+/g,""));e.push({id:g,name:n[o]})}),e}},methods:{setChargePointConnectedVehicle(n){n.id!=this.mqttStore.getChargePointConnectedVehicleId(this.chargePointId)&&this.$root.sendTopicToBroker(`openWB/chargepoint/${this.chargePointId}/config/ev`,n.id)}}},[["render",function(n,e,o,g,t,l){const f=h("i-button"),k=h("i-button-group"),v=h("i-form-group"),s=h("i-form"),_=h("i-modal");return d(),u(_,{"model-value":o.modelValue,class:"modal-vehicle-select",size:"lg","onUpdate:modelValue":e[0]||(e[0]=m=>n.$emit("update:modelValue",m))},{header:i(()=>[c(' Fahrzeug an "'+p(t.mqttStore.getChargePointName(o.chargePointId))+'" auswählen ',1)]),default:i(()=>[a(s,null,{default:i(()=>[a(v,null,{default:i(()=>[a(k,{vertical:"",block:""},{default:i(()=>[(d(!0),I(E,null,z(l.vehicleList,m=>(d(),u(f,{key:m.id,size:"lg",class:"large-button",active:t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId)==m.id,color:t.mqttStore.getChargePointConnectedVehicleId(o.chargePointId)==m.id?"primary":"",onClick:C=>l.setChargePointConnectedVehicle(m)},{default:i(()=>[c(p(m.name),1)]),_:2},1032,["active","color","onClick"]))),128))]),_:1})]),_:1})]),_:1})]),_:1},8,["model-value"])}],["__scopeId","data-v-b89baba5"]]);ke.register(be,qe,ye,Le,Me,xe,we,Te);const Oe={class:"chartContainer"},je=M({name:"ElectricityTariffChart",components:{ChartJsLine:ve},props:{modelValue:{type:Number,required:!1,default:void 0}},emits:["update:modelValue"],data:()=>({mqttStore:x(),chartDatasets:{datasets:[{label:"Stromtarif",unit:"ct/kWh",type:"line",stepped:!0,borderColor:"rgb(18, 111, 142)",backgroundColor:"rgb(18, 111, 142)",fill:!1,pointStyle:"circle",pointRadius:0,pointHoverRadius:4,cubicInterpolationMode:"monotone",hidden:!1,borderWidth:2,data:void 0,yAxisID:"y",parsing:{xAxisKey:"timestamp",yAxisKey:"price"}}]}}),computed:{chartDataRead(){return Object.keys(this.chartDataObject.datasets[0].data).length>0},chartDataObject(){let n=[];const e=this.mqttStore.getEtPrices;if(e&&Object.keys(e).length>0){for(const[t,l]of Object.entries(e))n.push({timestamp:1e3*t,price:1e5*l});const g=n.slice(-1)[0];n.push({timestamp:g.timestamp+3599e3,price:g.price})}const o=this.chartDatasets;return o.datasets[0].data=n,o},priceAnnotations(){const n="rgba(73, 238, 73, 0.2)",e="rgba(255, 10, 13, 0.2)",o=this.chartDataObject.datasets[0].data;class g{constructor(){w(this,"type","box");w(this,"drawTime","beforeDatasetsDraw");w(this,"xMin",o[0].timestamp);w(this,"xMax",o[0].timestamp);w(this,"borderWidth",2);w(this,"cornerRadius",0)}}let t=[];if(this.modelValue!==void 0){for(let l=0;lthis.modelValue){let f=new g;for(f.borderColor=e,f.backgroundColor=e,f.xMin=o[l].timestamp;lthis.modelValue;)l++;l==o.length&&l--,f.xMax=o[l].timestamp,t.push(f)}}return t},myChartOptions(){return{plugins:{title:{display:!1},legend:{display:!1},annotation:{annotations:this.priceAnnotations}},elements:{point:{radius:2}},responsive:!0,maintainAspectRatio:!1,interaction:{mode:"index",intersect:!1},scales:{x:{type:"time",time:{unit:"hour",text:"Zeit",maxTicksLimit:24},display:!0,title:{display:!0,text:"Uhrzeit",color:"#ffffff"},ticks:{font:{size:12},color:"#ffffff",maxTicksLimit:0},grid:{}},y:{position:"left",type:"linear",display:"auto",title:{font:{size:12},display:!0,text:"Preis [ct/kWh]",color:"#ffffff"},grid:{color:"#ffffff20"},ticks:{font:{size:12},stepSize:.1,maxTicksLimit:11,color:"#ffffff"}}}}}},methods:{chartClick(n){const e=this.$refs.priceChart.chart.getElementsAtEventForMode(n,"index",{intersect:!1},!0);e.length>0&&this.$emit("update:modelValue",Math.ceil(100*this.chartDataObject.datasets[0].data[e[0].index].price)/100)}}},[["render",function(n,e,o,g,t,l){const f=h("ChartJsLine");return d(),I("div",Oe,[l.chartDataRead?(d(),u(f,{key:0,ref:"priceChart",data:l.chartDataObject,options:l.myChartOptions,onClick:l.chartClick},null,8,["data","options","onClick"])):P("",!0)])}],["__scopeId","data-v-30ed2f35"]]);F.add(ue,Pe,pe,fe,_e,Ve,Se);const Ze={name:"ChargePointsView",components:{ChargePointCard:Fe,SimpleChargePointCard:Ue,ExtendedNumberInput:ae,ManualSocInput:We,ChargeModeModal:Ae,VehicleSelectModal:Re,FontAwesomeIcon:B,ElectricityTariffChart:je},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:x(),modalChargeModeSettingVisible:!1,modalVehicleSelectVisible:!1,modalChargePointSettingsVisible:!1,modalChargePointId:0,modalVehicleId:0,modalActiveTab:"tab-general",modalManualSocInputVisible:!1}),computed:{timeChargingEnabled(){return n=>this.mqttStore.getChargePointConnectedVehicleTimeChargingActive(n)===!0}},watch:{changesLocked(n,e){e!==!0&&n===!0&&(this.modalChargeModeSettingVisible=!1,this.modalVehicleSelectVisible=!1,this.modalChargePointSettingsVisible=!1,this.modalManualSocInputVisible=!1)}},methods:{toggleChargePointSettings(n){switch(this.mqttStore.getChargePointConnectedVehicleChargeMode(n).mode){case"pv_charging":this.modalActiveTab="tab-pv-charging";break;case"scheduled_charging":this.modalActiveTab="tab-scheduled-charging";break;case"eco_charging":this.modalActiveTab="tab-eco-charging";break;default:this.modalActiveTab="tab-instant-charging"}this.modalChargePointId=n,this.modalChargePointSettingsVisible=!0},handleChargeModeClick(n){this.changesLocked||(this.modalChargePointId=n,this.modalChargeModeSettingVisible=!0)},handleVehicleClick(n){this.changesLocked||(this.modalChargePointId=n,this.modalVehicleSelectVisible=!0)},handleSocClick(n){let e=this.mqttStore.getChargePointConnectedVehicleId(n);if(this.mqttStore.getVehicleSocIsManual(e))return this.modalVehicleId=e,void(this.modalManualSocInputVisible=!0);this.$root.sendTopicToBroker(`openWB/set/vehicle/${e}/get/force_soc_update`,1)},updateChargePointChargeTemplate(n,e,o=void 0){const g=this.mqttStore.updateState(`openWB/chargepoint/${n}/set/charge_template`,e,o);this.$root.sendTopicToBroker(`openWB/chargepoint/${n}/set/charge_template`,g)},setChargePointConnectedVehicleChargeMode(n,e){e.id!=this.mqttStore.getChargePointConnectedVehicleChargeMode(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.selected")},setChargePointConnectedVehiclePriority(n,e){e!=this.mqttStore.getChargePointConnectedVehiclePriority(n)&&this.updateChargePointChargeTemplate(n,e,"prio")},setChargePointConnectedVehicleTimeChargingActive(n,e){e!=this.mqttStore.getChargePointConnectedVehicleTimeChargingActive(n)&&this.updateChargePointChargeTemplate(n,e,"time_charging.active")},setChargePointConnectedVehicleInstantChargingCurrent(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingCurrent(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.instant_charging.current")},setChargePointConnectedVehicleInstantChargingPhases(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingPhases(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.instant_charging.phases_to_use")},setChargePointConnectedVehicleInstantChargingLimit(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(n).selected&&this.updateChargePointChargeTemplate(n,e,"chargemode.instant_charging.limit.selected")},setChargePointConnectedVehicleInstantChargingLimitSoc(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(n).soc&&this.updateChargePointChargeTemplate(n,parseInt(e),"chargemode.instant_charging.limit.soc")},setChargePointConnectedVehicleInstantChargingLimitAmount(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(n).amount&&this.updateChargePointChargeTemplate(n,e,"chargemode.instant_charging.limit.amount")},setChargePointConnectedVehiclePvChargingFeedInLimit(n,e){e!=this.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.pv_charging.feed_in_limit")},setChargePointConnectedVehiclePvChargingMinCurrent(n,e){let o=this.mqttStore.getChargePointConnectedVehiclePvChargingMinCurrent(n),g=parseInt(e);g==o||isNaN(g)||this.updateChargePointChargeTemplate(n,g,"chargemode.pv_charging.min_current")},setChargePointConnectedVehiclePvChargingPhases(n,e){e!==void 0&&e!=this.mqttStore.getChargePointConnectedVehiclePvChargingPhases(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.pv_charging.phases_to_use")},setChargePointConnectedVehiclePvChargingLimit(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehiclePvChargingLimit(n).selected&&this.updateChargePointChargeTemplate(n,e,"chargemode.pv_charging.limit.selected")},setChargePointConnectedVehiclePvChargingLimitSoc(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehiclePvChargingLimit(n).soc&&this.updateChargePointChargeTemplate(n,parseInt(e),"chargemode.pv_charging.limit.soc")},setChargePointConnectedVehiclePvChargingLimitAmount(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehiclePvChargingLimit(n).amount&&this.updateChargePointChargeTemplate(n,e,"chargemode.pv_charging.limit.amount")},setChargePointConnectedVehiclePvChargingMinSoc(n,e){let o=this.mqttStore.getChargePointConnectedVehiclePvChargingMinSoc(n),g=parseInt(e);g==o||isNaN(g)||this.updateChargePointChargeTemplate(n,g,"chargemode.pv_charging.min_soc")},setChargePointConnectedVehiclePvChargingMinSocCurrent(n,e){let o=this.mqttStore.getChargePointConnectedVehiclePvChargingMinSocCurrent(n),g=parseInt(e);g==o||isNaN(g)||this.updateChargePointChargeTemplate(n,g,"chargemode.pv_charging.min_soc_current")},setChargePointConnectedVehiclePvChargingMinSocPhases(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehiclePvChargingMinSocPhases(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.pv_charging.phases_to_use_min_soc")},setChargePointConnectedVehicleEcoChargingCurrent(n,e){let o=this.mqttStore.getChargePointConnectedVehicleEcoChargingCurrent(n),g=parseInt(e);g==o||isNaN(g)||this.updateChargePointChargeTemplate(n,g,"chargemode.eco_charging.current")},setChargePointConnectedVehicleEcoChargingPhases(n,e){e!==void 0&&e!=this.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(n)&&this.updateChargePointChargeTemplate(n,e,"chargemode.eco_charging.phases_to_use")},setChargePointConnectedVehicleEcoChargingLimit(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(n).selected&&this.updateChargePointChargeTemplate(n,e,"chargemode.eco_charging.limit.selected")},setChargePointConnectedVehicleEcoChargingLimitSoc(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(n).soc&&this.updateChargePointChargeTemplate(n,parseInt(e),"chargemode.eco_charging.limit.soc")},setChargePointConnectedVehicleEcoChargingLimitAmount(n,e){e&&e!=this.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(n).amount&&this.updateChargePointChargeTemplate(n,e,"chargemode.eco_charging.limit.amount")},setChargePointConnectedVehicleEcoChargingMaxPrice(n,e){let o=this.mqttStore.getChargePointConnectedVehicleEcoChargingMaxPrice(n),g=parseFloat(e);g==o||isNaN(g)||this.updateChargePointChargeTemplate(n,parseFloat((g/1e5).toFixed(7)),"chargemode.eco_charging.max_price")},setChargePointConnectedVehicleScheduledChargingPlanActive(n,e,o){const g=`openWB/chargepoint/${n}/set/charge_template`,t=this.mqttStore.updateState(g,o,`chargemode.scheduled_charging.plans.${e}.active`);this.$root.sendTopicToBroker(g,t)},setChargePointConnectedVehicleTimeChargingPlanActive(n,e,o){const g=`openWB/chargepoint/${n}/set/charge_template`,t=this.mqttStore.updateState(g,o,`time_charging.plans.${e}.active`);this.$root.sendTopicToBroker(g,t)}}},Ke={class:"charge-points-card-wrapper"},Je={class:"plan-name"},He={class:"plan-details"},Qe={key:0},Xe={key:1},Ye={key:2},Ge={key:3},et={key:4},tt={key:5},at={key:1},it={class:"plan-name"},nt={class:"plan-details"},ot={key:0},lt={key:1},rt={key:2},ct={key:3},dt={key:4},ft=M(Ze,[["render",function(n,e,o,g,t,l){const f=h("charge-mode-modal"),k=h("vehicle-select-modal"),v=h("i-tab-title"),s=h("i-form-label"),_=h("extended-number-input"),m=h("i-form-group"),C=h("i-button"),V=h("i-button-group"),y=h("i-form"),L=h("i-tab"),q=h("ElectricityTariffChart"),S=h("font-awesome-icon"),A=h("i-alert"),$=h("i-row"),U=h("i-container"),ie=h("i-tabs"),ne=h("i-modal"),oe=h("manual-soc-input");return d(),I(E,null,[T("div",Ke,[(d(!0),I(E,null,z(t.mqttStore.getChargePointIds,r=>(d(),u(Ee(t.mqttStore.getSimpleChargePointView?"SimpleChargePointCard":"ChargePointCard"),{key:r,"charge-point-id":r,"changes-locked":o.changesLocked,onVehicleClick:l.handleVehicleClick,onSocClick:l.handleSocClick,onChargeModeClick:l.handleChargeModeClick,onToggleChargePointSettings:l.toggleChargePointSettings,onSetChargePointConnectedVehicleChargeMode:l.setChargePointConnectedVehicleChargeMode},null,40,["charge-point-id","changes-locked","onVehicleClick","onSocClick","onChargeModeClick","onToggleChargePointSettings","onSetChargePointConnectedVehicleChargeMode"]))),128))]),a(f,{modelValue:t.modalChargeModeSettingVisible,"onUpdate:modelValue":e[0]||(e[0]=r=>t.modalChargeModeSettingVisible=r),"charge-point-id":t.modalChargePointId},null,8,["modelValue","charge-point-id"]),a(k,{modelValue:t.modalVehicleSelectVisible,"onUpdate:modelValue":e[1]||(e[1]=r=>t.modalVehicleSelectVisible=r),"charge-point-id":t.modalChargePointId},null,8,["modelValue","charge-point-id"]),a(ne,{modelValue:t.modalChargePointSettingsVisible,"onUpdate:modelValue":e[39]||(e[39]=r=>t.modalChargePointSettingsVisible=r),size:"lg"},{header:i(()=>[c(' Einstellungen für Fahrzeug "'+p(t.mqttStore.getChargePointConnectedVehicleName(t.modalChargePointId))+'" ',1)]),default:i(()=>[a(ie,{modelValue:t.modalActiveTab,"onUpdate:modelValue":e[38]||(e[38]=r=>t.modalActiveTab=r),stretch:""},{header:i(()=>[a(v,{for:"tab-instant-charging"},{default:i(()=>e[41]||(e[41]=[c(" Sofort ",-1)])),_:1,__:[41]}),a(v,{for:"tab-pv-charging"},{default:i(()=>e[42]||(e[42]=[c(" PV ",-1)])),_:1,__:[42]}),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(v,{key:0,for:"tab-eco-charging"},{default:i(()=>e[43]||(e[43]=[c(" Eco ",-1)])),_:1,__:[43]})),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(v,{key:1,for:"tab-scheduled-charging"},{default:i(()=>e[44]||(e[44]=[c(" Ziel ",-1)])),_:1,__:[44]})),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(v,{key:2,for:"tab-time-charging"},{default:i(()=>e[45]||(e[45]=[c(" Zeit ",-1)])),_:1,__:[45]}))]),default:i(()=>[a(L,{name:"tab-instant-charging"},{default:i(()=>[a(y,null,{default:i(()=>[a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[46]||(e[46]=[c("Stromstärke",-1)])),_:1,__:[46]}),a(_,{unit:"A",min:6,max:32,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingCurrent(t.modalChargePointId),"onUpdate:modelValue":e[2]||(e[2]=r=>l.setChargePointConnectedVehicleInstantChargingCurrent(t.modalChargePointId,r))},null,8,["model-value"])]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[47]||(e[47]=[c("Anzahl Phasen",-1)])),_:1,__:[47]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId)==1?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId)==1,onClick:e[3]||(e[3]=r=>l.setChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId,1))},{default:i(()=>e[48]||(e[48]=[c(" 1 ",-1)])),_:1,__:[48]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId)==3?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId)==3,onClick:e[4]||(e[4]=r=>l.setChargePointConnectedVehicleInstantChargingPhases(t.modalChargePointId,3))},{default:i(()=>e[49]||(e[49]=[c(" Maximum ",-1)])),_:1,__:[49]},8,["color","active"])]),_:1})]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[50]||(e[50]=[c("Begrenzung",-1)])),_:1,__:[50]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="none"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="none",onClick:e[5]||(e[5]=r=>l.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"none"))},{default:i(()=>e[51]||(e[51]=[c(" Aus ",-1)])),_:1,__:[51]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc",onClick:e[6]||(e[6]=r=>l.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"soc"))},{default:i(()=>e[52]||(e[52]=[c(" EV-SoC ",-1)])),_:1,__:[52]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount",onClick:e[7]||(e[7]=r=>l.setChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId,"amount"))},{default:i(()=>e[53]||(e[53]=[c(" Energie ",-1)])),_:1,__:[53]},8,["color","active"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="soc"?(d(),u(m,{key:0},{default:i(()=>[a(s,null,{default:i(()=>e[54]||(e[54]=[c("Max. SoC",-1)])),_:1,__:[54]}),a(_,{unit:"%",min:5,max:100,step:5,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).soc,"onUpdate:modelValue":e[8]||(e[8]=r=>l.setChargePointConnectedVehicleInstantChargingLimitSoc(t.modalChargePointId,r))},null,8,["model-value"])]),_:1})):P("",!0),t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).selected=="amount"?(d(),u(m,{key:1},{default:i(()=>[a(s,null,{default:i(()=>e[55]||(e[55]=[c("Max. Energie",-1)])),_:1,__:[55]}),a(_,{unit:"kWh",min:1,max:100,"model-value":t.mqttStore.getChargePointConnectedVehicleInstantChargingLimit(t.modalChargePointId).amount/1e3,"onUpdate:modelValue":e[9]||(e[9]=r=>l.setChargePointConnectedVehicleInstantChargingLimitAmount(t.modalChargePointId,1e3*r))},null,8,["model-value"])]),_:1})):P("",!0)]),_:1})]),_:1}),a(L,{name:"tab-pv-charging"},{default:i(()=>[a(y,null,{default:i(()=>[a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[56]||(e[56]=[c("Minimaler Dauerstrom",-1)])),_:1,__:[56]}),a(_,{unit:"A",labels:[{label:"Aus",value:0},{label:6,value:6},{label:7,value:7},{label:8,value:8},{label:9,value:9},{label:10,value:10},{label:11,value:11},{label:12,value:12},{label:13,value:13},{label:14,value:14},{label:15,value:15},{label:16,value:16}],"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinCurrent(t.modalChargePointId),"onUpdate:modelValue":e[10]||(e[10]=r=>l.setChargePointConnectedVehiclePvChargingMinCurrent(t.modalChargePointId,r))},null,8,["model-value"])]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[57]||(e[57]=[c("Anzahl Phasen",-1)])),_:1,__:[57]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==1?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==1,onClick:e[11]||(e[11]=r=>l.setChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId,1))},{default:i(()=>e[58]||(e[58]=[c(" 1 ",-1)])),_:1,__:[58]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==3?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==3,onClick:e[12]||(e[12]=r=>l.setChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId,3))},{default:i(()=>e[59]||(e[59]=[c(" Maximum ",-1)])),_:1,__:[59]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==0?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId)==0,onClick:e[13]||(e[13]=r=>l.setChargePointConnectedVehiclePvChargingPhases(t.modalChargePointId,0))},{default:i(()=>e[60]||(e[60]=[c(" Automatik ",-1)])),_:1,__:[60]},8,["color","active"])]),_:1})]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[61]||(e[61]=[c("Begrenzung",-1)])),_:1,__:[61]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="none"?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="none",onClick:e[14]||(e[14]=r=>l.setChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId,"none"))},{default:i(()=>e[62]||(e[62]=[c(" Aus ",-1)])),_:1,__:[62]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="soc"?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="soc",onClick:e[15]||(e[15]=r=>l.setChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId,"soc"))},{default:i(()=>e[63]||(e[63]=[c(" EV-SoC ",-1)])),_:1,__:[63]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="amount"?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="amount",onClick:e[16]||(e[16]=r=>l.setChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId,"amount"))},{default:i(()=>e[64]||(e[64]=[c(" Energie ",-1)])),_:1,__:[64]},8,["color","active"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="soc"?(d(),u(m,{key:0},{default:i(()=>[a(s,null,{default:i(()=>e[65]||(e[65]=[c("SoC-Limit für das Fahrzeug",-1)])),_:1,__:[65]}),a(_,{unit:"%",min:5,max:100,step:5,"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).soc,"onUpdate:modelValue":e[17]||(e[17]=r=>l.setChargePointConnectedVehiclePvChargingLimitSoc(t.modalChargePointId,r))},null,8,["model-value"])]),_:1})):P("",!0),t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).selected=="amount"?(d(),u(m,{key:1},{default:i(()=>[a(s,null,{default:i(()=>e[66]||(e[66]=[c("Energie-Limit",-1)])),_:1,__:[66]}),a(_,{unit:"kWh",min:1,max:100,"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingLimit(t.modalChargePointId).amount/1e3,"onUpdate:modelValue":e[18]||(e[18]=r=>l.setChargePointConnectedVehiclePvChargingLimitAmount(t.modalChargePointId,1e3*r))},null,8,["model-value"])]),_:1})):P("",!0),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[67]||(e[67]=[c("Mindest-SoC für das Fahrzeug",-1)])),_:1,__:[67]}),a(_,{unit:"%",labels:[{label:"Aus",value:0},{label:5,value:5},{label:10,value:10},{label:15,value:15},{label:20,value:20},{label:25,value:25},{label:30,value:30},{label:35,value:35},{label:40,value:40},{label:45,value:45},{label:50,value:50},{label:55,value:55},{label:60,value:60},{label:65,value:65},{label:70,value:70},{label:75,value:75},{label:80,value:80},{label:85,value:85},{label:90,value:90},{label:95,value:95}],"model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinSoc(t.modalChargePointId),"onUpdate:modelValue":e[19]||(e[19]=r=>l.setChargePointConnectedVehiclePvChargingMinSoc(t.modalChargePointId,r))},null,8,["model-value"])]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[68]||(e[68]=[c("Mindest-SoC Strom",-1)])),_:1,__:[68]}),a(_,{min:6,max:32,unit:"A","model-value":t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocCurrent(t.modalChargePointId),"onUpdate:modelValue":e[20]||(e[20]=r=>l.setChargePointConnectedVehiclePvChargingMinSocCurrent(t.modalChargePointId,r))},null,8,["model-value"])]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[69]||(e[69]=[c("Anzahl Phasen Mindest-SoC",-1)])),_:1,__:[69]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId)==1?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId)==1,onClick:e[21]||(e[21]=r=>l.setChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId,1))},{default:i(()=>e[70]||(e[70]=[c(" 1 ",-1)])),_:1,__:[70]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId)==3?"primary":"",active:t.mqttStore.getChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId)==3,onClick:e[22]||(e[22]=r=>l.setChargePointConnectedVehiclePvChargingMinSocPhases(t.modalChargePointId,3))},{default:i(()=>e[71]||(e[71]=[c(" Maximum ",-1)])),_:1,__:[71]},8,["color","active"])]),_:1})]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[72]||(e[72]=[c("Einspeisegrenze beachten",-1)])),_:1,__:[72]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId)!==!0?"danger":"",onClick:e[23]||(e[23]=r=>l.setChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId,!1))},{default:i(()=>e[73]||(e[73]=[c(" Nein ",-1)])),_:1,__:[73]},8,["color"]),a(C,{color:t.mqttStore.getChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId)===!0?"success":"",onClick:e[24]||(e[24]=r=>l.setChargePointConnectedVehiclePvChargingFeedInLimit(t.modalChargePointId,!0))},{default:i(()=>e[74]||(e[74]=[c(" Ja ",-1)])),_:1,__:[74]},8,["color"])]),_:1})]),_:1})]),_:1})]),_:1}),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(L,{key:0,name:"tab-eco-charging"},{default:i(()=>[a(y,null,{default:i(()=>[t.mqttStore.getEtConfigured?(d(),u(m,{key:0},{default:i(()=>[a(s,null,{default:i(()=>e[75]||(e[75]=[c("Minimaler Dauerstrom unter Preisgrenze",-1)])),_:1,__:[75]}),a(_,{unit:"A",min:6,max:32,"model-value":t.mqttStore.getChargePointConnectedVehicleEcoChargingCurrent(t.modalChargePointId),"onUpdate:modelValue":e[25]||(e[25]=r=>l.setChargePointConnectedVehicleEcoChargingCurrent(t.modalChargePointId,r))},null,8,["model-value"])]),_:1})):P("",!0),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[76]||(e[76]=[c("Anzahl Phasen",-1)])),_:1,__:[76]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==1?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==1,onClick:e[26]||(e[26]=r=>l.setChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId,1))},{default:i(()=>e[77]||(e[77]=[c(" 1 ",-1)])),_:1,__:[77]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==3?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==3,onClick:e[27]||(e[27]=r=>l.setChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId,3))},{default:i(()=>e[78]||(e[78]=[c(" Maximum ",-1)])),_:1,__:[78]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==0?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId)==0,onClick:e[28]||(e[28]=r=>l.setChargePointConnectedVehicleEcoChargingPhases(t.modalChargePointId,0))},{default:i(()=>e[79]||(e[79]=[c(" Automatik ",-1)])),_:1,__:[79]},8,["color","active"])]),_:1})]),_:1}),a(m,null,{default:i(()=>[a(s,null,{default:i(()=>e[80]||(e[80]=[c("Begrenzung",-1)])),_:1,__:[80]}),a(V,{block:""},{default:i(()=>[a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="none"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="none",onClick:e[29]||(e[29]=r=>l.setChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId,"none"))},{default:i(()=>e[81]||(e[81]=[c(" Aus ",-1)])),_:1,__:[81]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="soc"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="soc",onClick:e[30]||(e[30]=r=>l.setChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId,"soc"))},{default:i(()=>e[82]||(e[82]=[c(" EV-SoC ",-1)])),_:1,__:[82]},8,["color","active"]),a(C,{color:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="amount"?"primary":"",active:t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="amount",onClick:e[31]||(e[31]=r=>l.setChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId,"amount"))},{default:i(()=>e[83]||(e[83]=[c(" Energie ",-1)])),_:1,__:[83]},8,["color","active"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="soc"?(d(),u(m,{key:1},{default:i(()=>[a(s,null,{default:i(()=>e[84]||(e[84]=[c("SoC-Limit für das Fahrzeug",-1)])),_:1,__:[84]}),a(_,{unit:"%",min:5,max:100,step:5,"model-value":t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).soc,"onUpdate:modelValue":e[32]||(e[32]=r=>l.setChargePointConnectedVehicleEcoChargingLimitSoc(t.modalChargePointId,r))},null,8,["model-value"])]),_:1})):P("",!0),t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).selected=="amount"?(d(),u(m,{key:2},{default:i(()=>[a(s,null,{default:i(()=>e[85]||(e[85]=[c("Energie-Limit",-1)])),_:1,__:[85]}),a(_,{unit:"kWh",min:1,max:100,"model-value":t.mqttStore.getChargePointConnectedVehicleEcoChargingLimit(t.modalChargePointId).amount/1e3,"onUpdate:modelValue":e[33]||(e[33]=r=>l.setChargePointConnectedVehicleEcoChargingLimitAmount(t.modalChargePointId,1e3*r))},null,8,["model-value"])]),_:1})):P("",!0),t.mqttStore.getEtConfigured?(d(),u(m,{key:3},{default:i(()=>[a(s,null,{default:i(()=>e[86]||(e[86]=[c("Preisgrenze für strompreisbasiertes Laden",-1)])),_:1,__:[86]}),a(_,{unit:"ct/kWh",min:-80,max:80,step:.01,precision:2,"model-value":t.mqttStore.getChargePointConnectedVehicleEcoChargingMaxPrice(t.modalChargePointId),"onUpdate:modelValue":e[34]||(e[34]=r=>l.setChargePointConnectedVehicleEcoChargingMaxPrice(t.modalChargePointId,r))},null,8,["model-value"]),a(q,{"model-value":t.mqttStore.getChargePointConnectedVehicleEcoChargingMaxPrice(t.modalChargePointId),"onUpdate:modelValue":e[35]||(e[35]=r=>l.setChargePointConnectedVehicleEcoChargingMaxPrice(t.modalChargePointId,r))},null,8,["model-value"])]),_:1})):P("",!0)]),_:1})]),_:1})),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(L,{key:1,name:"tab-scheduled-charging"},{default:i(()=>[t.mqttStore.getChargePointConnectedVehicleScheduledChargingPlans(t.modalChargePointId)&&t.mqttStore.getChargePointConnectedVehicleScheduledChargingPlans(t.modalChargePointId).length!==0?(d(),u(y,{key:1},{default:i(()=>[(d(!0),I(E,null,z(t.mqttStore.getChargePointConnectedVehicleScheduledChargingPlans(t.modalChargePointId),(r,N)=>(d(),u(m,{key:N},{default:i(()=>[a(U,null,{default:i(()=>[a($,null,{default:i(()=>[a(C,{size:"lg",block:"",color:r.active?"success":"danger",onClick:le=>l.setChargePointConnectedVehicleScheduledChargingPlanActive(t.modalChargePointId,r.id,!r.active)},{default:i(()=>[T("div",Je,p(r.name),1),T("div",He,[r.frequency.selected=="once"?(d(),I("div",Qe,[a(S,{icon:["fas","calendar-day"]}),c(" "+p(t.mqttStore.formatDate(r.frequency.once)),1)])):P("",!0),r.frequency.selected=="daily"?(d(),I("div",Xe,[a(S,{icon:["fas","calendar-week"]}),e[88]||(e[88]=c(" täglich ",-1))])):P("",!0),r.frequency.selected=="weekly"?(d(),I("div",Ye,[a(S,{icon:["fas","calendar-alt"]}),c(" "+p(t.mqttStore.formatWeeklyScheduleDays(r.frequency.weekly)),1)])):P("",!0),T("div",null,[a(S,{icon:["fas","clock"]}),c(" "+p(r.time),1)]),r.limit.selected=="soc"?(d(),I("div",Ge,[a(S,{icon:["fas","car-battery"]}),c(" "+p(r.limit.soc_scheduled)+" % ",1),a(S,{icon:["fas",r.bidi_charging_enabled?"right-left":"right-long"]},null,8,["icon"]),c(" "+p(r.limit.soc_limit)+" % ",1)])):P("",!0),r.limit.selected=="amount"?(d(),I("div",et,[a(S,{icon:["fas","bolt"]}),c(" "+p(r.limit.amount/1e3)+" kWh ",1)])):P("",!0),r.et_active?(d(),I("div",tt,[a(S,{icon:["fas","coins"]})])):P("",!0)])]),_:2},1032,["color","onClick"])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))]),_:1})):(d(),u(A,{key:0,color:"warning"},{icon:i(()=>[a(S,{"fixed-width":"",icon:["fas","fa-info-circle"]})]),default:i(()=>[e[87]||(e[87]=c(" Es wurden noch keine Zeitpläne für das Zielladen eingerichtet. ",-1))]),_:1,__:[87]}))]),_:1})),t.mqttStore.getSimpleChargePointView?P("",!0):(d(),u(L,{key:2,name:"tab-time-charging"},{default:i(()=>[a(y,null,{default:i(()=>[a(m,{class:"_margin-bottom:2"},{default:i(()=>[a(s,null,{default:i(()=>e[89]||(e[89]=[c("Zeitladen aktivieren",-1)])),_:1,__:[89]}),a(V,{block:""},{default:i(()=>[a(C,{color:l.timeChargingEnabled(t.modalChargePointId)?"":"danger",onClick:e[36]||(e[36]=r=>l.setChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId,!1))},{default:i(()=>e[90]||(e[90]=[c(" Nein ",-1)])),_:1,__:[90]},8,["color"]),a(C,{color:l.timeChargingEnabled(t.modalChargePointId)?"success":"",onClick:e[37]||(e[37]=r=>l.setChargePointConnectedVehicleTimeChargingActive(t.modalChargePointId,!0))},{default:i(()=>e[91]||(e[91]=[c(" Ja ",-1)])),_:1,__:[91]},8,["color"])]),_:1})]),_:1}),t.mqttStore.getChargePointConnectedVehicleTimeChargingPlans(t.modalChargePointId)&&t.mqttStore.getChargePointConnectedVehicleTimeChargingPlans(t.modalChargePointId).length!==0?(d(),I("div",at,[(d(!0),I(E,null,z(t.mqttStore.getChargePointConnectedVehicleTimeChargingPlans(t.modalChargePointId),(r,N)=>(d(),u(m,{key:N},{default:i(()=>[a(U,null,{default:i(()=>[a($,null,{default:i(()=>[a(C,{size:"lg",block:"",color:r.active?"success":"danger",onClick:le=>l.setChargePointConnectedVehicleTimeChargingPlanActive(t.modalChargePointId,r.id,!r.active)},{default:i(()=>[T("div",it,p(r.name),1),T("div",nt,[r.frequency.selected=="once"?(d(),I("div",ot,[a(S,{icon:["fas","calendar-day"]}),c(" "+p(t.mqttStore.formatDateRange(r.frequency.once)),1)])):P("",!0),r.frequency.selected=="daily"?(d(),I("div",lt,[a(S,{icon:["fas","calendar-week"]}),e[93]||(e[93]=c(" täglich ",-1))])):P("",!0),r.frequency.selected=="weekly"?(d(),I("div",rt,[a(S,{icon:["fas","calendar-alt"]}),c(" "+p(t.mqttStore.formatWeeklyScheduleDays(r.frequency.weekly)),1)])):P("",!0),T("div",null,[a(S,{icon:["fas","clock"]}),c(" "+p(r.time.join("-")),1)]),r.limit.selected=="soc"?(d(),I("div",ct,[a(S,{icon:["fas","car-battery"]}),c(" "+p(r.limit.soc)+" % ",1)])):P("",!0),r.limit.selected=="amount"?(d(),I("div",dt,[a(S,{icon:["fas","bolt"]}),c(" "+p(r.limit.amount/1e3)+" kWh ",1)])):P("",!0)])]),_:2},1032,["color","onClick"])]),_:2},1024)]),_:2},1024)]),_:2},1024))),128))])):(d(),u(A,{key:0,color:"warning"},{icon:i(()=>[a(S,{"fixed-width":"",icon:["fas","fa-circle-info"]})]),default:i(()=>[e[92]||(e[92]=c(" Es wurden noch keine Zeitpläne für das Zeitladen eingerichtet. ",-1))]),_:1,__:[92]}))]),_:1})]),_:1}))]),_:1},8,["modelValue"])]),_:1},8,["modelValue"]),a(oe,{modelValue:t.modalManualSocInputVisible,"onUpdate:modelValue":e[40]||(e[40]=r=>t.modalManualSocInputVisible=r),"vehicle-id":t.modalVehicleId},null,8,["modelValue","vehicle-id"])],64)}],["__scopeId","data-v-2850e23a"]]);export{ft as default}; diff --git a/packages/modules/display_themes/cards/web/assets/DashBoardCard-Bl80pQ9w.css b/packages/modules/display_themes/cards/web/assets/DashBoardCard-Bl80pQ9w.css deleted file mode 100644 index a81cdaf8f2..0000000000 --- a/packages/modules/display_themes/cards/web/assets/DashBoardCard-Bl80pQ9w.css +++ /dev/null @@ -1 +0,0 @@ -.column[data-v-c53b85bf]{padding-left:0;padding-right:0}.card[data-v-c53b85bf]{----background: inherit !important;----body--color: var(--contrast-color-for-dark-background) !important} diff --git a/packages/modules/display_themes/cards/web/assets/DashBoardCard-DJZ2GsC-.js b/packages/modules/display_themes/cards/web/assets/DashBoardCard-DJZ2GsC-.js deleted file mode 100644 index 5c701928a5..0000000000 --- a/packages/modules/display_themes/cards/web/assets/DashBoardCard-DJZ2GsC-.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as u}from"./vendor-inkline-C_NPDnDu.js";import{l,q as a,p as o,o as d,j as r,s as t,f as _}from"./vendor-BMrK3KHF.js";const $=u({name:"DashBoardCard",props:{color:{type:String,required:!0}}},[["render",function(e,p,i,h,g,m){const s=o("i-column"),n=o("i-row"),c=o("i-container"),f=o("i-card");return d(),l(f,{color:i.color},{header:a(()=>[t(c,null,{default:a(()=>[t(n,null,{default:a(()=>[t(s,null,{default:a(()=>[r(e.$slots,"headerLeft",{},void 0,!0)]),_:3}),e.$slots.headerRight?(d(),l(s,{key:0,class:"_flex-grow:0 _text-align:right _white-space:nowrap"},{default:a(()=>[r(e.$slots,"headerRight",{},void 0,!0)]),_:3})):_("",!0)]),_:3})]),_:3})]),default:a(()=>[r(e.$slots,"default",{},void 0,!0)]),_:3},8,["color"])}],["__scopeId","data-v-c53b85bf"]]);export{$ as D}; diff --git a/packages/modules/display_themes/cards/web/assets/DashBoardView-BvptEyMO.js b/packages/modules/display_themes/cards/web/assets/DashBoardView-BvptEyMO.js deleted file mode 100644 index 964d4e73b9..0000000000 --- a/packages/modules/display_themes/cards/web/assets/DashBoardView-BvptEyMO.js +++ /dev/null @@ -1 +0,0 @@ -import{u as l}from"./index-nI_NVV5B.js";import{D as q}from"./DashBoardCard-DJZ2GsC-.js";import{S as P,C as b}from"./ChargePointPlugBadge-bP9BlN1o.js";import{F as w,l as y,d as I,e as B,g as D,h as L,i as _}from"./vendor-fortawesome-CpQlJZ13.js";import{_ as p}from"./vendor-inkline-C_NPDnDu.js";import{l as c,q as a,p as e,o as s,s as i,k as g,x as h,f as m,e as x}from"./vendor-BMrK3KHF.js";y.add(I);const E=p({name:"GridCard",components:{DashBoardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dash-board-card");return s(),c(n,{color:"danger"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-gauge-high"]}),r[0]||(r[0]=g(" EVU "))]),headerRight:a(()=>[g(h(t.mqttStore.getGridPower()),1)]),default:a(()=>[i(d,{color:"var(--color--danger)","color-negative":"var(--color--success)",data:t.mqttStore.getGridPowerChartData},null,8,["data"])]),_:1})}]]);y.add(B);const F=p({name:"BatteryCard",components:{DashBoardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dash-board-card");return t.mqttStore.getBatteryConfigured?(s(),c(n,{key:0,color:"warning"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-car-battery"]}),r[0]||(r[0]=g(" Speicher "))]),headerRight:a(()=>[g(h(t.mqttStore.getBatterySoc())+" / "+h(t.mqttStore.getBatteryPower()),1)]),default:a(()=>[i(d,{color:"var(--color--warning)",data:t.mqttStore.getBatteryPowerChartData,"soc-data":t.mqttStore.getBatterySocChartData},null,8,["data","soc-data"])]),_:1})):m("",!0)}]]);y.add(D);const H=p({name:"InverterCard",components:{DashBoardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dash-board-card");return t.mqttStore.getPvConfigured?(s(),c(n,{key:0,color:"success"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-solar-panel"]}),r[0]||(r[0]=g(" PV "))]),headerRight:a(()=>[g(h(t.mqttStore.getPvPower()),1)]),default:a(()=>[i(d,{color:"var(--color--success)",data:t.mqttStore.getPvPowerChartData,inverted:!0},null,8,["data"])]),_:1})):m("",!0)}]]);y.add(L);const A=p({name:"HomeCard",components:{DashBoardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dash-board-card");return s(),c(n,{color:"light"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-home"]}),r[0]||(r[0]=g(" Hausverbrauch "))]),headerRight:a(()=>[g(h(t.mqttStore.getHomePower()),1)]),default:a(()=>[i(d,{color:"var(--color--light)",data:t.mqttStore.getHomePowerChartData},null,8,["data"])]),_:1})}]]);y.add(_);const G={name:"DashboardView",components:{GridCard:E,HomeCard:A,BatteryCard:F,InverterCard:H,ChargePointsCard:p({name:"ChargePointsCard",components:{DashBoardCard:q,SparkLine:P,FontAwesomeIcon:w,ChargePointPlugBadge:b},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("charge-point-plug-badge"),n=e("spark-line"),k=e("dash-board-card");return t.mqttStore.getChargePointIds.length>0?(s(),c(k,{key:0,color:"primary"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-charging-station"]}),g(" "+h(t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointName(t.mqttStore.getChargePointIds[0]):"Ladepunkte"),1)]),headerRight:a(()=>[g(h(t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointPower(t.mqttStore.getChargePointIds[0]):t.mqttStore.getChargePointSumPower())+" ",1),i(d,{"charge-point-id":t.mqttStore.getChargePointIds,"show-energy-charged":!1},null,8,["charge-point-id"])]),default:a(()=>[i(n,{color:"var(--color--primary)",data:t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointPowerChartData(t.mqttStore.getChargePointIds[0]):t.mqttStore.getChargePointSumPowerChartData},null,8,["data"])]),_:1})):m("",!0)}]])},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:l()})},R={class:"dash-board-card-wrapper"},K=p(G,[["render",function(f,r,C,S,t,u){const o=e("grid-card"),d=e("home-card"),n=e("battery-card"),k=e("inverter-card"),v=e("charge-points-card");return s(),x("div",R,[t.mqttStore.getGridCardEnabled?(s(),c(o,{key:0})):m("",!0),t.mqttStore.getHomeCardEnabled?(s(),c(d,{key:1})):m("",!0),t.mqttStore.getBatteryCardEnabled?(s(),c(n,{key:2})):m("",!0),t.mqttStore.getPvCardEnabled?(s(),c(k,{key:3})):m("",!0),t.mqttStore.getChargePointsCardEnabled?(s(),c(v,{key:4})):m("",!0)])}],["__scopeId","data-v-2085947b"]]);export{K as default}; diff --git a/packages/modules/display_themes/cards/web/assets/DashBoardView-CZvLtoU0.css b/packages/modules/display_themes/cards/web/assets/DashBoardView-CZvLtoU0.css deleted file mode 100644 index b5c67478b0..0000000000 --- a/packages/modules/display_themes/cards/web/assets/DashBoardView-CZvLtoU0.css +++ /dev/null @@ -1 +0,0 @@ -.dash-board-card-wrapper[data-v-2085947b]{display:grid;grid-template-columns:repeat(auto-fill,minmax(18rem,1fr));grid-gap:var(--spacing)} diff --git a/packages/modules/display_themes/cards/web/assets/DashboardCard-Cy_vzq6d.css b/packages/modules/display_themes/cards/web/assets/DashboardCard-Cy_vzq6d.css new file mode 100644 index 0000000000..fca53fe6a5 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/DashboardCard-Cy_vzq6d.css @@ -0,0 +1 @@ +.column[data-v-b2600acf]{padding-left:0;padding-right:0}.card[data-v-b2600acf]{----background: inherit !important;----body--color: var(--contrast-color-for-dark-background) !important} diff --git a/packages/modules/display_themes/cards/web/assets/DashboardCard-DS7AgUYm.js b/packages/modules/display_themes/cards/web/assets/DashboardCard-DS7AgUYm.js new file mode 100644 index 0000000000..dbe762c935 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/DashboardCard-DS7AgUYm.js @@ -0,0 +1 @@ +import{_ as u}from"./vendor-inkline-S9CBmrTS.js";import{l,q as a,p as o,o as d,j as r,s as t,f as _}from"./vendor-Bzn5cd2Y.js";const $=u({name:"DashboardCard",props:{color:{type:String,required:!0}}},[["render",function(e,p,i,h,g,m){const s=o("i-column"),n=o("i-row"),c=o("i-container"),f=o("i-card");return d(),l(f,{color:i.color},{header:a(()=>[t(c,null,{default:a(()=>[t(n,null,{default:a(()=>[t(s,null,{default:a(()=>[r(e.$slots,"headerLeft",{},void 0,!0)]),_:3}),e.$slots.headerRight?(d(),l(s,{key:0,class:"_flex-grow:0 _text-align:right _white-space:nowrap"},{default:a(()=>[r(e.$slots,"headerRight",{},void 0,!0)]),_:3})):_("",!0)]),_:3})]),_:3})]),default:a(()=>[r(e.$slots,"default",{},void 0,!0)]),_:3},8,["color"])}],["__scopeId","data-v-b2600acf"]]);export{$ as D}; diff --git a/packages/modules/display_themes/cards/web/assets/DashboardView-BYiJMWFT.css b/packages/modules/display_themes/cards/web/assets/DashboardView-BYiJMWFT.css new file mode 100644 index 0000000000..f3104ad49f --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/DashboardView-BYiJMWFT.css @@ -0,0 +1 @@ +.dashboard-card-wrapper[data-v-853975a6]{display:grid;grid-template-columns:repeat(auto-fill,minmax(18rem,1fr));grid-gap:var(--spacing)} diff --git a/packages/modules/display_themes/cards/web/assets/DashboardView-Ch-ScCyj.js b/packages/modules/display_themes/cards/web/assets/DashboardView-Ch-ScCyj.js new file mode 100644 index 0000000000..a666175597 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/DashboardView-Ch-ScCyj.js @@ -0,0 +1 @@ +import{u as l}from"./index-C0J-ZJ88.js";import{D as q}from"./DashboardCard-DS7AgUYm.js";import{S as P,C as v}from"./ChargePointPlugBadge-Dsdv-7Vn.js";import{F as w,l as y,d as I,e as D,g as L,h as B,i as _}from"./vendor-fortawesome-C1Wk2aFl.js";import{_ as p}from"./vendor-inkline-S9CBmrTS.js";import{l as c,q as a,p as e,o as s,s as i,k as g,x as h,f as m,e as x}from"./vendor-Bzn5cd2Y.js";y.add(I);const E=p({name:"GridCard",components:{DashboardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dashboard-card");return s(),c(n,{color:"danger"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-gauge-high"]}),r[0]||(r[0]=g(" EVU ",-1))]),headerRight:a(()=>[g(h(t.mqttStore.getGridPower()),1)]),default:a(()=>[i(d,{color:"var(--color--danger)","color-negative":"var(--color--success)",data:t.mqttStore.getGridPowerChartData},null,8,["data"])]),_:1})}]]);y.add(D);const F=p({name:"BatteryCard",components:{DashboardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dashboard-card");return t.mqttStore.getBatteryConfigured?(s(),c(n,{key:0,color:"warning"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-car-battery"]}),r[0]||(r[0]=g(" Speicher ",-1))]),headerRight:a(()=>[g(h(t.mqttStore.getBatterySoc())+" / "+h(t.mqttStore.getBatteryPower()),1)]),default:a(()=>[i(d,{color:"var(--color--warning)",data:t.mqttStore.getBatteryPowerChartData,"soc-data":t.mqttStore.getBatterySocChartData},null,8,["data","soc-data"])]),_:1})):m("",!0)}]]);y.add(L);const H=p({name:"InverterCard",components:{DashboardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dashboard-card");return t.mqttStore.getPvConfigured?(s(),c(n,{key:0,color:"success"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-solar-panel"]}),r[0]||(r[0]=g(" PV ",-1))]),headerRight:a(()=>[g(h(t.mqttStore.getPvPower()),1)]),default:a(()=>[i(d,{color:"var(--color--success)",data:t.mqttStore.getPvPowerChartData,inverted:!0},null,8,["data"])]),_:1})):m("",!0)}]]);y.add(B);const A=p({name:"HomeCard",components:{DashboardCard:q,SparkLine:P,FontAwesomeIcon:w},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("spark-line"),n=e("dashboard-card");return s(),c(n,{color:"light"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-home"]}),r[0]||(r[0]=g(" Hausverbrauch ",-1))]),headerRight:a(()=>[g(h(t.mqttStore.getHomePower()),1)]),default:a(()=>[i(d,{color:"var(--color--light)",data:t.mqttStore.getHomePowerChartData},null,8,["data"])]),_:1})}]]);y.add(_);const G={name:"DashboardView",components:{GridCard:E,HomeCard:A,BatteryCard:F,InverterCard:H,ChargePointsCard:p({name:"ChargePointsCard",components:{DashboardCard:q,SparkLine:P,FontAwesomeIcon:w,ChargePointPlugBadge:v},props:{},data:()=>({mqttStore:l()})},[["render",function(f,r,C,S,t,u){const o=e("font-awesome-icon"),d=e("charge-point-plug-badge"),n=e("spark-line"),b=e("dashboard-card");return t.mqttStore.getChargePointIds.length>0?(s(),c(b,{key:0,color:"primary"},{headerLeft:a(()=>[i(o,{"fixed-width":"",icon:["fas","fa-charging-station"]}),g(" "+h(t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointName(t.mqttStore.getChargePointIds[0]):"Ladepunkte"),1)]),headerRight:a(()=>[g(h(t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointPower(t.mqttStore.getChargePointIds[0]):t.mqttStore.getChargePointSumPower())+" ",1),i(d,{"charge-point-id":t.mqttStore.getChargePointIds,"show-energy-charged":!1},null,8,["charge-point-id"])]),default:a(()=>[i(n,{color:"var(--color--primary)",data:t.mqttStore.getChargePointIds.length==1?t.mqttStore.getChargePointPowerChartData(t.mqttStore.getChargePointIds[0]):t.mqttStore.getChargePointSumPowerChartData},null,8,["data"])]),_:1})):m("",!0)}]])},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:l()})},R={class:"dashboard-card-wrapper"},K=p(G,[["render",function(f,r,C,S,t,u){const o=e("grid-card"),d=e("home-card"),n=e("battery-card"),b=e("inverter-card"),k=e("charge-points-card");return s(),x("div",R,[t.mqttStore.getGridCardEnabled?(s(),c(o,{key:0})):m("",!0),t.mqttStore.getHomeCardEnabled?(s(),c(d,{key:1})):m("",!0),t.mqttStore.getBatteryCardEnabled?(s(),c(n,{key:2})):m("",!0),t.mqttStore.getPvCardEnabled?(s(),c(b,{key:3})):m("",!0),t.mqttStore.getChargePointsCardEnabled?(s(),c(k,{key:4})):m("",!0)])}],["__scopeId","data-v-853975a6"]]);export{K as default}; diff --git a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-BOyDZ-Ju.css b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-BOyDZ-Ju.css new file mode 100644 index 0000000000..93fa17245d --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-BOyDZ-Ju.css @@ -0,0 +1 @@ +.svg-container[data-v-e1dd1722]{display:flex;flex-direction:column;align-items:center}path[data-v-e1dd1722]{fill:none;fill-rule:evenodd;stroke:#404040;stroke-width:.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;transition:stroke .5s}path.animated[data-v-e1dd1722]{stroke:#fff;stroke-dasharray:5;animation:dash-e1dd1722 1s linear infinite}path.animatedReverse[data-v-e1dd1722]{stroke:#fff;stroke-dasharray:5;animation:dashReverse-e1dd1722 1s linear infinite}path.animated.grid[data-v-e1dd1722]{stroke:var(--color--danger)}path.animatedReverse.grid[data-v-e1dd1722]{stroke:var(--color--success)}path.animated.pv[data-v-e1dd1722],path.animatedReverse.pv[data-v-e1dd1722]{stroke:var(--color--success)}path.animated.battery[data-v-e1dd1722],path.animatedReverse.battery[data-v-e1dd1722]{stroke:var(--color--warning)}path.animated.charge-point[data-v-e1dd1722],path.animatedReverse.charge-point[data-v-e1dd1722]{stroke:var(--color--primary)}path.animated.vehicle[data-v-e1dd1722],path.animatedReverse.vehicle[data-v-e1dd1722]{stroke:var(--color--teal)}circle[data-v-e1dd1722]{fill:#000;fill-opacity:1;stroke:gray;stroke-width:var(--4830d832);stroke-miterlimit:2;stroke-opacity:1}rect[data-v-e1dd1722]{stroke-width:var(--4830d832)}@keyframes dash-e1dd1722{to{stroke-dashoffset:-20}}@keyframes dashReverse-e1dd1722{to{stroke-dashoffset:20}}text[data-v-e1dd1722]{font-size:var(--4cb129ac);line-height:1.25;font-family:Arial;fill:#fff;fill-opacity:1}text .fill-success[data-v-e1dd1722]{fill:var(--color--success)}text .fill-danger[data-v-e1dd1722]{fill:var(--color--danger)}text .fill-dark[data-v-e1dd1722]{fill:var(--color--dark)}.grid text[data-v-e1dd1722]{fill:var(--color--danger)}.grid circle[data-v-e1dd1722],.grid rect[data-v-e1dd1722]{stroke:var(--color--danger)}.grid circle[data-v-e1dd1722]{fill:var(--color--danger-90)}.pv text[data-v-e1dd1722]{fill:var(--color--success)}.pv circle[data-v-e1dd1722],.pv rect[data-v-e1dd1722]{stroke:var(--color--success)}.pv circle[data-v-e1dd1722]{fill:var(--color--success-90)}.battery text[data-v-e1dd1722]{fill:var(--color--warning)}.battery circle[data-v-e1dd1722],.battery rect[data-v-e1dd1722]{stroke:var(--color--warning)}.battery circle[data-v-e1dd1722]:not(.soc){fill:var(--color--warning-90)}.home text[data-v-e1dd1722]{fill:var(--color--light)}.home circle[data-v-e1dd1722],.home rect[data-v-e1dd1722]{stroke:var(--color--light)}.home circle[data-v-e1dd1722]{fill:var(--color--dark-70)}.charge-point text[data-v-e1dd1722]{fill:var(--color--primary)}.charge-point circle[data-v-e1dd1722],.charge-point rect[data-v-e1dd1722]{stroke:var(--color--primary)}.charge-point circle[data-v-e1dd1722]{fill:var(--color--primary-85)}.vehicle text[data-v-e1dd1722]{fill:var(--color--teal)}.vehicle circle[data-v-e1dd1722],.vehicle rect[data-v-e1dd1722]{stroke:var(--color--teal)}.vehicle circle[data-v-e1dd1722]:not(.soc){fill:var(--color--teal-85)} diff --git a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-C1Dw5jS1.js b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-C1Dw5jS1.js new file mode 100644 index 0000000000..69dad6c8de --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-C1Dw5jS1.js @@ -0,0 +1 @@ +import{u as y}from"./index-C0J-ZJ88.js";import{D as f}from"./DashboardCard-DS7AgUYm.js";import{C as q}from"./ChargeModeModal-CNWdoEpw.js";import{e as h,s as u,p as l,q as m,F as v,o as r,i as s,A as C,z as d,f as P,k as b,x as w,O as M,l as _}from"./vendor-Bzn5cd2Y.js";import{_ as p}from"./vendor-inkline-S9CBmrTS.js";import"./vendor-fortawesome-C1Wk2aFl.js";const S={name:"DashboardFlowCard",components:{DashboardCard:f,ChargeModeModal:q},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:y(),svgSize:{xMin:0,xMax:150,yMin:0,yMax:105,circleRadius:10,strokeWidth:.5,textSize:5,numRows:4,numColumns:3},modalChargeModeSettingsVisible:!1,modalChargePointId:0}),computed:{svgViewBox(){return`${this.svgSize.xMin} ${this.svgSize.yMin} ${this.svgSize.xMax} ${this.svgSize.yMax}`},svgFontSize(){return`${this.svgSize.textSize}px`},svgRectWidth(){return(this.svgSize.xMax-this.svgSize.xMin-this.svgSize.strokeWidth-this.svgSize.numColumns)/this.svgSize.numColumns},svgStrokeWidth(){return this.svgSize.strokeWidth},svgIconWidth(){return this.svgSize.circleRadius},svgIconHeight(){return this.svgSize.circleRadius},gridPower(){return this.mqttStore.getGridPower("object")},pvPower(){return this.mqttStore.getPvPower("object")},homePower(){return this.mqttStore.getHomePower("object")},batteryPower(){return this.mqttStore.getBatteryPower("object")},batterySoc(){return this.mqttStore.getBatterySoc("object").value/100},connectedChargePoints(){return this.mqttStore.getChargePointIds},chargePointSumPower(){return this.mqttStore.getChargePointSumPower("object")},chargePoint1Power(){return this.connectedChargePoints.length>0?this.mqttStore.getChargePointPower(this.connectedChargePoints[0],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint2Power(){return this.connectedChargePoints.length>1?this.mqttStore.getChargePointPower(this.connectedChargePoints[1],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint3Power(){return this.connectedChargePoints.length>2?this.mqttStore.getChargePointPower(this.connectedChargePoints[2],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint1Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[0])},chargePoint2Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[1])},chargePoint3Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[2])},chargePoint1VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[0])},chargePoint2VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[1])},chargePoint3VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[0])},chargePoint2ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[1])},chargePoint3ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[0])},chargePoint2ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[1])},chargePoint3ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[0]).soc/100},chargePoint2ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[1]).soc/100},chargePoint3ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[2]).soc/100},gridConsumption(){return this.gridPower.value>0},gridFeedIn(){return this.gridPower.value<0},homeConsumption(){return this.homePower.value>0},homeProduction(){return this.homePower.value<0},pvProduction(){return this.pvPower.value>0},batteryDischarging(){return this.batteryPower.value<0},batteryCharging(){return this.batteryPower.value>0},chargePointSumCharging(){return this.chargePointSumPower.value>0},chargePointSumDischarging(){return this.chargePointSumPower.value<0},chargePoint1Charging(){return this.chargePoint1Power.value>0},chargePoint1Discharging(){return this.chargePoint1Power.value<0},chargePoint2Charging(){return this.chargePoint2Power.value>0},chargePoint2Discharging(){return this.chargePoint2Power.value<0},chargePoint3Charging(){return this.chargePoint3Power.value>0},chargePoint3Discharging(){return this.chargePoint3Power.value<0},svgComponents(){var t,a,g,o,n,c;var e=[];return this.mqttStore.getThemeConfiguration.enable_dashboard_card_grid&&e.push({id:"grid",class:{base:"grid",valueLabel:this.gridFeedIn?"fill-success":this.gridConsumption?"fill-danger":"",animated:this.gridConsumption,animatedReverse:this.gridFeedIn},position:{row:0,column:0},label:["EVU",this.absoluteValue(this.gridPower).textValue],icon:"icons/owbGrid.svg"}),this.mqttStore.getThemeConfiguration.enable_dashboard_card_home_consumption&&e.push({id:"home",class:{base:"home",valueLabel:"",animated:this.homeProduction,animatedReverse:this.homeConsumption},position:{row:0,column:2},label:["Haus",this.absoluteValue(this.homePower).textValue],icon:"icons/owbHouse.svg"}),this.mqttStore.getPvConfigured&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_inverter_sum&&e.push({id:"pv",class:{base:"pv",valueLabel:this.pvProduction?"fill-success":"",animated:this.pvProduction,animatedReverse:!1},position:{row:1,column:0},label:["PV",this.absoluteValue(this.pvPower).textValue],icon:"icons/owbPV.svg"}),this.mqttStore.getBatteryConfigured&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_battery_sum&&e.push({id:"battery",class:{base:"battery",valueLabel:"",animated:this.batteryDischarging,animatedReverse:this.batteryCharging},position:{row:1,column:2},label:["Speicher",this.absoluteValue(this.batteryPower).textValue],soc:this.batterySoc,icon:"icons/owbBattery.svg"}),this.connectedChargePoints.length>0&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_charge_point_sum&&(this.connectedChargePoints.length<=3?(e.push({id:"charge-point-1",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint1Discharging,animatedReverse:this.chargePoint1Charging},position:{row:2,column:this.connectedChargePoints.length>1?0:1},label:[this.chargePoint1Name,this.absoluteValue(this.chargePoint1Power).textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint1VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-1",class:{base:"vehicle",valueLabel:"fill-"+((t=this.chargePoint1ConnectedVehicleChargeMode)==null?void 0:t.class),animated:this.chargePoint1Discharging,animatedReverse:this.chargePoint1Charging},position:{row:3,column:this.connectedChargePoints.length>1?0:1},label:[this.chargePoint1ConnectedVehicleName||"---",((a=this.chargePoint1ConnectedVehicleChargeMode)==null?void 0:a.label)||"---"],soc:this.chargePoint1ConnectedVehicleSoc,icon:"icons/owbVehicle.svg",clicked:()=>{this.selectChargeMode(this.connectedChargePoints[0])}}),this.connectedChargePoints.length>1&&(e.push({id:"charge-point-2",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint2Discharging,animatedReverse:this.chargePoint2Charging},position:{row:2,column:this.connectedChargePoints.length>2?1:2},label:[this.chargePoint2Name,this.absoluteValue(this.chargePoint2Power).textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint2VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-2",class:{base:"vehicle",valueLabel:"fill-"+((g=this.chargePoint2ConnectedVehicleChargeMode)==null?void 0:g.class),animated:this.chargePoint2Discharging,animatedReverse:this.chargePoint2Charging},position:{row:3,column:this.connectedChargePoints.length>2?1:2},label:[this.chargePoint2ConnectedVehicleName||"---",((o=this.chargePoint2ConnectedVehicleChargeMode)==null?void 0:o.label)||"---"],soc:this.chargePoint2ConnectedVehicleSoc,icon:"icons/owbVehicle.svg"}),this.connectedChargePoints.length>2&&(e.push({id:"charge-point-3",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint3Discharging,animatedReverse:this.chargePoint3Charging},position:{row:2,column:2},label:[this.chargePoint3Name,this.absoluteValue(this.chargePoint3Power).textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint3VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-3",class:{base:"vehicle",valueLabel:"fill-"+((n=this.chargePoint3ConnectedVehicleChargeMode)==null?void 0:n.class),animated:this.chargePoint3Discharging,animatedReverse:this.chargePoint3Charging},position:{row:3,column:2},label:[this.chargePoint3ConnectedVehicleName||"---",((c=this.chargePoint3ConnectedVehicleChargeMode)==null?void 0:c.label)||"---"],soc:this.chargePoint3ConnectedVehicleSoc,icon:"icons/owbVehicle.svg"})))):e.push({id:"charge-point-sum",class:{base:"charge-point",valueLabel:"",animated:this.chargePointSumDischarging,animatedReverse:this.chargePointSumCharging},position:{row:2,column:1},label:["Ladepunkte",this.absoluteValue(this.chargePointSumPower).textValue],icon:"icons/owbChargePoint.svg"})),(!this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles||this.connectedChargePoints.length===0||this.connectedChargePoints.length>3)&&this.setSvgNumRows(3),e}},watch:{changesLocked(e,t){t!==!0&&e===!0&&(this.modalChargeModeSettingsVisible=!1)}},methods:{absoluteValue(e){let t={...e};return t.textValue&&(t.textValue=t.textValue.replace("-","")),t.value&&(t.value=Math.abs(t.value)),t.scaledValue&&(t.scaledValue=Math.abs(t.scaledValue)),t},setSvgNumRows(e){this.svgSize.numRows=e},calcRowY(e){let t=this.svgSize.yMin+this.svgSize.strokeWidth+this.svgSize.circleRadius;return e*((this.svgSize.yMax-this.svgSize.strokeWidth-this.svgSize.circleRadius-t)/(this.svgSize.numRows-1))+t},calcColumnX(e){let t=this.svgSize.xMin+this.svgSize.strokeWidth+this.svgRectWidth/2;return e*((this.svgSize.xMax-this.svgSize.strokeWidth-this.svgRectWidth/2-t)/(this.svgSize.numColumns-1))+t},calcFlowLineAnchorX(e){let t=this.calcColumnX(e);return e<(this.svgSize.numColumns-1)/2?t+this.svgRectWidth/2-this.svgSize.circleRadius:e>(this.svgSize.numColumns-1)/2?t-this.svgRectWidth/2+this.svgSize.circleRadius:t},calcSvgElementBoundingBox(e){let t=document.getElementById(e);if(t==null)return{x:0,y:0,width:0,height:0};let a=t.getBBox();return{x:a.x,y:a.y,width:a.width,height:a.height}},beginAnimation(e){var t;this.$refs[e]!=null&&((t=this.$refs[e][0])==null||t.beginElement())},selectChargeMode(e){this.changesLocked||(this.modalChargePointId=e,this.modalChargeModeSettingsVisible=!0)}}},V=()=>{M(e=>({"4830d832":e.svgStrokeWidth,"4cb129ac":e.svgFontSize}))},x=S.setup;S.setup=x?(e,t)=>(V(),x(e,t)):V;const k={class:"svg-container"},W=["viewBox"],$={id:"layer1",style:{display:"inline"}},L=["d"],N={id:"layer2",style:{display:"inline"}},B=["cx","cy","r"],D=["transform","onClick"],I=["id"],F=["x","y","width","height"],A=["id"],j=["x","y","width","height","rx","ry"],T=["x","y","width","height","rx","ry"],X=["clip-path"],E=["id","x","y"],Y=["values"],H=["id","x","y"],G=["transform"],U=["r"],O=["r","clip-path"],J=["href","x","y","height","width"],ne=p({name:"EnergyFlowView",components:{FlowCard:p(S,[["render",function(e,t,a,g,o,n){const c=l("charge-mode-modal"),z=l("i-container"),R=l("dashboard-card");return r(),h(v,null,[u(c,{modelValue:o.modalChargeModeSettingsVisible,"onUpdate:modelValue":t[0]||(t[0]=i=>o.modalChargeModeSettingsVisible=i),"charge-point-id":o.modalChargePointId},null,8,["modelValue","charge-point-id"]),u(R,{color:"primary"},{headerLeft:m(()=>t[1]||(t[1]=[b(" Übersicht - Energiefluss ",-1)])),default:m(()=>[u(z,null,{default:m(()=>[s("div",k,[(r(),h("svg",{viewBox:n.svgViewBox,version:"1.1",xmlns:"http://www.w3.org/2000/svg","xmlns:svg":"http://www.w3.org/2000/svg"},[s("g",$,[(r(!0),h(v,null,C(n.svgComponents,i=>(r(),h("path",{key:i.id,class:d([i.class.base,{animated:i.class.animated},{animatedReverse:i.class.animatedReverse}]),d:i.class.base!=="vehicle"?`M ${n.calcFlowLineAnchorX(i.position.column)}, ${n.calcRowY(i.position.row)} ${n.calcColumnX(1)}, ${n.calcRowY(1)}`:`M ${n.calcFlowLineAnchorX(i.position.column)}, ${n.calcRowY(i.position.row)} ${n.calcFlowLineAnchorX(i.position.column)}, ${n.calcRowY(i.position.row-1)}`},null,10,L))),128))]),s("g",N,[s("circle",{id:"center",cx:n.calcColumnX(1),cy:n.calcRowY(1),r:o.svgSize.circleRadius/3},null,8,B),(r(!0),h(v,null,C(n.svgComponents,i=>(r(),h("g",{key:i.id,class:d(i.class.base),transform:`translate(${n.calcColumnX(i.position.column)}, ${n.calcRowY(i.position.row)})`,onClick:K=>{n.beginAnimation(`animate-label-${i.id}`),i.clicked&&i.clicked()}},[s("defs",null,[i.soc?(r(),h("clipPath",{key:0,id:`clip-soc-${i.id}`},[s("rect",{x:-o.svgSize.circleRadius-o.svgSize.strokeWidth,y:(o.svgSize.circleRadius+o.svgSize.strokeWidth)*(1-2*i.soc),width:2*(o.svgSize.circleRadius+o.svgSize.strokeWidth),height:2*(o.svgSize.circleRadius+o.svgSize.strokeWidth)*i.soc},null,8,F)],8,I)):P("",!0),s("clipPath",{id:`clip-label-${i.id}`},[s("rect",{x:-n.svgRectWidth/2,y:-o.svgSize.circleRadius,width:n.svgRectWidth,height:2*o.svgSize.circleRadius,rx:o.svgSize.circleRadius,ry:o.svgSize.circleRadius},null,8,j)],8,A)]),s("rect",{x:-n.svgRectWidth/2,y:-o.svgSize.circleRadius,width:n.svgRectWidth,height:2*o.svgSize.circleRadius,rx:o.svgSize.circleRadius,ry:o.svgSize.circleRadius},null,8,T),s("text",{"clip-path":`url(#clip-label-${i.id})`},[s("tspan",{id:`label-${i.id}`,"text-anchor":"start",x:-n.svgRectWidth/2+2*o.svgSize.circleRadius+o.svgSize.strokeWidth,y:-o.svgSize.textSize/2},[n.calcSvgElementBoundingBox(`label-${i.id}`).width>n.svgRectWidth-2*o.svgSize.circleRadius-2*o.svgSize.strokeWidth?(r(),h("animate",{key:0,ref_for:!0,ref:`animate-label-${i.id}`,xmlns:"http://www.w3.org/2000/svg",attributeName:"x",dur:"5s",values:"0; "+(-n.calcSvgElementBoundingBox(`label-${i.id}`).width+n.svgRectWidth-2.5*o.svgSize.circleRadius-2*o.svgSize.strokeWidth)+"; 0;",repeatCount:"0",begin:"2s",additive:"sum"},null,8,Y)):P("",!0),b(" "+w(i.label[0]),1)],8,E),s("tspan",{id:`value-${i.id}`,class:d(i.class.valueLabel),"text-anchor":"end",x:2*o.svgSize.circleRadius+o.svgSize.strokeWidth,y:o.svgSize.textSize},w(i.label[1]),11,H)],8,X),s("g",{transform:`translate(${o.svgSize.circleRadius-n.svgRectWidth/2}, 0)`},[s("circle",{cx:"0",cy:"0",r:o.svgSize.circleRadius,class:d({soc:i.soc})},null,10,U),i.soc?(r(),h("circle",{key:0,cx:"0",cy:"0",r:o.svgSize.circleRadius,"clip-path":`url(#clip-soc-${i.id})`},null,8,O)):P("",!0),s("image",{href:i.icon,x:-n.svgIconWidth/2,y:-n.svgIconHeight/2,height:n.svgIconHeight,width:n.svgIconWidth},null,8,J)],8,G)],10,D))),128))])],8,W))])]),_:1})]),_:1})],64)}],["__scopeId","data-v-e1dd1722"]])},props:{changesLocked:{required:!1,type:Boolean,default:!1}}},[["render",function(e,t,a,g,o,n){const c=l("FlowCard");return r(),_(c,{"changes-locked":a.changesLocked},null,8,["changes-locked"])}]]);export{ne as default}; diff --git a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-CMZu-SNR.css b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-CMZu-SNR.css deleted file mode 100644 index 298588d4fe..0000000000 --- a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-CMZu-SNR.css +++ /dev/null @@ -1 +0,0 @@ -.svg-container[data-v-9e868ed0]{display:flex;flex-direction:column;align-items:center}path[data-v-9e868ed0]{fill:none;fill-rule:evenodd;stroke:#404040;stroke-width:.75;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;transition:stroke .5s}path.animated[data-v-9e868ed0]{stroke:#fff;stroke-dasharray:5;animation:dash-9e868ed0 1s linear infinite}path.animatedReverse[data-v-9e868ed0]{stroke:#fff;stroke-dasharray:5;animation:dashReverse-9e868ed0 1s linear infinite}path.animated.grid[data-v-9e868ed0]{stroke:var(--color--danger)}path.animatedReverse.grid[data-v-9e868ed0]{stroke:var(--color--success)}path.animated.pv[data-v-9e868ed0],path.animatedReverse.pv[data-v-9e868ed0]{stroke:var(--color--success)}path.animated.battery[data-v-9e868ed0],path.animatedReverse.battery[data-v-9e868ed0]{stroke:var(--color--warning)}path.animated.charge-point[data-v-9e868ed0],path.animatedReverse.charge-point[data-v-9e868ed0]{stroke:var(--color--primary)}circle[data-v-9e868ed0]{fill:#000;fill-opacity:1;stroke:gray;stroke-width:var(--2d21ce23);stroke-miterlimit:2;stroke-opacity:1}rect[data-v-9e868ed0]{stroke-width:var(--2d21ce23)}@keyframes dash-9e868ed0{to{stroke-dashoffset:-20}}@keyframes dashReverse-9e868ed0{to{stroke-dashoffset:20}}text[data-v-9e868ed0]{font-size:var(--0cb0615b);line-height:1.25;font-family:Arial;fill:#fff;fill-opacity:1}text .fill-success[data-v-9e868ed0]{fill:var(--color--success)}text .fill-danger[data-v-9e868ed0]{fill:var(--color--danger)}text .fill-dark[data-v-9e868ed0]{fill:var(--color--dark)}.grid text[data-v-9e868ed0]{fill:var(--color--danger)}.grid circle[data-v-9e868ed0],.grid rect[data-v-9e868ed0]{stroke:var(--color--danger)}.grid circle[data-v-9e868ed0]{fill:var(--color--danger-90)}.pv text[data-v-9e868ed0]{fill:var(--color--success)}.pv circle[data-v-9e868ed0],.pv rect[data-v-9e868ed0]{stroke:var(--color--success)}.pv circle[data-v-9e868ed0]{fill:var(--color--success-90)}.battery text[data-v-9e868ed0]{fill:var(--color--warning)}.battery circle[data-v-9e868ed0],.battery rect[data-v-9e868ed0]{stroke:var(--color--warning)}.battery circle[data-v-9e868ed0]:not(.soc){fill:var(--color--warning-90)}.home text[data-v-9e868ed0]{fill:var(--color--light)}.home circle[data-v-9e868ed0],.home rect[data-v-9e868ed0]{stroke:var(--color--light)}.home circle[data-v-9e868ed0]{fill:var(--color--dark-70)}.charge-point text[data-v-9e868ed0]{fill:var(--color--primary)}.charge-point circle[data-v-9e868ed0],.charge-point rect[data-v-9e868ed0]{stroke:var(--color--primary)}.charge-point circle[data-v-9e868ed0]{fill:var(--color--primary-85)}.vehicle text[data-v-9e868ed0]{fill:var(--color--teal)}.vehicle circle[data-v-9e868ed0],.vehicle rect[data-v-9e868ed0]{stroke:var(--color--teal)}.vehicle circle[data-v-9e868ed0]:not(.soc){fill:var(--color--teal-85)} diff --git a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-D3Xr79nb.js b/packages/modules/display_themes/cards/web/assets/EnergyFlowView-D3Xr79nb.js deleted file mode 100644 index 6ad534270a..0000000000 --- a/packages/modules/display_themes/cards/web/assets/EnergyFlowView-D3Xr79nb.js +++ /dev/null @@ -1 +0,0 @@ -import{u as R}from"./index-nI_NVV5B.js";import{D as V}from"./DashBoardCard-DJZ2GsC-.js";import{l as v,q as l,p as d,o as r,s as f,i as s,e as a,F as P,A as C,z as c,f as u,k as b,x as S,O as q}from"./vendor-BMrK3KHF.js";import{_ as w}from"./vendor-inkline-C_NPDnDu.js";import"./vendor-fortawesome-CpQlJZ13.js";const m={name:"DashboardFlowCard",components:{DashBoardCard:V},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:R(),svgSize:{xMin:0,xMax:150,yMin:0,yMax:105,circleRadius:10,strokeWidth:.5,textSize:5,numRows:4,numColumns:3}}),computed:{svgViewBox(){return`${this.svgSize.xMin} ${this.svgSize.yMin} ${this.svgSize.xMax} ${this.svgSize.yMax}`},svgFontSize(){return`${this.svgSize.textSize}px`},svgRectWidth(){return(this.svgSize.xMax-this.svgSize.xMin-this.svgSize.strokeWidth-this.svgSize.numColumns)/this.svgSize.numColumns},svgStrokeWidth(){return this.svgSize.strokeWidth},svgIconWidth(){return this.svgSize.circleRadius},svgIconHeight(){return this.svgSize.circleRadius},gridPower(){return this.mqttStore.getGridPower("object")},pvPower(){return this.mqttStore.getPvPower("object")},homePower(){return this.mqttStore.getHomePower("object")},batteryPower(){return this.mqttStore.getBatteryPower("object")},batterySoc(){return this.mqttStore.getBatterySoc("object").value/100},connectedChargePoints(){return this.mqttStore.getChargePointIds},chargePointSumPower(){return this.mqttStore.getChargePointSumPower("object")},chargePoint1Power(){return this.connectedChargePoints.length>0?this.mqttStore.getChargePointPower(this.connectedChargePoints[0],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint2Power(){return this.connectedChargePoints.length>1?this.mqttStore.getChargePointPower(this.connectedChargePoints[1],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint3Power(){return this.connectedChargePoints.length>2?this.mqttStore.getChargePointPower(this.connectedChargePoints[2],"object")||{textValue:"Loading..."}:{textValue:"N/A"}},chargePoint1Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[0])},chargePoint2Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[1])},chargePoint3Name(){return this.mqttStore.getChargePointName(this.connectedChargePoints[2])},chargePoint1VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[0])},chargePoint2VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[1])},chargePoint3VehicleConnected(){return this.mqttStore.getChargePointPlugState(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[0])},chargePoint2ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[1])},chargePoint3ConnectedVehicleName(){return this.mqttStore.getChargePointConnectedVehicleName(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[0])},chargePoint2ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[1])},chargePoint3ConnectedVehicleChargeMode(){return this.mqttStore.getChargePointConnectedVehicleChargeMode(this.connectedChargePoints[2])},chargePoint1ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[0]).soc/100},chargePoint2ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[1]).soc/100},chargePoint3ConnectedVehicleSoc(){return this.mqttStore.getChargePointConnectedVehicleSoc(this.connectedChargePoints[2]).soc/100},gridConsumption(){return this.gridPower.value>0},gridFeedIn(){return this.gridPower.value<0},homeConsumption(){return this.homePower.value>0},homeProduction(){return this.homePower.value<0},pvProduction(){return this.pvPower.value>0},batteryDischarging(){return this.batteryPower.value<0},batteryCharging(){return this.batteryPower.value>0},chargePointSumCharging(){return this.chargePointSumPower.value>0},chargePointSumDischarging(){return this.chargePointSumPower.value<0},chargePoint1Charging(){return this.chargePoint1Power.value>0},chargePoint1Discharging(){return this.chargePoint1Power.value<0},chargePoint2Charging(){return this.chargePoint2Power.value>0},chargePoint2Discharging(){return this.chargePoint2Power.value<0},chargePoint3Charging(){return this.chargePoint3Power.value>0},chargePoint3Discharging(){return this.chargePoint3Power.value<0},svgComponents(){var e=[];return this.mqttStore.getThemeConfiguration.enable_dashboard_card_grid&&e.push({id:"grid",class:{base:"grid",valueLabel:this.gridFeedIn?"fill-success":this.gridConsumption?"fill-danger":"",animated:this.gridConsumption,animatedReverse:this.gridFeedIn},position:{row:0,column:0},label:["EVU",this.gridPower.textValue],icon:"icons/owbGrid.svg"}),this.mqttStore.getThemeConfiguration.enable_dashboard_card_home_consumption&&e.push({id:"home",class:{base:"home",valueLabel:"",animated:this.homeProduction,animatedReverse:this.homeConsumption},position:{row:0,column:2},label:["Haus",this.homePower.textValue],icon:"icons/owbHouse.svg"}),this.mqttStore.getPvConfigured&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_inverter_sum&&e.push({id:"pv",class:{base:"pv",valueLabel:this.pvProduction?"fill-success":"",animated:this.pvProduction,animatedReverse:!1},position:{row:1,column:0},label:["PV",this.pvPower.textValue],icon:"icons/owbPV.svg"}),this.mqttStore.getBatteryConfigured&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_battery_sum&&e.push({id:"battery",class:{base:"battery",valueLabel:"",animated:this.batteryDischarging,animatedReverse:this.batteryCharging},position:{row:1,column:2},label:["Speicher",this.batteryPower.textValue],soc:this.batterySoc,icon:"icons/owbBattery.svg"}),this.connectedChargePoints.length>0&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_charge_point_sum&&(this.connectedChargePoints.length<=3?(e.push({id:"charge-point-1",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint1Discharging,animatedReverse:this.chargePoint1Charging},position:{row:2,column:this.connectedChargePoints.length>1?0:1},label:[this.chargePoint1Name,this.chargePoint1Power.textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint1VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-1",class:{base:"vehicle",valueLabel:"fill-"+this.chargePoint1ConnectedVehicleChargeMode.class},position:{row:3,column:this.connectedChargePoints.length>1?0:1},label:[this.chargePoint1ConnectedVehicleName||"---",this.chargePoint1ConnectedVehicleChargeMode.label||"---"],soc:this.chargePoint1ConnectedVehicleSoc,icon:"icons/owbVehicle.svg"}),this.connectedChargePoints.length>1&&(e.push({id:"charge-point-2",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint2Discharging,animatedReverse:this.chargePoint2Charging},position:{row:2,column:this.connectedChargePoints.length>2?1:2},label:[this.chargePoint2Name,this.chargePoint2Power.textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint2VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-2",class:{base:"vehicle",valueLabel:"fill-"+this.chargePoint2ConnectedVehicleChargeMode.class},position:{row:3,column:this.connectedChargePoints.length>2?1:2},label:[this.chargePoint2ConnectedVehicleName||"---",this.chargePoint2ConnectedVehicleChargeMode.label||"---"],soc:this.chargePoint2ConnectedVehicleSoc,icon:"icons/owbVehicle.svg"}),this.connectedChargePoints.length>2&&(e.push({id:"charge-point-3",class:{base:"charge-point",valueLabel:"",animated:this.chargePoint3Discharging,animatedReverse:this.chargePoint3Charging},position:{row:2,column:2},label:[this.chargePoint3Name,this.chargePoint3Power.textValue],icon:"icons/owbChargePoint.svg"}),this.chargePoint3VehicleConnected&&this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles&&e.push({id:"vehicle-3",class:{base:"vehicle",valueLabel:"fill-"+this.chargePoint3ConnectedVehicleChargeMode.class},position:{row:3,column:2},label:[this.chargePoint3ConnectedVehicleName||"---",this.chargePoint3ConnectedVehicleChargeMode.label||"---"],soc:this.chargePoint3ConnectedVehicleSoc,icon:"icons/owbVehicle.svg"})))):e.push({id:"charge-point-sum",class:{base:"charge-point",valueLabel:"",animated:this.chargePointSumDischarging,animatedReverse:this.chargePointSumCharging},position:{row:2,column:1},label:["Ladepunkte",this.chargePointSumPower.textValue],icon:"icons/owbChargePoint.svg"})),(!this.mqttStore.getThemeConfiguration.enable_dashboard_card_vehicles||this.connectedChargePoints.length===0||this.connectedChargePoints.length>3)&&this.setSvgNumRows(3),e}},methods:{setSvgNumRows(e){this.svgSize.numRows=e},calcRowY(e){let o=this.svgSize.yMin+this.svgSize.strokeWidth+this.svgSize.circleRadius;return e*((this.svgSize.yMax-this.svgSize.strokeWidth-this.svgSize.circleRadius-o)/(this.svgSize.numRows-1))+o},calcColumnX(e){let o=this.svgSize.xMin+this.svgSize.strokeWidth+this.svgRectWidth/2;return e*((this.svgSize.xMax-this.svgSize.strokeWidth-this.svgRectWidth/2-o)/(this.svgSize.numColumns-1))+o},calcFlowLineAnchorX(e){let o=this.calcColumnX(e);return e<(this.svgSize.numColumns-1)/2?o+this.svgRectWidth/2-this.svgSize.circleRadius:e>(this.svgSize.numColumns-1)/2?o-this.svgRectWidth/2+this.svgSize.circleRadius:o},calcSvgElementBoundingBox(e){let o=document.getElementById(e);if(o==null)return{x:0,y:0,width:0,height:0};let h=o.getBBox();return{x:h.x,y:h.y,width:h.width,height:h.height}},beginAnimation(e){var o;this.$refs[e]!=null&&((o=this.$refs[e][0])==null||o.beginElement())}}},p=()=>{q(e=>({"2d21ce23":e.svgStrokeWidth,"0cb0615b":e.svgFontSize}))},x=m.setup;m.setup=x?(e,o)=>(p(),x(e,o)):p;const _={class:"svg-container"},W=["viewBox"],k={id:"layer1",style:{display:"inline"}},M=["d"],N={id:"layer2",style:{display:"inline"}},$=["cx","cy","r"],L=["transform","onClick"],B=["id"],D=["x","y","width","height"],I=["id"],F=["x","y","width","height","rx","ry"],j=["x","y","width","height","rx","ry"],T=["clip-path"],A=["id","x","y"],E=["values"],X=["id","x","y"],H=["transform"],Y=["r"],G=["r","clip-path"],O=["href","x","y","height","width"],te=w({name:"EnergyFlowView",components:{FlowCard:w(m,[["render",function(e,o,h,z,t,n){const g=d("i-container"),y=d("dash-board-card");return r(),v(y,{color:"primary"},{headerLeft:l(()=>o[0]||(o[0]=[b(" Übersicht - Energiefluss ")])),default:l(()=>[f(g,null,{default:l(()=>[s("div",_,[(r(),a("svg",{viewBox:n.svgViewBox,version:"1.1",xmlns:"http://www.w3.org/2000/svg","xmlns:svg":"http://www.w3.org/2000/svg"},[s("g",k,[(r(!0),a(P,null,C(n.svgComponents,i=>(r(),a("path",{key:i.id,class:c([i.class.base,{animated:i.class.animated},{animatedReverse:i.class.animatedReverse}]),d:i.class.base!=="vehicle"?`M ${n.calcFlowLineAnchorX(i.position.column)}, ${n.calcRowY(i.position.row)} ${n.calcColumnX(1)}, ${n.calcRowY(1)}`:""},null,10,M))),128))]),s("g",N,[s("circle",{id:"center",cx:n.calcColumnX(1),cy:n.calcRowY(1),r:t.svgSize.circleRadius/3},null,8,$),(r(!0),a(P,null,C(n.svgComponents,i=>(r(),a("g",{key:i.id,class:c(i.class.base),transform:`translate(${n.calcColumnX(i.position.column)}, ${n.calcRowY(i.position.row)})`,onClick:U=>n.beginAnimation(`animate-label-${i.id}`)},[s("defs",null,[i.soc?(r(),a("clipPath",{key:0,id:`clip-soc-${i.id}`},[s("rect",{x:-t.svgSize.circleRadius-t.svgSize.strokeWidth,y:(t.svgSize.circleRadius+t.svgSize.strokeWidth)*(1-2*i.soc),width:2*(t.svgSize.circleRadius+t.svgSize.strokeWidth),height:2*(t.svgSize.circleRadius+t.svgSize.strokeWidth)*i.soc},null,8,D)],8,B)):u("",!0),s("clipPath",{id:`clip-label-${i.id}`},[s("rect",{x:-n.svgRectWidth/2,y:-t.svgSize.circleRadius,width:n.svgRectWidth,height:2*t.svgSize.circleRadius,rx:t.svgSize.circleRadius,ry:t.svgSize.circleRadius},null,8,F)],8,I)]),s("rect",{x:-n.svgRectWidth/2,y:-t.svgSize.circleRadius,width:n.svgRectWidth,height:2*t.svgSize.circleRadius,rx:t.svgSize.circleRadius,ry:t.svgSize.circleRadius},null,8,j),s("text",{"clip-path":`url(#clip-label-${i.id})`},[s("tspan",{id:`label-${i.id}`,"text-anchor":"start",x:-n.svgRectWidth/2+2*t.svgSize.circleRadius+t.svgSize.strokeWidth,y:-t.svgSize.textSize/2},[n.calcSvgElementBoundingBox(`label-${i.id}`).width>n.svgRectWidth-2*t.svgSize.circleRadius-2*t.svgSize.strokeWidth?(r(),a("animate",{key:0,ref_for:!0,ref:`animate-label-${i.id}`,xmlns:"http://www.w3.org/2000/svg",attributeName:"x",dur:"5s",values:"0; "+(-n.calcSvgElementBoundingBox(`label-${i.id}`).width+n.svgRectWidth-2.5*t.svgSize.circleRadius-2*t.svgSize.strokeWidth)+"; 0;",repeatCount:"0",begin:"2s",additive:"sum"},null,8,E)):u("",!0),b(" "+S(i.label[0]),1)],8,A),s("tspan",{id:`value-${i.id}`,class:c(i.class.valueLabel),"text-anchor":"end",x:2*t.svgSize.circleRadius+t.svgSize.strokeWidth,y:t.svgSize.textSize},S(i.label[1]),11,X)],8,T),s("g",{transform:`translate(${t.svgSize.circleRadius-n.svgRectWidth/2}, 0)`},[s("circle",{cx:"0",cy:"0",r:t.svgSize.circleRadius,class:c({soc:i.soc})},null,10,Y),i.soc?(r(),a("circle",{key:0,cx:"0",cy:"0",r:t.svgSize.circleRadius,"clip-path":`url(#clip-soc-${i.id})`},null,8,G)):u("",!0),s("image",{href:i.icon,x:-n.svgIconWidth/2,y:-n.svgIconHeight/2,height:n.svgIconHeight,width:n.svgIconWidth},null,8,O)],8,H)],10,L))),128))])],8,W))])]),_:1})]),_:1})}],["__scopeId","data-v-9e868ed0"]])},props:{changesLocked:{required:!1,type:Boolean,default:!1}}},[["render",function(e,o,h,z,t,n){const g=d("FlowCard");return r(),v(g)}]]);export{te as default}; diff --git a/packages/modules/display_themes/cards/web/assets/StatusView-9xI7RUa0.js b/packages/modules/display_themes/cards/web/assets/StatusView-9xI7RUa0.js new file mode 100644 index 0000000000..a2263d6861 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/StatusView-9xI7RUa0.js @@ -0,0 +1 @@ +import{u as y}from"./index-C0J-ZJ88.js";import{F as b,l as k,G as C,H as A}from"./vendor-fortawesome-C1Wk2aFl.js";import{_ as p}from"./vendor-inkline-S9CBmrTS.js";import{l as c,q as t,p as l,o as _,k as n,s as o,I as x,i as I,f as M}from"./vendor-Bzn5cd2Y.js";import{D as z}from"./DashboardCard-DS7AgUYm.js";k.add(C);const F=p({name:"ReloadButton",components:{FontAwesomeIcon:b},props:{},data:()=>({}),methods:{reloadDisplay(){location.reload()}}},[["render",function(w,e,f,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-button");return _(),c(a,{color:"success",size:"lg",onClick:e[0]||(e[0]=s=>m.reloadDisplay())},{default:t(()=>[e[1]||(e[1]=n(" Display neu laden ",-1)),o(i,{"fixed-width":"",icon:["fas","fa-undo"]})]),_:1,__:[1]})}]]);k.add(C);const P=p({name:"RebootButton",components:{FontAwesomeIcon:b},props:{},data:()=>({mqttStore:y(),showModal:!1}),methods:{toggleModal(){this.showModal=!this.showModal},cancel(){this.toggleModal()},confirm(){this.toggleModal(),this.mqttStore.settings.parentChargePoint1!==void 0?this.$root.sendSystemCommand("chargepointReboot",{chargePoint:this.mqttStore.settings.parentChargePoint1}):this.$root.sendSystemCommand("systemReboot")}}},[["render",function(w,e,f,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-container"),s=l("i-button"),d=l("i-column"),h=l("i-row"),g=l("i-modal");return _(),c(s,{color:"warning",size:"lg",onClick:e[3]||(e[3]=r=>m.toggleModal())},{default:t(()=>[e[8]||(e[8]=n(" openWB neu starten ",-1)),o(i,{"fixed-width":"",icon:["fas","fa-undo"]}),(_(),c(x,{to:"body"},[o(g,{modelValue:u.showModal,"onUpdate:modelValue":e[2]||(e[2]=r=>u.showModal=r),size:"sm"},{header:t(()=>e[4]||(e[4]=[n(" openWB neu starten... ",-1)])),footer:t(()=>[o(a,null,{default:t(()=>[o(h,null,{default:t(()=>[o(d,{class:"_text-align:right"},{default:t(()=>[o(s,{color:"success",onClick:e[0]||(e[0]=r=>m.cancel())},{default:t(()=>e[6]||(e[6]=[n(" Zurück ",-1)])),_:1,__:[6]})]),_:1}),o(d,null,{default:t(()=>[o(s,{color:"danger",onClick:e[1]||(e[1]=r=>m.confirm())},{default:t(()=>e[7]||(e[7]=[n(" Neustart ",-1)])),_:1,__:[7]})]),_:1})]),_:1})]),_:1})]),default:t(()=>[o(a,null,{default:t(()=>e[5]||(e[5]=[n(" Möchten Sie diese openWB wirklich neu starten? ",-1)])),_:1,__:[5]})]),_:1},8,["modelValue"])]))]),_:1,__:[8]})}]]);k.add(A);const Z=p({name:"StatusView",components:{ReloadButton:F,RebootButton:P,ShutdownButton:p({name:"ShutdownButton",components:{FontAwesomeIcon:b},props:{},data:()=>({mqttStore:y(),showModal:!1}),methods:{toggleModal(){this.showModal=!this.showModal},cancel(){this.toggleModal()},confirm(){this.toggleModal(),this.mqttStore.settings.parentChargePoint1!==void 0?this.$root.sendSystemCommand("chargepointShutdown",{chargePoint:this.mqttStore.settings.parentChargePoint1}):this.$root.sendSystemCommand("systemShutdown")}}},[["render",function(w,e,f,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-container"),s=l("i-button"),d=l("i-column"),h=l("i-row"),g=l("i-modal");return _(),c(s,{color:"danger",size:"lg",onClick:e[3]||(e[3]=r=>m.toggleModal())},{default:t(()=>[e[8]||(e[8]=n(" openWB ausschalten ",-1)),o(i,{"fixed-width":"",icon:["fas","fa-power-off"]}),(_(),c(x,{to:"body"},[o(g,{modelValue:u.showModal,"onUpdate:modelValue":e[2]||(e[2]=r=>u.showModal=r),size:"sm"},{header:t(()=>e[4]||(e[4]=[n(" openWB ausschalten... ",-1)])),footer:t(()=>[o(a,null,{default:t(()=>[o(h,null,{default:t(()=>[o(d,{class:"_text-align:right"},{default:t(()=>[o(s,{color:"success",onClick:e[0]||(e[0]=r=>m.cancel())},{default:t(()=>e[6]||(e[6]=[n(" Zurück ",-1)])),_:1,__:[6]})]),_:1}),o(d,null,{default:t(()=>[o(s,{color:"danger",onClick:e[1]||(e[1]=r=>m.confirm())},{default:t(()=>e[7]||(e[7]=[n(" Ausschalten ",-1)])),_:1,__:[7]})]),_:1})]),_:1})]),_:1})]),default:t(()=>[o(a,null,{default:t(()=>e[5]||(e[5]=[n(" Möchten Sie diese openWB wirklich ausschalten?",-1),I("br",null,null,-1),n(" Nach dem Ausschalten muss die Ladestation komplett spannungsfrei geschaltet werden. Erst beim erneuten Zuschalten der Spannung fährt das System wieder hoch. ",-1)])),_:1,__:[5]})]),_:1},8,["modelValue"])]))]),_:1,__:[8]})}]]),DashboardCard:z},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:y()})},[["render",function(w,e,f,S,u,m){const i=l("i-form-label"),a=l("i-column"),s=l("i-input"),d=l("i-row"),h=l("i-form-group"),g=l("reload-button"),r=l("reboot-button"),q=l("shutdown-button"),B=l("i-form"),v=l("i-container"),V=l("dashboard-card");return _(),c(V,{color:"primary"},{headerLeft:t(()=>e[0]||(e[0]=[n(" Status ",-1)])),default:t(()=>[o(v,null,{default:t(()=>[o(B,null,{default:t(()=>[o(d,null,{default:t(()=>[o(a,null,{default:t(()=>[o(h,null,{default:t(()=>[o(d,{class:"_margin-top:2"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[1]||(e[1]=[n(" IP-Adresse ",-1)])),_:1,__:[1]})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_ip",plaintext:"","model-value":u.mqttStore.getSystemIp},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[2]||(e[2]=[n(" Systemzeit ",-1)])),_:1,__:[2]})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_time",plaintext:"","model-value":u.mqttStore.getSystemTime},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[3]||(e[3]=[n(" Version ",-1)])),_:1,__:[3]})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_version",plaintext:"","model-value":u.mqttStore.getSystemVersion},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[4]||(e[4]=[n(" Version (Details) ",-1)])),_:1,__:[4]})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_commit",plaintext:"","model-value":u.mqttStore.getSystemCurrentCommit},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[5]||(e[5]=[n(" Entwicklungszweig ",-1)])),_:1,__:[5]})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_branch",plaintext:"","model-value":u.mqttStore.getSystemBranch},null,8,["model-value"])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),f.changesLocked?M("",!0):(_(),c(d,{key:0,class:"_margin-top:5",between:""},{default:t(()=>[o(a,null,{default:t(()=>[o(g,{block:""})]),_:1})]),_:1})),f.changesLocked?M("",!0):(_(),c(d,{key:1,between:""},{default:t(()=>[o(a,null,{default:t(()=>[o(r,{block:"",class:"_margin-top:2"})]),_:1}),o(a,null,{default:t(()=>[o(q,{block:"",class:"_margin-top:2"})]),_:1})]),_:1}))]),_:1})]),_:1})]),_:1})}]]);export{Z as default}; diff --git a/packages/modules/display_themes/cards/web/assets/StatusView-rjhbJP_1.js b/packages/modules/display_themes/cards/web/assets/StatusView-rjhbJP_1.js deleted file mode 100644 index 206972e98b..0000000000 --- a/packages/modules/display_themes/cards/web/assets/StatusView-rjhbJP_1.js +++ /dev/null @@ -1 +0,0 @@ -import{u as y}from"./index-nI_NVV5B.js";import{F as b,l as k,C as M,D as A}from"./vendor-fortawesome-CpQlJZ13.js";import{_ as p}from"./vendor-inkline-C_NPDnDu.js";import{l as c,q as t,p as l,o as f,k as n,s as o,I as x,i as I,f as C}from"./vendor-BMrK3KHF.js";import{D as z}from"./DashBoardCard-DJZ2GsC-.js";k.add(M);const D=p({name:"ReloadButton",components:{FontAwesomeIcon:b},props:{},data:()=>({}),methods:{reloadDisplay(){location.reload()}}},[["render",function(w,e,h,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-button");return f(),c(a,{color:"success",size:"lg",onClick:e[0]||(e[0]=s=>m.reloadDisplay())},{default:t(()=>[e[1]||(e[1]=n(" Display neu laden ")),o(i,{"fixed-width":"",icon:["fas","fa-undo"]})]),_:1})}]]);k.add(M);const F=p({name:"RebootButton",components:{FontAwesomeIcon:b},props:{},data:()=>({mqttStore:y(),showModal:!1}),methods:{toggleModal(){this.showModal=!this.showModal},cancel(){this.toggleModal()},confirm(){this.toggleModal(),this.mqttStore.settings.parentChargePoint1!==void 0?this.$root.sendSystemCommand("chargepointReboot",{chargePoint:this.mqttStore.settings.parentChargePoint1}):this.$root.sendSystemCommand("systemReboot")}}},[["render",function(w,e,h,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-container"),s=l("i-button"),d=l("i-column"),_=l("i-row"),g=l("i-modal");return f(),c(s,{color:"warning",size:"lg",onClick:e[3]||(e[3]=r=>m.toggleModal())},{default:t(()=>[e[8]||(e[8]=n(" openWB neu starten ")),o(i,{"fixed-width":"",icon:["fas","fa-undo"]}),(f(),c(x,{to:"body"},[o(g,{modelValue:u.showModal,"onUpdate:modelValue":e[2]||(e[2]=r=>u.showModal=r),size:"sm"},{header:t(()=>e[4]||(e[4]=[n(" openWB neu starten... ")])),footer:t(()=>[o(a,null,{default:t(()=>[o(_,null,{default:t(()=>[o(d,{class:"_text-align:right"},{default:t(()=>[o(s,{color:"success",onClick:e[0]||(e[0]=r=>m.cancel())},{default:t(()=>e[6]||(e[6]=[n(" Zurück ")])),_:1})]),_:1}),o(d,null,{default:t(()=>[o(s,{color:"danger",onClick:e[1]||(e[1]=r=>m.confirm())},{default:t(()=>e[7]||(e[7]=[n(" Neustart ")])),_:1})]),_:1})]),_:1})]),_:1})]),default:t(()=>[o(a,null,{default:t(()=>e[5]||(e[5]=[n(" Möchten Sie diese openWB wirklich neu starten? ")])),_:1})]),_:1},8,["modelValue"])]))]),_:1})}]]);k.add(A);const Z=p({name:"StatusView",components:{ReloadButton:D,RebootButton:F,ShutdownButton:p({name:"ShutdownButton",components:{FontAwesomeIcon:b},props:{},data:()=>({mqttStore:y(),showModal:!1}),methods:{toggleModal(){this.showModal=!this.showModal},cancel(){this.toggleModal()},confirm(){this.toggleModal(),this.mqttStore.settings.parentChargePoint1!==void 0?this.$root.sendSystemCommand("chargepointShutdown",{chargePoint:this.mqttStore.settings.parentChargePoint1}):this.$root.sendSystemCommand("systemShutdown")}}},[["render",function(w,e,h,S,u,m){const i=l("FontAwesomeIcon"),a=l("i-container"),s=l("i-button"),d=l("i-column"),_=l("i-row"),g=l("i-modal");return f(),c(s,{color:"danger",size:"lg",onClick:e[3]||(e[3]=r=>m.toggleModal())},{default:t(()=>[e[8]||(e[8]=n(" openWB ausschalten ")),o(i,{"fixed-width":"",icon:["fas","fa-power-off"]}),(f(),c(x,{to:"body"},[o(g,{modelValue:u.showModal,"onUpdate:modelValue":e[2]||(e[2]=r=>u.showModal=r),size:"sm"},{header:t(()=>e[4]||(e[4]=[n(" openWB ausschalten... ")])),footer:t(()=>[o(a,null,{default:t(()=>[o(_,null,{default:t(()=>[o(d,{class:"_text-align:right"},{default:t(()=>[o(s,{color:"success",onClick:e[0]||(e[0]=r=>m.cancel())},{default:t(()=>e[6]||(e[6]=[n(" Zurück ")])),_:1})]),_:1}),o(d,null,{default:t(()=>[o(s,{color:"danger",onClick:e[1]||(e[1]=r=>m.confirm())},{default:t(()=>e[7]||(e[7]=[n(" Ausschalten ")])),_:1})]),_:1})]),_:1})]),_:1})]),default:t(()=>[o(a,null,{default:t(()=>e[5]||(e[5]=[n(" Möchten Sie diese openWB wirklich ausschalten?"),I("br",null,null,-1),n(" Nach dem Ausschalten muss die Ladestation komplett spannungsfrei geschaltet werden. Erst beim erneuten Zuschalten der Spannung fährt das System wieder hoch. ")])),_:1})]),_:1},8,["modelValue"])]))]),_:1})}]]),DashBoardCard:z},props:{changesLocked:{required:!1,type:Boolean,default:!1}},data:()=>({mqttStore:y()})},[["render",function(w,e,h,S,u,m){const i=l("i-form-label"),a=l("i-column"),s=l("i-input"),d=l("i-row"),_=l("i-form-group"),g=l("reload-button"),r=l("reboot-button"),B=l("shutdown-button"),q=l("i-form"),v=l("i-container"),V=l("dash-board-card");return f(),c(V,{color:"primary"},{headerLeft:t(()=>e[0]||(e[0]=[n(" Status ")])),default:t(()=>[o(v,null,{default:t(()=>[o(q,null,{default:t(()=>[o(d,null,{default:t(()=>[o(a,null,{default:t(()=>[o(_,null,{default:t(()=>[o(d,{class:"_margin-top:2"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[1]||(e[1]=[n(" IP-Adresse ")])),_:1})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_ip",plaintext:"","model-value":u.mqttStore.getSystemIp},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[2]||(e[2]=[n(" Systemzeit ")])),_:1})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_time",plaintext:"","model-value":u.mqttStore.getSystemTime},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[3]||(e[3]=[n(" Version ")])),_:1})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_version",plaintext:"","model-value":u.mqttStore.getSystemVersion},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[4]||(e[4]=[n(" Version (Details) ")])),_:1})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_commit",plaintext:"","model-value":u.mqttStore.getSystemCurrentCommit},null,8,["model-value"])]),_:1})]),_:1}),o(d,{class:"_margin-top:1"},{default:t(()=>[o(a,{xl:"2",lg:"3",md:"4"},{default:t(()=>[o(i,null,{default:t(()=>e[5]||(e[5]=[n(" Entwicklungszweig ")])),_:1})]),_:1}),o(a,null,{default:t(()=>[o(s,{id:"input_system_branch",plaintext:"","model-value":u.mqttStore.getSystemBranch},null,8,["model-value"])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1}),h.changesLocked?C("",!0):(f(),c(d,{key:0,class:"_margin-top:5",between:""},{default:t(()=>[o(a,null,{default:t(()=>[o(g,{block:""})]),_:1})]),_:1})),h.changesLocked?C("",!0):(f(),c(d,{key:1,between:""},{default:t(()=>[o(a,null,{default:t(()=>[o(r,{block:"",class:"_margin-top:2"})]),_:1}),o(a,null,{default:t(()=>[o(B,{block:"",class:"_margin-top:2"})]),_:1})]),_:1}))]),_:1})]),_:1})]),_:1})}]]);export{Z as default}; diff --git a/packages/modules/display_themes/cards/web/assets/index-BBaYm_Sj.css b/packages/modules/display_themes/cards/web/assets/index-BBaYm_Sj.css deleted file mode 100644 index bbe9cecd9a..0000000000 --- a/packages/modules/display_themes/cards/web/assets/index-BBaYm_Sj.css +++ /dev/null @@ -1 +0,0 @@ -.nav-item[data-v-1db6ae73]{margin-bottom:var(--spacing)!important;border-radius:var(--border-radius)}.nav-item.-active[data-v-1db6ae73]{background-color:var(--color--primary)}.pin-button-column[data-v-4e2103db]{display:flex;flex-grow:1}.pin-button[data-v-4e2103db]{min-height:2em;flex-grow:1;font-size:200%;font-weight:700}.touch-blocker[data-v-e4e632d4]{-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}[data-v-e4e632d4] .modal{box-shadow:none}[data-v-e4e632d4] .modal>.modal-body{display:flex;flex-direction:column;align-items:center;background:transparent;border:none}.layout-aside[data-v-6804b914]{----width: 10rem !important}.layout-content[data-v-6804b914]{margin-left:calc(10rem + var(--spacing));margin-right:var(--spacing)}hr[data-v-6804b914]{border-color:var(--color--primary);margin:var(--spacing) 0}.container[data-v-16686e30],.row[data-v-16686e30]{height:100vh}.card[data-v-16686e30]{----background: inherit !important;----body--color: var(--contrast-color-for-dark-background) !important}img.logo[data-v-16686e30]{max-width:100%}:root{--spacing: .5rem}html,body{overscroll-behavior:none}body{overflow-y:overlay}html,body{scrollbar-width:thin;scrollbar-color:var(--color--primary-70) transparent}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--color--primary-70);border-radius:2px} diff --git a/packages/modules/display_themes/cards/web/assets/index-C0J-ZJ88.js b/packages/modules/display_themes/cards/web/assets/index-C0J-ZJ88.js new file mode 100644 index 0000000000..e03ca0aaeb --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/index-C0J-ZJ88.js @@ -0,0 +1,2 @@ +const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/DashboardView-Ch-ScCyj.js","assets/DashboardCard-DS7AgUYm.js","assets/vendor-inkline-S9CBmrTS.js","assets/vendor-Bzn5cd2Y.js","assets/vendor-inkline-BxOXWZNy.css","assets/DashboardCard-Cy_vzq6d.css","assets/ChargePointPlugBadge-Dsdv-7Vn.js","assets/vendor-fortawesome-C1Wk2aFl.js","assets/ChargePointPlugBadge-HWp2u3dG.css","assets/DashboardView-BYiJMWFT.css","assets/EnergyFlowView-C1Dw5jS1.js","assets/ChargeModeModal-CNWdoEpw.js","assets/ChargeModeModal-CGs_z8ao.css","assets/EnergyFlowView-BOyDZ-Ju.css","assets/ChargePointsView-InJz5_Tj.js","assets/ChargePointsView-BF43Lifc.css","assets/StatusView-9xI7RUa0.js"])))=>i.map(i=>d[i]); +import{e as P,k as f,j as V,x as k,F as S,o as p,i as W,H as F,l as C,q as c,p as h,f as w,s,A as D,I as E,z as A,R as H,J as R,K as U,L as z,M as G,N as K}from"./vendor-Bzn5cd2Y.js";import{_ as b,I as J,c as Y}from"./vendor-inkline-S9CBmrTS.js";import{l as $,f as L,a as q,F as O,b as Z,c as Q}from"./vendor-fortawesome-C1Wk2aFl.js";(function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const o of document.querySelectorAll('link[rel="modulepreload"]'))t(o);new MutationObserver(o=>{for(const n of o)if(n.type==="childList")for(const a of n.addedNodes)a.tagName==="LINK"&&a.rel==="modulepreload"&&t(a)}).observe(document,{childList:!0,subtree:!0})}function t(o){if(o.ep)return;o.ep=!0;const n=function(a){const i={};return a.integrity&&(i.integrity=a.integrity),a.referrerPolicy&&(i.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?i.credentials="include":a.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}(o);fetch(o.href,n)}})();const X=b({name:"DateTime",data:()=>({dateTimeInterval:"",date:"",time:""}),mounted(){this.update(),this.dateTimeInterval=setInterval(this.update,1e3)},beforeUnmount(){clearInterval(this.dateTimeInterval)},methods:{update(){const e=new Date;this.date=e.toLocaleDateString(void 0,{weekday:"short",year:"numeric",month:"2-digit",day:"2-digit"}),this.time=e.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit"})}}},[["render",function(e,t,o,n,a,i){return p(),P(S,null,[f(k(a.time),1),V(e.$slots,"default",{},()=>[t[0]||(t[0]=W("br",null,null,-1))]),f(k(a.date),1)],64)}]]),B=F("mqtt",{state:()=>({settings:{localIp:void 0,localBranch:void 0,localCommit:void 0,localVersion:void 0,parentChargePoint1:void 0,parentChargePoint2:void 0},topics:{},chartData:{}}),getters:{getChargePointFilter:e=>{let t=[];return e.settings.parentChargePoint1!==void 0&&t.push(e.settings.parentChargePoint1),e.settings.parentChargePoint2!==void 0&&t.push(e.settings.parentChargePoint2),t},getWildcardIndexList:e=>(t,o=!1)=>{let n=t;o||(n="^"+t.replaceAll("/","\\/").replaceAll("+","[^+/]+").replaceAll("#","[^#/]+")+"$");let a=Object.keys(e.topics).filter(i=>i.match(n));return a.forEach((i,r,l)=>{l[r]=parseInt(i.match(/(?:\/)([0-9]+)(?=\/)*/g)[0].replace(/[^0-9]+/g,""))}),a},getWildcardTopics:e=>(t,o=!1)=>{let n=t;return o||(n="^"+t.replaceAll("/","\\/").replaceAll("+","[^+/]+").replaceAll("#","[^#/]+")+"$"),Object.keys(e.topics).filter(a=>a.match(n)).reduce((a,i)=>({...a,[i]:e.topics[i]}),{})},getObjectIds:e=>t=>function o(n){let a=[];return n!==void 0&&n.forEach(i=>{i.type==t&&a.push(i.id),a=[...a,...o(i.children)]}),a}(e.topics["openWB/counter/get/hierarchy"]),getValueBool:e=>(t,o=!1)=>{let n=e.topics[t];return n!==void 0?n:o},getValueString:e=>(t,o="W",n="",a=!0,i=!1,r="---",l=void 0)=>{var g=!1,d=e.topics[t];if(d===void 0||l!==void 0&&d[l]===void 0)m=r;else{l!==void 0&&(d=d[l]),i&&(d*=-1);for(var m=d.toLocaleString(void 0,{minimumFractionDigits:0,maximumFractionDigits:0}),u=d;a&&(u>999||u<-999);)switch(u/=1e3,g=!0,n){case"":n="k";break;case"k":n="M";break;case"M":n="G"}m=u.toLocaleString(void 0,{minimumFractionDigits:g?2:0,maximumFractionDigits:g?2:0})}return{textValue:`${m} ${n}${o}`,value:d,unit:o,scaledValue:u,scaledUnit:`${n}${o}`}},getChartData:e=>t=>e.chartData[t]===void 0?[]:e.chartData[t],getDisplayStandby:e=>e.topics["openWB/optional/int_display/standby"],getThemeConfiguration:e=>{if("openWB/optional/int_display/theme"in e.topics&&e.topics["openWB/optional/int_display/theme"]!==void 0&&"configuration"in e.topics["openWB/optional/int_display/theme"])return e.topics["openWB/optional/int_display/theme"].configuration},getDefaultView:e=>{if(e.getThemeConfiguration){const t={dashboard:e.getThemeConfiguration.enable_dashboard_view,"energy-flow":e.getThemeConfiguration.enable_energy_flow_view,"charge-points":e.getThemeConfiguration.enable_charge_points_view,status:e.getThemeConfiguration.enable_status_view};if(e.getThemeConfiguration.default_view!==void 0&&t[e.getThemeConfiguration.default_view]===!0)return e.getThemeConfiguration.default_view;for(const[o,n]of Object.entries(t))if(n)return o}},getDefaultViewTimeout:e=>e.getThemeConfiguration?e.getThemeConfiguration.default_view_timeout:0,getDashboardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_view,getEnergyFlowEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_energy_flow_view,getChargePointsEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_charge_points_view,getStateEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_status_view,getGridCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_grid,getHomeCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_home_consumption,getBatteryCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_battery_sum,getChargePointsCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_charge_point_sum,getPvCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_inverter_sum,getLockChanges:e=>!e.getThemeConfiguration||e.getThemeConfiguration.lock_changes,getSimpleChargePointView:e=>!!e.getThemeConfiguration&&e.getThemeConfiguration.simple_charge_point_view,getGridId(e){let t=e.topics["openWB/counter/get/hierarchy"];if(t!==void 0&&Object.keys(t).length>0){let o=Object.keys(e.topics["openWB/counter/get/hierarchy"])[0];if(e.topics["openWB/counter/get/hierarchy"][o].type=="counter")return e.topics["openWB/counter/get/hierarchy"][o].id}},getGridPower:e=>(t="textValue")=>{let o=e.getGridId;if(o===void 0)return"---";let n=e.getValueString(`openWB/counter/${o}/get/power`,"W");return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getGridPowerChartData(e){let t=e.getGridId;return t===void 0?[]:e.getChartData(`openWB/counter/${t}/get/power`)},getHomePower:e=>(t="textValue")=>{let o=e.getValueString("openWB/counter/set/home_consumption","W");return Object.hasOwnProperty.call(o,t)?o[t]:t=="object"?o:void 0},getHomePowerChartData:e=>e.getChartData("openWB/counter/set/home_consumption"),getBatteryConfigured:e=>e.getValueBool("openWB/bat/config/configured"),getBatteryPower:e=>(t="textValue")=>{let o=e.getValueString("openWB/bat/get/power","W");return Object.hasOwnProperty.call(o,t)?o[t]:t=="object"?o:void 0},getBatteryPowerChartData:e=>e.getChartData("openWB/bat/get/power"),getBatterySoc:e=>(t="textValue")=>{let o=e.getValueString("openWB/bat/get/soc","%","",!1);return Object.hasOwnProperty.call(o,t)?o[t]:t=="object"?o:void 0},getBatterySocChartData:e=>e.getChartData("openWB/bat/get/soc"),getPvConfigured:e=>e.getValueBool("openWB/pv/config/configured"),getPvPower:e=>(t="textValue")=>{var o=e.getValueString("openWB/pv/get/power","W","",!0,!0);return Object.hasOwnProperty.call(o,t)?o[t]:t=="object"?o:void 0},getPvPowerChartData:e=>e.getChartData("openWB/pv/get/power").map(t=>-1*t),getChargePointSumPower:e=>(t="textValue")=>{var o=e.getValueString("openWB/chargepoint/get/power","W");return Object.hasOwnProperty.call(o,t)?o[t]:t=="object"?o:void 0},getChargePointSumPowerChartData:e=>e.getChartData("openWB/chargepoint/get/power"),getChargePointIds(e){let t=e.getObjectIds("cp"),o=this.getChargePointFilter;return o.length>0?t.filter(n=>o.includes(n)):t},getChargePointName:e=>t=>e.topics[`openWB/chargepoint/${t}/config`]!==void 0?e.topics[`openWB/chargepoint/${t}/config`].name:"---",getChargePointPower:e=>(t,o="textValue")=>{var n=e.getValueString(`openWB/chargepoint/${t}/get/power`,"W");return Object.hasOwnProperty.call(n,o)?n[o]:o=="object"?n:void 0},getChargePointImportedSincePlugged:e=>t=>({energy:e.getValueString(`openWB/chargepoint/${t}/set/log`,"Wh","",!0,!1,"---","imported_since_plugged").textValue,range:e.getValueString(`openWB/chargepoint/${t}/set/log`,"m","k",!1,!1,"---","range_charged").textValue}),getChargePointPowerChartData:e=>t=>e.getChartData(`openWB/chargepoint/${t}/get/power`),getChargePointSetCurrent:e=>(t,o="textValue")=>{let n=e.getValueString(`openWB/chargepoint/${t}/set/current`,"A");return Object.hasOwnProperty.call(n,o)?n[o]:o=="object"?n:void 0},getChargePointPhasesInUse:e=>t=>{const o=["/","①","②","③"],n=e.topics[`openWB/chargepoint/${t}/get/phases_in_use`];return n!==void 0&&n>=0&&nt=>e.getValueBool(`openWB/chargepoint/${t}/get/plug_state`),getChargePointChargeState:e=>t=>e.getValueBool(`openWB/chargepoint/${t}/get/charge_state`),getChargePointManualLock:e=>t=>e.getValueBool(`openWB/chargepoint/${t}/set/manual_lock`),getChargepointTagState:e=>t=>[void 0,null,""].includes(e.topics[`openWB/chargepoint/${t}/set/rfid`])?[void 0,null,""].includes(e.topics[`openWB/chargepoint/${t}/get/rfid`])?0:1:2,getChargePointConnectedVehicleConfig:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/config`],getChargePointConnectedVehicleChargeMode:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.translateChargeMode(e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.selected)},getChargePointConnectedVehiclePriority:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).prio},getChargePointConnectedVehicleInfo:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`],getChargePointConnectedVehicleId:e=>t=>{if(e.getChargePointConnectedVehicleInfo(t))return e.getChargePointConnectedVehicleInfo(t).id},getChargePointConnectedVehicleChargeTemplateIndex:e=>t=>{if(e.getChargePointConnectedVehicleConfig(t))return e.getChargePointConnectedVehicleConfig(t).charge_template},getChargePointConnectedVehicleChargeTemplate:e=>t=>e.topics[`openWB/chargepoint/${t}/set/charge_template`],getChargePointConnectedVehicleEvTemplate:e=>t=>{if(e.getChargePointConnectedVehicleConfig(t))return e.getChargePointConnectedVehicleConfig(t).ev_template},getChargePointConnectedVehicleName:e=>t=>{if(e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`])return e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`].name},getChargePointConnectedVehicleSoc:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/soc`],getChargePointConnectedVehicleTimeChargingActive:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).time_charging.active},getChargePointConnectedVehicleTimeChargingRunning:e=>t=>{let o=e.getChargePointConnectedVehicleConfig(t).time_charging_in_use;return o!==void 0&&o},getChargePointConnectedVehicleInstantChargingCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.instant_charging.current},getChargePointConnectedVehicleInstantChargingLimit:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.instant_charging.limit:{selected:void 0},getChargePointConnectedVehicleInstantChargingPhases:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.instant_charging.phases_to_use},getChargePointConnectedVehiclePvChargingFeedInLimit:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.feed_in_limit},getChargePointConnectedVehiclePvChargingMinCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_current},getChargePointConnectedVehiclePvChargingPhases:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.phases_to_use},getChargePointConnectedVehiclePvChargingLimit:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.limit:{selected:void 0},getChargePointConnectedVehiclePvChargingMinSoc:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_soc},getChargePointConnectedVehiclePvChargingMinSocCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_soc_current},getChargePointConnectedVehiclePvChargingMinSocPhases:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.phases_to_use_min_soc},getChargePointConnectedVehicleEcoChargingCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.eco_charging.current},getChargePointConnectedVehicleEcoChargingPhases:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.eco_charging.phases_to_use},getChargePointConnectedVehicleEcoChargingLimit:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.eco_charging.limit:{selected:void 0},getChargePointConnectedVehicleEcoChargingMaxPrice:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return 1e5*e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.eco_charging.max_price},getChargePointConnectedVehicleScheduledChargingPlans:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.scheduled_charging.plans:{},getChargePointConnectedVehicleTimeChargingPlans:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).time_charging.plans:{},getVehicleList:e=>e.getWildcardTopics("openWB/vehicle/+/name"),getVehicleName:e=>t=>e.topics[`openWB/vehicle/${t}/name`],getVehicleSocConfigured:e=>t=>e.topics[`openWB/vehicle/${t}/soc_module/config`].type!=null,getVehicleSocIsManual:e=>t=>e.topics[`openWB/vehicle/${t}/soc_module/config`].type=="manual",getVehicleFaultState:e=>t=>e.topics[`openWB/vehicle/${t}/get/fault_state`]?e.topics[`openWB/vehicle/${t}/get/fault_state`]:0,getSystemTime(e){if(e.topics["openWB/system/time"])return new Date(1e3*e.topics["openWB/system/time"]).toLocaleString()},getSystemIp:e=>e.settings.localIp!==void 0?e.settings.localIp:e.topics["openWB/system/ip_address"]?e.topics["openWB/system/ip_address"]:void 0,getSystemVersion:e=>e.settings.localVersion!==void 0?e.settings.localVersion:e.topics["openWB/system/version"]?e.topics["openWB/system/version"]:void 0,getSystemBranch:e=>e.settings.localBranch!==void 0?e.settings.localBranch:e.topics["openWB/system/current_branch"]?e.topics["openWB/system/current_branch"]:void 0,getSystemCurrentCommit:e=>e.settings.localCommit!==void 0?e.settings.localCommit:e.topics["openWB/system/current_commit"]?e.topics["openWB/system/current_commit"]:void 0,getRfidEnabled(){return this.getValueBool("openWB/optional/rfid/active")},getEtConfigured:e=>e.topics["openWB/optional/et/provider"]!==void 0&&e.topics["openWB/optional/et/provider"].type!==null,getEtPrices:e=>e.topics["openWB/optional/et/get/prices"]},actions:{updateSetting(e,t){e in this.settings&&(this.settings[e]=t)},initTopic(e,t=void 0){e.includes("#")||e.includes("+")||this.addTopic(e,t)},addTopic(e,t){this.topics[e]=t},removeTopic(e){e.includes("#")||e.includes("+")?Object.keys(this.getWildcardTopics(e)).forEach(t=>{delete this.topics[t]}):delete this.topics[e]},updateTopic(e,t,o=void 0){return e in this.topics?(o!=null?(n=this.topics[e],i=t,(a=o).split(".").reduce((r,l,g)=>r[l]=a.split(".").length===++g?i:r[l]||{},n)):this.topics[e]=t,this.topics[e]):void 0;var n,a,i},updateChartData(){for(const[e,t]of Object.entries(this.topics))(e.endsWith("home_consumption")||e.endsWith("power")||e.endsWith("soc"))&&(this.chartData[e]===void 0&&(this.chartData[e]=[]),t!=null&&(this.chartData[e].push(t),this.chartData[e].slice(-128)))},updateState(e,t,o=void 0){return this.updateTopic(e,t,o)},chargeModeList(){var e=[{id:"instant_charging"},{id:"pv_charging"},{id:"scheduled_charging"},{id:"eco_charging"},{id:"stop"}];return e.forEach(t=>{t.label=this.translateChargeMode(t.id).label,t.class=this.translateChargeMode(t.id).class}),e},translateChargeMode(e){switch(e){case"instant_charging":return{mode:e,label:"Sofort",class:"danger"};case"pv_charging":return{mode:e,label:"PV",class:"success"};case"scheduled_charging":return{mode:e,label:"Ziel",class:"primary"};case"time_charging":return{mode:e,label:"Zeit",class:"warning"};case"eco_charging":return{mode:e,label:"Eco",class:"secondary"};case"stop":return{mode:e,label:"Stop",class:"dark"};default:return{mode:e,label:e,class:e}}},checkChangesLockCode(e){return!(!this.getThemeConfiguration||this.getThemeConfiguration.lock_changes_code!=e)},formatDate:(e,t={year:"numeric",month:"2-digit",day:"2-digit"})=>new Date(e).toLocaleDateString(void 0,t),formatDateRange(e,t="-"){const o={year:"numeric",month:"2-digit",day:"2-digit"};let n={day:"2-digit"};const a=new Date(e[0]),i=new Date(e[1]);return a.getFullYear()==i.getFullYear()?(t=`.${t}`,a.getMonth()!=i.getMonth()&&(n.month=o.month)):n=o,`${this.formatDate(e[0],n)}${t}${this.formatDate(e[1],o)}`},formatWeeklyScheduleDays(e){const t=["Mo","Di","Mi","Do","Fr","Sa","So"];let o=[],n=null;return e.forEach((a,i)=>{a?n===null&&(n=i):n!==null&&(n===i-1?o.push(t[n]):o.push(`${t[n]}-${t[i-1]}`),n=null)}),n!==null&&(n===e.length-1?o.push(t[n]):o.push(`${t[n]}-${t[e.length-1]}`)),o.join(", ")}}}),ee=b({name:"NavBar",components:{NavItem:b({name:"NavItem",props:{to:{type:Object,required:!0}}},[["render",function(e,t,o,n,a,i){const r=h("i-nav-item");return p(),C(r,{to:o.to,"active-class":"-active",class:"_border _border-color:primary _text-align:center"},{default:c(()=>[V(e.$slots,"default",{},void 0,!0)]),_:3},8,["to"])}],["__scopeId","data-v-1db6ae73"]])},data:()=>({mqttStore:B()})},[["render",function(e,t,o,n,a,i){const r=h("nav-item"),l=h("i-nav");return p(),C(l,{vertical:"",class:"_align-items:stretch",size:"lg"},{default:c(()=>[a.mqttStore.getDashboardEnabled?(p(),C(r,{key:0,to:{name:"dashboard"}},{default:c(()=>t[0]||(t[0]=[f(" Übersicht ",-1)])),_:1,__:[0]})):w("",!0),a.mqttStore.getEnergyFlowEnabled?(p(),C(r,{key:1,to:{name:"energy-flow"}},{default:c(()=>t[1]||(t[1]=[f(" Energiefluss ",-1)])),_:1,__:[1]})):w("",!0),a.mqttStore.getChargePointsEnabled&&a.mqttStore.getChargePointIds.length>0?(p(),C(r,{key:2,to:{name:"charge-points"}},{default:c(()=>t[2]||(t[2]=[f(" Ladepunkte ",-1)])),_:1,__:[2]})):w("",!0),a.mqttStore.getStateEnabled?(p(),C(r,{key:3,to:{name:"status"}},{default:c(()=>t[3]||(t[3]=[f(" Status ",-1)])),_:1,__:[3]})):w("",!0)]),_:1})}]]);$.add(L,q);const te=b({name:"NumberPad",components:{FontAwesomeIcon:O},emits:["key:digit","key:clear","key:delete"],data:()=>({buttonRows:[[{value:1,label:"1"},{value:2,label:"2"},{value:3,label:"3"}],[{value:4,label:"4"},{value:5,label:"5"},{value:6,label:"6"}],[{value:7,label:"7"},{value:8,label:"8"},{value:9,label:"9"}]]}),methods:{emitDigit(e){this.$emit("key:digit",e)},emitClear(){this.$emit("key:clear")},emitDelete(){this.$emit("key:delete")}}},[["render",function(e,t,o,n,a,i){const r=h("i-button"),l=h("i-column"),g=h("i-row"),d=h("FontAwesomeIcon"),m=h("i-container");return p(),C(m,null,{default:c(()=>[(p(!0),P(S,null,D(a.buttonRows,u=>(p(),C(g,{key:u,center:"",class:"_padding-bottom:1"},{default:c(()=>[(p(!0),P(S,null,D(u,_=>(p(),C(l,{key:_.value,class:"pin-button-column"},{default:c(()=>[s(r,{size:"lg",class:"pin-button",onClick:v=>i.emitDigit(_.value)},{default:c(()=>[f(k(_.label),1)]),_:2},1032,["onClick"])]),_:2},1024))),128))]),_:2},1024))),128)),s(g,{center:""},{default:c(()=>[s(l,{class:"pin-button-column"},{default:c(()=>[s(r,{size:"lg",class:"pin-button",onClick:t[0]||(t[0]=u=>i.emitClear())},{default:c(()=>[s(d,{"fixed-width":"",icon:["fas","fa-eraser"]})]),_:1})]),_:1}),s(l,{class:"pin-button-column"},{default:c(()=>[s(r,{size:"lg",class:"pin-button",onClick:t[1]||(t[1]=u=>i.emitDigit("0"))},{default:c(()=>t[3]||(t[3]=[f(" 0 ",-1)])),_:1,__:[3]})]),_:1}),s(l,{class:"pin-button-column"},{default:c(()=>[s(r,{size:"lg",class:"pin-button",onClick:t[2]||(t[2]=u=>i.emitDelete())},{default:c(()=>[s(d,{"fixed-width":"",icon:["fas","fa-delete-left"]})]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-4e2103db"]]),oe=b({name:"CodeInputModal",components:{NumberPad:te},props:{modelValue:{type:Boolean,required:!0},backgroundColor:{type:String,default:"warning"},placeholderCharacter:{type:String,default:"*",validator:e=>e.length==1},inputVisible:{type:Boolean,default:!1},minLength:{type:Number,default:4},maxLength:{type:Number,default:4}},emits:["update:modelValue","update:inputValue"],data(){return{number:"",modalBackground:this.backgroundColor}},computed:{placeholder(){return this.placeholderCharacter.repeat(this.minLength)},enableSubmit(){return this.number.length>=this.minLength&&this.number.length<=this.maxLength}},watch:{modelValue(e,t){e===!1&&t===!0&&this.clear()}},methods:{abort(){this.$emit("update:modelValue",!1)},addDigit(e){this.number.length{this.$emit("update:modelValue",!1),this.modalBackground=this.backgroundColor},500)},error(e="danger"){this.modalBackground=e,setTimeout(()=>{this.clear(),this.modalBackground=this.backgroundColor},2e3)}}},[["render",function(e,t,o,n,a,i){const r=h("i-input"),l=h("i-column"),g=h("i-row"),d=h("NumberPad"),m=h("i-container"),u=h("i-button"),_=h("i-modal");return p(),C(E,{to:"body"},[s(_,{"model-value":o.modelValue,color:a.modalBackground,"onUpdate:modelValue":t[4]||(t[4]=v=>e.$emit("update:modelValue",v))},{header:c(()=>[V(e.$slots,"header",{},()=>[t[5]||(t[5]=f(" **HEADER** ",-1))])]),footer:c(()=>[s(m,null,{default:c(()=>[s(g,null,{default:c(()=>[s(l,null,{default:c(()=>[s(u,{color:"danger",onClick:i.abort},{default:c(()=>[V(e.$slots,"abort",{},()=>[t[6]||(t[6]=f(" Zurück ",-1))])]),_:3},8,["onClick"])]),_:3}),s(l,{class:"_text-align:right"},{default:c(()=>[i.enableSubmit?(p(),C(u,{key:0,color:"success",onClick:i.submit},{default:c(()=>[V(e.$slots,"submit",{},()=>[t[7]||(t[7]=f(" OK ",-1))])]),_:3},8,["onClick"])):w("",!0)]),_:3})]),_:3})]),_:3})]),default:c(()=>[s(m,null,{default:c(()=>[s(g,{center:"",class:"_padding-bottom:1"},{default:c(()=>[s(l,null,{default:c(()=>[s(r,{modelValue:a.number,"onUpdate:modelValue":t[0]||(t[0]=v=>a.number=v),placeholder:i.placeholder,readonly:"",size:"lg",type:o.inputVisible?"text":"password",class:"_text-align:center"},null,8,["modelValue","placeholder","type"])]),_:1})]),_:1}),s(d,{"onKey:digit":t[1]||(t[1]=v=>i.addDigit(v)),"onKey:clear":t[2]||(t[2]=v=>i.clear()),"onKey:delete":t[3]||(t[3]=v=>i.removeDigit(v))})]),_:1})]),_:3},8,["model-value","color"])])}]]);$.add(Z,Q,L,q);const ne={key:0,class:"_padding-left:1"},x="/openWB/web/display/themes/cards/openWB_logo_dark.png",ie=b({name:"OpenwbDisplayCardsApp",components:{RouterView:H,DateTime:X,NavBar:ee,LockNavItem:b({name:"LockNavItem",components:{FontAwesomeIcon:O,CodeInputModal:oe},props:{},data:()=>({mqttStore:B(),modalPinEntryVisible:!1,modalPinEntryColor:"warning",code:"",countdown:0,countdownInterval:void 0,events:["mousemove","touchmove","wheel","click"]}),computed:{changesLocked:{get(){return this.mqttStore.settings.changesLocked},set(e){this.mqttStore.settings.changesLocked=e}},timer(){return Math.trunc(this.countdown/60).toString()+":"+(this.countdown%60).toString().padStart(2,"0")}},mounted(){this.changesLocked=!0},methods:{toggleChangesLock(){this.changesLocked?this.unlockChanges():this.lockChanges()},unlockChanges(){this.modalPinEntryVisible=!0},checkUnlockCode(e){this.mqttStore.checkChangesLockCode(e)?(this.$refs.lockInput.success("success"),this.changesLocked=!1,this.mqttStore.getDisplayStandby>0&&(this.countdown=this.mqttStore.getDisplayStandby,this.countdownInterval=setInterval(this.updateCountdown,1e3),this.events.forEach(t=>{document.addEventListener(t,this.handleDocumentEvent,{passive:!0})}))):this.$refs.lockInput.error("danger")},lockChanges(){this.changesLocked=!0,this.events.forEach(e=>{document.removeEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.countdownInterval!==void 0&&(clearInterval(this.countdownInterval),this.countdownInterval=void 0)},updateCountdown(){this.countdown-=1,this.countdown<1&&this.lockChanges()},handleDocumentEvent(){this.countdown=this.mqttStore.getDisplayStandby}}},[["render",function(e,t,o,n,a,i){const r=h("FontAwesomeIcon"),l=h("i-button"),g=h("CodeInputModal");return p(),P(S,null,[a.mqttStore.getLockChanges?(p(),C(l,{key:0,class:"_padding-left:0 _padding-right:0 _margin-bottom:1",size:"lg",block:"",color:i.changesLocked?"danger":"success",onClick:t[0]||(t[0]=d=>i.toggleChangesLock())},{default:c(()=>[s(r,{"fixed-width":"",icon:i.changesLocked?["fas","fa-lock"]:["fas","fa-lock-open"],class:A(i.changesLocked?"_color:danger-80":"_color:success-80")},null,8,["icon","class"]),!i.changesLocked&&a.countdownInterval?(p(),P("span",ne,k(i.timer),1)):w("",!0)]),_:1},8,["color"])):w("",!0),s(g,{ref:"lockInput",modelValue:a.modalPinEntryVisible,"onUpdate:modelValue":t[1]||(t[1]=d=>a.modalPinEntryVisible=d),"min-length":4,"max-length":10,"onUpdate:inputValue":i.checkUnlockCode},{header:c(()=>t[2]||(t[2]=[f(" Bitte den PIN zur Freigabe von Änderungen eingeben. ",-1)])),_:1},8,["modelValue","onUpdate:inputValue"])],64)}]]),TouchBlocker:b({name:"TouchBlocker",data:()=>({mqttStore:B(),show:!1,touchBlockerCountdown:void 0,touchBlockerCountdownInterval:void 0,defaultViewCountdown:void 0,defaultViewCountdownInterval:void 0,events:["mousemove","touchmove","wheel","click"],eventHandlerSetup:!1}),computed:{configuredDisplayStandby(){if(this.mqttStore.getDisplayStandby!==0&&this.mqttStore.getDisplayStandby!==void 0)return this.mqttStore.getDisplayStandby},configuredDefaultViewTimeout(){if(this.mqttStore.getDefaultViewTimeout!==0&&this.mqttStore.getDefaultViewTimeout!==void 0)return this.mqttStore.getDefaultViewTimeout},touchBlockerTimeout(){return Math.max(this.configuredDisplayStandby-3,1)},defaultViewTimeout(){return this.configuredDefaultViewTimeout}},mounted(){this.setupEventHandler(),this.setupTouchBlockerTimeout(),this.setupDefaultViewTimeout()},unmounted(){this.clearEventHandler(),this.clearTouchBlockerTimeout(),this.clearDefaultViewTimeout()},methods:{handleTouchBlockerClick(e){e===!1&&(this.show=!1,this.setupEventHandler(),this.setupTouchBlockerTimeout(),this.setupDefaultViewTimeout())},setupEventHandler(){this.eventHandlerSetup||(this.events.forEach(e=>{document.addEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.eventHandlerSetup=!0)},clearEventHandler(){this.eventHandlerSetup&&(this.events.forEach(e=>{document.removeEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.eventHandlerSetup=!1)},setupTouchBlockerTimeout(){this.touchBlockerCountdownCountdownInterval===void 0&&(this.touchBlockerCountdownCountdownInterval=setInterval(this.updateTouchBlockerCountdown,1e3))},clearTouchBlockerTimeout(){this.touchBlockerCountdownCountdownInterval!==void 0&&(clearInterval(this.touchBlockerCountdownCountdownInterval),this.touchBlockerCountdownCountdownInterval=void 0)},updateTouchBlockerCountdown(){this.touchBlockerCountdown===void 0?this.touchBlockerCountdown=this.touchBlockerTimeout:(this.touchBlockerCountdown-=1,this.touchBlockerCountdown<1&&this.showTouchBlocker())},setupDefaultViewTimeout(){this.defaultViewCountdownInterval===void 0&&this.mqttStore.getDefaultView!==this.$route.name&&this.defaultViewTimeout!==void 0&&(this.defaultViewCountdownInterval=setInterval(this.updateDefaultViewCountdown,1e3))},clearDefaultViewTimeout(){this.defaultViewCountdownInterval!==void 0&&(clearInterval(this.defaultViewCountdownInterval),this.defaultViewCountdownInterval=void 0)},updateDefaultViewCountdown(){this.defaultViewCountdown===void 0&&this.defaultViewTimeout!==void 0?this.defaultViewCountdown=this.defaultViewTimeout:this.$route.name===this.mqttStore.getDefaultView?this.clearDefaultViewTimeout():(this.defaultViewCountdown-=1,this.defaultViewCountdown<1&&this.showDefaultView())},handleDocumentEvent(){this.touchBlockerCountdown=this.touchBlockerTimeout,this.defaultViewCountdown=this.defaultViewTimeout,this.setupDefaultViewTimeout(),this.show=!1},showTouchBlocker(){this.show=!0,this.clearTouchBlockerTimeout()},showDefaultView(){this.clearDefaultViewTimeout(),this.$route.name!==this.mqttStore.getDefaultView&&this.$router.push({name:this.mqttStore.getDefaultView})}}},[["render",function(e,t,o,n,a,i){const r=h("IModal");return p(),C(E,{to:"body"},[s(r,{class:"touch-blocker",size:"sm",color:"dark","model-value":a.show,"onUpdate:modelValue":t[0]||(t[0]=l=>i.handleTouchBlockerClick(l))},{default:c(()=>t[1]||(t[1]=[W("img",{class:"logo",src:x},null,-1),W("p",null," Bitte das Display berühren. ",-1)])),_:1,__:[1]},8,["model-value"])])}],["__scopeId","data-v-2d58d6dd"]])},data:()=>({client:{connected:!1},connection:{protocol:location.protocol=="https:"?"wss":"ws",host:location.hostname,port:parseInt(location.port)||(location.protocol=="https:"?443:80),endpoint:"/ws",connectTimeout:4e3,reconnectPeriod:4e3},mqttTopicsToSubscribe:["openWB/bat/config/configured","openWB/bat/get/power","openWB/bat/get/soc","openWB/chargepoint/+/config","openWB/chargepoint/+/get/charge_state","openWB/chargepoint/+/get/connected_vehicle/+","openWB/chargepoint/+/set/charge_template","openWB/chargepoint/+/set/charge_template/chargemode/scheduled_charging/plans/+","openWB/chargepoint/+/set/charge_template/time_charging/plans/+","openWB/chargepoint/+/get/phases_in_use","openWB/chargepoint/+/get/plug_state","openWB/chargepoint/+/get/power","openWB/chargepoint/+/get/rfid","openWB/chargepoint/+/set/current","openWB/chargepoint/+/set/manual_lock","openWB/chargepoint/+/set/log","openWB/chargepoint/+/set/rfid","openWB/chargepoint/get/power","openWB/counter/+/get/power","openWB/counter/get/hierarchy","openWB/counter/set/home_consumption","openWB/optional/et/provider","openWB/optional/et/get/prices","openWB/optional/int_display/theme","openWB/optional/int_display/standby","openWB/optional/rfid/active","openWB/pv/config/configured","openWB/pv/get/power","openWB/system/current_branch","openWB/system/current_commit","openWB/system/ip_address","openWB/system/time","openWB/system/version","openWB/vehicle/+/get/fault_state","openWB/vehicle/+/name","openWB/vehicle/+/soc_module/config"],mqttStore:B(),chartInterval:"",clearConsoleHandler:void 0}),computed:{changesLocked(){return this.mqttStore.getLockChanges&&this.mqttStore.settings.changesLocked}},created(){this.createConnection()},mounted(){let e=window.location.search;if(e!=""){let n=new URLSearchParams(e);if(n.has("data")){let a=JSON.parse(n.get("data"));Object.entries(a).forEach(([i,r])=>{i.startsWith("parentChargePoint")?this.mqttStore.updateSetting(i,parseInt(r)):this.mqttStore.updateSetting(i,r)})}}this.doSubscribe(this.mqttTopicsToSubscribe),this.chartInterval=setInterval(this.mqttStore.updateChartData,5e3);const t=new Date,o=new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).getTime()-t.getTime();this.clearConsoleHandler=setTimeout(()=>this.clearConsole(),o)},beforeUnmount(){this.doUnsubscribe(this.mqttTopicsToSubscribe),clearInterval(this.chartInterval),clearTimeout(this.clearConsoleHandler)},methods:{clearConsole(){this.clearConsoleHandler=setTimeout(()=>this.clearConsole(),864e5)},createConnection(){const{protocol:e,host:t,port:o,endpoint:n,...a}=this.connection,i=`${e}://${t}:${o}${n}`;try{this.client=R.connect(i,a)}catch{}this.client.on("connect",()=>{}),this.client.on("error",r=>{}),this.client.on("message",(r,l)=>{if(l.toString().length>0){let g;try{g=JSON.parse(l.toString())}catch{g=l.toString()}this.mqttStore.addTopic(r,g)}else this.mqttStore.removeTopic(r)})},doSubscribe(e){e.forEach(t=>{this.mqttStore.initTopic(t)}),this.client.subscribe(e,{},t=>{})},doUnsubscribe(e){e.forEach(t=>{this.mqttStore.removeTopic(t)}),this.client.unsubscribe(e,t=>{})},doPublish(e,t,o=!0,n=2){let a={qos:n,retain:o};this.client.publish(e,JSON.stringify(t),a,i=>{})},sendTopicToBroker(e,t=void 0){let o=e.replace("openWB/","openWB/set/");t===void 0&&(t=this.mqttStore.topics[e]),this.doPublish(o,t)},sendCommand(e){this.doPublish("openWB/set/command/"+this.client.options.clientId+"/todo",e,!1)},sendSystemCommand(e,t={}){this.sendCommand({command:e,data:t})}}},[["render",function(e,t,o,n,a,i){const r=h("DateTime"),l=h("i-column"),g=h("i-row"),d=h("i-container"),m=h("LockNavItem"),u=h("NavBar"),_=h("TouchBlocker"),v=h("i-layout-aside"),j=h("RouterView"),M=h("i-layout-content"),N=h("i-layout");return p(),C(N,{vertical:""},{default:c(()=>[s(v,{class:"_position:fixed"},{default:c(()=>[s(d,{fluid:"",class:"_margin-bottom:1"},{default:c(()=>[s(g,{center:""},{default:c(()=>[s(l,null,{default:c(()=>[s(r)]),_:1})]),_:1})]),_:1}),s(m),s(u,{"changes-locked":i.changesLocked},null,8,["changes-locked"]),s(_)]),_:1}),s(M,null,{default:c(()=>[s(j,{"changes-locked":i.changesLocked},null,8,["changes-locked"])]),_:1})]),_:1})}],["__scopeId","data-v-6f8bc44f"]]),I={},T=function(e,t,o){let n=Promise.resolve();if(t&&t.length>0){document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),r=(i==null?void 0:i.nonce)||(i==null?void 0:i.getAttribute("nonce"));n=Promise.allSettled(t.map(l=>{if((l=function(u){return"/openWB/web/display/themes/cards/"+u}(l))in I)return;I[l]=!0;const g=l.endsWith(".css"),d=g?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${d}`))return;const m=document.createElement("link");return m.rel=g?"stylesheet":"modulepreload",g||(m.as="script"),m.crossOrigin="",m.href=l,r&&m.setAttribute("nonce",r),document.head.appendChild(m),g?new Promise((u,_)=>{m.addEventListener("load",u),m.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${l}`)))}):void 0}))}function a(i){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=i,window.dispatchEvent(r),!r.defaultPrevented)throw i}return n.then(i=>{for(const r of i||[])r.status==="rejected"&&a(r.reason);return e().catch(a)})},ae=b({name:"WelcomeView",data:()=>({mqttStore:B()}),mounted(){setTimeout(this.selectFirstRoute,3e3)},methods:{selectFirstRoute(){this.mqttStore.getDefaultView&&this.$router.push({name:this.mqttStore.getDefaultView})}}},[["render",function(e,t,o,n,a,i){const r=h("i-card"),l=h("i-column"),g=h("i-row"),d=h("i-container");return p(),C(d,null,{default:c(()=>[s(g,{center:"",middle:""},{default:c(()=>[s(l,null,{default:c(()=>[s(r,{color:"primary"},{header:c(()=>t[0]||(t[0]=[f(" Cards Theme ",-1)])),default:c(()=>[t[1]||(t[1]=W("img",{class:"logo",src:x},null,-1))]),_:1,__:[1]})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-e364c277"]]),re=U({history:z("/openWB/web/display/themes/cards/"),routes:[{path:"/",name:"welcome",component:ae},{path:"/Dashboard",name:"dashboard",component:()=>T(()=>import("./DashboardView-Ch-ScCyj.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))},{path:"/EnergyFlow",name:"energy-flow",component:()=>T(()=>import("./EnergyFlowView-C1Dw5jS1.js"),__vite__mapDeps([10,1,2,3,4,5,11,12,7,13]))},{path:"/ChargePoints",name:"charge-points",component:()=>T(()=>import("./ChargePointsView-InJz5_Tj.js"),__vite__mapDeps([14,1,2,3,4,5,6,7,8,11,12,15]))},{path:"/Status",name:"status",component:()=>T(()=>import("./StatusView-9xI7RUa0.js"),__vite__mapDeps([16,7,3,2,4,1,5]))}]}),y=G(ie);y.use(K()),y.use(re),y.use(J,{colorMode:"dark",components:Y}),y.mount("#app");export{oe as C,te as N,B as u}; diff --git a/packages/modules/display_themes/cards/web/assets/index-CcZCkj7F.css b/packages/modules/display_themes/cards/web/assets/index-CcZCkj7F.css new file mode 100644 index 0000000000..176c308feb --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/index-CcZCkj7F.css @@ -0,0 +1 @@ +.nav-item[data-v-1db6ae73]{margin-bottom:var(--spacing)!important;border-radius:var(--border-radius)}.nav-item.-active[data-v-1db6ae73]{background-color:var(--color--primary)}.pin-button-column[data-v-4e2103db]{display:flex;flex-grow:1}.pin-button[data-v-4e2103db]{min-height:2em;flex-grow:1;font-size:200%;font-weight:700}.touch-blocker[data-v-2d58d6dd]{background-color:#000}[data-v-2d58d6dd] .modal{box-shadow:none}[data-v-2d58d6dd] .modal>.modal-body{display:flex;flex-direction:column;align-items:center;background:transparent;border:none}.layout-aside[data-v-6f8bc44f]{----width: 10rem !important}.layout-content[data-v-6f8bc44f]{margin-left:calc(10rem + var(--spacing));margin-right:var(--spacing)}hr[data-v-6f8bc44f]{border-color:var(--color--primary);margin:var(--spacing) 0}.container[data-v-e364c277],.row[data-v-e364c277]{height:100vh}.card[data-v-e364c277]{----background: inherit !important;----body--color: var(--contrast-color-for-dark-background) !important}img.logo[data-v-e364c277]{max-width:100%}:root{--spacing: .5rem}html,body{overscroll-behavior:none}body{overflow-y:overlay}html,body{scrollbar-width:thin;scrollbar-color:var(--color--primary-70) transparent}::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--color--primary-70);border-radius:2px} diff --git a/packages/modules/display_themes/cards/web/assets/index-nI_NVV5B.js b/packages/modules/display_themes/cards/web/assets/index-nI_NVV5B.js deleted file mode 100644 index 2d3f885c16..0000000000 --- a/packages/modules/display_themes/cards/web/assets/index-nI_NVV5B.js +++ /dev/null @@ -1,2 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/DashBoardView-BvptEyMO.js","assets/DashBoardCard-DJZ2GsC-.js","assets/vendor-inkline-C_NPDnDu.js","assets/vendor-BMrK3KHF.js","assets/vendor-inkline-BxOXWZNy.css","assets/DashBoardCard-Bl80pQ9w.css","assets/ChargePointPlugBadge-bP9BlN1o.js","assets/vendor-fortawesome-CpQlJZ13.js","assets/ChargePointPlugBadge-HWp2u3dG.css","assets/DashBoardView-CZvLtoU0.css","assets/EnergyFlowView-D3Xr79nb.js","assets/EnergyFlowView-CMZu-SNR.css","assets/ChargePointsView-DAOuhP6h.js","assets/ChargePointsView-ALIhGmhe.css","assets/StatusView-rjhbJP_1.js"])))=>i.map(i=>d[i]); -import{e as P,k as f,j as B,x as k,F as W,o as p,i as T,H as F,l as C,q as c,p as d,f as y,s as l,A as I,I as E,z as A,R as H,J as R,K as U,L as z,M as G,N as K}from"./vendor-BMrK3KHF.js";import{_ as b,I as J,c as Y}from"./vendor-inkline-C_NPDnDu.js";import{l as $,f as L,a as q,F as O,b as Z,c as Q}from"./vendor-fortawesome-CpQlJZ13.js";(function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const n of document.querySelectorAll('link[rel="modulepreload"]'))t(n);new MutationObserver(n=>{for(const a of n)if(a.type==="childList")for(const i of a.addedNodes)i.tagName==="LINK"&&i.rel==="modulepreload"&&t(i)}).observe(document,{childList:!0,subtree:!0})}function t(n){if(n.ep)return;n.ep=!0;const a=function(i){const o={};return i.integrity&&(o.integrity=i.integrity),i.referrerPolicy&&(o.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?o.credentials="include":i.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}(n);fetch(n.href,a)}})();const X=b({name:"DateTime",data:()=>({dateTimeInterval:"",date:"",time:""}),mounted(){this.update(),this.dateTimeInterval=setInterval(this.update,1e3)},beforeUnmount(){clearInterval(this.dateTimeInterval)},methods:{update(){const e=new Date;this.date=e.toLocaleDateString(void 0,{weekday:"short",year:"numeric",month:"2-digit",day:"2-digit"}),this.time=e.toLocaleTimeString(void 0,{hour:"2-digit",minute:"2-digit",second:"2-digit"})}}},[["render",function(e,t,n,a,i,o){return p(),P(W,null,[f(k(i.time),1),B(e.$slots,"default",{},()=>[t[0]||(t[0]=T("br",null,null,-1))]),f(k(i.date),1)],64)}]]),w=F("mqtt",{state:()=>({settings:{localIp:void 0,localBranch:void 0,localCommit:void 0,localVersion:void 0,parentChargePoint1:void 0,parentChargePoint2:void 0},topics:{},chartData:{}}),getters:{getChargePointFilter:e=>{let t=[];return e.settings.parentChargePoint1!==void 0&&t.push(e.settings.parentChargePoint1),e.settings.parentChargePoint2!==void 0&&t.push(e.settings.parentChargePoint2),t},getWildcardIndexList:e=>(t,n=!1)=>{let a=t;n||(a="^"+t.replaceAll("/","\\/").replaceAll("+","[^+/]+").replaceAll("#","[^#/]+")+"$");let i=Object.keys(e.topics).filter(o=>o.match(a));return i.forEach((o,r,s)=>{s[r]=parseInt(o.match(/(?:\/)([0-9]+)(?=\/)*/g)[0].replace(/[^0-9]+/g,""))}),i},getWildcardTopics:e=>(t,n=!1)=>{let a=t;return n||(a="^"+t.replaceAll("/","\\/").replaceAll("+","[^+/]+").replaceAll("#","[^#/]+")+"$"),Object.keys(e.topics).filter(i=>i.match(a)).reduce((i,o)=>({...i,[o]:e.topics[o]}),{})},getObjectIds:e=>t=>function n(a){let i=[];return a!==void 0&&a.forEach(o=>{o.type==t&&i.push(o.id),i=[...i,...n(o.children)]}),i}(e.topics["openWB/counter/get/hierarchy"]),getValueBool:e=>(t,n=!1)=>{let a=e.topics[t];return a!==void 0?a:n},getValueString:e=>(t,n="W",a="",i=!0,o=!1,r="---",s=void 0)=>{var h=!1,g=e.topics[t];if(g===void 0||s!==void 0&&g[s]===void 0)m=r;else{s!==void 0&&(g=g[s]),o&&(g*=-1);for(var m=g.toLocaleString(void 0,{minimumFractionDigits:0,maximumFractionDigits:0}),u=g;i&&(u>999||u<-999);)switch(u/=1e3,h=!0,a){case"":a="k";break;case"k":a="M";break;case"M":a="G"}m=u.toLocaleString(void 0,{minimumFractionDigits:h?2:0,maximumFractionDigits:h?2:0})}return{textValue:`${m} ${a}${n}`,value:g,unit:n,scaledValue:u,scaledUnit:`${a}${n}`}},getChartData:e=>t=>e.chartData[t]===void 0?[]:e.chartData[t],getDisplayStandby:e=>e.topics["openWB/optional/int_display/standby"],getThemeConfiguration:e=>{if("openWB/optional/int_display/theme"in e.topics&&e.topics["openWB/optional/int_display/theme"]!==void 0&&"configuration"in e.topics["openWB/optional/int_display/theme"])return e.topics["openWB/optional/int_display/theme"].configuration},getDashBoardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_view,getEnergyFlowEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_energy_flow_view,getChargePointsEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_charge_points_view,getStateEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_status_view,getGridCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_grid,getHomeCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_home_consumption,getBatteryCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_battery_sum,getChargePointsCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_charge_point_sum,getPvCardEnabled:e=>!e.getThemeConfiguration||e.getThemeConfiguration.enable_dashboard_card_inverter_sum,getLockChanges:e=>!e.getThemeConfiguration||e.getThemeConfiguration.lock_changes,getSimpleChargePointView:e=>!!e.getThemeConfiguration&&e.getThemeConfiguration.simple_charge_point_view,getGridId(e){let t=e.topics["openWB/counter/get/hierarchy"];if(t!==void 0&&Object.keys(t).length>0){let n=Object.keys(e.topics["openWB/counter/get/hierarchy"])[0];if(e.topics["openWB/counter/get/hierarchy"][n].type=="counter")return e.topics["openWB/counter/get/hierarchy"][n].id}},getGridPower:e=>(t="textValue")=>{let n=e.getGridId;if(n===void 0)return"---";let a=e.getValueString(`openWB/counter/${n}/get/power`,"W");return Object.hasOwnProperty.call(a,t)?a[t]:t=="object"?a:void 0},getGridPowerChartData(e){let t=e.getGridId;return t===void 0?[]:e.getChartData(`openWB/counter/${t}/get/power`)},getHomePower:e=>(t="textValue")=>{let n=e.getValueString("openWB/counter/set/home_consumption","W");return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getHomePowerChartData:e=>e.getChartData("openWB/counter/set/home_consumption"),getBatteryConfigured:e=>e.getValueBool("openWB/bat/config/configured"),getBatteryPower:e=>(t="textValue")=>{let n=e.getValueString("openWB/bat/get/power","W");return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getBatteryPowerChartData:e=>e.getChartData("openWB/bat/get/power"),getBatterySoc:e=>(t="textValue")=>{let n=e.getValueString("openWB/bat/get/soc","%","",!1);return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getBatterySocChartData:e=>e.getChartData("openWB/bat/get/soc"),getPvConfigured:e=>e.getValueBool("openWB/pv/config/configured"),getPvPower:e=>(t="textValue")=>{var n=e.getValueString("openWB/pv/get/power","W","",!0,!0);return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getPvPowerChartData:e=>e.getChartData("openWB/pv/get/power").map(t=>-1*t),getChargePointSumPower:e=>(t="textValue")=>{var n=e.getValueString("openWB/chargepoint/get/power","W");return Object.hasOwnProperty.call(n,t)?n[t]:t=="object"?n:void 0},getChargePointSumPowerChartData:e=>e.getChartData("openWB/chargepoint/get/power"),getChargePointIds(e){let t=e.getObjectIds("cp"),n=this.getChargePointFilter;return n.length>0?t.filter(a=>n.includes(a)):t},getChargePointName:e=>t=>e.topics[`openWB/chargepoint/${t}/config`]!==void 0?e.topics[`openWB/chargepoint/${t}/config`].name:"---",getChargePointPower:e=>(t,n="textValue")=>{var a=e.getValueString(`openWB/chargepoint/${t}/get/power`,"W");return Object.hasOwnProperty.call(a,n)?a[n]:n=="object"?a:void 0},getChargePointImportedSincePlugged:e=>t=>({energy:e.getValueString(`openWB/chargepoint/${t}/set/log`,"Wh","",!0,!1,"---","imported_since_plugged").textValue,range:e.getValueString(`openWB/chargepoint/${t}/set/log`,"m","k",!1,!1,"---","range_charged").textValue}),getChargePointPowerChartData:e=>t=>e.getChartData(`openWB/chargepoint/${t}/get/power`),getChargePointSetCurrent:e=>(t,n="textValue")=>{let a=e.getValueString(`openWB/chargepoint/${t}/set/current`,"A");return Object.hasOwnProperty.call(a,n)?a[n]:n=="object"?a:void 0},getChargePointPhasesInUse:e=>t=>{const n=["/","①","②","③"],a=e.topics[`openWB/chargepoint/${t}/get/phases_in_use`];return a!==void 0&&a>=0&&at=>e.getValueBool(`openWB/chargepoint/${t}/get/plug_state`),getChargePointChargeState:e=>t=>e.getValueBool(`openWB/chargepoint/${t}/get/charge_state`),getChargePointManualLock:e=>t=>e.getValueBool(`openWB/chargepoint/${t}/set/manual_lock`),getChargepointTagState:e=>t=>[void 0,null,""].includes(e.topics[`openWB/chargepoint/${t}/set/rfid`])?[void 0,null,""].includes(e.topics[`openWB/chargepoint/${t}/get/rfid`])?0:1:2,getChargePointConnectedVehicleConfig:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/config`],getChargePointConnectedVehicleChargeMode:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.translateChargeMode(e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.selected)},getChargePointConnectedVehiclePriority:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).prio},getChargePointConnectedVehicleInfo:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`],getChargePointConnectedVehicleId:e=>t=>{if(e.getChargePointConnectedVehicleInfo(t))return e.getChargePointConnectedVehicleInfo(t).id},getChargePointConnectedVehicleChargeTemplateIndex:e=>t=>{if(e.getChargePointConnectedVehicleConfig(t))return e.getChargePointConnectedVehicleConfig(t).charge_template},getChargePointConnectedVehicleChargeTemplate:e=>t=>{let n=e.getChargePointConnectedVehicleChargeTemplateIndex(t);return e.topics[`openWB/vehicle/template/charge_template/${n}`]},getChargePointConnectedVehicleEvTemplate:e=>t=>{if(e.getChargePointConnectedVehicleConfig(t))return e.getChargePointConnectedVehicleConfig(t).ev_template},getChargePointConnectedVehicleName:e=>t=>{if(e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`])return e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/info`].name},getChargePointConnectedVehicleSoc:e=>t=>e.topics[`openWB/chargepoint/${t}/get/connected_vehicle/soc`],getChargePointConnectedVehicleTimeChargingActive:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).time_charging.active},getChargePointConnectedVehicleTimeChargingRunning:e=>t=>{let n=e.getChargePointConnectedVehicleConfig(t).time_charging_in_use;return n!==void 0&&n},getChargePointConnectedVehicleInstantChargingCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.instant_charging.current},getChargePointConnectedVehicleInstantChargingLimit:e=>t=>e.getChargePointConnectedVehicleChargeTemplate(t)?e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.instant_charging.limit:{selected:void 0},getChargePointConnectedVehiclePvChargingFeedInLimit:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.feed_in_limit},getChargePointConnectedVehiclePvChargingMinCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_current},getChargePointConnectedVehiclePvChargingMinSoc:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_soc},getChargePointConnectedVehiclePvChargingMinSocCurrent:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.min_soc_current},getChargePointConnectedVehiclePvChargingMaxSoc:e=>t=>{if(e.getChargePointConnectedVehicleChargeTemplate(t))return e.getChargePointConnectedVehicleChargeTemplate(t).chargemode.pv_charging.max_soc},getChargePointConnectedVehicleScheduledChargingPlans:e=>t=>{let n=e.getChargePointConnectedVehicleChargeTemplateIndex(t);return e.getWildcardTopics(`openWB/vehicle/template/charge_template/${n}/chargemode/scheduled_charging/plans/+`)},getChargePointConnectedVehicleTimeChargingPlans:e=>t=>{let n=e.getChargePointConnectedVehicleChargeTemplateIndex(t);return e.getWildcardTopics(`openWB/vehicle/template/charge_template/${n}/time_charging/plans/+`)},getVehicleList:e=>e.getWildcardTopics("openWB/vehicle/+/name"),getVehicleName:e=>t=>e.topics[`openWB/vehicle/${t}/name`],getVehicleSocConfigured:e=>t=>e.topics[`openWB/vehicle/${t}/soc_module/config`].type!=null,getVehicleSocIsManual:e=>t=>e.topics[`openWB/vehicle/${t}/soc_module/config`].type=="manual",getVehicleFaultState:e=>t=>e.topics[`openWB/vehicle/${t}/get/fault_state`]?e.topics[`openWB/vehicle/${t}/get/fault_state`]:0,getSystemTime(e){if(e.topics["openWB/system/time"])return new Date(1e3*e.topics["openWB/system/time"]).toLocaleString()},getSystemIp:e=>e.settings.localIp!==void 0?e.settings.localIp:e.topics["openWB/system/ip_address"]?e.topics["openWB/system/ip_address"]:void 0,getSystemVersion:e=>e.settings.localVersion!==void 0?e.settings.localVersion:e.topics["openWB/system/version"]?e.topics["openWB/system/version"]:void 0,getSystemBranch:e=>e.settings.localBranch!==void 0?e.settings.localBranch:e.topics["openWB/system/current_branch"]?e.topics["openWB/system/current_branch"]:void 0,getSystemCurrentCommit:e=>e.settings.localCommit!==void 0?e.settings.localCommit:e.topics["openWB/system/current_commit"]?e.topics["openWB/system/current_commit"]:void 0,getRfidEnabled(){return this.getValueBool("openWB/optional/rfid/active")}},actions:{updateSetting(e,t){e in this.settings&&(this.settings[e]=t)},initTopic(e,t=void 0){e.includes("#")||e.includes("+")||this.addTopic(e,t)},addTopic(e,t){this.topics[e]=t},removeTopic(e){e.includes("#")||e.includes("+")?Object.keys(this.getWildcardTopics(e)).forEach(t=>{delete this.topics[t]}):delete this.topics[e]},updateTopic(e,t,n=void 0){var a,i,o;e in this.topics&&(n!=null?(a=this.topics[e],o=t,(i=n).split(".").reduce((r,s,h)=>r[s]=i.split(".").length===++h?o:r[s]||{},a)):this.topics[e]=t)},updateChartData(){for(const[e,t]of Object.entries(this.topics))(e.endsWith("home_consumption")||e.endsWith("power")||e.endsWith("soc"))&&(this.chartData[e]===void 0&&(this.chartData[e]=[]),t!=null&&(this.chartData[e].push(t),this.chartData[e].slice(-128)))},updateState(e,t,n=void 0){this.updateTopic(e,t,n)},chargeModeList(){var e=[{id:"instant_charging"},{id:"pv_charging"},{id:"scheduled_charging"},{id:"standby"},{id:"stop"}];return e.forEach(t=>{t.label=this.translateChargeMode(t.id).label,t.class=this.translateChargeMode(t.id).class}),e},translateChargeMode(e){switch(e){case"instant_charging":return{mode:e,label:"Sofort",class:"danger"};case"pv_charging":return{mode:e,label:"PV",class:"success"};case"scheduled_charging":return{mode:e,label:"Zielladen",class:"primary"};case"time_charging":return{mode:e,label:"Zeitladen",class:"warning"};case"standby":return{mode:e,label:"Standby",class:"secondary"};case"stop":return{mode:e,label:"Stop",class:"dark"};default:return{mode:e,label:e,class:e}}},checkChangesLockCode(e){return!(!this.getThemeConfiguration||this.getThemeConfiguration.lock_changes_code!=e)},formatDate:(e,t={year:"numeric",month:"2-digit",day:"2-digit"})=>new Date(e).toLocaleDateString(void 0,t),formatDateRange(e,t="-"){const n={year:"numeric",month:"2-digit",day:"2-digit"};let a={day:"2-digit"};const i=new Date(e[0]),o=new Date(e[1]);return i.getFullYear()==o.getFullYear()?i.getMonth()!=o.getMonth()&&(a.month=n.month):a=n,`${this.formatDate(e[0],a)}${t}${this.formatDate(e[1],n)}`},formatWeeklyScheduleDays(e){const t=["Mo","Di","Mi","Do","Fr","Sa","So"];let n=[];return e.forEach(function(a,i){a==1&&n.push(t[i])}),n.join(",")}}}),ee=b({name:"NavBar",components:{NavItem:b({name:"NavItem",props:{to:{type:Object,required:!0}}},[["render",function(e,t,n,a,i,o){const r=d("i-nav-item");return p(),C(r,{to:n.to,"active-class":"-active",class:"_border _border-color:primary _text-align:center"},{default:c(()=>[B(e.$slots,"default",{},void 0,!0)]),_:3},8,["to"])}],["__scopeId","data-v-1db6ae73"]])},data:()=>({mqttStore:w()})},[["render",function(e,t,n,a,i,o){const r=d("nav-item"),s=d("i-nav");return p(),C(s,{vertical:"",class:"_align-items:stretch",size:"lg"},{default:c(()=>[i.mqttStore.getDashBoardEnabled?(p(),C(r,{key:0,to:{name:"dash-board"}},{default:c(()=>t[0]||(t[0]=[f(" Übersicht ")])),_:1})):y("",!0),i.mqttStore.getEnergyFlowEnabled?(p(),C(r,{key:1,to:{name:"energy-flow"}},{default:c(()=>t[1]||(t[1]=[f(" Energiefluss ")])),_:1})):y("",!0),i.mqttStore.getChargePointsEnabled&&i.mqttStore.getChargePointIds.length>0?(p(),C(r,{key:2,to:{name:"charge-points"}},{default:c(()=>t[2]||(t[2]=[f(" Ladepunkte ")])),_:1})):y("",!0),i.mqttStore.getStateEnabled?(p(),C(r,{key:3,to:{name:"status"}},{default:c(()=>t[3]||(t[3]=[f(" Status ")])),_:1})):y("",!0)]),_:1})}]]);$.add(L,q);const te=b({name:"NumberPad",components:{FontAwesomeIcon:O},emits:["key:digit","key:clear","key:delete"],data:()=>({buttonRows:[[{value:1,label:"1"},{value:2,label:"2"},{value:3,label:"3"}],[{value:4,label:"4"},{value:5,label:"5"},{value:6,label:"6"}],[{value:7,label:"7"},{value:8,label:"8"},{value:9,label:"9"}]]}),methods:{emitDigit(e){this.$emit("key:digit",e)},emitClear(){this.$emit("key:clear")},emitDelete(){this.$emit("key:delete")}}},[["render",function(e,t,n,a,i,o){const r=d("i-button"),s=d("i-column"),h=d("i-row"),g=d("FontAwesomeIcon"),m=d("i-container");return p(),C(m,null,{default:c(()=>[(p(!0),P(W,null,I(i.buttonRows,u=>(p(),C(h,{key:u,center:"",class:"_padding-bottom:1"},{default:c(()=>[(p(!0),P(W,null,I(u,_=>(p(),C(s,{key:_.value,class:"pin-button-column"},{default:c(()=>[l(r,{size:"lg",class:"pin-button",onClick:v=>o.emitDigit(_.value)},{default:c(()=>[f(k(_.label),1)]),_:2},1032,["onClick"])]),_:2},1024))),128))]),_:2},1024))),128)),l(h,{center:""},{default:c(()=>[l(s,{class:"pin-button-column"},{default:c(()=>[l(r,{size:"lg",class:"pin-button",onClick:t[0]||(t[0]=u=>o.emitClear())},{default:c(()=>[l(g,{"fixed-width":"",icon:["fas","fa-eraser"]})]),_:1})]),_:1}),l(s,{class:"pin-button-column"},{default:c(()=>[l(r,{size:"lg",class:"pin-button",onClick:t[1]||(t[1]=u=>o.emitDigit("0"))},{default:c(()=>t[3]||(t[3]=[f(" 0 ")])),_:1})]),_:1}),l(s,{class:"pin-button-column"},{default:c(()=>[l(r,{size:"lg",class:"pin-button",onClick:t[2]||(t[2]=u=>o.emitDelete())},{default:c(()=>[l(g,{"fixed-width":"",icon:["fas","fa-delete-left"]})]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-4e2103db"]]),ne=b({name:"CodeInputModal",components:{NumberPad:te},props:{modelValue:{type:Boolean,required:!0},backgroundColor:{type:String,default:"warning"},placeholderCharacter:{type:String,default:"*",validator:e=>e.length==1},inputVisible:{type:Boolean,default:!1},minLength:{type:Number,default:4},maxLength:{type:Number,default:4}},emits:["update:modelValue","update:inputValue"],data(){return{number:"",modalBackground:this.backgroundColor}},computed:{placeholder(){return this.placeholderCharacter.repeat(this.minLength)},enableSubmit(){return this.number.length>=this.minLength&&this.number.length<=this.maxLength}},watch:{modelValue(e,t){e===!1&&t===!0&&this.clear()}},methods:{abort(){this.$emit("update:modelValue",!1)},addDigit(e){this.number.length{this.$emit("update:modelValue",!1),this.modalBackground=this.backgroundColor},500)},error(e="danger"){this.modalBackground=e,setTimeout(()=>{this.clear(),this.modalBackground=this.backgroundColor},2e3)}}},[["render",function(e,t,n,a,i,o){const r=d("i-input"),s=d("i-column"),h=d("i-row"),g=d("NumberPad"),m=d("i-container"),u=d("i-button"),_=d("i-modal");return p(),C(E,{to:"body"},[l(_,{"model-value":n.modelValue,color:i.modalBackground,"onUpdate:modelValue":t[4]||(t[4]=v=>e.$emit("update:modelValue",v))},{header:c(()=>[B(e.$slots,"header",{},()=>[t[5]||(t[5]=f(" **HEADER** "))])]),footer:c(()=>[l(m,null,{default:c(()=>[l(h,null,{default:c(()=>[l(s,null,{default:c(()=>[l(u,{color:"danger",onClick:o.abort},{default:c(()=>[B(e.$slots,"abort",{},()=>[t[6]||(t[6]=f(" Zurück "))])]),_:3},8,["onClick"])]),_:3}),l(s,{class:"_text-align:right"},{default:c(()=>[o.enableSubmit?(p(),C(u,{key:0,color:"success",onClick:o.submit},{default:c(()=>[B(e.$slots,"submit",{},()=>[t[7]||(t[7]=f(" OK "))])]),_:3},8,["onClick"])):y("",!0)]),_:3})]),_:3})]),_:3})]),default:c(()=>[l(m,null,{default:c(()=>[l(h,{center:"",class:"_padding-bottom:1"},{default:c(()=>[l(s,null,{default:c(()=>[l(r,{modelValue:i.number,"onUpdate:modelValue":t[0]||(t[0]=v=>i.number=v),placeholder:o.placeholder,readonly:"",size:"lg",type:n.inputVisible?"text":"password",class:"_text-align:center"},null,8,["modelValue","placeholder","type"])]),_:1})]),_:1}),l(g,{"onKey:digit":t[1]||(t[1]=v=>o.addDigit(v)),"onKey:clear":t[2]||(t[2]=v=>o.clear()),"onKey:delete":t[3]||(t[3]=v=>o.removeDigit(v))})]),_:1})]),_:3},8,["model-value","color"])])}]]);$.add(Z,Q,L,q);const oe={key:0,class:"_padding-left:1"},x="/openWB/web/display/themes/cards/openWB_logo_dark.png",ae=b({name:"OpenwbDisplayCardsApp",components:{RouterView:H,DateTime:X,NavBar:ee,LockNavItem:b({name:"LockNavItem",components:{FontAwesomeIcon:O,CodeInputModal:ne},props:{},data:()=>({mqttStore:w(),modalPinEntryVisible:!1,modalPinEntryColor:"warning",code:"",countdown:0,countdownInterval:void 0,events:["mousemove","touchmove","wheel","click"]}),computed:{changesLocked:{get(){return this.mqttStore.settings.changesLocked},set(e){this.mqttStore.settings.changesLocked=e}},timer(){return Math.trunc(this.countdown/60).toString()+":"+(this.countdown%60).toString().padStart(2,"0")}},mounted(){this.changesLocked=!0},methods:{toggleChangesLock(){this.changesLocked?this.unlockChanges():this.lockChanges()},unlockChanges(){this.modalPinEntryVisible=!0},checkUnlockCode(e){this.mqttStore.checkChangesLockCode(e)?(this.$refs.lockInput.success("success"),this.changesLocked=!1,this.mqttStore.getDisplayStandby>0&&(this.countdown=this.mqttStore.getDisplayStandby,this.countdownInterval=setInterval(this.updateCountdown,1e3),this.events.forEach(t=>{document.addEventListener(t,this.handleDocumentEvent,{passive:!0})}))):this.$refs.lockInput.error("danger")},lockChanges(){this.changesLocked=!0,this.events.forEach(e=>{document.removeEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.countdownInterval!==void 0&&(clearInterval(this.countdownInterval),this.countdownInterval=void 0)},updateCountdown(){this.countdown-=1,this.countdown<1&&this.lockChanges()},handleDocumentEvent(){this.countdown=this.mqttStore.getDisplayStandby}}},[["render",function(e,t,n,a,i,o){const r=d("FontAwesomeIcon"),s=d("i-button"),h=d("CodeInputModal");return p(),P(W,null,[i.mqttStore.getLockChanges?(p(),C(s,{key:0,class:"_padding-left:0 _padding-right:0 _margin-bottom:1",size:"lg",block:"",color:o.changesLocked?"danger":"success",onClick:t[0]||(t[0]=g=>o.toggleChangesLock())},{default:c(()=>[l(r,{"fixed-width":"",icon:o.changesLocked?["fas","fa-lock"]:["fas","fa-lock-open"],class:A(o.changesLocked?"_color:danger-80":"_color:success-80")},null,8,["icon","class"]),!o.changesLocked&&i.countdownInterval?(p(),P("span",oe,k(o.timer),1)):y("",!0)]),_:1},8,["color"])):y("",!0),l(h,{ref:"lockInput",modelValue:i.modalPinEntryVisible,"onUpdate:modelValue":t[1]||(t[1]=g=>i.modalPinEntryVisible=g),"min-length":4,"max-length":10,"onUpdate:inputValue":o.checkUnlockCode},{header:c(()=>t[2]||(t[2]=[f(" Bitte den PIN zur Freigabe von Änderungen eingeben. ")])),_:1},8,["modelValue","onUpdate:inputValue"])],64)}]]),TouchBlocker:b({name:"TouchBlocker",data:()=>({mqttStore:w(),show:!1,countdown:void 0,countdownInterval:void 0,events:["mousemove","touchmove","wheel","click"],eventHandlerSetup:!1}),computed:{configuredDisplayStandby(){if(this.mqttStore.getDisplayStandby!==0&&this.mqttStore.getDisplayStandby!==void 0)return this.mqttStore.getDisplayStandby},touchBlockerTimeout(){return Math.max(this.configuredDisplayStandby-3,1)}},mounted(){this.setupEventHandler(),this.setupTimeout()},unmounted(){this.clearEventHandler(),this.clearTimeout()},methods:{handleTouchBlockerClick(e){e===!1&&(this.show=!1,this.setupEventHandler(),this.setupTimeout())},setupEventHandler(){this.eventHandlerSetup||(this.events.forEach(e=>{document.addEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.eventHandlerSetup=!0)},clearEventHandler(){this.eventHandlerSetup&&(this.events.forEach(e=>{document.removeEventListener(e,this.handleDocumentEvent,{passive:!0})}),this.eventHandlerSetup=!1)},setupTimeout(){this.countdownInterval===void 0&&(this.countdownInterval=setInterval(this.updateCountdown,1e3))},clearTimeout(){this.countdownInterval!==void 0&&(clearInterval(this.countdownInterval),this.countdownInterval=void 0)},updateCountdown(){this.countdown===void 0?this.countdown=this.touchBlockerTimeout:(this.countdown-=1,this.countdown<1&&this.showTouchBlocker())},handleDocumentEvent(){this.countdown=this.touchBlockerTimeout,this.show=!1},showTouchBlocker(){this.show=!0,this.clearTimeout()}}},[["render",function(e,t,n,a,i,o){const r=d("IModal");return p(),C(E,{to:"body"},[l(r,{class:"touch-blocker",size:"sm",color:"dark","model-value":i.show,"onUpdate:modelValue":t[0]||(t[0]=s=>o.handleTouchBlockerClick(s))},{default:c(()=>t[1]||(t[1]=[T("img",{class:"logo",src:x},null,-1),T("p",null," Bitte das Display berühren. ",-1)])),_:1},8,["model-value"])])}],["__scopeId","data-v-e4e632d4"]])},data:()=>({client:{connected:!1},connection:{protocol:location.protocol=="https:"?"wss":"ws",host:location.hostname,port:parseInt(location.port)||(location.protocol=="https:"?443:80),endpoint:"/ws",connectTimeout:4e3,reconnectPeriod:4e3},mqttTopicsToSubscribe:["openWB/bat/config/configured","openWB/bat/get/power","openWB/bat/get/soc","openWB/chargepoint/+/config","openWB/chargepoint/+/get/charge_state","openWB/chargepoint/+/get/connected_vehicle/+","openWB/chargepoint/+/get/phases_in_use","openWB/chargepoint/+/get/plug_state","openWB/chargepoint/+/get/power","openWB/chargepoint/+/get/rfid","openWB/chargepoint/+/set/current","openWB/chargepoint/+/set/manual_lock","openWB/chargepoint/+/set/log","openWB/chargepoint/+/set/rfid","openWB/chargepoint/get/power","openWB/counter/+/get/power","openWB/counter/get/hierarchy","openWB/counter/set/home_consumption","openWB/optional/int_display/theme","openWB/optional/int_display/standby","openWB/optional/rfid/active","openWB/pv/config/configured","openWB/pv/get/power","openWB/system/current_branch","openWB/system/current_commit","openWB/system/ip_address","openWB/system/time","openWB/system/version","openWB/vehicle/+/get/fault_state","openWB/vehicle/+/name","openWB/vehicle/+/soc_module/config","openWB/vehicle/template/charge_template/#"],mqttStore:w(),chartInterval:"",clearConsoleHandler:void 0}),computed:{changesLocked(){return this.mqttStore.getLockChanges&&this.mqttStore.settings.changesLocked}},created(){this.createConnection()},mounted(){let e=window.location.search;if(e!=""){let a=new URLSearchParams(e);if(a.has("data")){let i=JSON.parse(a.get("data"));Object.entries(i).forEach(([o,r])=>{o.startsWith("parentChargePoint")?this.mqttStore.updateSetting(o,parseInt(r)):this.mqttStore.updateSetting(o,r)})}}this.doSubscribe(this.mqttTopicsToSubscribe),this.chartInterval=setInterval(this.mqttStore.updateChartData,5e3);const t=new Date,n=new Date(t.getFullYear(),t.getMonth(),t.getDate()+1,0,0,0,0).getTime()-t.getTime();this.clearConsoleHandler=setTimeout(()=>this.clearConsole(),n)},beforeUnmount(){this.doUnsubscribe(this.mqttTopicsToSubscribe),clearInterval(this.chartInterval),clearTimeout(this.clearConsoleHandler)},methods:{clearConsole(){this.clearConsoleHandler=setTimeout(()=>this.clearConsole(),864e5)},createConnection(){const{protocol:e,host:t,port:n,endpoint:a,...i}=this.connection,o=`${e}://${t}:${n}${a}`;try{this.client=R.connect(o,i)}catch{}this.client.on("connect",()=>{}),this.client.on("error",r=>{}),this.client.on("message",(r,s)=>{if(s.toString().length>0){let h;try{h=JSON.parse(s.toString())}catch{h=s.toString()}this.mqttStore.addTopic(r,h)}else this.mqttStore.removeTopic(r)})},doSubscribe(e){e.forEach(t=>{this.mqttStore.initTopic(t)}),this.client.subscribe(e,{},t=>{})},doUnsubscribe(e){e.forEach(t=>{this.mqttStore.removeTopic(t)}),this.client.unsubscribe(e,t=>{})},doPublish(e,t,n=!0,a=2){let i={qos:a,retain:n};this.client.publish(e,JSON.stringify(t),i,o=>{})},sendTopicToBroker(e,t=void 0){let n=e.replace("openWB/","openWB/set/");t===void 0&&(t=this.mqttStore.topics[e]),this.doPublish(n,t)},sendCommand(e){this.doPublish("openWB/set/command/"+this.client.options.clientId+"/todo",e,!1)},sendSystemCommand(e,t={}){this.sendCommand({command:e,data:t})}}},[["render",function(e,t,n,a,i,o){const r=d("DateTime"),s=d("i-column"),h=d("i-row"),g=d("i-container"),m=d("LockNavItem"),u=d("NavBar"),_=d("TouchBlocker"),v=d("i-layout-aside"),j=d("RouterView"),M=d("i-layout-content"),N=d("i-layout");return p(),C(N,{vertical:""},{default:c(()=>[l(v,{class:"_position:fixed"},{default:c(()=>[l(g,{fluid:"",class:"_margin-bottom:1"},{default:c(()=>[l(h,{center:""},{default:c(()=>[l(s,null,{default:c(()=>[l(r)]),_:1})]),_:1})]),_:1}),l(m),l(u,{"changes-locked":o.changesLocked},null,8,["changes-locked"]),l(_)]),_:1}),l(M,null,{default:c(()=>[l(j,{"changes-locked":o.changesLocked},null,8,["changes-locked"])]),_:1})]),_:1})}],["__scopeId","data-v-6804b914"]]),D={},S=function(e,t,n){let a=Promise.resolve();if(t&&t.length>0){document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),r=(o==null?void 0:o.nonce)||(o==null?void 0:o.getAttribute("nonce"));a=Promise.allSettled(t.map(s=>{if((s=function(u){return"/openWB/web/display/themes/cards/"+u}(s))in D)return;D[s]=!0;const h=s.endsWith(".css"),g=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${s}"]${g}`))return;const m=document.createElement("link");return m.rel=h?"stylesheet":"modulepreload",h||(m.as="script"),m.crossOrigin="",m.href=s,r&&m.setAttribute("nonce",r),document.head.appendChild(m),h?new Promise((u,_)=>{m.addEventListener("load",u),m.addEventListener("error",()=>_(new Error(`Unable to preload CSS for ${s}`)))}):void 0}))}function i(o){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=o,window.dispatchEvent(r),!r.defaultPrevented)throw o}return a.then(o=>{for(const r of o||[])r.status==="rejected"&&i(r.reason);return e().catch(i)})},ie=b({name:"WelcomeView",data:()=>({mqttStore:w()}),computed:{firstView(){if(this.mqttStore.getThemeConfiguration){if(this.mqttStore.getThemeConfiguration.enable_dashboard_view)return"dash-board";if(this.mqttStore.getThemeConfiguration.enable_energy_flow_view)return"energy-flow";if(this.mqttStore.getThemeConfiguration.enable_charge_points_view)return"charge-points";if(this.mqttStore.getThemeConfiguration.enable_status_view)return"status"}}},mounted(){setTimeout(this.selectFirstRoute,3e3)},methods:{selectFirstRoute(){this.firstView&&this.$router.push({name:this.firstView})}}},[["render",function(e,t,n,a,i,o){const r=d("i-card"),s=d("i-column"),h=d("i-row"),g=d("i-container");return p(),C(g,null,{default:c(()=>[l(h,{center:"",middle:""},{default:c(()=>[l(s,null,{default:c(()=>[l(r,{color:"primary"},{header:c(()=>t[0]||(t[0]=[f(" Cards Theme ")])),default:c(()=>[t[1]||(t[1]=T("img",{class:"logo",src:x},null,-1))]),_:1})]),_:1})]),_:1})]),_:1})}],["__scopeId","data-v-16686e30"]]),re=U({history:z("/openWB/web/display/themes/cards/"),routes:[{path:"/",name:"welcome",component:ie},{path:"/DashBoard",name:"dash-board",component:()=>S(()=>import("./DashBoardView-BvptEyMO.js"),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9]))},{path:"/EnergyFlow",name:"energy-flow",component:()=>S(()=>import("./EnergyFlowView-D3Xr79nb.js"),__vite__mapDeps([10,1,2,3,4,5,7,11]))},{path:"/ChargePoints",name:"charge-points",component:()=>S(()=>import("./ChargePointsView-DAOuhP6h.js"),__vite__mapDeps([12,1,2,3,4,5,6,7,8,13]))},{path:"/Status",name:"status",component:()=>S(()=>import("./StatusView-rjhbJP_1.js"),__vite__mapDeps([14,7,3,2,4,1,5]))}]}),V=G(ae);V.use(K()),V.use(re),V.use(J,{colorMode:"dark",components:Y}),V.mount("#app");export{ne as C,te as N,w as u}; diff --git a/packages/modules/display_themes/cards/web/assets/vendor-BMrK3KHF.js b/packages/modules/display_themes/cards/web/assets/vendor-BMrK3KHF.js deleted file mode 100644 index 469e50f488..0000000000 --- a/packages/modules/display_themes/cards/web/assets/vendor-BMrK3KHF.js +++ /dev/null @@ -1,29 +0,0 @@ -var Dd=Object.defineProperty;var Hl=t=>{throw TypeError(t)};var Fd=(t,e,r)=>e in t?Dd(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var Ht=(t,e,r)=>Fd(t,typeof e!="symbol"?e+"":e,r),Do=(t,e,r)=>e.has(t)||Hl("Cannot "+r);var K=(t,e,r)=>(Do(t,e,"read from private field"),r?r.call(t):e.get(t)),Pt=(t,e,r)=>e.has(t)?Hl("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),St=(t,e,r,n)=>(Do(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),Et=(t,e,r)=>(Do(t,e,"access private method"),r);var Ii=(t,e,r,n)=>({set _(l){St(t,e,l,r)},get _(){return K(t,e,n)}});const to=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{};/** -* @vue/shared v3.5.13 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**//*! #__NO_SIDE_EFFECTS__ */function nl(t){const e=Object.create(null);for(const r of t.split(","))e[r]=1;return r=>r in e}const Ut={},kn=[],Ge=()=>{},Wd=()=>!1,bo=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&(t.charCodeAt(2)>122||t.charCodeAt(2)<97),il=t=>t.startsWith("onUpdate:"),ee=Object.assign,ol=(t,e)=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)},$d=Object.prototype.hasOwnProperty,Mt=(t,e)=>$d.call(t,e),At=Array.isArray,In=t=>vo(t)==="[object Map]",Xc=t=>vo(t)==="[object Set]",It=t=>typeof t=="function",qt=t=>typeof t=="string",gr=t=>typeof t=="symbol",Wt=t=>t!==null&&typeof t=="object",Zc=t=>(Wt(t)||It(t))&&It(t.then)&&It(t.catch),th=Object.prototype.toString,vo=t=>th.call(t),eh=t=>vo(t)==="[object Object]",sl=t=>qt(t)&&t!=="NaN"&&t[0]!=="-"&&""+parseInt(t,10)===t,Jn=nl(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),wo=t=>{const e=Object.create(null);return r=>e[r]||(e[r]=t(r))},Vd=/-(\w)/g,Ce=wo(t=>t.replace(Vd,(e,r)=>r?r.toUpperCase():"")),qd=/\B([A-Z])/g,Nr=wo(t=>t.replace(qd,"-$1").toLowerCase()),_o=wo(t=>t.charAt(0).toUpperCase()+t.slice(1)),Fo=wo(t=>t?`on${_o(t)}`:""),Pr=(t,e)=>!Object.is(t,e),Wo=(t,...e)=>{for(let r=0;r{Object.defineProperty(t,e,{configurable:!0,enumerable:!1,writable:n,value:r})},Hd=t=>{const e=parseFloat(t);return isNaN(e)?t:e};let zl;const eo=()=>zl||(zl=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:to!==void 0?to:{});function Eo(t){if(At(t)){const e={};for(let r=0;r{if(r){const n=r.split(Kd);n.length>1&&(e[n[0].trim()]=n[1].trim())}}),e}function So(t){let e="";if(qt(t))e=t;else if(At(t))for(let r=0;r!(!t||t.__v_isRef!==!0),Jd=t=>qt(t)?t:t==null?"":At(t)||Wt(t)&&(t.toString===th||!It(t.toString))?ih(t)?Jd(t.value):JSON.stringify(t,oh,2):String(t),oh=(t,e)=>ih(e)?oh(t,e.value):In(e)?{[`Map(${e.size})`]:[...e.entries()].reduce((r,[n,l],o)=>(r[$o(n,o)+" =>"]=l,r),{})}:Xc(e)?{[`Set(${e.size})`]:[...e.values()].map(r=>$o(r))}:gr(e)?$o(e):!Wt(e)||At(e)||eh(e)?e:String(e),$o=(t,e="")=>{var r;return gr(t)?`Symbol(${(r=t.description)!=null?r:e})`:t};/** -* @vue/reactivity v3.5.13 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/let fe,Ft;class sh{constructor(e=!1){this.detached=e,this._active=!0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=fe,!e&&fe&&(this.index=(fe.scopes||(fe.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){let e,r;if(this._isPaused=!0,this.scopes)for(e=0,r=this.scopes.length;e0)return;if(Zn){let e=Zn;for(Zn=void 0;e;){const r=e.next;e.next=void 0,e.flags&=-9,e=r}}let t;for(;Xn;){let e=Xn;for(Xn=void 0;e;){const r=e.next;if(e.next=void 0,e.flags&=-9,1&e.flags)try{e.trigger()}catch(n){t||(t=n)}e=r}}if(t)throw t}function fh(t){for(let e=t.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function ph(t){let e,r=t.depsTail,n=r;for(;n;){const l=n.prevDep;n.version===-1?(n===r&&(r=l),ul(n),Xd(n)):e=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=l}t.deps=e,t.depsTail=r}function Os(t){for(let e=t.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(dh(e.dep.computed)||e.dep.version!==e.version))return!0;return!!t._dirty}function dh(t){if(4&t.flags&&!(16&t.flags)||(t.flags&=-17,t.globalVersion===ui))return;t.globalVersion=ui;const e=t.dep;if(t.flags|=2,e.version>0&&!t.isSSR&&t.deps&&!Os(t))return void(t.flags&=-3);const r=Ft,n=Ue;Ft=t,Ue=!0;try{fh(t);const l=t.fn(t._value);(e.version===0||Pr(l,t._value))&&(t._value=l,e.version++)}catch(l){throw e.version++,l}finally{Ft=r,Ue=n,ph(t),t.flags&=-3}}function ul(t,e=!1){const{dep:r,prevSub:n,nextSub:l}=t;if(n&&(n.nextSub=l,t.prevSub=void 0),l&&(l.prevSub=n,t.nextSub=void 0),r.subs===t&&(r.subs=n,!n&&r.computed)){r.computed.flags&=-5;for(let o=r.computed.deps;o;o=o.nextDep)ul(o,!0)}e||--r.sc||!r.map||r.map.delete(r.key)}function Xd(t){const{prevDep:e,nextDep:r}=t;e&&(e.nextDep=r,t.prevDep=void 0),r&&(r.prevDep=e,t.nextDep=void 0)}let Ue=!0;const gh=[];function Lr(){gh.push(Ue),Ue=!1}function jr(){const t=gh.pop();Ue=t===void 0||t}function Kl(t){const{cleanup:e}=t;if(t.cleanup=void 0,e){const r=Ft;Ft=void 0;try{e()}finally{Ft=r}}}let ui=0;class Zd{constructor(e,r){this.sub=e,this.dep=r,this.version=r.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class cl{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0}track(e){if(!Ft||!Ue||Ft===this.computed)return;let r=this.activeLink;if(r===void 0||r.sub!==Ft)r=this.activeLink=new Zd(Ft,this),Ft.deps?(r.prevDep=Ft.depsTail,Ft.depsTail.nextDep=r,Ft.depsTail=r):Ft.deps=Ft.depsTail=r,mh(r);else if(r.version===-1&&(r.version=this.version,r.nextDep)){const n=r.nextDep;n.prevDep=r.prevDep,r.prevDep&&(r.prevDep.nextDep=n),r.prevDep=Ft.depsTail,r.nextDep=void 0,Ft.depsTail.nextDep=r,Ft.depsTail=r,Ft.deps===r&&(Ft.deps=n)}return r}trigger(e){this.version++,ui++,this.notify(e)}notify(e){al();try{for(let r=this.subs;r;r=r.prevSub)r.sub.notify()&&r.sub.dep.notify()}finally{ll()}}}function mh(t){if(t.dep.sc++,4&t.sub.flags){const e=t.dep.computed;if(e&&!t.dep.subs){e.flags|=20;for(let n=e.deps;n;n=n.nextDep)mh(n)}const r=t.dep.subs;r!==t&&(t.prevSub=r,r&&(r.nextSub=t)),t.dep.subs=t}}const ro=new WeakMap,nn=Symbol(""),xs=Symbol(""),ci=Symbol("");function le(t,e,r){if(Ue&&Ft){let n=ro.get(t);n||ro.set(t,n=new Map);let l=n.get(r);l||(n.set(r,l=new cl),l.map=n,l.key=r),l.track()}}function ar(t,e,r,n,l,o){const s=ro.get(t);if(!s)return void ui++;const i=a=>{a&&a.trigger()};if(al(),e==="clear")s.forEach(i);else{const a=At(t),u=a&&sl(r);if(a&&r==="length"){const c=Number(n);s.forEach((d,f)=>{(f==="length"||f===ci||!gr(f)&&f>=c)&&i(d)})}else switch((r!==void 0||s.has(void 0))&&i(s.get(r)),u&&i(s.get(ci)),e){case"add":a?u&&i(s.get("length")):(i(s.get(nn)),In(t)&&i(s.get(xs)));break;case"delete":a||(i(s.get(nn)),In(t)&&i(s.get(xs)));break;case"set":In(t)&&i(s.get(nn))}}ll()}function mn(t){const e=Rt(t);return e===t?e:(le(e,0,ci),xe(t)?e:e.map(ue))}function Ao(t){return le(t=Rt(t),0,ci),t}const tg={__proto__:null,[Symbol.iterator](){return qo(this,Symbol.iterator,ue)},concat(...t){return mn(this).concat(...t.map(e=>At(e)?mn(e):e))},entries(){return qo(this,"entries",t=>(t[1]=ue(t[1]),t))},every(t,e){return Xe(this,"every",t,e,void 0,arguments)},filter(t,e){return Xe(this,"filter",t,e,r=>r.map(ue),arguments)},find(t,e){return Xe(this,"find",t,e,ue,arguments)},findIndex(t,e){return Xe(this,"findIndex",t,e,void 0,arguments)},findLast(t,e){return Xe(this,"findLast",t,e,ue,arguments)},findLastIndex(t,e){return Xe(this,"findLastIndex",t,e,void 0,arguments)},forEach(t,e){return Xe(this,"forEach",t,e,void 0,arguments)},includes(...t){return Ho(this,"includes",t)},indexOf(...t){return Ho(this,"indexOf",t)},join(t){return mn(this).join(t)},lastIndexOf(...t){return Ho(this,"lastIndexOf",t)},map(t,e){return Xe(this,"map",t,e,void 0,arguments)},pop(){return Dn(this,"pop")},push(...t){return Dn(this,"push",t)},reduce(t,...e){return Gl(this,"reduce",t,e)},reduceRight(t,...e){return Gl(this,"reduceRight",t,e)},shift(){return Dn(this,"shift")},some(t,e){return Xe(this,"some",t,e,void 0,arguments)},splice(...t){return Dn(this,"splice",t)},toReversed(){return mn(this).toReversed()},toSorted(t){return mn(this).toSorted(t)},toSpliced(...t){return mn(this).toSpliced(...t)},unshift(...t){return Dn(this,"unshift",t)},values(){return qo(this,"values",ue)}};function qo(t,e,r){const n=Ao(t),l=n[e]();return n===t||xe(t)||(l._next=l.next,l.next=()=>{const o=l._next();return o.value&&(o.value=r(o.value)),o}),l}const eg=Array.prototype;function Xe(t,e,r,n,l,o){const s=Ao(t),i=s!==t&&!xe(t),a=s[e];if(a!==eg[e]){const d=a.apply(t,o);return i?ue(d):d}let u=r;s!==t&&(i?u=function(d,f){return r.call(this,ue(d),f,t)}:r.length>2&&(u=function(d,f){return r.call(this,d,f,t)}));const c=a.call(s,u,n);return i&&l?l(c):c}function Gl(t,e,r,n){const l=Ao(t);let o=r;return l!==t&&(xe(t)?r.length>3&&(o=function(s,i,a){return r.call(this,s,i,a,t)}):o=function(s,i,a){return r.call(this,s,ue(i),a,t)}),l[e](o,...n)}function Ho(t,e,r){const n=Rt(t);le(n,0,ci);const l=n[e](...r);return l!==-1&&l!==!1||!pl(r[0])?l:(r[0]=Rt(r[0]),n[e](...r))}function Dn(t,e,r=[]){Lr(),al();const n=Rt(t)[e].apply(t,r);return ll(),jr(),n}const rg=nl("__proto__,__v_isRef,__isVue"),yh=new Set(Object.getOwnPropertyNames(Symbol).filter(t=>t!=="arguments"&&t!=="caller").map(t=>Symbol[t]).filter(gr));function ng(t){gr(t)||(t=String(t));const e=Rt(this);return le(e,0,t),e.hasOwnProperty(t)}class bh{constructor(e=!1,r=!1){this._isReadonly=e,this._isShallow=r}get(e,r,n){if(r==="__v_skip")return e.__v_skip;const l=this._isReadonly,o=this._isShallow;if(r==="__v_isReactive")return!l;if(r==="__v_isReadonly")return l;if(r==="__v_isShallow")return o;if(r==="__v_raw")return n===(l?o?fg:Eh:o?_h:wh).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;const s=At(e);if(!l){let a;if(s&&(a=tg[r]))return a;if(r==="hasOwnProperty")return ng}const i=Reflect.get(e,r,Qt(e)?e:n);return(gr(r)?yh.has(r):rg(r))?i:(l||le(e,0,r),o?i:Qt(i)?s&&sl(r)?i:i.value:Wt(i)?l?Ah(i):wi(i):i)}}class vh extends bh{constructor(e=!1){super(!1,e)}set(e,r,n,l){let o=e[r];if(!this._isShallow){const a=cn(o);if(xe(n)||cn(n)||(o=Rt(o),n=Rt(n)),!At(e)&&Qt(o)&&!Qt(n))return!a&&(o.value=n,!0)}const s=At(e)&&sl(r)?Number(r)t,Ti=t=>Reflect.getPrototypeOf(t);function Oi(t){return function(...e){return t!=="delete"&&(t==="clear"?void 0:this)}}function lg(t,e){const r={get(n){const l=this.__v_raw,o=Rt(l),s=Rt(n);t||(Pr(n,s)&&le(o,0,n),le(o,0,s));const{has:i}=Ti(o),a=e?zo:t?Ko:ue;return i.call(o,n)?a(l.get(n)):i.call(o,s)?a(l.get(s)):void(l!==o&&l.get(n))},get size(){const n=this.__v_raw;return!t&&le(Rt(n),0,nn),Reflect.get(n,"size",n)},has(n){const l=this.__v_raw,o=Rt(l),s=Rt(n);return t||(Pr(n,s)&&le(o,0,n),le(o,0,s)),n===s?l.has(n):l.has(n)||l.has(s)},forEach(n,l){const o=this,s=o.__v_raw,i=Rt(s),a=e?zo:t?Ko:ue;return!t&&le(i,0,nn),s.forEach((u,c)=>n.call(l,a(u),a(c),o))}};return ee(r,t?{add:Oi("add"),set:Oi("set"),delete:Oi("delete"),clear:Oi("clear")}:{add(n){e||xe(n)||cn(n)||(n=Rt(n));const l=Rt(this);return Ti(l).has.call(l,n)||(l.add(n),ar(l,"add",n,n)),this},set(n,l){e||xe(l)||cn(l)||(l=Rt(l));const o=Rt(this),{has:s,get:i}=Ti(o);let a=s.call(o,n);a||(n=Rt(n),a=s.call(o,n));const u=i.call(o,n);return o.set(n,l),a?Pr(l,u)&&ar(o,"set",n,l):ar(o,"add",n,l),this},delete(n){const l=Rt(this),{has:o,get:s}=Ti(l);let i=o.call(l,n);i||(n=Rt(n),i=o.call(l,n)),s&&s.call(l,n);const a=l.delete(n);return i&&ar(l,"delete",n,void 0),a},clear(){const n=Rt(this),l=n.size!==0,o=n.clear();return l&&ar(n,"clear",void 0,void 0),o}}),["keys","values","entries",Symbol.iterator].forEach(n=>{r[n]=function(l,o,s){return function(...i){const a=this.__v_raw,u=Rt(a),c=In(u),d=l==="entries"||l===Symbol.iterator&&c,f=l==="keys"&&c,g=a[l](...i),b=s?zo:o?Ko:ue;return!o&&le(u,0,f?xs:nn),{next(){const{value:_,done:S}=g.next();return S?{value:_,done:S}:{value:d?[b(_[0]),b(_[1])]:b(_),done:S}},[Symbol.iterator](){return this}}}}(n,t,e)}),r}function hl(t,e){const r=lg(t,e);return(n,l,o)=>l==="__v_isReactive"?!t:l==="__v_isReadonly"?t:l==="__v_raw"?n:Reflect.get(Mt(r,l)&&l in n?r:n,l,o)}const ug={get:hl(!1,!1)},cg={get:hl(!1,!0)},hg={get:hl(!0,!1)},wh=new WeakMap,_h=new WeakMap,Eh=new WeakMap,fg=new WeakMap;function pg(t){return t.__v_skip||!Object.isExtensible(t)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}((e=>vo(e).slice(8,-1))(t))}function wi(t){return cn(t)?t:fl(t,!1,og,ug,wh)}function Sh(t){return fl(t,!1,ag,cg,_h)}function Ah(t){return fl(t,!0,sg,hg,Eh)}function fl(t,e,r,n,l){if(!Wt(t)||t.__v_raw&&(!e||!t.__v_isReactive))return t;const o=l.get(t);if(o)return o;const s=pg(t);if(s===0)return t;const i=new Proxy(t,s===2?n:r);return l.set(t,i),i}function Br(t){return cn(t)?Br(t.__v_raw):!(!t||!t.__v_isReactive)}function cn(t){return!(!t||!t.__v_isReadonly)}function xe(t){return!(!t||!t.__v_isShallow)}function pl(t){return!!t&&!!t.__v_raw}function Rt(t){const e=t&&t.__v_raw;return e?Rt(e):t}function dl(t){return!Mt(t,"__v_skip")&&Object.isExtensible(t)&&rh(t,"__v_skip",!0),t}const ue=t=>Wt(t)?wi(t):t,Ko=t=>Wt(t)?Ah(t):t;function Qt(t){return!!t&&t.__v_isRef===!0}function gl(t){return kh(t,!1)}function kh(t,e){return Qt(t)?t:new dg(t,e)}class dg{constructor(e,r){this.dep=new cl,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=r?e:Rt(e),this._value=r?e:ue(e),this.__v_isShallow=r}get value(){return this.dep.track(),this._value}set value(e){const r=this._rawValue,n=this.__v_isShallow||xe(e)||cn(e);e=n?e:Rt(e),Pr(e,r)&&(this._rawValue=e,this._value=n?e:ue(e),this.dep.trigger())}}function Tn(t){return Qt(t)?t.value:t}const gg={get:(t,e,r)=>e==="__v_raw"?t:Tn(Reflect.get(t,e,r)),set:(t,e,r,n)=>{const l=t[e];return Qt(l)&&!Qt(r)?(l.value=r,!0):Reflect.set(t,e,r,n)}};function Ih(t){return Br(t)?t:new Proxy(t,gg)}class mg{constructor(e,r,n){this._object=e,this._key=r,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0}get value(){const e=this._object[this._key];return this._value=e===void 0?this._defaultValue:e}set value(e){this._object[this._key]=e}get dep(){return function(e,r){const n=ro.get(e);return n&&n.get(r)}(Rt(this._object),this._key)}}function yg(t,e,r){const n=t[e];return Qt(n)?n:new mg(t,e,r)}class bg{constructor(e,r,n){this.fn=e,this.setter=r,this._value=void 0,this.dep=new cl(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=ui-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!r,this.isSSR=n}notify(){if(this.flags|=16,!(8&this.flags)&&Ft!==this)return hh(this,!0),!0}get value(){const e=this.dep.track();return dh(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}}const xi={},Ci=new WeakMap;let Fr;function vg(t,e,r=Ut){const{immediate:n,deep:l,once:o,scheduler:s,augmentJob:i,call:a}=r,u=$=>l?$:xe($)||l===!1||l===0?lr($,1):lr($);let c,d,f,g,b=!1,_=!1;if(Qt(t)?(d=()=>t.value,b=xe(t)):Br(t)?(d=()=>u(t),b=!0):At(t)?(_=!0,b=t.some($=>Br($)||xe($)),d=()=>t.map($=>Qt($)?$.value:Br($)?u($):It($)?a?a($,2):$():void 0)):d=It(t)?e?a?()=>a(t,2):t:()=>{if(f){Lr();try{f()}finally{jr()}}const $=Fr;Fr=c;try{return a?a(t,3,[g]):t(g)}finally{Fr=$}}:Ge,e&&l){const $=d,X=l===!0?1/0:l;d=()=>lr($(),X)}const S=lh(),U=()=>{c.stop(),S&&S.active&&ol(S.effects,c)};if(o&&e){const $=e;e=(...X)=>{$(...X),U()}}let D=_?new Array(t.length).fill(xi):xi;const G=$=>{if(1&c.flags&&(c.dirty||$))if(e){const X=c.run();if(l||b||(_?X.some((B,A)=>Pr(B,D[A])):Pr(X,D))){f&&f();const B=Fr;Fr=c;try{const A=[X,D===xi?void 0:_&&D[0]===xi?[]:D,g];a?a(e,3,A):e(...A),D=X}finally{Fr=B}}}else c.run()};return i&&i(G),c=new uh(d),c.scheduler=s?()=>s(G,!1):G,g=$=>function(X,B=!1,A=Fr){if(A){let E=Ci.get(A);E||Ci.set(A,E=[]),E.push(X)}}($,!1,c),f=c.onStop=()=>{const $=Ci.get(c);if($){if(a)a($,4);else for(const X of $)X();Ci.delete(c)}},e?n?G(!0):D=c.run():s?s(G.bind(null,!0),!0):c.run(),U.pause=c.pause.bind(c),U.resume=c.resume.bind(c),U.stop=U,U}function lr(t,e=1/0,r){if(e<=0||!Wt(t)||t.__v_skip||(r=r||new Set).has(t))return t;if(r.add(t),e--,Qt(t))lr(t.value,e,r);else if(At(t))for(let n=0;n{lr(n,e,r)});else if(eh(t)){for(const n in t)lr(t[n],e,r);for(const n of Object.getOwnPropertySymbols(t))Object.prototype.propertyIsEnumerable.call(t,n)&&lr(t[n],e,r)}return t}/** -* @vue/runtime-core v3.5.13 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/function _i(t,e,r,n){try{return n?t(...n):t()}catch(l){ko(l,e,r)}}function Ne(t,e,r,n){if(It(t)){const l=_i(t,e,r,n);return l&&Zc(l)&&l.catch(o=>{ko(o,e,r)}),l}if(At(t)){const l=[];for(let o=0;o=ti(r)?pe.push(t):pe.splice(function(n){let l=Ve+1,o=pe.length;for(;l>>1,i=pe[s],a=ti(i);ati(r)-ti(n));if(On.length=0,Tr)return void Tr.push(...e);for(Tr=e,wn=0;wnt.id==null?2&t.flags?-1:1/0:t.id;function Rh(t){try{for(Ve=0;Ve{n._d&&gu(-1);const o=io(e);let s;try{s=t(...l)}finally{io(o),n._d&&gu(1)}return s};return n._n=!0,n._c=!0,n._d=!0,n}function $y(t,e){if(Jt===null)return t;const r=xo(Jt),n=t.dirs||(t.dirs=[]);for(let l=0;lt.__isTeleport,Gn=t=>t&&(t.disabled||t.disabled===""),Yl=t=>t&&(t.defer||t.defer===""),Jl=t=>typeof SVGElement<"u"&&t instanceof SVGElement,Xl=t=>typeof MathMLElement=="function"&&t instanceof MathMLElement,Go=(t,e)=>{const r=t&&t.to;return qt(r)?e?e(r):null:r},Lh={name:"Teleport",__isTeleport:!0,process(t,e,r,n,l,o,s,i,a,u){const{mc:c,pc:d,pbc:f,o:{insert:g,querySelector:b,createText:_,createComment:S}}=u,U=Gn(e.props);let{shapeFlag:D,children:G,dynamicChildren:$}=e;if(t==null){const X=e.el=_(""),B=e.anchor=_("");g(X,r,n),g(B,r,n);const A=(w,M)=>{16&D&&(l&&l.isCE&&(l.ce._teleportTarget=w),c(G,w,M,l,o,s,i,a))},E=()=>{const w=e.target=Go(e.props,b),M=Zl(w,e,_,g);w&&(s!=="svg"&&Jl(w)?s="svg":s!=="mathml"&&Xl(w)&&(s="mathml"),U||(A(w,M),Pi(e,!1)))};U&&(A(r,B),Pi(e,!0)),Yl(e.props)?he(()=>{E(),e.el.__isMounted=!0},o):E()}else{if(Yl(e.props)&&!t.el.__isMounted)return void he(()=>{Lh.process(t,e,r,n,l,o,s,i,a,u),delete t.el.__isMounted},o);e.el=t.el,e.targetStart=t.targetStart;const X=e.anchor=t.anchor,B=e.target=t.target,A=e.targetAnchor=t.targetAnchor,E=Gn(t.props),w=E?r:B,M=E?X:A;if(s==="svg"||Jl(B)?s="svg":(s==="mathml"||Xl(B))&&(s="mathml"),$?(f(t.dynamicChildren,$,w,l,o,s,i),Sl(t,e,!0)):a||d(t,e,w,M,l,o,s,i,!1),U)E?e.props&&t.props&&e.props.to!==t.props.to&&(e.props.to=t.props.to):Ri(e,r,X,u,1);else if((e.props&&e.props.to)!==(t.props&&t.props.to)){const R=e.target=Go(e.props,b);R&&Ri(e,R,null,u,0)}else E&&Ri(e,B,A,u,1);Pi(e,U)}},remove(t,e,r,{um:n,o:{remove:l}},o){const{shapeFlag:s,children:i,anchor:a,targetStart:u,targetAnchor:c,target:d,props:f}=t;if(d&&(l(u),l(c)),o&&l(a),16&s){const g=o||!Gn(f);for(let b=0;b{const e=t.subTree;return e.component?Uh(e.component):e},_g={name:"BaseTransition",props:jh,setup(t,{slots:e}){const r=cf(),n=function(){const l={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return bl(()=>{l.isMounted=!0}),Hh(()=>{l.isUnmounting=!0}),l}();return()=>{const l=e.default&&Fh(e.default(),!0);if(!l||!l.length)return;const o=Nh(l),s=Rt(t),{mode:i}=s;if(n.isLeaving)return Qo(o);const a=tu(o);if(!a)return Qo(o);let u=Cs(a,s,n,r,d=>u=d);a.type!==de&&hi(a,u);let c=r.subTree&&tu(r.subTree);if(c&&c.type!==de&&!Zr(a,c)&&Uh(r).type!==de){let d=Cs(c,s,n,r);if(hi(c,d),i==="out-in"&&a.type!==de)return n.isLeaving=!0,d.afterLeave=()=>{n.isLeaving=!1,8&r.job.flags||r.update(),delete d.afterLeave,c=void 0},Qo(o);i==="in-out"&&a.type!==de?d.delayLeave=(f,g,b)=>{Dh(n,c)[String(c.key)]=c,f[Or]=()=>{g(),f[Or]=void 0,delete u.delayedLeave,c=void 0},u.delayedLeave=()=>{b(),delete u.delayedLeave,c=void 0}}:c=void 0}else c&&(c=void 0);return o}}};function Nh(t){let e=t[0];if(t.length>1){for(const r of t)if(r.type!==de){e=r;break}}return e}const Eg=_g;function Dh(t,e){const{leavingVNodes:r}=t;let n=r.get(e.type);return n||(n=Object.create(null),r.set(e.type,n)),n}function Cs(t,e,r,n,l){const{appear:o,mode:s,persisted:i=!1,onBeforeEnter:a,onEnter:u,onAfterEnter:c,onEnterCancelled:d,onBeforeLeave:f,onLeave:g,onAfterLeave:b,onLeaveCancelled:_,onBeforeAppear:S,onAppear:U,onAfterAppear:D,onAppearCancelled:G}=e,$=String(t.key),X=Dh(r,t),B=(w,M)=>{w&&Ne(w,n,9,M)},A=(w,M)=>{const R=M[1];B(w,M),At(w)?w.every(nt=>nt.length<=1)&&R():w.length<=1&&R()},E={mode:s,persisted:i,beforeEnter(w){let M=a;if(!r.isMounted){if(!o)return;M=S||a}w[Or]&&w[Or](!0);const R=X[$];R&&Zr(t,R)&&R.el[Or]&&R.el[Or](),B(M,[w])},enter(w){let M=u,R=c,nt=d;if(!r.isMounted){if(!o)return;M=U||u,R=D||c,nt=G||d}let st=!1;const Z=w[Bi]=j=>{st||(st=!0,B(j?nt:R,[w]),E.delayedLeave&&E.delayedLeave(),w[Bi]=void 0)};M?A(M,[w,Z]):Z()},leave(w,M){const R=String(t.key);if(w[Bi]&&w[Bi](!0),r.isUnmounting)return M();B(f,[w]);let nt=!1;const st=w[Or]=Z=>{nt||(nt=!0,M(),B(Z?_:b,[w]),w[Or]=void 0,X[R]===t&&delete X[R])};X[R]=t,g?A(g,[w,st]):st()},clone(w){const M=Cs(w,e,r,n,l);return l&&l(M),M}};return E}function Qo(t){if(Io(t))return(t=Ur(t)).children=null,t}function tu(t){if(!Io(t))return Mh(t.type)&&t.children?Nh(t.children):t;const{shapeFlag:e,children:r}=t;if(r){if(16&e)return r[0];if(32&e&&It(r.default))return r.default()}}function hi(t,e){6&t.shapeFlag&&t.component?(t.transition=e,hi(t.component.subTree,e)):128&t.shapeFlag?(t.ssContent.transition=e.clone(t.ssContent),t.ssFallback.transition=e.clone(t.ssFallback)):t.transition=e}function Fh(t,e=!1,r){let n=[],l=0;for(let o=0;o1)for(let o=0;ooo(b,e&&(At(e)?e[_]:e),r,n,l));if(Sn(n)&&!l)return void(512&n.shapeFlag&&n.type.__asyncResolved&&n.component.subTree.component&&oo(t,e,r,n.component.subTree));const o=4&n.shapeFlag?xo(n.component):n.el,s=l?null:o,{i,r:a}=t,u=e&&e.r,c=i.refs===Ut?i.refs={}:i.refs,d=i.setupState,f=Rt(d),g=d===Ut?()=>!1:b=>Mt(f,b);if(u!=null&&u!==a&&(qt(u)?(c[u]=null,g(u)&&(d[u]=null)):Qt(u)&&(u.value=null)),It(a))_i(a,i,12,[s,c]);else{const b=qt(a),_=Qt(a);if(b||_){const S=()=>{if(t.f){const U=b?g(a)?d[a]:c[a]:a.value;l?At(U)&&ol(U,o):At(U)?U.includes(o)||U.push(o):b?(c[a]=[o],g(a)&&(d[a]=c[a])):(a.value=[o],t.k&&(c[t.k]=a.value))}else b?(c[a]=s,g(a)&&(d[a]=s)):_&&(a.value=s,t.k&&(c[t.k]=s))};s?(S.id=-1,he(S,r)):S()}}}eo().requestIdleCallback,eo().cancelIdleCallback;const Sn=t=>!!t.type.__asyncLoader,Io=t=>t.type.__isKeepAlive;function Sg(t,e){Vh(t,"a",e)}function Ag(t,e){Vh(t,"da",e)}function Vh(t,e,r=te){const n=t.__wdc||(t.__wdc=()=>{let l=r;for(;l;){if(l.isDeactivated)return;l=l.parent}return t()});if(To(e,n,r),r){let l=r.parent;for(;l&&l.parent;)Io(l.parent.vnode)&&kg(n,e,r,l),l=l.parent}}function kg(t,e,r,n){const l=To(e,t,n,!0);vl(()=>{ol(n[e],l)},r)}function To(t,e,r=te,n=!1){if(r){const l=r[t]||(r[t]=[]),o=e.__weh||(e.__weh=(...s)=>{Lr();const i=Ei(r),a=Ne(e,r,t,s);return i(),jr(),a});return n?l.unshift(o):l.push(o),o}}const mr=t=>(e,r=te)=>{di&&t!=="sp"||To(t,(...n)=>e(...n),r)},Ig=mr("bm"),bl=mr("m"),qh=mr("bu"),Tg=mr("u"),Hh=mr("bum"),vl=mr("um"),Og=mr("sp"),xg=mr("rtg"),Cg=mr("rtc");function Rg(t,e=te){To("ec",t,e)}const wl="components";function qy(t,e){return _l(wl,t,!0,e)||t}const zh=Symbol.for("v-ndc");function Hy(t){return qt(t)?_l(wl,t,!1)||t:t||zh}function zy(t){return _l("directives",t)}function _l(t,e,r=!0,n=!1){const l=Jt||te;if(l){const o=l.type;if(t===wl){const i=Zg(o,!1);if(i&&(i===e||i===Ce(e)||i===_o(Ce(e))))return o}const s=eu(l[t]||o[t],e)||eu(l.appContext[t],e);return!s&&n?o:s}}function eu(t,e){return t&&(t[e]||t[Ce(e)]||t[_o(Ce(e))])}function Ky(t,e,r,n){let l;const o=r,s=At(t);if(s||qt(t)){let i=!1;s&&Br(t)&&(i=!xe(t),t=Ao(t)),l=new Array(t.length);for(let a=0,u=t.length;ae(i,a,void 0,o));else{const i=Object.keys(t);l=new Array(i.length);for(let a=0,u=i.length;a{const o=n.fn(...l);return o&&(o.key=n.key),o}:n.fn)}return t}function Qy(t,e,r={},n,l){if(Jt.ce||Jt.parent&&Sn(Jt.parent)&&Jt.parent.ce)return e!=="default"&&(r.name=e),Ls(),js(me,null,[ye("slot",r,n&&n())],64);let o=t[e];o&&o._c&&(o._d=!1),Ls();const s=o&&Kh(o(r)),i=r.key||s&&s.key,a=js(me,{key:(i&&!gr(i)?i:`_${e}`)+(!s&&n?"_fb":"")},s||(n?n():[]),s&&t._===1?64:-2);return!l&&a.scopeId&&(a.slotScopeIds=[a.scopeId+"-s"]),o&&o._c&&(o._d=!0),a}function Kh(t){return t.some(e=>!pi(e)||e.type!==de&&!(e.type===me&&!Kh(e.children)))?t:null}const Rs=t=>t?hf(t)?xo(t):Rs(t.parent):null,ei=ee(Object.create(null),{$:t=>t,$el:t=>t.vnode.el,$data:t=>t.data,$props:t=>t.props,$attrs:t=>t.attrs,$slots:t=>t.slots,$refs:t=>t.refs,$parent:t=>Rs(t.parent),$root:t=>Rs(t.root),$host:t=>t.ce,$emit:t=>t.emit,$options:t=>Qh(t),$forceUpdate:t=>t.f||(t.f=()=>{yl(t.update)}),$nextTick:t=>t.n||(t.n=ml.bind(t.proxy)),$watch:t=>$g.bind(t)}),Yo=(t,e)=>t!==Ut&&!t.__isScriptSetup&&Mt(t,e),Pg={get({_:t},e){if(e==="__v_skip")return!0;const{ctx:r,setupState:n,data:l,props:o,accessCache:s,type:i,appContext:a}=t;let u;if(e[0]!=="$"){const g=s[e];if(g!==void 0)switch(g){case 1:return n[e];case 2:return l[e];case 4:return r[e];case 3:return o[e]}else{if(Yo(n,e))return s[e]=1,n[e];if(l!==Ut&&Mt(l,e))return s[e]=2,l[e];if((u=t.propsOptions[0])&&Mt(u,e))return s[e]=3,o[e];if(r!==Ut&&Mt(r,e))return s[e]=4,r[e];Ps&&(s[e]=0)}}const c=ei[e];let d,f;return c?(e==="$attrs"&&le(t.attrs,0,""),c(t)):(d=i.__cssModules)&&(d=d[e])?d:r!==Ut&&Mt(r,e)?(s[e]=4,r[e]):(f=a.config.globalProperties,Mt(f,e)?f[e]:void 0)},set({_:t},e,r){const{data:n,setupState:l,ctx:o}=t;return Yo(l,e)?(l[e]=r,!0):n!==Ut&&Mt(n,e)?(n[e]=r,!0):!Mt(t.props,e)&&(e[0]!=="$"||!(e.slice(1)in t))&&(o[e]=r,!0)},has({_:{data:t,setupState:e,accessCache:r,ctx:n,appContext:l,propsOptions:o}},s){let i;return!!r[s]||t!==Ut&&Mt(t,s)||Yo(e,s)||(i=o[0])&&Mt(i,s)||Mt(n,s)||Mt(ei,s)||Mt(l.config.globalProperties,s)},defineProperty(t,e,r){return r.get!=null?t._.accessCache[e]=0:Mt(r,"value")&&this.set(t,e,r.value,null),Reflect.defineProperty(t,e,r)}};function ru(t){return At(t)?t.reduce((e,r)=>(e[r]=null,e),{}):t}let Ps=!0;function Bg(t){const e=Qh(t),r=t.proxy,n=t.ctx;Ps=!1,e.beforeCreate&&nu(e.beforeCreate,t,"bc");const{data:l,computed:o,methods:s,watch:i,provide:a,inject:u,created:c,beforeMount:d,mounted:f,beforeUpdate:g,updated:b,activated:_,deactivated:S,beforeDestroy:U,beforeUnmount:D,destroyed:G,unmounted:$,render:X,renderTracked:B,renderTriggered:A,errorCaptured:E,serverPrefetch:w,expose:M,inheritAttrs:R,components:nt,directives:st,filters:Z}=e;if(u&&function(N,F){At(N)&&(N=Bs(N));for(const it in N){const J=N[it];let Y;Y=Wt(J)?"default"in J?Qe(J.from||it,J.default,!0):Qe(J.from||it):Qe(J),Qt(Y)?Object.defineProperty(F,it,{enumerable:!0,configurable:!0,get:()=>Y.value,set:k=>Y.value=k}):F[it]=Y}}(u,n,null),s)for(const N in s){const F=s[N];It(F)&&(n[N]=F.bind(r))}if(l){const N=l.call(r,r);Wt(N)&&(t.data=wi(N))}if(Ps=!0,o)for(const N in o){const F=o[N],it=It(F)?F.bind(r,r):It(F.get)?F.get.bind(r,r):Ge,J=!It(F)&&It(F.set)?F.set.bind(r):Ge,Y=Te({get:it,set:J});Object.defineProperty(n,N,{enumerable:!0,configurable:!0,get:()=>Y.value,set:k=>Y.value=k})}if(i)for(const N in i)Gh(i[N],n,r,N);if(a){const N=It(a)?a.call(r):a;Reflect.ownKeys(N).forEach(F=>{$i(F,N[F])})}function j(N,F){At(F)?F.forEach(it=>N(it.bind(r))):F&&N(F.bind(r))}if(c&&nu(c,t,"c"),j(Ig,d),j(bl,f),j(qh,g),j(Tg,b),j(Sg,_),j(Ag,S),j(Rg,E),j(Cg,B),j(xg,A),j(Hh,D),j(vl,$),j(Og,w),At(M))if(M.length){const N=t.exposed||(t.exposed={});M.forEach(F=>{Object.defineProperty(N,F,{get:()=>r[F],set:it=>r[F]=it})})}else t.exposed||(t.exposed={});X&&t.render===Ge&&(t.render=X),R!=null&&(t.inheritAttrs=R),nt&&(t.components=nt),st&&(t.directives=st),w&&$h(t)}function nu(t,e,r){Ne(At(t)?t.map(n=>n.bind(e.proxy)):t.bind(e.proxy),e,r)}function Gh(t,e,r,n){let l=n.includes(".")?nf(r,n):()=>r[n];if(qt(t)){const o=e[t];It(o)&&xn(l,o)}else if(It(t))xn(l,t.bind(r));else if(Wt(t))if(At(t))t.forEach(o=>Gh(o,e,r,n));else{const o=It(t.handler)?t.handler.bind(r):e[t.handler];It(o)&&xn(l,o,t)}}function Qh(t){const e=t.type,{mixins:r,extends:n}=e,{mixins:l,optionsCache:o,config:{optionMergeStrategies:s}}=t.appContext,i=o.get(e);let a;return i?a=i:l.length||r||n?(a={},l.length&&l.forEach(u=>so(a,u,s,!0)),so(a,e,s)):a=e,Wt(e)&&o.set(e,a),a}function so(t,e,r,n=!1){const{mixins:l,extends:o}=e;o&&so(t,o,r,!0),l&&l.forEach(s=>so(t,s,r,!0));for(const s in e)if(!(n&&s==="expose")){const i=Mg[s]||r&&r[s];t[s]=i?i(t[s],e[s]):e[s]}return t}const Mg={data:iu,props:ou,emits:ou,methods:Fn,computed:Fn,beforeCreate:ce,created:ce,beforeMount:ce,mounted:ce,beforeUpdate:ce,updated:ce,beforeDestroy:ce,beforeUnmount:ce,destroyed:ce,unmounted:ce,activated:ce,deactivated:ce,errorCaptured:ce,serverPrefetch:ce,components:Fn,directives:Fn,watch:function(t,e){if(!t)return e;if(!e)return t;const r=ee(Object.create(null),t);for(const n in e)r[n]=ce(t[n],e[n]);return r},provide:iu,inject:function(t,e){return Fn(Bs(t),Bs(e))}};function iu(t,e){return e?t?function(){return ee(It(t)?t.call(this,this):t,It(e)?e.call(this,this):e)}:e:t}function Bs(t){if(At(t)){const e={};for(let r=0;r(o.has(u)||(u&&It(u.install)?(o.add(u),u.install(a,...c)):It(u)&&(o.add(u),u(a,...c))),a),mixin:u=>(l.mixins.includes(u)||l.mixins.push(u),a),component:(u,c)=>c?(l.components[u]=c,a):l.components[u],directive:(u,c)=>c?(l.directives[u]=c,a):l.directives[u],mount(u,c,d){if(!i){const f=a._ceVNode||ye(r,n);return f.appContext=l,d===!0?d="svg":d===!1&&(d=void 0),t(f,u,d),i=!0,a._container=u,u.__vue_app__=a,xo(f.component)}},onUnmount(u){s.push(u)},unmount(){i&&(Ne(s,a._instance,16),t(null,a._container),delete a._container.__vue_app__)},provide:(u,c)=>(l.provides[u]=c,a),runWithContext(u){const c=on;on=a;try{return u()}finally{on=c}}};return a}}let on=null;function $i(t,e){if(te){let r=te.provides;const n=te.parent&&te.parent.provides;n===r&&(r=te.provides=Object.create(n)),r[t]=e}}function Qe(t,e,r=!1){const n=te||Jt;if(n||on){const l=on?on._context.provides:n?n.parent==null?n.vnode.appContext&&n.vnode.appContext.provides:n.parent.provides:void 0;if(l&&t in l)return l[t];if(arguments.length>1)return r&&It(e)?e.call(n&&n.proxy):e}}const Jh={},su=()=>Object.create(Jh),Xh=t=>Object.getPrototypeOf(t)===Jh;function au(t,e,r,n){const[l,o]=t.propsOptions;let s,i=!1;if(e)for(let a in e){if(Jn(a))continue;const u=e[a];let c;l&&Mt(l,c=Ce(a))?o&&o.includes(c)?(s||(s={}))[c]=u:r[c]=u:ao(t.emitsOptions,a)||a in n&&u===n[a]||(n[a]=u,i=!0)}if(o){const a=Rt(r),u=s||Ut;for(let c=0;c{a=!0;const[f,g]=Zh(d,e,!0);ee(s,f),g&&i.push(...g)};!r&&e.mixins.length&&e.mixins.forEach(c),t.extends&&c(t.extends),t.mixins&&t.mixins.forEach(c)}if(!o&&!a)return Wt(t)&&n.set(t,kn),kn;if(At(o))for(let c=0;ct[0]==="_"||t==="$stable",El=t=>At(t)?t.map(qe):[qe(t)],Ng=(t,e,r)=>{if(e._n)return e;const n=wg((...l)=>El(e(...l)),r);return n._c=!1,n},uu=(t,e,r)=>{const n=t._ctx;for(const l in t){if(tf(l))continue;const o=t[l];if(It(o))e[l]=Ng(0,o,n);else if(o!=null){const s=El(o);e[l]=()=>s}}},cu=(t,e)=>{const r=El(e);t.slots.default=()=>r},hu=(t,e,r)=>{for(const n in e)(r||n!=="_")&&(t[n]=e[n])},he=function(t,e){e&&e.pendingBranch?At(t)?e.effects.push(...t):e.effects.push(t):xh(t)};function Dg(t){return function(e){eo().__VUE__=!0;const{insert:r,remove:n,patchProp:l,createElement:o,createText:s,createComment:i,setText:a,setElementText:u,parentNode:c,nextSibling:d,setScopeId:f=Ge,insertStaticContent:g}=e,b=(P,W,C,Q=null,h=null,p=null,v=void 0,O=null,x=!!W.dynamicChildren)=>{if(P===W)return;P&&!Zr(P,W)&&(Q=ut(P),k(P,h,p,!0),P=null),W.patchFlag===-2&&(x=!1,W.dynamicChildren=null);const{type:I,ref:m,shapeFlag:y}=W;switch(I){case Oo:_(P,W,C,Q);break;case de:S(P,W,C,Q);break;case Vi:P==null&&U(W,C,Q,v);break;case me:R(P,W,C,Q,h,p,v,O,x);break;default:1&y?$(P,W,C,Q,h,p,v,O,x):6&y?nt(P,W,C,Q,h,p,v,O,x):(64&y||128&y)&&I.process(P,W,C,Q,h,p,v,O,x,rt)}m!=null&&h&&oo(m,P&&P.ref,p,W||P,!W)},_=(P,W,C,Q)=>{if(P==null)r(W.el=s(W.children),C,Q);else{const h=W.el=P.el;W.children!==P.children&&a(h,W.children)}},S=(P,W,C,Q)=>{P==null?r(W.el=i(W.children||""),C,Q):W.el=P.el},U=(P,W,C,Q)=>{[P.el,P.anchor]=g(P.children,W,C,Q,P.el,P.anchor)},D=({el:P,anchor:W},C,Q)=>{let h;for(;P&&P!==W;)h=d(P),r(P,C,Q),P=h;r(W,C,Q)},G=({el:P,anchor:W})=>{let C;for(;P&&P!==W;)C=d(P),n(P),P=C;n(W)},$=(P,W,C,Q,h,p,v,O,x)=>{W.type==="svg"?v="svg":W.type==="math"&&(v="mathml"),P==null?X(W,C,Q,h,p,v,O,x):E(P,W,h,p,v,O,x)},X=(P,W,C,Q,h,p,v,O)=>{let x,I;const{props:m,shapeFlag:y,transition:T,dirs:L}=P;if(x=P.el=o(P.type,p,m&&m.is,m),8&y?u(x,P.children):16&y&&A(P.children,x,null,Q,h,Jo(P,p),v,O),L&&Wr(P,null,Q,"created"),B(x,P,P.scopeId,v,Q),m){for(const V in m)V==="value"||Jn(V)||l(x,V,null,m[V],p,Q);"value"in m&&l(x,"value",null,m.value,p),(I=m.onVnodeBeforeMount)&&Fe(I,Q,P)}L&&Wr(P,null,Q,"beforeMount");const z=function(V,lt){return(!V||V&&!V.pendingBranch)&<&&!lt.persisted}(h,T);z&&T.beforeEnter(x),r(x,W,C),((I=m&&m.onVnodeMounted)||z||L)&&he(()=>{I&&Fe(I,Q,P),z&&T.enter(x),L&&Wr(P,null,Q,"mounted")},h)},B=(P,W,C,Q,h)=>{if(C&&f(P,C),Q)for(let p=0;p{for(let I=x;I{const O=W.el=P.el;let{patchFlag:x,dynamicChildren:I,dirs:m}=W;x|=16&P.patchFlag;const y=P.props||Ut,T=W.props||Ut;let L;if(C&&$r(C,!1),(L=T.onVnodeBeforeUpdate)&&Fe(L,C,W,P),m&&Wr(W,P,C,"beforeUpdate"),C&&$r(C,!0),(y.innerHTML&&T.innerHTML==null||y.textContent&&T.textContent==null)&&u(O,""),I?w(P.dynamicChildren,I,O,C,Q,Jo(W,h),p):v||F(P,W,O,null,C,Q,Jo(W,h),p,!1),x>0){if(16&x)M(O,y,T,C,h);else if(2&x&&y.class!==T.class&&l(O,"class",null,T.class,h),4&x&&l(O,"style",y.style,T.style,h),8&x){const z=W.dynamicProps;for(let V=0;V{L&&Fe(L,C,W,P),m&&Wr(W,P,C,"updated")},Q)},w=(P,W,C,Q,h,p,v)=>{for(let O=0;O{if(W!==C){if(W!==Ut)for(const p in W)Jn(p)||p in C||l(P,p,W[p],null,h,Q);for(const p in C){if(Jn(p))continue;const v=C[p],O=W[p];v!==O&&p!=="value"&&l(P,p,O,v,h,Q)}"value"in C&&l(P,"value",W.value,C.value,h)}},R=(P,W,C,Q,h,p,v,O,x)=>{const I=W.el=P?P.el:s(""),m=W.anchor=P?P.anchor:s("");let{patchFlag:y,dynamicChildren:T,slotScopeIds:L}=W;L&&(O=O?O.concat(L):L),P==null?(r(I,C,Q),r(m,C,Q),A(W.children||[],C,m,h,p,v,O,x)):y>0&&64&y&&T&&P.dynamicChildren?(w(P.dynamicChildren,T,C,h,p,v,O),(W.key!=null||h&&W===h.subTree)&&Sl(P,W,!0)):F(P,W,C,m,h,p,v,O,x)},nt=(P,W,C,Q,h,p,v,O,x)=>{W.slotScopeIds=O,P==null?512&W.shapeFlag?h.ctx.activate(W,C,Q,v,x):st(W,C,Q,h,p,v,x):Z(P,W,x)},st=(P,W,C,Q,h,p,v)=>{const O=P.component=function(x,I,m){const y=x.type,T=(I?I.appContext:x.appContext)||Yg,L={uid:Jg++,vnode:x,type:y,parent:I,appContext:T,root:null,next:null,subTree:null,effect:null,update:null,job:null,scope:new sh(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:I?I.provides:Object.create(T.provides),ids:I?I.ids:["",0,0],accessCache:null,renderCache:[],components:null,directives:null,propsOptions:Zh(y,T),emitsOptions:of(y,T),emit:null,emitted:null,propsDefaults:Ut,inheritAttrs:y.inheritAttrs,ctx:Ut,data:Ut,props:Ut,attrs:Ut,slots:Ut,refs:Ut,setupState:Ut,setupContext:null,suspense:m,suspenseId:m?m.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return L.ctx={_:L},L.root=I?I.root:L,L.emit=qg.bind(null,L),x.ce&&x.ce(L),L}(P,Q,h);if(Io(P)&&(O.ctx.renderer=rt),function(x,I=!1,m=!1){I&&Us(I);const{props:y,children:T}=x.vnode,L=hf(x);(function(V,lt,ct,yt=!1){const bt={},wt=su();V.propsDefaults=Object.create(null),au(V,lt,bt,wt);for(const Ot in V.propsOptions[0])Ot in bt||(bt[Ot]=void 0);ct?V.props=yt?bt:Sh(bt):V.type.props?V.props=bt:V.props=wt,V.attrs=wt})(x,y,L,I),((V,lt,ct)=>{const yt=V.slots=su();if(32&V.vnode.shapeFlag){const bt=lt._;bt?(hu(yt,lt,ct),ct&&rh(yt,"_",bt,!0)):uu(lt,yt)}else lt&&cu(V,lt)})(x,T,m);const z=L?function(V,lt){const ct=V.type;V.accessCache=Object.create(null),V.proxy=new Proxy(V.ctx,Pg);const{setup:yt}=ct;if(yt){Lr();const bt=V.setupContext=yt.length>1?function(_t){const Tt=kt=>{_t.exposed=kt||{}};return{attrs:new Proxy(_t.attrs,Xg),slots:_t.slots,emit:_t.emit,expose:Tt}}(V):null,wt=Ei(V),Ot=_i(yt,V,0,[V.props,bt]),xt=Zc(Ot);if(jr(),wt(),!xt&&!V.sp||Sn(V)||$h(V),xt){if(Ot.then(mu,mu),lt)return Ot.then(_t=>{yu(V,_t)}).catch(_t=>{ko(_t,V,0)});V.asyncDep=Ot}else yu(V,Ot)}else ff(V)}(x,I):void 0;I&&Us(!1)}(O,!1,v),O.asyncDep){if(h&&h.registerDep(O,j,v),!P.el){const x=O.subTree=ye(de);S(null,x,W,C)}}else j(O,P,W,C,h,p,v)},Z=(P,W,C)=>{const Q=W.component=P.component;if(function(h,p,v){const{props:O,children:x,component:I}=h,{props:m,children:y,patchFlag:T}=p,L=I.emitsOptions;if(p.dirs||p.transition)return!0;if(!(v&&T>=0))return!(!x&&!y||y&&y.$stable)||O!==m&&(O?!m||du(O,m,L):!!m);if(1024&T)return!0;if(16&T)return O?du(O,m,L):!!m;if(8&T){const z=p.dynamicProps;for(let V=0;V{const O=()=>{if(P.isMounted){let{next:y,bu:T,u:L,parent:z,vnode:V}=P;{const wt=ef(P);if(wt)return y&&(y.el=V.el,N(P,y,v)),void wt.asyncDep.then(()=>{P.isUnmounted||O()})}let lt,ct=y;$r(P,!1),y?(y.el=V.el,N(P,y,v)):y=V,T&&Wo(T),(lt=y.props&&y.props.onVnodeBeforeUpdate)&&Fe(lt,z,y,V),$r(P,!0);const yt=pu(P),bt=P.subTree;P.subTree=yt,b(bt,yt,c(bt.el),ut(bt),P,h,p),y.el=yt.el,ct===null&&function({vnode:wt,parent:Ot},xt){for(;Ot;){const _t=Ot.subTree;if(_t.suspense&&_t.suspense.activeBranch===wt&&(_t.el=wt.el),_t!==wt)break;(wt=Ot.vnode).el=xt,Ot=Ot.parent}}(P,yt.el),L&&he(L,h),(lt=y.props&&y.props.onVnodeUpdated)&&he(()=>Fe(lt,z,y,V),h)}else{let y;const{el:T,props:L}=W,{bm:z,m:V,parent:lt,root:ct,type:yt}=P,bt=Sn(W);$r(P,!1),z&&Wo(z),!bt&&(y=L&&L.onVnodeBeforeMount)&&Fe(y,lt,W),$r(P,!0);{ct.ce&&ct.ce._injectChildStyle(yt);const wt=P.subTree=pu(P);b(null,wt,C,Q,P,h,p),W.el=wt.el}if(V&&he(V,h),!bt&&(y=L&&L.onVnodeMounted)){const wt=W;he(()=>Fe(y,lt,wt),h)}(256&W.shapeFlag||lt&&Sn(lt.vnode)&&256<.vnode.shapeFlag)&&P.a&&he(P.a,h),P.isMounted=!0,W=C=Q=null}};P.scope.on();const x=P.effect=new uh(O);P.scope.off();const I=P.update=x.run.bind(x),m=P.job=x.runIfDirty.bind(x);m.i=P,m.id=P.uid,x.scheduler=()=>yl(m),$r(P,!0),I()},N=(P,W,C)=>{W.component=P;const Q=P.vnode.props;P.vnode=W,P.next=null,function(h,p,v,O){const{props:x,attrs:I,vnode:{patchFlag:m}}=h,y=Rt(x),[T]=h.propsOptions;let L=!1;if(!(O||m>0)||16&m){let z;au(h,p,x,I)&&(L=!0);for(const V in y)p&&(Mt(p,V)||(z=Nr(V))!==V&&Mt(p,z))||(T?!v||v[V]===void 0&&v[z]===void 0||(x[V]=Ms(T,y,V,void 0,h,!0)):delete x[V]);if(I!==y)for(const V in I)p&&Mt(p,V)||(delete I[V],L=!0)}else if(8&m){const z=h.vnode.dynamicProps;for(let V=0;V{const{vnode:O,slots:x}=h;let I=!0,m=Ut;if(32&O.shapeFlag){const y=p._;y?v&&y===1?I=!1:hu(x,p,v):(I=!p.$stable,uu(p,x)),m=p}else p&&(cu(h,p),m={default:1});if(I)for(const y in x)tf(y)||m[y]!=null||delete x[y]})(P,W.children,C),Lr(),Ql(P),jr()},F=(P,W,C,Q,h,p,v,O,x=!1)=>{const I=P&&P.children,m=P?P.shapeFlag:0,y=W.children,{patchFlag:T,shapeFlag:L}=W;if(T>0){if(128&T)return void J(I,y,C,Q,h,p,v,O,x);if(256&T)return void it(I,y,C,Q,h,p,v,O,x)}8&L?(16&m&&at(I,h,p),y!==I&&u(C,y)):16&m?16&L?J(I,y,C,Q,h,p,v,O,x):at(I,h,p,!0):(8&m&&u(C,""),16&L&&A(y,C,Q,h,p,v,O,x))},it=(P,W,C,Q,h,p,v,O,x)=>{W=W||kn;const I=(P=P||kn).length,m=W.length,y=Math.min(I,m);let T;for(T=0;Tm?at(P,h,p,!0,!1,y):A(W,C,Q,h,p,v,O,x,y)},J=(P,W,C,Q,h,p,v,O,x)=>{let I=0;const m=W.length;let y=P.length-1,T=m-1;for(;I<=y&&I<=T;){const L=P[I],z=W[I]=x?xr(W[I]):qe(W[I]);if(!Zr(L,z))break;b(L,z,C,null,h,p,v,O,x),I++}for(;I<=y&&I<=T;){const L=P[y],z=W[T]=x?xr(W[T]):qe(W[T]);if(!Zr(L,z))break;b(L,z,C,null,h,p,v,O,x),y--,T--}if(I>y){if(I<=T){const L=T+1,z=LT)for(;I<=y;)k(P[I],h,p,!0),I++;else{const L=I,z=I,V=new Map;for(I=z;I<=T;I++){const _t=W[I]=x?xr(W[I]):qe(W[I]);_t.key!=null&&V.set(_t.key,I)}let lt,ct=0;const yt=T-z+1;let bt=!1,wt=0;const Ot=new Array(yt);for(I=0;I=yt){k(_t,h,p,!0);continue}let Tt;if(_t.key!=null)Tt=V.get(_t.key);else for(lt=z;lt<=T;lt++)if(Ot[lt-z]===0&&Zr(_t,W[lt])){Tt=lt;break}Tt===void 0?k(_t,h,p,!0):(Ot[Tt-z]=I+1,Tt>=wt?wt=Tt:bt=!0,b(_t,W[Tt],C,null,h,p,v,O,x),ct++)}const xt=bt?function(_t){const Tt=_t.slice(),kt=[0];let vt,Ct,re,Yt,se;const Je=_t.length;for(vt=0;vt>1,_t[kt[se]]<_e?re=se+1:Yt=se;_e<_t[kt[re]]&&(re>0&&(Tt[vt]=kt[re-1]),kt[re]=vt)}}for(re=kt.length,Yt=kt[re-1];re-- >0;)kt[re]=Yt,Yt=Tt[Yt];return kt}(Ot):kn;for(lt=xt.length-1,I=yt-1;I>=0;I--){const _t=z+I,Tt=W[_t],kt=_t+1{const{el:p,type:v,transition:O,children:x,shapeFlag:I}=P;if(6&I)return void Y(P.component.subTree,W,C,Q);if(128&I)return void P.suspense.move(W,C,Q);if(64&I)return void v.move(P,W,C,rt);if(v===me){r(p,W,C);for(let m=0;mO.enter(p),h);else{const{leave:m,delayLeave:y,afterLeave:T}=O,L=()=>r(p,W,C),z=()=>{m(p,()=>{L(),T&&T()})};y?y(p,L,z):z()}else r(p,W,C)},k=(P,W,C,Q=!1,h=!1)=>{const{type:p,props:v,ref:O,children:x,dynamicChildren:I,shapeFlag:m,patchFlag:y,dirs:T,cacheIndex:L}=P;if(y===-2&&(h=!1),O!=null&&oo(O,null,C,P,!0),L!=null&&(W.renderCache[L]=void 0),256&m)return void W.ctx.deactivate(P);const z=1&m&&T,V=!Sn(P);let lt;if(V&&(lt=v&&v.onVnodeBeforeUnmount)&&Fe(lt,W,P),6&m)ot(P.component,C,Q);else{if(128&m)return void P.suspense.unmount(C,Q);z&&Wr(P,null,W,"beforeUnmount"),64&m?P.type.remove(P,W,C,rt,Q):I&&!I.hasOnce&&(p!==me||y>0&&64&y)?at(I,W,C,!1,!0):(p===me&&384&y||!h&&16&m)&&at(x,W,C),Q&&q(P)}(V&&(lt=v&&v.onVnodeUnmounted)||z)&&he(()=>{lt&&Fe(lt,W,P),z&&Wr(P,null,W,"unmounted")},C)},q=P=>{const{type:W,el:C,anchor:Q,transition:h}=P;if(W===me)return void et(C,Q);if(W===Vi)return void G(P);const p=()=>{n(C),h&&!h.persisted&&h.afterLeave&&h.afterLeave()};if(1&P.shapeFlag&&h&&!h.persisted){const{leave:v,delayLeave:O}=h,x=()=>v(C,p);O?O(P.el,p,x):x()}else p()},et=(P,W)=>{let C;for(;P!==W;)C=d(P),n(P),P=C;n(W)},ot=(P,W,C)=>{const{bum:Q,scope:h,job:p,subTree:v,um:O,m:x,a:I}=P;fu(x),fu(I),Q&&Wo(Q),h.stop(),p&&(p.flags|=8,k(v,P,W,C)),O&&he(O,W),he(()=>{P.isUnmounted=!0},W),W&&W.pendingBranch&&!W.isUnmounted&&P.asyncDep&&!P.asyncResolved&&P.suspenseId===W.pendingId&&(W.deps--,W.deps===0&&W.resolve())},at=(P,W,C,Q=!1,h=!1,p=0)=>{for(let v=p;v{if(6&P.shapeFlag)return ut(P.component.subTree);if(128&P.shapeFlag)return P.suspense.next();const W=d(P.anchor||P.el),C=W&&W[Bh];return C?d(C):W};let dt=!1;const H=(P,W,C)=>{P==null?W._vnode&&k(W._vnode,null,null,!0):b(W._vnode||null,P,W,null,null,null,C),W._vnode=P,dt||(dt=!0,Ql(),Ch(),dt=!1)},rt={p:b,um:k,m:Y,r:q,mt:st,mc:A,pc:F,pbc:w,n:ut,o:e};return{render:H,hydrate:void 0,createApp:jg(H)}}(t)}function Jo({type:t,props:e},r){return r==="svg"&&t==="foreignObject"||r==="mathml"&&t==="annotation-xml"&&e&&e.encoding&&e.encoding.includes("html")?void 0:r}function $r({effect:t,job:e},r){r?(t.flags|=32,e.flags|=4):(t.flags&=-33,e.flags&=-5)}function Sl(t,e,r=!1){const n=t.children,l=e.children;if(At(n)&&At(l))for(let o=0;oQe(Fg);function xn(t,e,r){return rf(t,e,r)}function rf(t,e,r=Ut){const{immediate:n,deep:l,flush:o,once:s}=r,i=ee({},r),a=e&&n||!e&&o!=="post";let u;if(di){if(o==="sync"){const g=Wg();u=g.__watcherHandles||(g.__watcherHandles=[])}else if(!a){const g=()=>{};return g.stop=Ge,g.resume=Ge,g.pause=Ge,g}}const c=te;i.call=(g,b,_)=>Ne(g,c,b,_);let d=!1;o==="post"?i.scheduler=g=>{he(g,c&&c.suspense)}:o!=="sync"&&(d=!0,i.scheduler=(g,b)=>{b?g():yl(g)}),i.augmentJob=g=>{e&&(g.flags|=4),d&&(g.flags|=2,c&&(g.id=c.uid,g.i=c))};const f=vg(t,e,i);return di&&(u?u.push(f):a&&f()),f}function $g(t,e,r){const n=this.proxy,l=qt(t)?t.includes(".")?nf(n,t):()=>n[t]:t.bind(n,n);let o;It(e)?o=e:(o=e.handler,r=e);const s=Ei(this),i=rf(l,o.bind(n),r);return s(),i}function nf(t,e){const r=e.split(".");return()=>{let n=t;for(let l=0;le==="modelValue"||e==="model-value"?t.modelModifiers:t[`${e}Modifiers`]||t[`${Ce(e)}Modifiers`]||t[`${Nr(e)}Modifiers`];function qg(t,e,...r){if(t.isUnmounted)return;const n=t.vnode.props||Ut;let l=r;const o=e.startsWith("update:"),s=o&&Vg(n,e.slice(7));let i;s&&(s.trim&&(l=r.map(c=>qt(c)?c.trim():c)),s.number&&(l=r.map(Hd)));let a=n[i=Fo(e)]||n[i=Fo(Ce(e))];!a&&o&&(a=n[i=Fo(Nr(e))]),a&&Ne(a,t,6,l);const u=n[i+"Once"];if(u){if(t.emitted){if(t.emitted[i])return}else t.emitted={};t.emitted[i]=!0,Ne(u,t,6,l)}}function of(t,e,r=!1){const n=e.emitsCache,l=n.get(t);if(l!==void 0)return l;const o=t.emits;let s={},i=!1;if(!It(t)){const a=u=>{const c=of(u,e,!0);c&&(i=!0,ee(s,c))};!r&&e.mixins.length&&e.mixins.forEach(a),t.extends&&a(t.extends),t.mixins&&t.mixins.forEach(a)}return o||i?(At(o)?o.forEach(a=>s[a]=null):ee(s,o),Wt(t)&&n.set(t,s),s):(Wt(t)&&n.set(t,null),null)}function ao(t,e){return!(!t||!bo(e))&&(e=e.slice(2).replace(/Once$/,""),Mt(t,e[0].toLowerCase()+e.slice(1))||Mt(t,Nr(e))||Mt(t,e))}function pu(t){const{type:e,vnode:r,proxy:n,withProxy:l,propsOptions:[o],slots:s,attrs:i,emit:a,render:u,renderCache:c,props:d,data:f,setupState:g,ctx:b,inheritAttrs:_}=t,S=io(t);let U,D;try{if(4&r.shapeFlag){const $=l||n,X=$;U=qe(u.call(X,$,c,d,g,f,b)),D=i}else{const $=e;U=qe($.length>1?$(d,{attrs:i,slots:s,emit:a}):$(d,null)),D=e.props?i:Hg(i)}}catch($){ri.length=0,ko($,t,1),U=ye(de)}let G=U;if(D&&_!==!1){const $=Object.keys(D),{shapeFlag:X}=G;$.length&&7&X&&(o&&$.some(il)&&(D=zg(D,o)),G=Ur(G,D,!1,!0))}return r.dirs&&(G=Ur(G,null,!1,!0),G.dirs=G.dirs?G.dirs.concat(r.dirs):r.dirs),r.transition&&hi(G,r.transition),U=G,io(S),U}const Hg=t=>{let e;for(const r in t)(r==="class"||r==="style"||bo(r))&&((e||(e={}))[r]=t[r]);return e},zg=(t,e)=>{const r={};for(const n in t)il(n)&&n.slice(9)in e||(r[n]=t[n]);return r};function du(t,e,r){const n=Object.keys(e);if(n.length!==Object.keys(t).length)return!0;for(let l=0;lt.__isSuspense,me=Symbol.for("v-fgt"),Oo=Symbol.for("v-txt"),de=Symbol.for("v-cmt"),Vi=Symbol.for("v-stc"),ri=[];let Se=null;function Ls(t=!1){ri.push(Se=t?null:[])}let fi=1;function gu(t,e=!1){fi+=t,t<0&&Se&&e&&(Se.hasOnce=!0)}function af(t){return t.dynamicChildren=fi>0?Se||kn:null,ri.pop(),Se=ri[ri.length-1]||null,fi>0&&Se&&Se.push(t),t}function Yy(t,e,r,n,l,o){return af(uf(t,e,r,n,l,o,!0))}function js(t,e,r,n,l){return af(ye(t,e,r,n,l,!0))}function pi(t){return!!t&&t.__v_isVNode===!0}function Zr(t,e){return t.type===e.type&&t.key===e.key}const lf=({key:t})=>t??null,qi=({ref:t,ref_key:e,ref_for:r})=>(typeof t=="number"&&(t=""+t),t!=null?qt(t)||Qt(t)||It(t)?{i:Jt,r:t,k:e,f:!!r}:t:null);function uf(t,e=null,r=null,n=0,l=null,o=t===me?0:1,s=!1,i=!1){const a={__v_isVNode:!0,__v_skip:!0,type:t,props:e,key:e&&lf(e),ref:e&&qi(e),scopeId:Ph,slotScopeIds:null,children:r,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:n,dynamicProps:l,dynamicChildren:null,appContext:null,ctx:Jt};return i?(Al(a,r),128&o&&t.normalize(a)):r&&(a.shapeFlag|=qt(r)?8:16),fi>0&&!s&&Se&&(a.patchFlag>0||6&o)&&a.patchFlag!==32&&Se.push(a),a}const ye=function(t,e=null,r=null,n=0,l=null,o=!1){if(t&&t!==zh||(t=de),pi(t)){const a=Ur(t,e,!0);return r&&Al(a,r),fi>0&&!o&&Se&&(6&a.shapeFlag?Se[Se.indexOf(t)]=a:Se.push(a)),a.patchFlag=-2,a}s=t,It(s)&&"__vccOpts"in s&&(t=t.__vccOpts);var s;if(e){e=Kg(e);let{class:a,style:u}=e;a&&!qt(a)&&(e.class=So(a)),Wt(u)&&(pl(u)&&!At(u)&&(u=ee({},u)),e.style=Eo(u))}const i=qt(t)?1:sf(t)?128:Mh(t)?64:Wt(t)?4:It(t)?2:0;return uf(t,e,r,n,l,i,o,!0)};function Kg(t){return t?pl(t)||Xh(t)?ee({},t):t:null}function Ur(t,e,r=!1,n=!1){const{props:l,ref:o,patchFlag:s,children:i,transition:a}=t,u=e?Qg(l||{},e):l,c={__v_isVNode:!0,__v_skip:!0,type:t.type,props:u,key:u&&lf(u),ref:e&&e.ref?r&&o?At(o)?o.concat(qi(e)):[o,qi(e)]:qi(e):o,scopeId:t.scopeId,slotScopeIds:t.slotScopeIds,children:i,target:t.target,targetStart:t.targetStart,targetAnchor:t.targetAnchor,staticCount:t.staticCount,shapeFlag:t.shapeFlag,patchFlag:e&&t.type!==me?s===-1?16:16|s:s,dynamicProps:t.dynamicProps,dynamicChildren:t.dynamicChildren,appContext:t.appContext,dirs:t.dirs,transition:a,component:t.component,suspense:t.suspense,ssContent:t.ssContent&&Ur(t.ssContent),ssFallback:t.ssFallback&&Ur(t.ssFallback),el:t.el,anchor:t.anchor,ctx:t.ctx,ce:t.ce};return a&&n&&hi(c,a.clone(c)),c}function Gg(t=" ",e=0){return ye(Oo,null,t,e)}function Jy(t="",e=!1){return e?(Ls(),js(de,null,t)):ye(de,null,t)}function qe(t){return t==null||typeof t=="boolean"?ye(de):At(t)?ye(me,null,t.slice()):pi(t)?xr(t):ye(Oo,null,String(t))}function xr(t){return t.el===null&&t.patchFlag!==-1||t.memo?t:Ur(t)}function Al(t,e){let r=0;const{shapeFlag:n}=t;if(e==null)e=null;else if(At(e))r=16;else if(typeof e=="object"){if(65&n){const l=e.default;return void(l&&(l._c&&(l._d=!1),Al(t,l()),l._c&&(l._d=!0)))}{r=32;const l=e._;l||Xh(e)?l===3&&Jt&&(Jt.slots._===1?e._=1:(e._=2,t.patchFlag|=1024)):e._ctx=Jt}}else It(e)?(e={default:e,_ctx:Jt},r=32):(e=String(e),64&n?(r=16,e=[Gg(e)]):r=8);t.children=e,t.shapeFlag|=r}function Qg(...t){const e={};for(let r=0;rte||Jt;let lo,Us;{const t=eo(),e=(r,n)=>{let l;return(l=t[r])||(l=t[r]=[]),l.push(n),o=>{l.length>1?l.forEach(s=>s(o)):l[0](o)}};lo=e("__VUE_INSTANCE_SETTERS__",r=>te=r),Us=e("__VUE_SSR_SETTERS__",r=>di=r)}const Ei=t=>{const e=te;return lo(t),t.scope.on(),()=>{t.scope.off(),lo(e)}},mu=()=>{te&&te.scope.off(),lo(null)};function hf(t){return 4&t.vnode.shapeFlag}let di=!1;function yu(t,e,r){It(e)?t.type.__ssrInlineRender?t.ssrRender=e:t.render=e:Wt(e)&&(t.setupState=Ih(e)),ff(t)}function ff(t,e,r){const n=t.type;t.render||(t.render=n.render||Ge);{const l=Ei(t);Lr();try{Bg(t)}finally{jr(),l()}}}const Xg={get:(t,e)=>(le(t,0,""),t[e])};function xo(t){return t.exposed?t.exposeProxy||(t.exposeProxy=new Proxy(Ih(dl(t.exposed)),{get:(e,r)=>r in e?e[r]:r in ei?ei[r](t):void 0,has:(e,r)=>r in e||r in ei})):t.proxy}function Zg(t,e=!0){return It(t)?t.displayName||t.name:t.name||e&&t.__name}const Te=(t,e)=>function(n,l,o=!1){let s,i;return It(n)?s=n:(s=n.get,i=n.set),new bg(s,i,o)}(t,0,di);function kl(t,e,r){const n=arguments.length;return n===2?Wt(e)&&!At(e)?pi(e)?ye(t,null,[e]):ye(t,e):ye(t,null,e):(n>3?r=Array.prototype.slice.call(arguments,2):n===3&&pi(r)&&(r=[r]),ye(t,e,r))}const tm="3.5.13";/** -* @vue/runtime-dom v3.5.13 -* (c) 2018-present Yuxi (Evan) You and Vue contributors -* @license MIT -**/let Ns;const bu=typeof window<"u"&&window.trustedTypes;if(bu)try{Ns=bu.createPolicy("vue",{createHTML:t=>t})}catch{}const pf=Ns?t=>Ns.createHTML(t):t=>t,ir=typeof document<"u"?document:null,vu=ir&&ir.createElement("template"),em={insert:(t,e,r)=>{e.insertBefore(t,r||null)},remove:t=>{const e=t.parentNode;e&&e.removeChild(t)},createElement:(t,e,r,n)=>{const l=e==="svg"?ir.createElementNS("http://www.w3.org/2000/svg",t):e==="mathml"?ir.createElementNS("http://www.w3.org/1998/Math/MathML",t):r?ir.createElement(t,{is:r}):ir.createElement(t);return t==="select"&&n&&n.multiple!=null&&l.setAttribute("multiple",n.multiple),l},createText:t=>ir.createTextNode(t),createComment:t=>ir.createComment(t),setText:(t,e)=>{t.nodeValue=e},setElementText:(t,e)=>{t.textContent=e},parentNode:t=>t.parentNode,nextSibling:t=>t.nextSibling,querySelector:t=>ir.querySelector(t),setScopeId(t,e){t.setAttribute(e,"")},insertStaticContent(t,e,r,n,l,o){const s=r?r.previousSibling:e.lastChild;if(l&&(l===o||l.nextSibling))for(;e.insertBefore(l.cloneNode(!0),r),l!==o&&(l=l.nextSibling););else{vu.innerHTML=pf(n==="svg"?`${t}`:n==="mathml"?`${t}`:t);const i=vu.content;if(n==="svg"||n==="mathml"){const a=i.firstChild;for(;a.firstChild;)i.appendChild(a.firstChild);i.removeChild(a)}e.insertBefore(i,r)}return[s?s.nextSibling:e.firstChild,r?r.previousSibling:e.lastChild]}},wr="transition",Wn="animation",gi=Symbol("_vtc"),df={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},rm=ee({},jh,df),Xy=(t=>(t.displayName="Transition",t.props=rm,t))((t,{slots:e})=>kl(Eg,function(r){const n={};for(const Z in r)Z in df||(n[Z]=r[Z]);if(r.css===!1)return n;const{name:l="v",type:o,duration:s,enterFromClass:i=`${l}-enter-from`,enterActiveClass:a=`${l}-enter-active`,enterToClass:u=`${l}-enter-to`,appearFromClass:c=i,appearActiveClass:d=a,appearToClass:f=u,leaveFromClass:g=`${l}-leave-from`,leaveActiveClass:b=`${l}-leave-active`,leaveToClass:_=`${l}-leave-to`}=r,S=function(Z){if(Z==null)return null;if(Wt(Z))return[Xo(Z.enter),Xo(Z.leave)];{const j=Xo(Z);return[j,j]}}(s),U=S&&S[0],D=S&&S[1],{onBeforeEnter:G,onEnter:$,onEnterCancelled:X,onLeave:B,onLeaveCancelled:A,onBeforeAppear:E=G,onAppear:w=$,onAppearCancelled:M=X}=n,R=(Z,j,N,F)=>{Z._enterCancelled=F,qr(Z,j?f:u),qr(Z,j?d:a),N&&N()},nt=(Z,j)=>{Z._isLeaving=!1,qr(Z,g),qr(Z,_),qr(Z,b),j&&j()},st=Z=>(j,N)=>{const F=Z?w:$,it=()=>R(j,Z,N);Vr(F,[j,it]),_u(()=>{qr(j,Z?c:i),Ze(j,Z?f:u),wu(F)||Eu(j,o,U,it)})};return ee(n,{onBeforeEnter(Z){Vr(G,[Z]),Ze(Z,i),Ze(Z,a)},onBeforeAppear(Z){Vr(E,[Z]),Ze(Z,c),Ze(Z,d)},onEnter:st(!1),onAppear:st(!0),onLeave(Z,j){Z._isLeaving=!0;const N=()=>nt(Z,j);Ze(Z,g),Z._enterCancelled?(Ze(Z,b),ku()):(ku(),Ze(Z,b)),_u(()=>{Z._isLeaving&&(qr(Z,g),Ze(Z,_),wu(B)||Eu(Z,o,D,N))}),Vr(B,[Z,N])},onEnterCancelled(Z){R(Z,!1,void 0,!0),Vr(X,[Z])},onAppearCancelled(Z){R(Z,!0,void 0,!0),Vr(M,[Z])},onLeaveCancelled(Z){nt(Z),Vr(A,[Z])}})}(t),e)),Vr=(t,e=[])=>{At(t)?t.forEach(r=>r(...e)):t&&t(...e)},wu=t=>!!t&&(At(t)?t.some(e=>e.length>1):t.length>1);function Xo(t){return(r=>{const n=qt(r)?Number(r):NaN;return isNaN(n)?r:n})(t)}function Ze(t,e){e.split(/\s+/).forEach(r=>r&&t.classList.add(r)),(t[gi]||(t[gi]=new Set)).add(e)}function qr(t,e){e.split(/\s+/).forEach(n=>n&&t.classList.remove(n));const r=t[gi];r&&(r.delete(e),r.size||(t[gi]=void 0))}function _u(t){requestAnimationFrame(()=>{requestAnimationFrame(t)})}let nm=0;function Eu(t,e,r,n){const l=t._endId=++nm,o=()=>{l===t._endId&&n()};if(r!=null)return setTimeout(o,r);const{type:s,timeout:i,propCount:a}=function(g,b){const _=window.getComputedStyle(g),S=R=>(_[R]||"").split(", "),U=S(`${wr}Delay`),D=S(`${wr}Duration`),G=Su(U,D),$=S(`${Wn}Delay`),X=S(`${Wn}Duration`),B=Su($,X);let A=null,E=0,w=0;b===wr?G>0&&(A=wr,E=G,w=D.length):b===Wn?B>0&&(A=Wn,E=B,w=X.length):(E=Math.max(G,B),A=E>0?G>B?wr:Wn:null,w=A?A===wr?D.length:X.length:0);const M=A===wr&&/\b(transform|all)(,|$)/.test(S(`${wr}Property`).toString());return{type:A,timeout:E,propCount:w,hasTransform:M}}(t,e);if(!s)return n();const u=s+"end";let c=0;const d=()=>{t.removeEventListener(u,f),o()},f=g=>{g.target===t&&++c>=a&&d()};setTimeout(()=>{cAu(r)+Au(t[n])))}function Au(t){return t==="auto"?0:1e3*Number(t.slice(0,-1).replace(",","."))}function ku(){return document.body.offsetHeight}const uo=Symbol("_vod"),gf=Symbol("_vsh"),Zy={beforeMount(t,{value:e},{transition:r}){t[uo]=t.style.display==="none"?"":t.style.display,r&&e?r.beforeEnter(t):$n(t,e)},mounted(t,{value:e},{transition:r}){r&&e&&r.enter(t)},updated(t,{value:e,oldValue:r},{transition:n}){!e!=!r&&(n?e?(n.beforeEnter(t),$n(t,!0),n.enter(t)):n.leave(t,()=>{$n(t,!1)}):$n(t,e))},beforeUnmount(t,{value:e}){$n(t,e)}};function $n(t,e){t.style.display=e?t[uo]:"none",t[gf]=!e}const mf=Symbol("");function tb(t){const e=cf();if(!e)return;const r=e.ut=(l=t(e.proxy))=>{Array.from(document.querySelectorAll(`[data-v-owner="${e.uid}"]`)).forEach(o=>co(o,l))},n=()=>{const l=t(e.proxy);e.ce?co(e.ce,l):Ds(e.subTree,l),r(l)};qh(()=>{xh(n)}),bl(()=>{xn(n,Ge,{flush:"post"});const l=new MutationObserver(n);l.observe(e.subTree.el.parentNode,{childList:!0}),vl(()=>l.disconnect())})}function Ds(t,e){if(128&t.shapeFlag){const r=t.suspense;t=r.activeBranch,r.pendingBranch&&!r.isHydrating&&r.effects.push(()=>{Ds(r.activeBranch,e)})}for(;t.component;)t=t.component.subTree;if(1&t.shapeFlag&&t.el)co(t.el,e);else if(t.type===me)t.children.forEach(r=>Ds(r,e));else if(t.type===Vi){let{el:r,anchor:n}=t;for(;r&&(co(r,e),r!==n);)r=r.nextSibling}}function co(t,e){if(t.nodeType===1){const r=t.style;let n="";for(const l in e)r.setProperty(`--${l}`,e[l]),n+=`--${l}: ${e[l]};`;r[mf]=n}}const im=/(^|;)\s*display\s*:/,Iu=/\s*!important$/;function Hi(t,e,r){if(At(r))r.forEach(n=>Hi(t,e,n));else if(r==null&&(r=""),e.startsWith("--"))t.setProperty(e,r);else{const n=function(l,o){const s=Zo[o];if(s)return s;let i=Ce(o);if(i!=="filter"&&i in l)return Zo[o]=i;i=_o(i);for(let a=0;a{if(g._vts){if(g._vts<=f.attached)return}else g._vts=Date.now();Ne(function(b,_){if(At(_)){const S=b.stopImmediatePropagation;return b.stopImmediatePropagation=()=>{S.call(b),b._stopped=!0},_.map(U=>D=>!D._stopped&&U&&U(D))}return _}(g,f.value),d,5,[g])};return f.value=c,f.attached=am(),f}(n,l);(function(c,d,f,g){c.addEventListener(d,f,g)})(t,i,u,a)}else s&&(function(u,c,d,f){u.removeEventListener(c,d,f)}(t,i,s,a),o[e]=void 0)}}const Pu=/(?:Once|Passive|Capture)$/;let ts=0;const sm=Promise.resolve(),am=()=>ts||(sm.then(()=>ts=0),ts=Date.now()),Bu=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&t.charCodeAt(2)>96&&t.charCodeAt(2)<123,lm=["ctrl","shift","alt","meta"],um={stop:t=>t.stopPropagation(),prevent:t=>t.preventDefault(),self:t=>t.target!==t.currentTarget,ctrl:t=>!t.ctrlKey,shift:t=>!t.shiftKey,alt:t=>!t.altKey,meta:t=>!t.metaKey,left:t=>"button"in t&&t.button!==0,middle:t=>"button"in t&&t.button!==1,right:t=>"button"in t&&t.button!==2,exact:(t,e)=>lm.some(r=>t[`${r}Key`]&&!e.includes(r))},eb=(t,e)=>{const r=t._withMods||(t._withMods={}),n=e.join(".");return r[n]||(r[n]=(l,...o)=>{for(let s=0;s{const r=t._withKeys||(t._withKeys={}),n=e.join(".");return r[n]||(r[n]=l=>{if(!("key"in l))return;const o=Nr(l.key);return e.some(s=>s===o||cm[s]===o)?t(l):void 0})},hm=ee({patchProp:(t,e,r,n,l,o)=>{const s=l==="svg";e==="class"?function(i,a,u){const c=i[gi];c&&(a=(a?[a,...c]:[...c]).join(" ")),a==null?i.removeAttribute("class"):u?i.setAttribute("class",a):i.className=a}(t,n,s):e==="style"?function(i,a,u){const c=i.style,d=qt(u);let f=!1;if(u&&!d){if(a)if(qt(a))for(const g of a.split(";")){const b=g.slice(0,g.indexOf(":")).trim();u[b]==null&&Hi(c,b,"")}else for(const g in a)u[g]==null&&Hi(c,g,"");for(const g in u)g==="display"&&(f=!0),Hi(c,g,u[g])}else if(d){if(a!==u){const g=c[mf];g&&(u+=";"+g),c.cssText=u,f=im.test(u)}}else a&&i.removeAttribute("style");uo in i&&(i[uo]=f?c.display:"",i[gf]&&(c.display="none"))}(t,r,n):bo(e)?il(e)||om(t,e,0,n,o):(e[0]==="."?(e=e.slice(1),1):e[0]==="^"?(e=e.slice(1),0):function(i,a,u,c){if(c)return a==="innerHTML"||a==="textContent"||!!(a in i&&Bu(a)&&It(u));if(a==="spellcheck"||a==="draggable"||a==="translate"||a==="form"||a==="list"&&i.tagName==="INPUT"||a==="type"&&i.tagName==="TEXTAREA")return!1;if(a==="width"||a==="height"){const d=i.tagName;if(d==="IMG"||d==="VIDEO"||d==="CANVAS"||d==="SOURCE")return!1}return Bu(a)&&qt(u)?!1:a in i}(t,e,n,s))?(Cu(t,e,n),t.tagName.includes("-")||e!=="value"&&e!=="checked"&&e!=="selected"||xu(t,e,n,s,0,e!=="value")):!t._isVueCE||!/[A-Z]/.test(e)&&qt(n)?(e==="true-value"?t._trueValue=n:e==="false-value"&&(t._falseValue=n),xu(t,e,n,s)):Cu(t,Ce(e),n,0,e)}},em);let Mu;const nb=(...t)=>{const e=(Mu||(Mu=Dg(hm))).createApp(...t),{mount:r}=e;return e.mount=n=>{const l=function(i){return qt(i)?document.querySelector(i):i}(n);if(!l)return;const o=e._component;It(o)||o.render||o.template||(o.template=l.innerHTML),l.nodeType===1&&(l.textContent="");const s=r(l,!1,function(i){if(i instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&i instanceof MathMLElement)return"mathml"}(l));return l instanceof Element&&(l.removeAttribute("v-cloak"),l.setAttribute("data-v-app","")),s},e};let yf;const Co=t=>yf=t,bf=Symbol();function Fs(t){return t&&typeof t=="object"&&Object.prototype.toString.call(t)==="[object Object]"&&typeof t.toJSON!="function"}var ni,es;function ib(){const t=ah(!0),e=t.run(()=>gl({}));let r=[],n=[];const l=dl({install(o){Co(l),l._a=o,o.provide(bf,l),o.config.globalProperties.$pinia=l,n.forEach(s=>r.push(s)),n=[]},use(o){return this._a?r.push(o):n.push(o),this},_p:r,_a:null,_e:t,_s:new Map,state:e});return l}(es=ni||(ni={})).direct="direct",es.patchObject="patch object",es.patchFunction="patch function";const vf=()=>{};function Lu(t,e,r,n=vf){t.push(e);const l=()=>{const o=t.indexOf(e);o>-1&&(t.splice(o,1),n())};return!r&&lh()&&function(o){fe&&fe.cleanups.push(o)}(l),l}function yn(t,...e){t.slice().forEach(r=>{r(...e)})}const fm=t=>t(),ju=Symbol(),rs=Symbol();function Ws(t,e){t instanceof Map&&e instanceof Map?e.forEach((r,n)=>t.set(n,r)):t instanceof Set&&e instanceof Set&&e.forEach(t.add,t);for(const r in e){if(!e.hasOwnProperty(r))continue;const n=e[r],l=t[r];Fs(l)&&Fs(n)&&t.hasOwnProperty(r)&&!Qt(n)&&!Br(n)?t[r]=Ws(l,n):t[r]=n}return t}const pm=Symbol(),{assign:Er}=Object;function dm(t,e,r,n){const{state:l,actions:o,getters:s}=e,i=r.state.value[t];let a;return a=wf(t,function(){i||(r.state.value[t]=l?l():{});const u=function(c){const d=At(c)?new Array(c.length):{};for(const f in c)d[f]=yg(c,f);return d}(r.state.value[t]);return Er(u,o,Object.keys(s||{}).reduce((c,d)=>(c[d]=dl(Te(()=>{Co(r);const f=r._s.get(t);return s[d].call(f,f)})),c),{}))},e,r,n,!0),a}function wf(t,e,r={},n,l,o){let s;const i=Er({actions:{}},r),a={deep:!0};let u,c,d,f=[],g=[];const b=n.state.value[t];let _;function S(A){let E;u=c=!1,typeof A=="function"?(A(n.state.value[t]),E={type:ni.patchFunction,storeId:t,events:d}):(Ws(n.state.value[t],A),E={type:ni.patchObject,payload:A,storeId:t,events:d});const w=_=Symbol();ml().then(()=>{_===w&&(u=!0)}),c=!0,yn(f,E,n.state.value[t])}o||b||(n.state.value[t]={}),gl({});const U=o?function(){const{state:A}=r,E=A?A():{};this.$patch(w=>{Er(w,E)})}:vf,D=(A,E="")=>{if(ju in A)return A[rs]=E,A;const w=function(){Co(n);const M=Array.from(arguments),R=[],nt=[];let st;yn(g,{args:M,name:w[rs],store:G,after:function(Z){R.push(Z)},onError:function(Z){nt.push(Z)}});try{st=A.apply(this&&this.$id===t?this:G,M)}catch(Z){throw yn(nt,Z),Z}return st instanceof Promise?st.then(Z=>(yn(R,Z),Z)).catch(Z=>(yn(nt,Z),Promise.reject(Z))):(yn(R,st),st)};return w[ju]=!0,w[rs]=E,w},G=wi({_p:n,$id:t,$onAction:Lu.bind(null,g),$patch:S,$reset:U,$subscribe(A,E={}){const w=Lu(f,A,E.detached,()=>M()),M=s.run(()=>xn(()=>n.state.value[t],R=>{(E.flush==="sync"?c:u)&&A({storeId:t,type:ni.direct,events:d},R)},Er({},a,E)));return w},$dispose:function(){s.stop(),f=[],g=[],n._s.delete(t)}});n._s.set(t,G);const $=(n._a&&n._a.runWithContext||fm)(()=>n._e.run(()=>(s=ah()).run(()=>e({action:D}))));for(const A in $){const E=$[A];if(Qt(E)&&(!Qt(B=E)||!B.effect)||Br(E))o||(!b||Fs(X=E)&&X.hasOwnProperty(pm)||(Qt(E)?E.value=b[A]:Ws(E,b[A])),n.state.value[t][A]=E);else if(typeof E=="function"){const w=D(E,A);$[A]=w,i.actions[A]=E}}var X,B;return Er(G,$),Er(Rt(G),$),Object.defineProperty(G,"$state",{get:()=>n.state.value[t],set:A=>{S(E=>{Er(E,A)})}}),n._p.forEach(A=>{Er(G,s.run(()=>A({store:G,app:n._a,pinia:n,options:i})))}),b&&o&&r.hydrate&&r.hydrate(G.$state,b),u=!0,c=!0,G}/*! #__NO_SIDE_EFFECTS__ */function ob(t,e,r){let n,l;const o=typeof e=="function";function s(i,a){return(i=i||(te||Jt||on?Qe(bf,null):null))&&Co(i),(i=yf)._s.has(n)||(o?wf(n,e,l,i):dm(n,l,i)),i._s.get(n)}return n=t,l=o?r:e,s.$id=n,s}/*! - * vue-router v4.5.0 - * (c) 2024 Eduardo San Martin Morote - * @license MIT - */const _n=typeof document<"u";function Uu(t){return typeof t=="object"||"displayName"in t||"props"in t||"__vccOpts"in t}const Bt=Object.assign;function ns(t,e){const r={};for(const n in e){const l=e[n];r[n]=De(l)?l.map(t):t(l)}return r}const ii=()=>{},De=Array.isArray,_f=/#/g,gm=/&/g,mm=/\//g,ym=/=/g,bm=/\?/g,Ef=/\+/g,vm=/%5B/g,wm=/%5D/g,Sf=/%5E/g,_m=/%60/g,Af=/%7B/g,Em=/%7C/g,kf=/%7D/g,Sm=/%20/g;function Il(t){return encodeURI(""+t).replace(Em,"|").replace(vm,"[").replace(wm,"]")}function is(t){return Il(t).replace(Ef,"%2B").replace(Sm,"+").replace(_f,"%23").replace(gm,"%26").replace(_m,"`").replace(Af,"{").replace(kf,"}").replace(Sf,"^")}function Am(t){return t==null?"":function(e){return Il(e).replace(_f,"%23").replace(bm,"%3F")}(t).replace(mm,"%2F")}function mi(t){try{return decodeURIComponent(""+t)}catch{}return""+t}const km=/\/$/;function os(t,e,r="/"){let n,l={},o="",s="";const i=e.indexOf("#");let a=e.indexOf("?");return i=0&&(a=-1),a>-1&&(n=e.slice(0,a),o=e.slice(a+1,i>-1?i:e.length),l=t(o)),i>-1&&(n=n||e.slice(0,i),s=e.slice(i,e.length)),n=function(u,c){if(u.startsWith("/"))return u;if(!u)return c;const d=c.split("/"),f=u.split("/"),g=f[f.length-1];g!==".."&&g!=="."||f.push("");let b,_,S=d.length-1;for(b=0;b1&&S--}return d.slice(0,S).join("/")+"/"+f.slice(b).join("/")}(n??e,r),{fullPath:n+(o&&"?")+o+s,path:n,query:l,hash:mi(s)}}function Nu(t,e){return e&&t.toLowerCase().startsWith(e.toLowerCase())?t.slice(e.length)||"/":t}function Cn(t,e){return(t.aliasOf||t)===(e.aliasOf||e)}function If(t,e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(const r in t)if(!Im(t[r],e[r]))return!1;return!0}function Im(t,e){return De(t)?Du(t,e):De(e)?Du(e,t):t===e}function Du(t,e){return De(e)?t.length===e.length&&t.every((r,n)=>r===e[n]):t.length===1&&t[0]===e}const _r={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var yi,Fu,oi,ss;function Tm(t){if(!t)if(_n){const e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^\w+:\/\/[^\/]+/,"")}else t="/";return t[0]!=="/"&&t[0]!=="#"&&(t="/"+t),t.replace(km,"")}(Fu=yi||(yi={})).pop="pop",Fu.push="push",(ss=oi||(oi={})).back="back",ss.forward="forward",ss.unknown="";const Om=/^[^#]+#/;function xm(t,e){return t.replace(Om,"#")+e}const Ro=()=>({left:window.scrollX,top:window.scrollY});function Cm(t){let e;if("el"in t){const r=t.el,n=typeof r=="string"&&r.startsWith("#"),l=typeof r=="string"?n?document.getElementById(r.slice(1)):document.querySelector(r):r;if(!l)return;e=function(o,s){const i=document.documentElement.getBoundingClientRect(),a=o.getBoundingClientRect();return{behavior:s.behavior,left:a.left-i.left-(s.left||0),top:a.top-i.top-(s.top||0)}}(l,t)}else e=t;"scrollBehavior"in document.documentElement.style?window.scrollTo(e):window.scrollTo(e.left!=null?e.left:window.scrollX,e.top!=null?e.top:window.scrollY)}function Wu(t,e){return(history.state?history.state.position-e:-1)+t}const as=new Map;function Tf(t,e){const{pathname:r,search:n,hash:l}=e,o=t.indexOf("#");if(o>-1){let s=l.includes(t.slice(o))?t.slice(o).length:1,i=l.slice(s);return i[0]!=="/"&&(i="/"+i),Nu(i,"")}return Nu(r,t)+n+l}function $u(t,e,r,n=!1,l=!1){return{back:t,current:e,forward:r,replaced:n,position:window.history.length,scroll:l?Ro():null}}function Rm(t){const{history:e,location:r}=window,n={value:Tf(t,r)},l={value:e.state};function o(s,i,a){const u=t.indexOf("#"),c=u>-1?(r.host&&document.querySelector("base")?t:t.slice(u))+s:location.protocol+"//"+location.host+t+s;try{e[a?"replaceState":"pushState"](i,"",c),l.value=i}catch{r[a?"replace":"assign"](c)}}return l.value||o(n.value,{back:null,current:n.value,forward:null,position:e.length-1,replaced:!0,scroll:null},!0),{location:n,state:l,push:function(s,i){const a=Bt({},l.value,e.state,{forward:s,scroll:Ro()});o(a.current,a,!0),o(s,Bt({},$u(n.value,s,null),{position:a.position+1},i),!1),n.value=s},replace:function(s,i){o(s,Bt({},e.state,$u(l.value.back,s,l.value.forward,!0),i,{position:l.value.position}),!0),n.value=s}}}function Pm(t){const e=Rm(t=Tm(t)),r=function(l,o,s,i){let a=[],u=[],c=null;const d=({state:g})=>{const b=Tf(l,location),_=s.value,S=o.value;let U=0;if(g){if(s.value=b,o.value=g,c&&c===_)return void(c=null);U=S?g.position-S.position:0}else i(b);a.forEach(D=>{D(s.value,_,{delta:U,type:yi.pop,direction:U?U>0?oi.forward:oi.back:oi.unknown})})};function f(){const{history:g}=window;g.state&&g.replaceState(Bt({},g.state,{scroll:Ro()}),"")}return window.addEventListener("popstate",d),window.addEventListener("beforeunload",f,{passive:!0}),{pauseListeners:function(){c=s.value},listen:function(g){a.push(g);const b=()=>{const _=a.indexOf(g);_>-1&&a.splice(_,1)};return u.push(b),b},destroy:function(){for(const g of u)g();u=[],window.removeEventListener("popstate",d),window.removeEventListener("beforeunload",f)}}}(t,e.state,e.location,e.replace),n=Bt({location:"",base:t,go:function(l,o=!0){o||r.pauseListeners(),history.go(l)},createHref:xm.bind(null,t)},e,r);return Object.defineProperty(n,"location",{enumerable:!0,get:()=>e.location.value}),Object.defineProperty(n,"state",{enumerable:!0,get:()=>e.state.value}),n}function sb(t){return(t=location.host?t||location.pathname+location.search:"").includes("#")||(t+="#"),Pm(t)}function Of(t){return typeof t=="string"||typeof t=="symbol"}const xf=Symbol("");var Vu,bn;function Rn(t,e){return Bt(new Error,{type:t,[xf]:!0},e)}function tr(t,e){return t instanceof Error&&xf in t&&(e==null||!!(t.type&e))}(bn=Vu||(Vu={}))[bn.aborted=4]="aborted",bn[bn.cancelled=8]="cancelled",bn[bn.duplicated=16]="duplicated";const qu="[^/]+?",Bm={sensitive:!1,strict:!1,start:!0,end:!0},Mm=/[.+*?^${}()[\]/\\]/g;function Lm(t,e){let r=0;for(;re.length?e.length===1&&e[0]===80?1:-1:0}function Hu(t,e){let r=0;const n=t.score,l=e.score;for(;r0&&e[e.length-1]<0}const jm={type:0,value:""},Um=/[a-zA-Z0-9_]/;function Nm(t,e,r){const n=function(o,s){const i=Bt({},Bm,s),a=[];let u=i.start?"^":"";const c=[];for(const f of o){const g=f.length?[]:[90];i.strict&&!f.length&&(u+="/");for(let b=0;b1&&(f==="*"||f==="+")&&s(`A repeatable param (${b}) must be alone in its segment. eg: '/:ids+.`),c.push({type:1,value:b,regexp:_,repeatable:f==="*"||f==="+",optional:f==="*"||f==="?"})):s("Invalid state to consume buffer"),b="")}function U(){b+=f}for(;g{o(_)}:ii}function o(i){if(Of(i)){const a=n.get(i);a&&(n.delete(i),r.splice(r.indexOf(a),1),a.children.forEach(o),a.alias.forEach(o))}else{const a=r.indexOf(i);a>-1&&(r.splice(a,1),i.record.name&&n.delete(i.record.name),i.children.forEach(o),i.alias.forEach(o))}}function s(i){const a=function(u,c){let d=0,f=c.length;for(;d!==f;){const b=d+f>>1;Hu(u,c[b])<0?f=b:d=b+1}const g=function(b){let _=b;for(;_=_.parent;)if(Ju(_)&&Hu(b,_)===0)return _}(u);return g&&(f=c.lastIndexOf(g,f-1)),f}(i,r);r.splice(a,0,i),i.record.name&&!Qu(i)&&n.set(i.record.name,i)}return e=Yu({strict:!1,end:!0,sensitive:!1},e),t.forEach(i=>l(i)),{addRoute:l,resolve:function(i,a){let u,c,d,f={};if("name"in i&&i.name){if(u=n.get(i.name),!u)throw Rn(1,{location:i});d=u.record.name,f=Bt(Ku(a.params,u.keys.filter(_=>!_.optional).concat(u.parent?u.parent.keys.filter(_=>_.optional):[]).map(_=>_.name)),i.params&&Ku(i.params,u.keys.map(_=>_.name))),c=u.stringify(f)}else if(i.path!=null)c=i.path,u=r.find(_=>_.re.test(c)),u&&(f=u.parse(c),d=u.record.name);else{if(u=a.name?n.get(a.name):r.find(_=>_.re.test(a.path)),!u)throw Rn(1,{location:i,currentLocation:a});d=u.record.name,f=Bt({},a.params,i.params),c=u.stringify(f)}const g=[];let b=u;for(;b;)g.unshift(b.record),b=b.parent;return{name:d,path:c,params:f,matched:g,meta:Wm(g)}},removeRoute:o,clearRoutes:function(){r.length=0,n.clear()},getRoutes:function(){return r},getRecordMatcher:function(i){return n.get(i)}}}function Ku(t,e){const r={};for(const n of e)n in t&&(r[n]=t[n]);return r}function Gu(t){const e={path:t.path,redirect:t.redirect,name:t.name,meta:t.meta||{},aliasOf:t.aliasOf,beforeEnter:t.beforeEnter,props:Fm(t),children:t.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in t?t.components||null:t.component&&{default:t.component}};return Object.defineProperty(e,"mods",{value:{}}),e}function Fm(t){const e={},r=t.props||!1;if("component"in t)e.default=r;else for(const n in t.components)e[n]=typeof r=="object"?r[n]:r;return e}function Qu(t){for(;t;){if(t.record.aliasOf)return!0;t=t.parent}return!1}function Wm(t){return t.reduce((e,r)=>Bt(e,r.meta),{})}function Yu(t,e){const r={};for(const n in t)r[n]=n in e?e[n]:t[n];return r}function Ju({record:t}){return!!(t.name||t.components&&Object.keys(t.components).length||t.redirect)}function $m(t){const e={};if(t===""||t==="?")return e;const r=(t[0]==="?"?t.slice(1):t).split("&");for(let n=0;no&&is(o)):[n&&is(n)]).forEach(o=>{o!==void 0&&(e+=(e.length?"&":"")+r,o!=null&&(e+="="+o))})}return e}function Vm(t){const e={};for(const r in t){const n=t[r];n!==void 0&&(e[r]=De(n)?n.map(l=>l==null?null:""+l):n==null?n:""+n)}return e}const qm=Symbol(""),Zu=Symbol(""),Tl=Symbol(""),Cf=Symbol(""),$s=Symbol("");function Vn(){let t=[];return{add:function(e){return t.push(e),()=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)}},list:()=>t.slice(),reset:function(){t=[]}}}function Cr(t,e,r,n,l,o=s=>s()){const s=n&&(n.enterCallbacks[l]=n.enterCallbacks[l]||[]);return()=>new Promise((i,a)=>{const u=f=>{var g;f===!1?a(Rn(4,{from:r,to:e})):f instanceof Error?a(f):typeof(g=f)=="string"||g&&typeof g=="object"?a(Rn(2,{from:e,to:f})):(s&&n.enterCallbacks[l]===s&&typeof f=="function"&&s.push(f),i())},c=o(()=>t.call(n&&n.instances[l],e,r,u));let d=Promise.resolve(c);t.length<3&&(d=d.then(u)),d.catch(f=>a(f))})}function ls(t,e,r,n,l=o=>o()){const o=[];for(const s of t)for(const i in s.components){let a=s.components[i];if(e==="beforeRouteEnter"||s.instances[i])if(Uu(a)){const u=(a.__vccOpts||a)[e];u&&o.push(Cr(u,r,n,s,i,l))}else{let u=a();o.push(()=>u.then(c=>{if(!c)throw new Error(`Couldn't resolve component "${i}" at "${s.path}"`);const d=(f=c).__esModule||f[Symbol.toStringTag]==="Module"||f.default&&Uu(f.default)?c.default:c;var f;s.mods[i]=c,s.components[i]=d;const g=(d.__vccOpts||d)[e];return g&&Cr(g,r,n,s,i,l)()}))}}return o}function tc(t){const e=Qe(Tl),r=Qe(Cf),n=Te(()=>{const i=Tn(t.to);return e.resolve(i)}),l=Te(()=>{const{matched:i}=n.value,{length:a}=i,u=i[a-1],c=r.matched;if(!u||!c.length)return-1;const d=c.findIndex(Cn.bind(null,u));if(d>-1)return d;const f=ec(i[a-2]);return a>1&&ec(u)===f&&c[c.length-1].path!==f?c.findIndex(Cn.bind(null,i[a-2])):d}),o=Te(()=>l.value>-1&&function(i,a){for(const u in a){const c=a[u],d=i[u];if(typeof c=="string"){if(c!==d)return!1}else if(!De(d)||d.length!==c.length||c.some((f,g)=>f!==d[g]))return!1}return!0}(r.params,n.value.params)),s=Te(()=>l.value>-1&&l.value===r.matched.length-1&&If(r.params,n.value.params));return{route:n,href:Te(()=>n.value.href),isActive:o,isExactActive:s,navigate:function(i={}){if(function(a){if(!(a.metaKey||a.altKey||a.ctrlKey||a.shiftKey)&&!a.defaultPrevented&&!(a.button!==void 0&&a.button!==0)){if(a.currentTarget&&a.currentTarget.getAttribute){const u=a.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(u))return}return a.preventDefault&&a.preventDefault(),!0}}(i)){const a=e[Tn(t.replace)?"replace":"push"](Tn(t.to)).catch(ii);return t.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>a),a}return Promise.resolve()}}}const Hm=Wh({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:tc,setup(t,{slots:e}){const r=wi(tc(t)),{options:n}=Qe(Tl),l=Te(()=>({[rc(t.activeClass,n.linkActiveClass,"router-link-active")]:r.isActive,[rc(t.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:r.isExactActive}));return()=>{const o=e.default&&((s=e.default(r)).length===1?s[0]:s);var s;return t.custom?o:kl("a",{"aria-current":r.isExactActive?t.ariaCurrentValue:null,href:r.href,onClick:r.navigate,class:l.value},o)}}});function ec(t){return t?t.aliasOf?t.aliasOf.path:t.path:""}const rc=(t,e,r)=>t??e??r;function nc(t,e){if(!t)return null;const r=t(e);return r.length===1?r[0]:r}const zm=Wh({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(t,{attrs:e,slots:r}){const n=Qe($s),l=Te(()=>t.route||n.value),o=Qe(Zu,0),s=Te(()=>{let u=Tn(o);const{matched:c}=l.value;let d;for(;(d=c[u])&&!d.components;)u++;return u}),i=Te(()=>l.value.matched[s.value]);$i(Zu,Te(()=>s.value+1)),$i(qm,i),$i($s,l);const a=gl();return xn(()=>[a.value,i.value,t.name],([u,c,d],[f,g,b])=>{c&&(c.instances[d]=u,g&&g!==c&&u&&u===f&&(c.leaveGuards.size||(c.leaveGuards=g.leaveGuards),c.updateGuards.size||(c.updateGuards=g.updateGuards))),!u||!c||g&&Cn(c,g)&&f||(c.enterCallbacks[d]||[]).forEach(_=>_(u))},{flush:"post"}),()=>{const u=l.value,c=t.name,d=i.value,f=d&&d.components[c];if(!f)return nc(r.default,{Component:f,route:u});const g=d.props[c],b=g?g===!0?u.params:typeof g=="function"?g(u):g:null,_=kl(f,Bt({},b,e,{onVnodeUnmounted:S=>{S.component.isUnmounted&&(d.instances[c]=null)},ref:a}));return nc(r.default,{Component:_,route:u})||_}}});function ab(t){const e=Dm(t.routes,t),r=t.parseQuery||$m,n=t.stringifyQuery||Xu,l=t.history,o=Vn(),s=Vn(),i=Vn(),a=kh(_r,!0);let u=_r;_n&&t.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const c=ns.bind(null,k=>""+k),d=ns.bind(null,Am),f=ns.bind(null,mi);function g(k,q){if(q=Bt({},q||a.value),typeof k=="string"){const rt=os(r,k,q.path),tt=e.resolve({path:rt.path},q),P=l.createHref(rt.fullPath);return Bt(rt,tt,{params:f(tt.params),hash:mi(rt.hash),redirectedFrom:void 0,href:P})}let et;if(k.path!=null)et=Bt({},k,{path:os(r,k.path,q.path).path});else{const rt=Bt({},k.params);for(const tt in rt)rt[tt]==null&&delete rt[tt];et=Bt({},k,{params:d(rt)}),q.params=d(q.params)}const ot=e.resolve(et,q),at=k.hash||"";ot.params=c(f(ot.params));const ut=function(rt,tt){const P=tt.query?rt(tt.query):"";return tt.path+(P&&"?")+P+(tt.hash||"")}(n,Bt({},k,{hash:(dt=at,Il(dt).replace(Af,"{").replace(kf,"}").replace(Sf,"^")),path:ot.path}));var dt;const H=l.createHref(ut);return Bt({fullPath:ut,hash:at,query:n===Xu?Vm(k.query):k.query||{}},ot,{redirectedFrom:void 0,href:H})}function b(k){return typeof k=="string"?os(r,k,a.value.path):Bt({},k)}function _(k,q){if(u!==k)return Rn(8,{from:q,to:k})}function S(k){return D(k)}function U(k){const q=k.matched[k.matched.length-1];if(q&&q.redirect){const{redirect:et}=q;let ot=typeof et=="function"?et(k):et;return typeof ot=="string"&&(ot=ot.includes("?")||ot.includes("#")?ot=b(ot):{path:ot},ot.params={}),Bt({query:k.query,hash:k.hash,params:ot.path!=null?{}:k.params},ot)}}function D(k,q){const et=u=g(k),ot=a.value,at=k.state,ut=k.force,dt=k.replace===!0,H=U(et);if(H)return D(Bt(b(H),{state:typeof H=="object"?Bt({},at,H.state):at,force:ut,replace:dt}),q||et);const rt=et;let tt;return rt.redirectedFrom=q,!ut&&function(P,W,C){const Q=W.matched.length-1,h=C.matched.length-1;return Q>-1&&Q===h&&Cn(W.matched[Q],C.matched[h])&&If(W.params,C.params)&&P(W.query)===P(C.query)&&W.hash===C.hash}(n,ot,et)&&(tt=Rn(16,{to:rt,from:ot}),j(ot,ot,!0,!1)),(tt?Promise.resolve(tt):X(rt,ot)).catch(P=>tr(P)?tr(P,2)?P:Z(P):st(P,rt,ot)).then(P=>{if(P){if(tr(P,2))return D(Bt({replace:dt},b(P.to),{state:typeof P.to=="object"?Bt({},at,P.to.state):at,force:ut}),q||rt)}else P=A(rt,ot,!0,dt,at);return B(rt,ot,P),P})}function G(k,q){const et=_(k,q);return et?Promise.reject(et):Promise.resolve()}function $(k){const q=it.values().next().value;return q&&typeof q.runWithContext=="function"?q.runWithContext(k):k()}function X(k,q){let et;const[ot,at,ut]=function(H,rt){const tt=[],P=[],W=[],C=Math.max(rt.matched.length,H.matched.length);for(let Q=0;QCn(v,h))?P.push(h):tt.push(h));const p=H.matched[Q];p&&(rt.matched.find(v=>Cn(v,p))||W.push(p))}return[tt,P,W]}(k,q);et=ls(ot.reverse(),"beforeRouteLeave",k,q);for(const H of ot)H.leaveGuards.forEach(rt=>{et.push(Cr(rt,k,q))});const dt=G.bind(null,k,q);return et.push(dt),Y(et).then(()=>{et=[];for(const H of o.list())et.push(Cr(H,k,q));return et.push(dt),Y(et)}).then(()=>{et=ls(at,"beforeRouteUpdate",k,q);for(const H of at)H.updateGuards.forEach(rt=>{et.push(Cr(rt,k,q))});return et.push(dt),Y(et)}).then(()=>{et=[];for(const H of ut)if(H.beforeEnter)if(De(H.beforeEnter))for(const rt of H.beforeEnter)et.push(Cr(rt,k,q));else et.push(Cr(H.beforeEnter,k,q));return et.push(dt),Y(et)}).then(()=>(k.matched.forEach(H=>H.enterCallbacks={}),et=ls(ut,"beforeRouteEnter",k,q,$),et.push(dt),Y(et))).then(()=>{et=[];for(const H of s.list())et.push(Cr(H,k,q));return et.push(dt),Y(et)}).catch(H=>tr(H,8)?H:Promise.reject(H))}function B(k,q,et){i.list().forEach(ot=>$(()=>ot(k,q,et)))}function A(k,q,et,ot,at){const ut=_(k,q);if(ut)return ut;const dt=q===_r,H=_n?history.state:{};et&&(ot||dt?l.replace(k.fullPath,Bt({scroll:dt&&H&&H.scroll},at)):l.push(k.fullPath,at)),a.value=k,j(k,q,et,dt),Z()}let E;function w(){E||(E=l.listen((k,q,et)=>{if(!J.listening)return;const ot=g(k),at=U(ot);if(at)return void D(Bt(at,{replace:!0,force:!0}),ot).catch(ii);u=ot;const ut=a.value;var dt,H;_n&&(dt=Wu(ut.fullPath,et.delta),H=Ro(),as.set(dt,H)),X(ot,ut).catch(rt=>tr(rt,12)?rt:tr(rt,2)?(D(Bt(b(rt.to),{force:!0}),ot).then(tt=>{tr(tt,20)&&!et.delta&&et.type===yi.pop&&l.go(-1,!1)}).catch(ii),Promise.reject()):(et.delta&&l.go(-et.delta,!1),st(rt,ot,ut))).then(rt=>{(rt=rt||A(ot,ut,!1))&&(et.delta&&!tr(rt,8)?l.go(-et.delta,!1):et.type===yi.pop&&tr(rt,20)&&l.go(-1,!1)),B(ot,ut,rt)}).catch(ii)}))}let M,R=Vn(),nt=Vn();function st(k,q,et){Z(k);const ot=nt.list();return ot.length&&ot.forEach(at=>at(k,q,et)),Promise.reject(k)}function Z(k){return M||(M=!k,w(),R.list().forEach(([q,et])=>k?et(k):q()),R.reset()),k}function j(k,q,et,ot){const{scrollBehavior:at}=t;if(!_n||!at)return Promise.resolve();const ut=!et&&function(dt){const H=as.get(dt);return as.delete(dt),H}(Wu(k.fullPath,0))||(ot||!et)&&history.state&&history.state.scroll||null;return ml().then(()=>at(k,q,ut)).then(dt=>dt&&Cm(dt)).catch(dt=>st(dt,k,q))}const N=k=>l.go(k);let F;const it=new Set,J={currentRoute:a,listening:!0,addRoute:function(k,q){let et,ot;return Of(k)?(et=e.getRecordMatcher(k),ot=q):ot=k,e.addRoute(ot,et)},removeRoute:function(k){const q=e.getRecordMatcher(k);q&&e.removeRoute(q)},clearRoutes:e.clearRoutes,hasRoute:function(k){return!!e.getRecordMatcher(k)},getRoutes:function(){return e.getRoutes().map(k=>k.record)},resolve:g,options:t,push:S,replace:function(k){return S(Bt(b(k),{replace:!0}))},go:N,back:()=>N(-1),forward:()=>N(1),beforeEach:o.add,beforeResolve:s.add,afterEach:i.add,onError:nt.add,isReady:function(){return M&&a.value!==_r?Promise.resolve():new Promise((k,q)=>{R.add([k,q])})},install(k){k.component("RouterLink",Hm),k.component("RouterView",zm),k.config.globalProperties.$router=this,Object.defineProperty(k.config.globalProperties,"$route",{enumerable:!0,get:()=>Tn(a)}),_n&&!F&&a.value===_r&&(F=!0,S(l.location).catch(ot=>{}));const q={};for(const ot in _r)Object.defineProperty(q,ot,{get:()=>a.value[ot],enumerable:!0});k.provide(Tl,this),k.provide(Cf,Sh(q)),k.provide($s,a);const et=k.unmount;it.add(k),k.unmount=function(){it.delete(k),it.size<1&&(u=_r,E&&E(),E=null,a.value=_r,F=!1,M=!1),et()}}};function Y(k){return k.reduce((q,et)=>q.then(()=>$(et)),Promise.resolve())}return J}var Vs=Object.defineProperty,Km=Object.getOwnPropertyDescriptor,Gm=Object.getOwnPropertyNames,Qm=Object.prototype.hasOwnProperty,ne=(t,e)=>()=>(t&&(e=t(t=0)),e),mt=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),fn=(t,e)=>{for(var r in e)Vs(t,r,{get:e[r],enumerable:!0})},Dt=t=>((e,r,n,l)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Gm(r))!Qm.call(e,o)&&o!==n&&Vs(e,o,{get:()=>r[o],enumerable:!(l=Km(r,o))||l.enumerable});return e})(Vs({},"__esModule",{value:!0}),t),ht=ne(()=>{}),Nt={};function Ol(t){throw new Error("Node.js process "+t+" is not supported by JSPM core outside of Node.js")}function Ym(){!sn||!en||(sn=!1,en.length?He=en.concat(He):si=-1,He.length&&Rf())}function Rf(){if(!sn){var t=setTimeout(Ym,0);sn=!0;for(var e=He.length;e;){for(en=He,He=[];++si1)for(var r=1;rEa,_debugProcess:()=>_a,_events:()=>Vf,_eventsCount:()=>qf,_exiting:()=>aa,_fatalExceptions:()=>ba,_getActiveHandles:()=>Uf,_getActiveRequests:()=>jf,_kill:()=>ca,_linkedBinding:()=>Mf,_maxListeners:()=>$f,_preload_modules:()=>Ma,_rawDebug:()=>ia,_startProfilerIdleNotifier:()=>Sa,_stopProfilerIdleNotifier:()=>Aa,_tickCallback:()=>wa,abort:()=>Oa,addListener:()=>Hf,allowedNodeEnvironmentFlags:()=>ma,arch:()=>Hs,argv:()=>Gs,argv0:()=>Ba,assert:()=>Nf,binding:()=>Zs,chdir:()=>ra,config:()=>la,cpuUsage:()=>Ki,cwd:()=>ea,debugPort:()=>Pa,default:()=>xl,dlopen:()=>Lf,domain:()=>sa,emit:()=>Yf,emitWarning:()=>Xs,env:()=>Ks,execArgv:()=>Qs,execPath:()=>Ra,exit:()=>da,features:()=>ya,hasUncaughtExceptionCaptureCallback:()=>Df,hrtime:()=>zi,kill:()=>pa,listeners:()=>Wf,memoryUsage:()=>fa,moduleLoadList:()=>oa,nextTick:()=>Pf,off:()=>Kf,on:()=>rr,once:()=>zf,openStdin:()=>ga,pid:()=>xa,platform:()=>zs,ppid:()=>Ca,prependListener:()=>Jf,prependOnceListener:()=>Xf,reallyExit:()=>ua,release:()=>na,removeAllListeners:()=>Qf,removeListener:()=>Gf,resourceUsage:()=>ha,setSourceMapsEnabled:()=>La,setUncaughtExceptionCaptureCallback:()=>va,stderr:()=>Ia,stdin:()=>Ta,stdout:()=>ka,title:()=>qs,umask:()=>ta,uptime:()=>Ff,version:()=>Ys,versions:()=>Js});var He,sn,en,si,qs,Hs,zs,Ks,Gs,Qs,Ys,Js,Xs,Zs,ta,ea,ra,na,ia,oa,sa,aa,la,ua,ca,Ki,ha,fa,pa,da,ga,ma,ya,ba,va,wa,_a,Ea,Sa,Aa,ka,Ia,Ta,Oa,xa,Ca,Ra,Pa,Ba,Ma,La,Rr,us,Gi,$f,Vf,qf,Hf,zf,Kf,Gf,Qf,Yf,Jf,Xf,xl,Jm=ne(()=>{ht(),pt(),ft(),He=[],sn=!1,si=-1,Bf.prototype.run=function(){this.fun.apply(null,this.array)},qs="browser",Hs="x64",zs="browser",Ks={PATH:"/usr/bin",LANG:navigator.language+".UTF-8",PWD:"/",HOME:"/home",TMP:"/tmp"},Gs=["/usr/bin/node"],Qs=[],Ys="v16.8.0",Js={},Xs=function(t,e){},Zs=function(t){Ol("binding")},ta=function(t){return 0},ea=function(){return"/"},ra=function(t){},na={name:"node",sourceUrl:"",headersUrl:"",libUrl:""},ia=ae,oa=[],sa={},aa=!1,la={},ua=ae,ca=ae,ha=Ki=function(){return{}},fa=Ki,pa=ae,da=ae,ga=ae,ma={},ya={inspector:!1,debug:!1,uv:!1,ipv6:!1,tls_alpn:!1,tls_sni:!1,tls_ocsp:!1,tls:!1,cached_builtins:!0},ba=ae,va=ae,wa=ae,_a=ae,Ea=ae,Sa=ae,Aa=ae,ka=void 0,Ia=void 0,Ta=void 0,Oa=ae,xa=2,Ca=1,Ra="/bin/usr/node",Pa=9229,Ba="node",Ma=[],La=ae,(Rr={now:typeof performance<"u"?performance.now.bind(performance):void 0,timing:typeof performance<"u"?performance.timing:void 0}).now===void 0&&(us=Date.now(),Rr.timing&&Rr.timing.navigationStart&&(us=Rr.timing.navigationStart),Rr.now=()=>Date.now()-us),Gi=1e9,zi.bigint=function(t){var e=zi(t);return typeof BigInt>"u"?e[0]*Gi+e[1]:BigInt(e[0]*Gi)+BigInt(e[1])},xl={version:Ys,versions:Js,arch:Hs,platform:zs,release:na,_rawDebug:ia,moduleLoadList:oa,binding:Zs,_linkedBinding:Mf,_events:Vf={},_eventsCount:qf=0,_maxListeners:$f=10,on:rr,addListener:Hf=rr,once:zf=rr,off:Kf=rr,removeListener:Gf=rr,removeAllListeners:Qf=rr,emit:Yf=ae,prependListener:Jf=rr,prependOnceListener:Xf=rr,listeners:Wf,domain:sa,_exiting:aa,config:la,dlopen:Lf,uptime:Ff,_getActiveRequests:jf,_getActiveHandles:Uf,reallyExit:ua,_kill:ca,cpuUsage:Ki,resourceUsage:ha,memoryUsage:fa,kill:pa,exit:da,openStdin:ga,allowedNodeEnvironmentFlags:ma,assert:Nf,features:ya,_fatalExceptions:ba,setUncaughtExceptionCaptureCallback:va,hasUncaughtExceptionCaptureCallback:Df,emitWarning:Xs,nextTick:Pf,_tickCallback:wa,_debugProcess:_a,_debugEnd:Ea,_startProfilerIdleNotifier:Sa,_stopProfilerIdleNotifier:Aa,stdout:ka,stdin:Ta,stderr:Ia,abort:Oa,umask:ta,chdir:ra,cwd:ea,env:Ks,title:qs,argv:Gs,execArgv:Qs,pid:xa,ppid:Ca,execPath:Ra,debugPort:Pa,hrtime:zi,argv0:Ba,_preload_modules:Ma,setSourceMapsEnabled:La}}),ft=ne(()=>{Jm()}),ie={};function Xm(){if(Na)return Sr;Na=!0;let t=function(){if(ja)return En;ja=!0,En.byteLength=function(T){var L=I(T),z=L[0],V=L[1];return 3*(z+V)/4-V},En.toByteArray=function(T){var L,z,V=I(T),lt=V[0],ct=V[1],yt=new v(function(Ot,xt,_t){return 3*(xt+_t)/4-_t}(0,lt,ct)),bt=0,wt=ct>0?lt-4:lt;for(z=0;z>16&255,yt[bt++]=L>>8&255,yt[bt++]=255&L;return ct===2&&(L=p[T.charCodeAt(z)]<<2|p[T.charCodeAt(z+1)]>>4,yt[bt++]=255&L),ct===1&&(L=p[T.charCodeAt(z)]<<10|p[T.charCodeAt(z+1)]<<4|p[T.charCodeAt(z+2)]>>2,yt[bt++]=L>>8&255,yt[bt++]=255&L),yt},En.fromByteArray=function(T){for(var L,z=T.length,V=z%3,lt=[],ct=16383,yt=0,bt=z-V;ytbt?bt:yt+ct));return V===1?(L=T[z-1],lt.push(h[L>>2]+h[L<<4&63]+"==")):V===2&&(L=(T[z-2]<<8)+T[z-1],lt.push(h[L>>10]+h[L>>4&63]+h[L<<2&63]+"=")),lt.join("")};for(var h=[],p=[],v=typeof Uint8Array<"u"?Uint8Array:Array,O="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",x=0;x<64;++x)h[x]=O[x],p[O.charCodeAt(x)]=x;function I(T){var L=T.length;if(L%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var z=T.indexOf("=");return z===-1&&(z=L),[z,z===L?0:4-z%4]}function m(T){return h[T>>18&63]+h[T>>12&63]+h[T>>6&63]+h[63&T]}function y(T,L,z){for(var V,lt=[],ct=L;ct>1,z=-7,V=v?x-1:0,lt=v?-1:1,ct=h[p+V];for(V+=lt,I=ct&(1<<-z)-1,ct>>=-z,z+=y;z>0;I=256*I+h[p+V],V+=lt,z-=8);for(m=I&(1<<-z)-1,I>>=-z,z+=O;z>0;m=256*m+h[p+V],V+=lt,z-=8);if(I===0)I=1-L;else{if(I===T)return m?NaN:1/0*(ct?-1:1);m+=Math.pow(2,O),I-=L}return(ct?-1:1)*m*Math.pow(2,I-O)},Qi.write=function(h,p,v,O,x,I){var m,y,T,L=8*I-x-1,z=(1<>1,lt=x===23?Math.pow(2,-24)-Math.pow(2,-77):0,ct=O?0:I-1,yt=O?1:-1,bt=p<0||p===0&&1/p<0?1:0;for(p=Math.abs(p),isNaN(p)||p===1/0?(y=isNaN(p)?1:0,m=z):(m=Math.floor(Math.log(p)/Math.LN2),p*(T=Math.pow(2,-m))<1&&(m--,T*=2),(p+=m+V>=1?lt/T:lt*Math.pow(2,1-V))*T>=2&&(m++,T/=2),m+V>=z?(y=0,m=z):m+V>=1?(y=(p*T-1)*Math.pow(2,x),m+=V):(y=p*Math.pow(2,V-1)*Math.pow(2,x),m=0));x>=8;h[v+ct]=255&y,ct+=yt,y/=256,x-=8);for(m=m<0;h[v+ct]=255&m,ct+=yt,m/=256,L-=8);h[v+ct-yt]|=128*bt}),Qi),r=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null;Sr.Buffer=o,Sr.SlowBuffer=function(h){return+h!=h&&(h=0),o.alloc(+h)},Sr.INSPECT_MAX_BYTES=50;let n=2147483647;function l(h){if(h>n)throw new RangeError('The value "'+h+'" is invalid for option "size"');let p=new Uint8Array(h);return Object.setPrototypeOf(p,o.prototype),p}function o(h,p,v){if(typeof h=="number"){if(typeof p=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return a(h)}return s(h,p,v)}function s(h,p,v){if(typeof h=="string")return function(I,m){if((typeof m!="string"||m==="")&&(m="utf8"),!o.isEncoding(m))throw new TypeError("Unknown encoding: "+m);let y=0|f(I,m),T=l(y),L=T.write(I,m);return L!==y&&(T=T.slice(0,L)),T}(h,p);if(ArrayBuffer.isView(h))return function(I){if(tt(I,Uint8Array)){let m=new Uint8Array(I);return c(m.buffer,m.byteOffset,m.byteLength)}return u(I)}(h);if(h==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof h);if(tt(h,ArrayBuffer)||h&&tt(h.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(tt(h,SharedArrayBuffer)||h&&tt(h.buffer,SharedArrayBuffer)))return c(h,p,v);if(typeof h=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');let O=h.valueOf&&h.valueOf();if(O!=null&&O!==h)return o.from(O,p,v);let x=function(I){if(o.isBuffer(I)){let m=0|d(I.length),y=l(m);return y.length===0||I.copy(y,0,0,m),y}if(I.length!==void 0)return typeof I.length!="number"||P(I.length)?l(0):u(I);if(I.type==="Buffer"&&Array.isArray(I.data))return u(I.data)}(h);if(x)return x;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof h[Symbol.toPrimitive]=="function")return o.from(h[Symbol.toPrimitive]("string"),p,v);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof h)}function i(h){if(typeof h!="number")throw new TypeError('"size" argument must be of type number');if(h<0)throw new RangeError('The value "'+h+'" is invalid for option "size"')}function a(h){return i(h),l(h<0?0:0|d(h))}function u(h){let p=h.length<0?0:0|d(h.length),v=l(p);for(let O=0;O=n)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+n.toString(16)+" bytes");return 0|h}function f(h,p){if(o.isBuffer(h))return h.length;if(ArrayBuffer.isView(h)||tt(h,ArrayBuffer))return h.byteLength;if(typeof h!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof h);let v=h.length,O=arguments.length>2&&arguments[2]===!0;if(!O&&v===0)return 0;let x=!1;for(;;)switch(p){case"ascii":case"latin1":case"binary":return v;case"utf8":case"utf-8":return dt(h).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*v;case"hex":return v>>>1;case"base64":return H(h).length;default:if(x)return O?-1:dt(h).length;p=(""+p).toLowerCase(),x=!0}}function g(h,p,v){let O=!1;if((p===void 0||p<0)&&(p=0),p>this.length||((v===void 0||v>this.length)&&(v=this.length),v<=0)||(v>>>=0)<=(p>>>=0))return"";for(h||(h="utf8");;)switch(h){case"hex":return R(this,p,v);case"utf8":case"utf-8":return A(this,p,v);case"ascii":return w(this,p,v);case"latin1":case"binary":return M(this,p,v);case"base64":return B(this,p,v);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return nt(this,p,v);default:if(O)throw new TypeError("Unknown encoding: "+h);h=(h+"").toLowerCase(),O=!0}}function b(h,p,v){let O=h[p];h[p]=h[v],h[v]=O}function _(h,p,v,O,x){if(h.length===0)return-1;if(typeof v=="string"?(O=v,v=0):v>2147483647?v=2147483647:v<-2147483648&&(v=-2147483648),P(v=+v)&&(v=x?0:h.length-1),v<0&&(v=h.length+v),v>=h.length){if(x)return-1;v=h.length-1}else if(v<0){if(!x)return-1;v=0}if(typeof p=="string"&&(p=o.from(p,O)),o.isBuffer(p))return p.length===0?-1:S(h,p,v,O,x);if(typeof p=="number")return p&=255,typeof Uint8Array.prototype.indexOf=="function"?x?Uint8Array.prototype.indexOf.call(h,p,v):Uint8Array.prototype.lastIndexOf.call(h,p,v):S(h,[p],v,O,x);throw new TypeError("val must be string, number or Buffer")}function S(h,p,v,O,x){let I,m=1,y=h.length,T=p.length;if(O!==void 0&&((O=String(O).toLowerCase())==="ucs2"||O==="ucs-2"||O==="utf16le"||O==="utf-16le")){if(h.length<2||p.length<2)return-1;m=2,y/=2,T/=2,v/=2}function L(z,V){return m===1?z[V]:z.readUInt16BE(V*m)}if(x){let z=-1;for(I=v;Iy&&(v=y-T),I=v;I>=0;I--){let z=!0;for(let V=0;Vx&&(O=x):O=x;let I,m=p.length;for(O>m/2&&(O=m/2),I=0;I>8,T=m%256,L.push(T),L.push(y);return L}(p,h.length-v),h,v,O)}function B(h,p,v){return p===0&&v===h.length?t.fromByteArray(h):t.fromByteArray(h.slice(p,v))}function A(h,p,v){v=Math.min(h.length,v);let O=[],x=p;for(;x239?4:I>223?3:I>191?2:1;if(x+y<=v){let T,L,z,V;switch(y){case 1:I<128&&(m=I);break;case 2:T=h[x+1],(192&T)==128&&(V=(31&I)<<6|63&T,V>127&&(m=V));break;case 3:T=h[x+1],L=h[x+2],(192&T)==128&&(192&L)==128&&(V=(15&I)<<12|(63&T)<<6|63&L,V>2047&&(V<55296||V>57343)&&(m=V));break;case 4:T=h[x+1],L=h[x+2],z=h[x+3],(192&T)==128&&(192&L)==128&&(192&z)==128&&(V=(15&I)<<18|(63&T)<<12|(63&L)<<6|63&z,V>65535&&V<1114112&&(m=V))}}m===null?(m=65533,y=1):m>65535&&(m-=65536,O.push(m>>>10&1023|55296),m=56320|1023&m),O.push(m),x+=y}return function(I){let m=I.length;if(m<=E)return String.fromCharCode.apply(String,I);let y="",T=0;for(;TO.length?(o.isBuffer(I)||(I=o.from(I)),I.copy(O,x)):Uint8Array.prototype.set.call(O,I,x);else{if(!o.isBuffer(I))throw new TypeError('"list" argument must be an Array of Buffers');I.copy(O,x)}x+=I.length}return O},o.byteLength=f,o.prototype._isBuffer=!0,o.prototype.swap16=function(){let h=this.length;if(h%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let p=0;pp&&(h+=" ... "),""},r&&(o.prototype[r]=o.prototype.inspect),o.prototype.compare=function(h,p,v,O,x){if(tt(h,Uint8Array)&&(h=o.from(h,h.offset,h.byteLength)),!o.isBuffer(h))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof h);if(p===void 0&&(p=0),v===void 0&&(v=h?h.length:0),O===void 0&&(O=0),x===void 0&&(x=this.length),p<0||v>h.length||O<0||x>this.length)throw new RangeError("out of range index");if(O>=x&&p>=v)return 0;if(O>=x)return-1;if(p>=v)return 1;if(this===h)return 0;let I=(x>>>=0)-(O>>>=0),m=(v>>>=0)-(p>>>=0),y=Math.min(I,m),T=this.slice(O,x),L=h.slice(p,v);for(let z=0;z>>=0,isFinite(v)?(v>>>=0,O===void 0&&(O="utf8")):(O=v,v=void 0)}let x=this.length-p;if((v===void 0||v>x)&&(v=x),h.length>0&&(v<0||p<0)||p>this.length)throw new RangeError("Attempt to write outside buffer bounds");O||(O="utf8");let I=!1;for(;;)switch(O){case"hex":return U(this,h,p,v);case"utf8":case"utf-8":return D(this,h,p,v);case"ascii":case"latin1":case"binary":return G(this,h,p,v);case"base64":return $(this,h,p,v);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return X(this,h,p,v);default:if(I)throw new TypeError("Unknown encoding: "+O);O=(""+O).toLowerCase(),I=!0}},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};let E=4096;function w(h,p,v){let O="";v=Math.min(h.length,v);for(let x=p;xO)&&(v=O);let x="";for(let I=p;Iv)throw new RangeError("Trying to access beyond buffer length")}function Z(h,p,v,O,x,I){if(!o.isBuffer(h))throw new TypeError('"buffer" argument must be a Buffer instance');if(p>x||ph.length)throw new RangeError("Index out of range")}function j(h,p,v,O,x){et(p,O,x,h,v,7);let I=Number(p&BigInt(4294967295));h[v++]=I,I>>=8,h[v++]=I,I>>=8,h[v++]=I,I>>=8,h[v++]=I;let m=Number(p>>BigInt(32)&BigInt(4294967295));return h[v++]=m,m>>=8,h[v++]=m,m>>=8,h[v++]=m,m>>=8,h[v++]=m,v}function N(h,p,v,O,x){et(p,O,x,h,v,7);let I=Number(p&BigInt(4294967295));h[v+7]=I,I>>=8,h[v+6]=I,I>>=8,h[v+5]=I,I>>=8,h[v+4]=I;let m=Number(p>>BigInt(32)&BigInt(4294967295));return h[v+3]=m,m>>=8,h[v+2]=m,m>>=8,h[v+1]=m,m>>=8,h[v]=m,v+8}function F(h,p,v,O,x,I){if(v+O>h.length)throw new RangeError("Index out of range");if(v<0)throw new RangeError("Index out of range")}function it(h,p,v,O,x){return p=+p,v>>>=0,x||F(h,0,v,4),e.write(h,p,v,O,23,4),v+4}function J(h,p,v,O,x){return p=+p,v>>>=0,x||F(h,0,v,8),e.write(h,p,v,O,52,8),v+8}o.prototype.slice=function(h,p){let v=this.length;(h=~~h)<0?(h+=v)<0&&(h=0):h>v&&(h=v),(p=p===void 0?v:~~p)<0?(p+=v)<0&&(p=0):p>v&&(p=v),p>>=0,p>>>=0,v||st(h,p,this.length);let O=this[h],x=1,I=0;for(;++I>>=0,p>>>=0,v||st(h,p,this.length);let O=this[h+--p],x=1;for(;p>0&&(x*=256);)O+=this[h+--p]*x;return O},o.prototype.readUint8=o.prototype.readUInt8=function(h,p){return h>>>=0,p||st(h,1,this.length),this[h]},o.prototype.readUint16LE=o.prototype.readUInt16LE=function(h,p){return h>>>=0,p||st(h,2,this.length),this[h]|this[h+1]<<8},o.prototype.readUint16BE=o.prototype.readUInt16BE=function(h,p){return h>>>=0,p||st(h,2,this.length),this[h]<<8|this[h+1]},o.prototype.readUint32LE=o.prototype.readUInt32LE=function(h,p){return h>>>=0,p||st(h,4,this.length),(this[h]|this[h+1]<<8|this[h+2]<<16)+16777216*this[h+3]},o.prototype.readUint32BE=o.prototype.readUInt32BE=function(h,p){return h>>>=0,p||st(h,4,this.length),16777216*this[h]+(this[h+1]<<16|this[h+2]<<8|this[h+3])},o.prototype.readBigUInt64LE=C(function(h){ot(h>>>=0,"offset");let p=this[h],v=this[h+7];(p===void 0||v===void 0)&&at(h,this.length-8);let O=p+256*this[++h]+65536*this[++h]+this[++h]*2**24,x=this[++h]+256*this[++h]+65536*this[++h]+v*2**24;return BigInt(O)+(BigInt(x)<>>=0,"offset");let p=this[h],v=this[h+7];(p===void 0||v===void 0)&&at(h,this.length-8);let O=p*2**24+65536*this[++h]+256*this[++h]+this[++h],x=this[++h]*2**24+65536*this[++h]+256*this[++h]+v;return(BigInt(O)<>>=0,p>>>=0,v||st(h,p,this.length);let O=this[h],x=1,I=0;for(;++I=x&&(O-=Math.pow(2,8*p)),O},o.prototype.readIntBE=function(h,p,v){h>>>=0,p>>>=0,v||st(h,p,this.length);let O=p,x=1,I=this[h+--O];for(;O>0&&(x*=256);)I+=this[h+--O]*x;return x*=128,I>=x&&(I-=Math.pow(2,8*p)),I},o.prototype.readInt8=function(h,p){return h>>>=0,p||st(h,1,this.length),128&this[h]?-1*(255-this[h]+1):this[h]},o.prototype.readInt16LE=function(h,p){h>>>=0,p||st(h,2,this.length);let v=this[h]|this[h+1]<<8;return 32768&v?4294901760|v:v},o.prototype.readInt16BE=function(h,p){h>>>=0,p||st(h,2,this.length);let v=this[h+1]|this[h]<<8;return 32768&v?4294901760|v:v},o.prototype.readInt32LE=function(h,p){return h>>>=0,p||st(h,4,this.length),this[h]|this[h+1]<<8|this[h+2]<<16|this[h+3]<<24},o.prototype.readInt32BE=function(h,p){return h>>>=0,p||st(h,4,this.length),this[h]<<24|this[h+1]<<16|this[h+2]<<8|this[h+3]},o.prototype.readBigInt64LE=C(function(h){ot(h>>>=0,"offset");let p=this[h],v=this[h+7];(p===void 0||v===void 0)&&at(h,this.length-8);let O=this[h+4]+256*this[h+5]+65536*this[h+6]+(v<<24);return(BigInt(O)<>>=0,"offset");let p=this[h],v=this[h+7];(p===void 0||v===void 0)&&at(h,this.length-8);let O=(p<<24)+65536*this[++h]+256*this[++h]+this[++h];return(BigInt(O)<>>=0,p||st(h,4,this.length),e.read(this,h,!0,23,4)},o.prototype.readFloatBE=function(h,p){return h>>>=0,p||st(h,4,this.length),e.read(this,h,!1,23,4)},o.prototype.readDoubleLE=function(h,p){return h>>>=0,p||st(h,8,this.length),e.read(this,h,!0,52,8)},o.prototype.readDoubleBE=function(h,p){return h>>>=0,p||st(h,8,this.length),e.read(this,h,!1,52,8)},o.prototype.writeUintLE=o.prototype.writeUIntLE=function(h,p,v,O){h=+h,p>>>=0,v>>>=0,!O&&Z(this,h,p,v,Math.pow(2,8*v)-1,0);let x=1,I=0;for(this[p]=255&h;++I>>=0,v>>>=0,!O&&Z(this,h,p,v,Math.pow(2,8*v)-1,0);let x=v-1,I=1;for(this[p+x]=255&h;--x>=0&&(I*=256);)this[p+x]=h/I&255;return p+v},o.prototype.writeUint8=o.prototype.writeUInt8=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,1,255,0),this[p]=255&h,p+1},o.prototype.writeUint16LE=o.prototype.writeUInt16LE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,2,65535,0),this[p]=255&h,this[p+1]=h>>>8,p+2},o.prototype.writeUint16BE=o.prototype.writeUInt16BE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,2,65535,0),this[p]=h>>>8,this[p+1]=255&h,p+2},o.prototype.writeUint32LE=o.prototype.writeUInt32LE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,4,4294967295,0),this[p+3]=h>>>24,this[p+2]=h>>>16,this[p+1]=h>>>8,this[p]=255&h,p+4},o.prototype.writeUint32BE=o.prototype.writeUInt32BE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,4,4294967295,0),this[p]=h>>>24,this[p+1]=h>>>16,this[p+2]=h>>>8,this[p+3]=255&h,p+4},o.prototype.writeBigUInt64LE=C(function(h,p=0){return j(this,h,p,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeBigUInt64BE=C(function(h,p=0){return N(this,h,p,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeIntLE=function(h,p,v,O){if(h=+h,p>>>=0,!O){let y=Math.pow(2,8*v-1);Z(this,h,p,v,y-1,-y)}let x=0,I=1,m=0;for(this[p]=255&h;++x>>=0,!O){let y=Math.pow(2,8*v-1);Z(this,h,p,v,y-1,-y)}let x=v-1,I=1,m=0;for(this[p+x]=255&h;--x>=0&&(I*=256);)h<0&&m===0&&this[p+x+1]!==0&&(m=1),this[p+x]=(h/I|0)-m&255;return p+v},o.prototype.writeInt8=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,1,127,-128),h<0&&(h=255+h+1),this[p]=255&h,p+1},o.prototype.writeInt16LE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,2,32767,-32768),this[p]=255&h,this[p+1]=h>>>8,p+2},o.prototype.writeInt16BE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,2,32767,-32768),this[p]=h>>>8,this[p+1]=255&h,p+2},o.prototype.writeInt32LE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,4,2147483647,-2147483648),this[p]=255&h,this[p+1]=h>>>8,this[p+2]=h>>>16,this[p+3]=h>>>24,p+4},o.prototype.writeInt32BE=function(h,p,v){return h=+h,p>>>=0,v||Z(this,h,p,4,2147483647,-2147483648),h<0&&(h=4294967295+h+1),this[p]=h>>>24,this[p+1]=h>>>16,this[p+2]=h>>>8,this[p+3]=255&h,p+4},o.prototype.writeBigInt64LE=C(function(h,p=0){return j(this,h,p,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),o.prototype.writeBigInt64BE=C(function(h,p=0){return N(this,h,p,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),o.prototype.writeFloatLE=function(h,p,v){return it(this,h,p,!0,v)},o.prototype.writeFloatBE=function(h,p,v){return it(this,h,p,!1,v)},o.prototype.writeDoubleLE=function(h,p,v){return J(this,h,p,!0,v)},o.prototype.writeDoubleBE=function(h,p,v){return J(this,h,p,!1,v)},o.prototype.copy=function(h,p,v,O){if(!o.isBuffer(h))throw new TypeError("argument should be a Buffer");if(v||(v=0),!O&&O!==0&&(O=this.length),p>=h.length&&(p=h.length),p||(p=0),O>0&&O=this.length)throw new RangeError("Index out of range");if(O<0)throw new RangeError("sourceEnd out of bounds");O>this.length&&(O=this.length),h.length-p>>=0,v=v===void 0?this.length:v>>>0,h||(h=0),typeof h=="number")for(x=p;x=O+4;v-=3)p=`_${h.slice(v-3,v)}${p}`;return`${h.slice(0,v)}${p}`}function et(h,p,v,O,x,I){if(h>v||h= 0${y} and < 2${y} ** ${8*(I+1)}${y}`:`>= -(2${y} ** ${8*(I+1)-1}${y}) and < 2 ** ${8*(I+1)-1}${y}`,new Y.ERR_OUT_OF_RANGE("value",m,h)}(function(m,y,T){ot(y,"offset"),(m[y]===void 0||m[y+T]===void 0)&&at(y,m.length-(T+1))})(O,x,I)}function ot(h,p){if(typeof h!="number")throw new Y.ERR_INVALID_ARG_TYPE(p,"number",h)}function at(h,p,v){throw Math.floor(h)!==h?(ot(h,v),new Y.ERR_OUT_OF_RANGE("offset","an integer",h)):p<0?new Y.ERR_BUFFER_OUT_OF_BOUNDS:new Y.ERR_OUT_OF_RANGE("offset",`>= 0 and <= ${p}`,h)}k("ERR_BUFFER_OUT_OF_BOUNDS",function(h){return h?`${h} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"},RangeError),k("ERR_INVALID_ARG_TYPE",function(h,p){return`The "${h}" argument must be of type number. Received type ${typeof p}`},TypeError),k("ERR_OUT_OF_RANGE",function(h,p,v){let O=`The value of "${h}" is out of range.`,x=v;return Number.isInteger(v)&&Math.abs(v)>2**32?x=q(String(v)):typeof v=="bigint"&&(x=String(v),(v>BigInt(2)**BigInt(32)||v<-(BigInt(2)**BigInt(32)))&&(x=q(x)),x+="n"),O+=` It must be ${p}. Received ${x}`,O},RangeError);let ut=/[^+/0-9A-Za-z-_]/g;function dt(h,p){p=p||1/0;let v,O=h.length,x=null,I=[];for(let m=0;m55295&&v<57344){if(!x){if(v>56319){(p-=3)>-1&&I.push(239,191,189);continue}if(m+1===O){(p-=3)>-1&&I.push(239,191,189);continue}x=v;continue}if(v<56320){(p-=3)>-1&&I.push(239,191,189),x=v;continue}v=65536+(x-55296<<10|v-56320)}else x&&(p-=3)>-1&&I.push(239,191,189);if(x=null,v<128){if((p-=1)<0)break;I.push(v)}else if(v<2048){if((p-=2)<0)break;I.push(v>>6|192,63&v|128)}else if(v<65536){if((p-=3)<0)break;I.push(v>>12|224,v>>6&63|128,63&v|128)}else{if(!(v<1114112))throw new Error("Invalid code point");if((p-=4)<0)break;I.push(v>>18|240,v>>12&63|128,v>>6&63|128,63&v|128)}}return I}function H(h){return t.toByteArray(function(p){if((p=(p=p.split("=")[0]).trim().replace(ut,"")).length<2)return"";for(;p.length%4!=0;)p+="=";return p}(h))}function rt(h,p,v,O){let x;for(x=0;x=p.length||x>=h.length);++x)p[x+v]=h[x];return x}function tt(h,p){return h instanceof p||h!=null&&h.constructor!=null&&h.constructor.name!=null&&h.constructor.name===p.name}function P(h){return h!=h}let W=function(){let h="0123456789abcdef",p=new Array(256);for(let v=0;v<16;++v){let O=16*v;for(let x=0;x<16;++x)p[O+x]=h[v]+h[x]}return p}();function C(h){return typeof BigInt>"u"?Q:h}function Q(){throw new Error("BigInt not supported")}return Sr}fn(ie,{Buffer:()=>ho,INSPECT_MAX_BYTES:()=>Zf,default:()=>Ar,kMaxLength:()=>tp});var En,ja,Qi,Ua,Sr,Na,Ar,ho,Zf,tp,oe=ne(()=>{ht(),pt(),ft(),En={},ja=!1,Qi={},Ua=!1,Sr={},Na=!1,(Ar=Xm()).Buffer,Ar.SlowBuffer,Ar.INSPECT_MAX_BYTES,Ar.kMaxLength,ho=Ar.Buffer,Zf=Ar.INSPECT_MAX_BYTES,tp=Ar.kMaxLength}),pt=ne(()=>{oe()}),Zm=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e){this.aliasToTopic={},this.max=e}put(e,r){return!(r===0||r>this.max)&&(this.aliasToTopic[r]=e,this.length=Object.keys(this.aliasToTopic).length,!0)}getTopicByAlias(e){return this.aliasToTopic[e]}clear(){this.aliasToTopic={}}}}),Xt=mt((t,e)=>{ht(),pt(),ft(),e.exports={ArrayIsArray:r=>Array.isArray(r),ArrayPrototypeIncludes:(r,n)=>r.includes(n),ArrayPrototypeIndexOf:(r,n)=>r.indexOf(n),ArrayPrototypeJoin:(r,n)=>r.join(n),ArrayPrototypeMap:(r,n)=>r.map(n),ArrayPrototypePop:(r,n)=>r.pop(n),ArrayPrototypePush:(r,n)=>r.push(n),ArrayPrototypeSlice:(r,n,l)=>r.slice(n,l),Error,FunctionPrototypeCall:(r,n,...l)=>r.call(n,...l),FunctionPrototypeSymbolHasInstance:(r,n)=>Function.prototype[Symbol.hasInstance].call(r,n),MathFloor:Math.floor,Number,NumberIsInteger:Number.isInteger,NumberIsNaN:Number.isNaN,NumberMAX_SAFE_INTEGER:Number.MAX_SAFE_INTEGER,NumberMIN_SAFE_INTEGER:Number.MIN_SAFE_INTEGER,NumberParseInt:Number.parseInt,ObjectDefineProperties:(r,n)=>Object.defineProperties(r,n),ObjectDefineProperty:(r,n,l)=>Object.defineProperty(r,n,l),ObjectGetOwnPropertyDescriptor:(r,n)=>Object.getOwnPropertyDescriptor(r,n),ObjectKeys:r=>Object.keys(r),ObjectSetPrototypeOf:(r,n)=>Object.setPrototypeOf(r,n),Promise,PromisePrototypeCatch:(r,n)=>r.catch(n),PromisePrototypeThen:(r,n,l)=>r.then(n,l),PromiseReject:r=>Promise.reject(r),ReflectApply:Reflect.apply,RegExpPrototypeTest:(r,n)=>r.test(n),SafeSet:Set,String,StringPrototypeSlice:(r,n,l)=>r.slice(n,l),StringPrototypeToLowerCase:r=>r.toLowerCase(),StringPrototypeToUpperCase:r=>r.toUpperCase(),StringPrototypeTrim:r=>r.trim(),Symbol,SymbolFor:Symbol.for,SymbolAsyncIterator:Symbol.asyncIterator,SymbolHasInstance:Symbol.hasInstance,SymbolIterator:Symbol.iterator,TypedArrayPrototypeSet:(r,n,l)=>r.set(n,l),Uint8Array}}),fr=mt((t,e)=>{ht(),pt(),ft();var r=(oe(),Dt(ie)),n=Object.getPrototypeOf(async function(){}).constructor,l=globalThis.Blob||r.Blob,o=typeof l<"u"?function(i){return i instanceof l}:function(i){return!1},s=class extends Error{constructor(i){if(!Array.isArray(i))throw new TypeError("Expected input to be an Array, got "+typeof i);let a="";for(let u=0;u{i=u,a=c}),resolve:i,reject:a}},promisify:i=>new Promise((a,u)=>{i((c,...d)=>c?u(c):a(...d))}),debuglog:()=>function(){},format:(i,...a)=>i.replace(/%([sdifj])/g,function(...[u,c]){let d=a.shift();return c==="f"?d.toFixed(6):c==="j"?JSON.stringify(d):c==="s"&&typeof d=="object"?`${d.constructor!==Object?d.constructor.name:""} {}`.trim():d.toString()}),inspect(i){switch(typeof i){case"string":if(i.includes("'")){if(!i.includes('"'))return`"${i}"`;if(!i.includes("`")&&!i.includes("${"))return`\`${i}\``}return`'${i}'`;case"number":return isNaN(i)?"NaN":Object.is(i,-0)?String(i):i;case"bigint":return`${String(i)}n`;case"boolean":case"undefined":return String(i);case"object":return"{}"}},types:{isAsyncFunction:i=>i instanceof n,isArrayBufferView:i=>ArrayBuffer.isView(i)},isBlob:o},e.exports.promisify.custom=Symbol.for("nodejs.util.promisify.custom")}),Cl=mt((t,e)=>{ht(),pt(),ft();var{AbortController:r,AbortSignal:n}=typeof self<"u"?self:typeof window<"u"?window:void 0;e.exports=r,e.exports.AbortSignal=n,e.exports.default=r}),we=mt((t,e)=>{ht(),pt(),ft();var{format:r,inspect:n,AggregateError:l}=fr(),o=globalThis.AggregateError||l,s=Symbol("kIsNodeError"),i=["string","function","number","object","Function","Object","boolean","bigint","symbol"],a=/^([A-Z][a-z0-9]*)+$/,u={};function c(_,S){if(!_)throw new u.ERR_INTERNAL_ASSERTION(S)}function d(_){let S="",U=_.length,D=_[0]==="-"?1:0;for(;U>=D+4;U-=3)S=`_${_.slice(U-3,U)}${S}`;return`${_.slice(0,U)}${S}`}function f(_,S,U){U||(U=Error);class D extends U{constructor(...$){super(function(X,B,A){if(typeof B=="function")return c(B.length<=A.length,`Code: ${X}; The provided arguments length (${A.length}) does not match the required ones (${B.length}).`),B(...A);let E=(B.match(/%[dfijoOs]/g)||[]).length;return c(E===A.length,`Code: ${X}; The provided arguments length (${A.length}) does not match the required ones (${E}).`),A.length===0?B:r(B,...A)}(_,S,$))}toString(){return`${this.name} [${_}]: ${this.message}`}}Object.defineProperties(D.prototype,{name:{value:U.name,writable:!0,enumerable:!1,configurable:!0},toString:{value(){return`${this.name} [${_}]: ${this.message}`},writable:!0,enumerable:!1,configurable:!0}}),D.prototype.code=_,D.prototype[s]=!0,u[_]=D}function g(_){let S="__node_internal_"+_.name;return Object.defineProperty(_,"name",{value:S}),_}var b=class extends Error{constructor(_="The operation was aborted",S=void 0){if(S!==void 0&&typeof S!="object")throw new u.ERR_INVALID_ARG_TYPE("options","Object",S);super(_,S),this.code="ABORT_ERR",this.name="AbortError"}};f("ERR_ASSERTION","%s",Error),f("ERR_INVALID_ARG_TYPE",(_,S,U)=>{c(typeof _=="string","'name' must be a string"),Array.isArray(S)||(S=[S]);let D="The ";_.endsWith(" argument")?D+=`${_} `:D+=`"${_}" ${_.includes(".")?"property":"argument"} `,D+="must be ";let G=[],$=[],X=[];for(let A of S)c(typeof A=="string","All expected entries have to be of type string"),i.includes(A)?G.push(A.toLowerCase()):a.test(A)?$.push(A):(c(A!=="object",'The value "object" should be written as "Object"'),X.push(A));if($.length>0){let A=G.indexOf("object");A!==-1&&(G.splice(G,A,1),$.push("Object"))}if(G.length>0){switch(G.length){case 1:D+=`of type ${G[0]}`;break;case 2:D+=`one of type ${G[0]} or ${G[1]}`;break;default:{let A=G.pop();D+=`one of type ${G.join(", ")}, or ${A}`}}($.length>0||X.length>0)&&(D+=" or ")}if($.length>0){switch($.length){case 1:D+=`an instance of ${$[0]}`;break;case 2:D+=`an instance of ${$[0]} or ${$[1]}`;break;default:{let A=$.pop();D+=`an instance of ${$.join(", ")}, or ${A}`}}X.length>0&&(D+=" or ")}switch(X.length){case 0:break;case 1:X[0].toLowerCase()!==X[0]&&(D+="an "),D+=`${X[0]}`;break;case 2:D+=`one of ${X[0]} or ${X[1]}`;break;default:{let A=X.pop();D+=`one of ${X.join(", ")}, or ${A}`}}if(U==null)D+=`. Received ${U}`;else if(typeof U=="function"&&U.name)D+=`. Received function ${U.name}`;else if(typeof U=="object"){var B;(B=U.constructor)!==null&&B!==void 0&&B.name?D+=`. Received an instance of ${U.constructor.name}`:D+=`. Received ${n(U,{depth:-1})}`}else{let A=n(U,{colors:!1});A.length>25&&(A=`${A.slice(0,25)}...`),D+=`. Received type ${typeof U} (${A})`}return D},TypeError),f("ERR_INVALID_ARG_VALUE",(_,S,U="is invalid")=>{let D=n(S);return D.length>128&&(D=D.slice(0,128)+"..."),`The ${_.includes(".")?"property":"argument"} '${_}' ${U}. Received ${D}`},TypeError),f("ERR_INVALID_RETURN_VALUE",(_,S,U)=>{var D;return`Expected ${_} to be returned from the "${S}" function but got ${U!=null&&(D=U.constructor)!==null&&D!==void 0&&D.name?`instance of ${U.constructor.name}`:"type "+typeof U}.`},TypeError),f("ERR_MISSING_ARGS",(..._)=>{c(_.length>0,"At least one arg needs to be specified");let S,U=_.length;switch(_=(Array.isArray(_)?_:[_]).map(D=>`"${D}"`).join(" or "),U){case 1:S+=`The ${_[0]} argument`;break;case 2:S+=`The ${_[0]} and ${_[1]} arguments`;break;default:{let D=_.pop();S+=`The ${_.join(", ")}, and ${D} arguments`}}return`${S} must be specified`},TypeError),f("ERR_OUT_OF_RANGE",(_,S,U)=>{let D;return c(S,'Missing "range" argument'),Number.isInteger(U)&&Math.abs(U)>2**32?D=d(String(U)):typeof U=="bigint"?(D=String(U),(U>2n**32n||U<-(2n**32n))&&(D=d(D)),D+="n"):D=n(U),`The value of "${_}" is out of range. It must be ${S}. Received ${D}`},RangeError),f("ERR_MULTIPLE_CALLBACK","Callback called multiple times",Error),f("ERR_METHOD_NOT_IMPLEMENTED","The %s method is not implemented",Error),f("ERR_STREAM_ALREADY_FINISHED","Cannot call %s after a stream was finished",Error),f("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable",Error),f("ERR_STREAM_DESTROYED","Cannot call %s after a stream was destroyed",Error),f("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),f("ERR_STREAM_PREMATURE_CLOSE","Premature close",Error),f("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF",Error),f("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event",Error),f("ERR_STREAM_WRITE_AFTER_END","write after end",Error),f("ERR_UNKNOWN_ENCODING","Unknown encoding: %s",TypeError),e.exports={AbortError:b,aggregateTwoErrors:g(function(_,S){if(_&&S&&_!==S){if(Array.isArray(S.errors))return S.errors.push(_),S;let U=new o([S,_],S.message);return U.code=S.code,U}return _||S}),hideStackFrames:g,codes:u}}),Po=mt((t,e)=>{ht(),pt(),ft();var{ArrayIsArray:r,ArrayPrototypeIncludes:n,ArrayPrototypeJoin:l,ArrayPrototypeMap:o,NumberIsInteger:s,NumberIsNaN:i,NumberMAX_SAFE_INTEGER:a,NumberMIN_SAFE_INTEGER:u,NumberParseInt:c,ObjectPrototypeHasOwnProperty:d,RegExpPrototypeExec:f,String:g,StringPrototypeToUpperCase:b,StringPrototypeTrim:_}=Xt(),{hideStackFrames:S,codes:{ERR_SOCKET_BAD_PORT:U,ERR_INVALID_ARG_TYPE:D,ERR_INVALID_ARG_VALUE:G,ERR_OUT_OF_RANGE:$,ERR_UNKNOWN_SIGNAL:X}}=we(),{normalizeEncoding:B}=fr(),{isAsyncFunction:A,isArrayBufferView:E}=fr().types,w={},M=/^[0-7]+$/,R=S((H,rt,tt=u,P=a)=>{if(typeof H!="number")throw new D(rt,"number",H);if(!s(H))throw new $(rt,"an integer",H);if(HP)throw new $(rt,`>= ${tt} && <= ${P}`,H)}),nt=S((H,rt,tt=-2147483648,P=2147483647)=>{if(typeof H!="number")throw new D(rt,"number",H);if(!s(H))throw new $(rt,"an integer",H);if(HP)throw new $(rt,`>= ${tt} && <= ${P}`,H)}),st=S((H,rt,tt=!1)=>{if(typeof H!="number")throw new D(rt,"number",H);if(!s(H))throw new $(rt,"an integer",H);let P=tt?1:0,W=4294967295;if(HW)throw new $(rt,`>= ${P} && <= ${W}`,H)});function Z(H,rt){if(typeof H!="string")throw new D(rt,"string",H)}var j=S((H,rt,tt)=>{if(!n(tt,H)){let P="must be one of: "+l(o(tt,W=>typeof W=="string"?`'${W}'`:g(W)),", ");throw new G(rt,H,P)}});function N(H,rt){if(typeof H!="boolean")throw new D(rt,"boolean",H)}function F(H,rt,tt){return H!=null&&d(H,rt)?H[rt]:tt}var it=S((H,rt,tt=null)=>{let P=F(tt,"allowArray",!1),W=F(tt,"allowFunction",!1);if(!F(tt,"nullable",!1)&&H===null||!P&&r(H)||typeof H!="object"&&(!W||typeof H!="function"))throw new D(rt,"Object",H)}),J=S((H,rt)=>{if(H!=null&&typeof H!="object"&&typeof H!="function")throw new D(rt,"a dictionary",H)}),Y=S((H,rt,tt=0)=>{if(!r(H))throw new D(rt,"Array",H);if(H.length{if(!E(H))throw new D(rt,["Buffer","TypedArray","DataView"],H)}),q=S((H,rt)=>{if(H!==void 0&&(H===null||typeof H!="object"||!("aborted"in H)))throw new D(rt,"AbortSignal",H)}),et=S((H,rt)=>{if(typeof H!="function")throw new D(rt,"Function",H)}),ot=S((H,rt)=>{if(typeof H!="function"||A(H))throw new D(rt,"Function",H)}),at=S((H,rt)=>{if(H!==void 0)throw new D(rt,"undefined",H)}),ut=/^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/;function dt(H,rt){if(typeof H>"u"||!f(ut,H))throw new G(rt,H,'must be an array or string of format "; rel=preload; as=style"')}e.exports={isInt32:function(H){return H===(0|H)},isUint32:function(H){return H===H>>>0},parseFileMode:function(H,rt,tt){if(typeof H>"u"&&(H=tt),typeof H=="string"){if(f(M,H)===null)throw new G(rt,H,"must be a 32-bit unsigned integer or an octal string");H=c(H,8)}return st(H,rt),H},validateArray:Y,validateStringArray:function(H,rt){Y(H,rt);for(let tt=0;ttP||(tt!=null||P!=null)&&i(H))throw new $(rt,`${tt!=null?`>= ${tt}`:""}${tt!=null&&P!=null?" && ":""}${P!=null?`<= ${P}`:""}`,H)},validateObject:it,validateOneOf:j,validatePlainFunction:ot,validatePort:function(H,rt="Port",tt=!0){if(typeof H!="number"&&typeof H!="string"||typeof H=="string"&&_(H).length===0||+H!=+H>>>0||H>65535||H===0&&!tt)throw new U(rt,H,tt);return 0|H},validateSignalName:function(H,rt="signal"){if(Z(H,rt),w[H]===void 0)throw w[b(H)]!==void 0?new X(H+" (signals must use all capital letters)"):new X(H)},validateString:Z,validateUint32:st,validateUndefined:at,validateUnion:function(H,rt,tt){if(!n(tt,H))throw new D(rt,`('${l(tt,"|")}')`,H)},validateAbortSignal:q,validateLinkHeaderValue:function(H){if(typeof H=="string")return dt(H,"hints"),H;if(r(H)){let rt=H.length,tt="";if(rt===0)return tt;for(let P=0;P; rel=preload; as=style"')}}}),pn=mt((t,e)=>{ht(),pt(),ft();var r,n,l=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function i(S){if(r===setTimeout)return setTimeout(S,0);if((r===o||!r)&&setTimeout)return r=setTimeout,setTimeout(S,0);try{return r(S,0)}catch{try{return r.call(null,S,0)}catch{return r.call(this,S,0)}}}(function(){try{r=typeof setTimeout=="function"?setTimeout:o}catch{r=o}try{n=typeof clearTimeout=="function"?clearTimeout:s}catch{n=s}})();var a,u=[],c=!1,d=-1;function f(){!c||!a||(c=!1,a.length?u=a.concat(u):d=-1,u.length&&g())}function g(){if(!c){var S=i(f);c=!0;for(var U=u.length;U;){for(a=u,u=[];++d1)for(var D=1;D{ht(),pt(),ft();var{Symbol:r,SymbolAsyncIterator:n,SymbolIterator:l,SymbolFor:o}=Xt(),s=r("kDestroyed"),i=r("kIsErrored"),a=r("kIsReadable"),u=r("kIsDisturbed"),c=o("nodejs.webstream.isClosedPromise"),d=o("nodejs.webstream.controllerErrorFunction");function f(w,M=!1){var R;return!(!w||typeof w.pipe!="function"||typeof w.on!="function"||M&&(typeof w.pause!="function"||typeof w.resume!="function")||w._writableState&&((R=w._readableState)===null||R===void 0?void 0:R.readable)===!1||w._writableState&&!w._readableState)}function g(w){var M;return!(!w||typeof w.write!="function"||typeof w.on!="function"||w._readableState&&((M=w._writableState)===null||M===void 0?void 0:M.writable)===!1)}function b(w){return w&&(w._readableState||w._writableState||typeof w.write=="function"&&typeof w.on=="function"||typeof w.pipe=="function"&&typeof w.on=="function")}function _(w){return!(!w||b(w)||typeof w.pipeThrough!="function"||typeof w.getReader!="function"||typeof w.cancel!="function")}function S(w){return!(!w||b(w)||typeof w.getWriter!="function"||typeof w.abort!="function")}function U(w){return!(!w||b(w)||typeof w.readable!="object"||typeof w.writable!="object")}function D(w){if(!b(w))return null;let M=w._writableState,R=w._readableState,nt=M||R;return!!(w.destroyed||w[s]||nt!=null&&nt.destroyed)}function G(w){if(!g(w))return null;if(w.writableEnded===!0)return!0;let M=w._writableState;return(M==null||!M.errored)&&(typeof(M==null?void 0:M.ended)!="boolean"?null:M.ended)}function $(w,M){if(!f(w))return null;let R=w._readableState;return(R==null||!R.errored)&&(typeof(R==null?void 0:R.endEmitted)!="boolean"?null:!!(R.endEmitted||M===!1&&R.ended===!0&&R.length===0))}function X(w){return w&&w[a]!=null?w[a]:typeof(w==null?void 0:w.readable)!="boolean"?null:!D(w)&&f(w)&&w.readable&&!$(w)}function B(w){return typeof(w==null?void 0:w.writable)!="boolean"?null:!D(w)&&g(w)&&w.writable&&!G(w)}function A(w){return typeof w._closed=="boolean"&&typeof w._defaultKeepAlive=="boolean"&&typeof w._removedConnection=="boolean"&&typeof w._removedContLen=="boolean"}function E(w){return typeof w._sent100=="boolean"&&A(w)}e.exports={kDestroyed:s,isDisturbed:function(w){var M;return!(!w||!((M=w[u])!==null&&M!==void 0?M:w.readableDidRead||w.readableAborted))},kIsDisturbed:u,isErrored:function(w){var M,R,nt,st,Z,j,N,F,it,J;return!(!w||!((M=(R=(nt=(st=(Z=(j=w[i])!==null&&j!==void 0?j:w.readableErrored)!==null&&Z!==void 0?Z:w.writableErrored)!==null&&st!==void 0?st:(N=w._readableState)===null||N===void 0?void 0:N.errorEmitted)!==null&&nt!==void 0?nt:(F=w._writableState)===null||F===void 0?void 0:F.errorEmitted)!==null&&R!==void 0?R:(it=w._readableState)===null||it===void 0?void 0:it.errored)!==null&&M!==void 0?M:(J=w._writableState)!==null&&J!==void 0&&J.errored))},kIsErrored:i,isReadable:X,kIsReadable:a,kIsClosedPromise:c,kControllerErrorFunction:d,isClosed:function(w){if(!b(w))return null;if(typeof w.closed=="boolean")return w.closed;let M=w._writableState,R=w._readableState;return typeof(M==null?void 0:M.closed)=="boolean"||typeof(R==null?void 0:R.closed)=="boolean"?(M==null?void 0:M.closed)||(R==null?void 0:R.closed):typeof w._closed=="boolean"&&A(w)?w._closed:null},isDestroyed:D,isDuplexNodeStream:function(w){return!(!w||typeof w.pipe!="function"||!w._readableState||typeof w.on!="function"||typeof w.write!="function")},isFinished:function(w,M){return b(w)?!!D(w)||!((M==null?void 0:M.readable)!==!1&&X(w)||(M==null?void 0:M.writable)!==!1&&B(w)):null},isIterable:function(w,M){return w!=null&&(M===!0?typeof w[n]=="function":M===!1?typeof w[l]=="function":typeof w[n]=="function"||typeof w[l]=="function")},isReadableNodeStream:f,isReadableStream:_,isReadableEnded:function(w){if(!f(w))return null;if(w.readableEnded===!0)return!0;let M=w._readableState;return!(!M||M.errored)&&(typeof(M==null?void 0:M.ended)!="boolean"?null:M.ended)},isReadableFinished:$,isReadableErrored:function(w){var M,R;return b(w)?w.readableErrored?w.readableErrored:(M=(R=w._readableState)===null||R===void 0?void 0:R.errored)!==null&&M!==void 0?M:null:null},isNodeStream:b,isWebStream:function(w){return _(w)||S(w)||U(w)},isWritable:B,isWritableNodeStream:g,isWritableStream:S,isWritableEnded:G,isWritableFinished:function(w,M){if(!g(w))return null;if(w.writableFinished===!0)return!0;let R=w._writableState;return(R==null||!R.errored)&&(typeof(R==null?void 0:R.finished)!="boolean"?null:!!(R.finished||M===!1&&R.ended===!0&&R.length===0))},isWritableErrored:function(w){var M,R;return b(w)?w.writableErrored?w.writableErrored:(M=(R=w._writableState)===null||R===void 0?void 0:R.errored)!==null&&M!==void 0?M:null:null},isServerRequest:function(w){var M;return typeof w._consuming=="boolean"&&typeof w._dumped=="boolean"&&((M=w.req)===null||M===void 0?void 0:M.upgradeOrConnect)===void 0},isServerResponse:E,willEmitClose:function(w){if(!b(w))return null;let M=w._writableState,R=w._readableState,nt=M||R;return!nt&&E(w)||!!(nt&&nt.autoDestroy&&nt.emitClose&&nt.closed===!1)},isTransformStream:U}}),Dr=mt((t,e)=>{ht(),pt(),ft();var r=pn(),{AbortError:n,codes:l}=we(),{ERR_INVALID_ARG_TYPE:o,ERR_STREAM_PREMATURE_CLOSE:s}=l,{kEmptyObject:i,once:a}=fr(),{validateAbortSignal:u,validateFunction:c,validateObject:d,validateBoolean:f}=Po(),{Promise:g,PromisePrototypeThen:b}=Xt(),{isClosed:_,isReadable:S,isReadableNodeStream:U,isReadableStream:D,isReadableFinished:G,isReadableErrored:$,isWritable:X,isWritableNodeStream:B,isWritableStream:A,isWritableFinished:E,isWritableErrored:w,isNodeStream:M,willEmitClose:R,kIsClosedPromise:nt}=yr(),st=()=>{};function Z(j,N,F){var it,J;if(arguments.length===2?(F=N,N=i):N==null?N=i:d(N,"options"),c(F,"callback"),u(N.signal,"options.signal"),F=a(F),D(j)||A(j))return function(p,v,O){let x=!1,I=st;if(v.signal)if(I=()=>{x=!0,O.call(p,new n(void 0,{cause:v.signal.reason}))},v.signal.aborted)r.nextTick(I);else{let y=O;O=a((...T)=>{v.signal.removeEventListener("abort",I),y.apply(p,T)}),v.signal.addEventListener("abort",I)}let m=(...y)=>{x||r.nextTick(()=>O.apply(p,y))};return b(p[nt].promise,m,m),st}(j,N,F);if(!M(j))throw new o("stream",["ReadableStream","WritableStream","Stream"],j);let Y=(it=N.readable)!==null&&it!==void 0?it:U(j),k=(J=N.writable)!==null&&J!==void 0?J:B(j),q=j._writableState,et=j._readableState,ot=()=>{j.writable||dt()},at=R(j)&&U(j)===Y&&B(j)===k,ut=E(j,!1),dt=()=>{ut=!0,j.destroyed&&(at=!1),(!at||j.readable&&!Y)&&(!Y||H)&&F.call(j)},H=G(j,!1),rt=()=>{H=!0,j.destroyed&&(at=!1),(!at||j.writable&&!k)&&(!k||ut)&&F.call(j)},tt=p=>{F.call(j,p)},P=_(j),W=()=>{P=!0;let p=w(j)||$(j);return p&&typeof p!="boolean"?F.call(j,p):Y&&!H&&U(j,!0)&&!G(j,!1)?F.call(j,new s):!k||ut||E(j,!1)?void F.call(j):F.call(j,new s)},C=()=>{P=!0;let p=w(j)||$(j);if(p&&typeof p!="boolean")return F.call(j,p);F.call(j)},Q=()=>{j.req.on("finish",dt)};(function(p){return p.setHeader&&typeof p.abort=="function"})(j)?(j.on("complete",dt),at||j.on("abort",W),j.req?Q():j.on("request",Q)):k&&!q&&(j.on("end",ot),j.on("close",ot)),!at&&typeof j.aborted=="boolean"&&j.on("aborted",W),j.on("end",rt),j.on("finish",dt),N.error!==!1&&j.on("error",tt),j.on("close",W),P?r.nextTick(W):q!=null&&q.errorEmitted||et!=null&&et.errorEmitted?at||r.nextTick(C):(!Y&&(!at||S(j))&&(ut||X(j)===!1)||!k&&(!at||X(j))&&(H||S(j)===!1)||et&&j.req&&j.aborted)&&r.nextTick(C);let h=()=>{F=st,j.removeListener("aborted",W),j.removeListener("complete",dt),j.removeListener("abort",W),j.removeListener("request",Q),j.req&&j.req.removeListener("finish",dt),j.removeListener("end",ot),j.removeListener("close",ot),j.removeListener("finish",dt),j.removeListener("end",rt),j.removeListener("error",tt),j.removeListener("close",W)};if(N.signal&&!P){let p=()=>{let v=F;h(),v.call(j,new n(void 0,{cause:N.signal.reason}))};if(N.signal.aborted)r.nextTick(p);else{let v=F;F=a((...O)=>{N.signal.removeEventListener("abort",p),v.apply(j,O)}),N.signal.addEventListener("abort",p)}}return h}e.exports=Z,e.exports.finished=function(j,N){var F;let it=!1;return N===null&&(N=i),(F=N)!==null&&F!==void 0&&F.cleanup&&(f(N.cleanup,"cleanup"),it=N.cleanup),new g((J,Y)=>{let k=Z(j,N,q=>{it&&k(),q?Y(q):J()})})}}),jn=mt((t,e)=>{ht(),pt(),ft();var r=pn(),{aggregateTwoErrors:n,codes:{ERR_MULTIPLE_CALLBACK:l},AbortError:o}=we(),{Symbol:s}=Xt(),{kDestroyed:i,isDestroyed:a,isFinished:u,isServerRequest:c}=yr(),d=s("kDestroy"),f=s("kConstruct");function g(E,w,M){E&&(E.stack,w&&!w.errored&&(w.errored=E),M&&!M.errored&&(M.errored=E))}function b(E,w,M){let R=!1;function nt(st){if(R)return;R=!0;let Z=E._readableState,j=E._writableState;g(st,j,Z),j&&(j.closed=!0),Z&&(Z.closed=!0),typeof M=="function"&&M(st),st?r.nextTick(_,E,st):r.nextTick(S,E)}try{E._destroy(w||null,nt)}catch(st){nt(st)}}function _(E,w){U(E,w),S(E)}function S(E){let w=E._readableState,M=E._writableState;M&&(M.closeEmitted=!0),w&&(w.closeEmitted=!0),(M!=null&&M.emitClose||w!=null&&w.emitClose)&&E.emit("close")}function U(E,w){let M=E._readableState,R=E._writableState;R!=null&&R.errorEmitted||M!=null&&M.errorEmitted||(R&&(R.errorEmitted=!0),M&&(M.errorEmitted=!0),E.emit("error",w))}function D(E,w,M){let R=E._readableState,nt=E._writableState;if(nt!=null&&nt.destroyed||R!=null&&R.destroyed)return this;R!=null&&R.autoDestroy||nt!=null&&nt.autoDestroy?E.destroy(w):w&&(w.stack,nt&&!nt.errored&&(nt.errored=w),R&&!R.errored&&(R.errored=w),M?r.nextTick(U,E,w):U(E,w))}function G(E){let w=!1;function M(R){if(w)return void D(E,R??new l);w=!0;let nt=E._readableState,st=E._writableState,Z=st||nt;nt&&(nt.constructed=!0),st&&(st.constructed=!0),Z.destroyed?E.emit(d,R):R?D(E,R,!0):r.nextTick($,E)}try{E._construct(R=>{r.nextTick(M,R)})}catch(R){r.nextTick(M,R)}}function $(E){E.emit(f)}function X(E){return(E==null?void 0:E.setHeader)&&typeof E.abort=="function"}function B(E){E.emit("close")}function A(E,w){E.emit("error",w),r.nextTick(B,E)}e.exports={construct:function(E,w){if(typeof E._construct!="function")return;let M=E._readableState,R=E._writableState;M&&(M.constructed=!1),R&&(R.constructed=!1),E.once(f,w),!(E.listenerCount(f)>1)&&r.nextTick(G,E)},destroyer:function(E,w){!E||a(E)||(!w&&!u(E)&&(w=new o),c(E)?(E.socket=null,E.destroy(w)):X(E)?E.abort():X(E.req)?E.req.abort():typeof E.destroy=="function"?E.destroy(w):typeof E.close=="function"?E.close():w?r.nextTick(A,E,w):r.nextTick(B,E),E.destroyed||(E[i]=!0))},destroy:function(E,w){let M=this._readableState,R=this._writableState,nt=R||M;return R!=null&&R.destroyed||M!=null&&M.destroyed?(typeof w=="function"&&w(),this):(g(E,R,M),R&&(R.destroyed=!0),M&&(M.destroyed=!0),nt.constructed?b(this,E,w):this.once(d,function(st){b(this,n(st,E),w)}),this)},undestroy:function(){let E=this._readableState,w=this._writableState;E&&(E.constructed=!0,E.closed=!1,E.closeEmitted=!1,E.destroyed=!1,E.errored=null,E.errorEmitted=!1,E.reading=!1,E.ended=E.readable===!1,E.endEmitted=E.readable===!1),w&&(w.constructed=!0,w.destroyed=!1,w.closed=!1,w.closeEmitted=!1,w.errored=null,w.errorEmitted=!1,w.finalCalled=!1,w.prefinished=!1,w.ended=w.writable===!1,w.ending=w.writable===!1,w.finished=w.writable===!1)},errorOrDestroy:D}});function Lt(){Lt.init.call(this)}function Yi(t){if(typeof t!="function")throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof t)}function ep(t){return t._maxListeners===void 0?Lt.defaultMaxListeners:t._maxListeners}function ic(t,e,r,n){var l,o,s;if(Yi(r),(o=t._events)===void 0?(o=t._events=Object.create(null),t._eventsCount=0):(o.newListener!==void 0&&(t.emit("newListener",e,r.listener?r.listener:r),o=t._events),s=o[e]),s===void 0)s=o[e]=r,++t._eventsCount;else if(typeof s=="function"?s=o[e]=n?[r,s]:[s,r]:n?s.unshift(r):s.push(r),(l=ep(t))>0&&s.length>l&&!s.warned){s.warned=!0;var i=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");i.name="MaxListenersExceededWarning",i.emitter=t,i.type=e,i.count=s.length}return t}function t0(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function oc(t,e,r){var n={fired:!1,wrapFn:void 0,target:t,type:e,listener:r},l=t0.bind(n);return l.listener=r,n.wrapFn=l,l}function sc(t,e,r){var n=t._events;if(n===void 0)return[];var l=n[e];return l===void 0?[]:typeof l=="function"?r?[l.listener||l]:[l]:r?function(o){for(var s=new Array(o.length),i=0;i{ht(),pt(),ft(),Hr=typeof Reflect=="object"?Reflect:null,cs=Hr&&typeof Hr.apply=="function"?Hr.apply:function(t,e,r){return Function.prototype.apply.call(t,e,r)},uc=Hr&&typeof Hr.ownKeys=="function"?Hr.ownKeys:Object.getOwnPropertySymbols?function(t){return Object.getOwnPropertyNames(t).concat(Object.getOwnPropertySymbols(t))}:function(t){return Object.getOwnPropertyNames(t)},hs=Number.isNaN||function(t){return t!=t},lc=Lt,Lt.EventEmitter=Lt,Lt.prototype._events=void 0,Lt.prototype._eventsCount=0,Lt.prototype._maxListeners=void 0,fs=10,Object.defineProperty(Lt,"defaultMaxListeners",{enumerable:!0,get:function(){return fs},set:function(t){if(typeof t!="number"||t<0||hs(t))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+t+".");fs=t}}),Lt.init=function(){this._events!==void 0&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},Lt.prototype.setMaxListeners=function(t){if(typeof t!="number"||t<0||hs(t))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+t+".");return this._maxListeners=t,this},Lt.prototype.getMaxListeners=function(){return ep(this)},Lt.prototype.emit=function(t){for(var e=[],r=1;r0&&(o=e[0]),o instanceof Error)throw o;var s=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw s.context=o,s}var i=l[t];if(i===void 0)return!1;if(typeof i=="function")cs(i,this,e);else{var a=i.length,u=rp(i,a);for(r=0;r=0;o--)if(r[o]===e||r[o].listener===e){s=r[o].listener,l=o;break}if(l<0)return this;l===0?r.shift():function(i,a){for(;a+1=0;n--)this.removeListener(t,e[n]);return this},Lt.prototype.listeners=function(t){return sc(this,t,!0)},Lt.prototype.rawListeners=function(t){return sc(this,t,!1)},Lt.listenerCount=function(t,e){return typeof t.listenerCount=="function"?t.listenerCount(e):ac.call(t,e)},Lt.prototype.listenerCount=ac,Lt.prototype.eventNames=function(){return this._eventsCount>0?uc(this._events):[]},(Ie=lc).EventEmitter,Ie.defaultMaxListeners,Ie.init,Ie.listenerCount,Ie.EventEmitter,Ie.defaultMaxListeners,Ie.init,Ie.listenerCount}),dn={};fn(dn,{EventEmitter:()=>np,default:()=>Ie,defaultMaxListeners:()=>ip,init:()=>op,listenerCount:()=>sp,on:()=>ap,once:()=>lp});var np,ip,op,sp,ap,lp,Un=ne(()=>{ht(),pt(),ft(),cc(),cc(),Ie.once=function(t,e){return new Promise((r,n)=>{function l(...s){o!==void 0&&t.removeListener("error",o),r(s)}let o;e!=="error"&&(o=s=>{t.removeListener(name,l),n(s)},t.once("error",o)),t.once(e,l)})},Ie.on=function(t,e){let r=[],n=[],l=null,o=!1,s={async next(){let u=r.shift();if(u)return createIterResult(u,!1);if(l){let c=Promise.reject(l);return l=null,c}return o?createIterResult(void 0,!0):new Promise((c,d)=>n.push({resolve:c,reject:d}))},async return(){t.removeListener(e,i),t.removeListener("error",a),o=!0;for(let u of n)u.resolve(createIterResult(void 0,!0));return createIterResult(void 0,!0)},throw(u){l=u,t.removeListener(e,i),t.removeListener("error",a)},[Symbol.asyncIterator](){return this}};return t.on(e,i),t.on("error",a),s;function i(...u){let c=n.shift();c?c.resolve(createIterResult(u,!1)):r.push(u)}function a(u){o=!0;let c=n.shift();c?c.reject(u):l=u,s.return()}},{EventEmitter:np,defaultMaxListeners:ip,init:op,listenerCount:sp,on:ap,once:lp}=Ie}),Rl=mt((t,e)=>{ht(),pt(),ft();var{ArrayIsArray:r,ObjectSetPrototypeOf:n}=Xt(),{EventEmitter:l}=(Un(),Dt(dn));function o(i){l.call(this,i)}function s(i,a,u){if(typeof i.prependListener=="function")return i.prependListener(a,u);i._events&&i._events[a]?r(i._events[a])?i._events[a].unshift(u):i._events[a]=[u,i._events[a]]:i.on(a,u)}n(o.prototype,l.prototype),n(o,l),o.prototype.pipe=function(i,a){let u=this;function c(U){i.writable&&i.write(U)===!1&&u.pause&&u.pause()}function d(){u.readable&&u.resume&&u.resume()}u.on("data",c),i.on("drain",d),!i._isStdio&&(!a||a.end!==!1)&&(u.on("end",g),u.on("close",b));let f=!1;function g(){f||(f=!0,i.end())}function b(){f||(f=!0,typeof i.destroy=="function"&&i.destroy())}function _(U){S(),l.listenerCount(this,"error")===0&&this.emit("error",U)}function S(){u.removeListener("data",c),i.removeListener("drain",d),u.removeListener("end",g),u.removeListener("close",b),u.removeListener("error",_),i.removeListener("error",_),u.removeListener("end",S),u.removeListener("close",S),i.removeListener("close",S)}return s(u,"error",_),s(i,"error",_),u.on("end",S),u.on("close",S),i.on("close",S),i.emit("pipe",u),i},e.exports={Stream:o,prependListener:s}}),Bo=mt((t,e)=>{ht(),pt(),ft();var{AbortError:r,codes:n}=we(),{isNodeStream:l,isWebStream:o,kControllerErrorFunction:s}=yr(),i=Dr(),{ERR_INVALID_ARG_TYPE:a}=n;e.exports.addAbortSignal=function(u,c){if(((d,f)=>{if(typeof d!="object"||!("aborted"in d))throw new a(f,"AbortSignal",d)})(u,"signal"),!l(c)&&!o(c))throw new a("stream",["ReadableStream","WritableStream","Stream"],c);return e.exports.addAbortSignalNoValidate(u,c)},e.exports.addAbortSignalNoValidate=function(u,c){if(typeof u!="object"||!("aborted"in u))return c;let d=l(c)?()=>{c.destroy(new r(void 0,{cause:u.reason}))}:()=>{c[s](new r(void 0,{cause:u.reason}))};return u.aborted?d():(u.addEventListener("abort",d),i(c,()=>u.removeEventListener("abort",d))),c}}),e0=mt((t,e)=>{ht(),pt(),ft();var{StringPrototypeSlice:r,SymbolIterator:n,TypedArrayPrototypeSet:l,Uint8Array:o}=Xt(),{Buffer:s}=(oe(),Dt(ie)),{inspect:i}=fr();e.exports=class{constructor(){this.head=null,this.tail=null,this.length=0}push(a){let u={data:a,next:null};this.length>0?this.tail.next=u:this.head=u,this.tail=u,++this.length}unshift(a){let u={data:a,next:this.head};this.length===0&&(this.tail=u),this.head=u,++this.length}shift(){if(this.length===0)return;let a=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,a}clear(){this.head=this.tail=null,this.length=0}join(a){if(this.length===0)return"";let u=this.head,c=""+u.data;for(;(u=u.next)!==null;)c+=a+u.data;return c}concat(a){if(this.length===0)return s.alloc(0);let u=s.allocUnsafe(a>>>0),c=this.head,d=0;for(;c;)l(u,c.data,d),d+=c.data.length,c=c.next;return u}consume(a,u){let c=this.head.data;if(af.length)){a===f.length?(u+=f,++d,c.next?this.head=c.next:this.head=this.tail=null):(u+=r(f,0,a),this.head=c,c.data=r(f,a));break}u+=f,a-=f.length,++d}while((c=c.next)!==null);return this.length-=d,u}_getBuffer(a){let u=s.allocUnsafe(a),c=a,d=this.head,f=0;do{let g=d.data;if(!(a>g.length)){a===g.length?(l(u,g,c-a),++f,d.next?this.head=d.next:this.head=this.tail=null):(l(u,new o(g.buffer,g.byteOffset,a),c-a),this.head=d,d.data=g.slice(a));break}l(u,g,c-a),a-=g.length,++f}while((d=d.next)!==null);return this.length-=f,u}[Symbol.for("nodejs.util.inspect.custom")](a,u){return i(this,{...u,depth:0,customInspect:!1})}}}),Pl=mt((t,e)=>{ht(),pt(),ft();var{MathFloor:r,NumberIsInteger:n}=Xt(),{ERR_INVALID_ARG_VALUE:l}=we().codes;function o(s){return s?16:16384}e.exports={getHighWaterMark:function(s,i,a,u){let c=function(d,f,g){return d.highWaterMark!=null?d.highWaterMark:f?d[g]:null}(i,u,a);if(c!=null){if(!n(c)||c<0)throw new l(u?`options.${a}`:"options.highWaterMark",c);return r(c)}return o(s.objectMode)},getDefaultHighWaterMark:o}});function hc(t){var e=t.length;if(e%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var r=t.indexOf("=");return r===-1&&(r=e),[r,r===e?0:4-r%4]}function r0(t,e,r){for(var n,l,o=[],s=e;s>18&63]+je[l>>12&63]+je[l>>6&63]+je[63&l]);return o.join("")}function ur(t){if(t>2147483647)throw new RangeError('The value "'+t+'" is invalid for option "size"');var e=new Uint8Array(t);return Object.setPrototypeOf(e,gt.prototype),e}function gt(t,e,r){if(typeof t=="number"){if(typeof e=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return Da(t)}return up(t,e,r)}function up(t,e,r){if(typeof t=="string")return function(o,s){if(typeof s=="string"&&s!==""||(s="utf8"),!gt.isEncoding(s))throw new TypeError("Unknown encoding: "+s);var i=0|hp(o,s),a=ur(i),u=a.write(o,s);return u!==i&&(a=a.slice(0,u)),a}(t,e);if(ArrayBuffer.isView(t))return ps(t);if(t==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof t);if(cr(t,ArrayBuffer)||t&&cr(t.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(cr(t,SharedArrayBuffer)||t&&cr(t.buffer,SharedArrayBuffer)))return function(o,s,i){if(s<0||o.byteLength=2147483647)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x7fffffff bytes");return 0|t}function hp(t,e){if(gt.isBuffer(t))return t.length;if(ArrayBuffer.isView(t)||cr(t,ArrayBuffer))return t.byteLength;if(typeof t!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof t);var r=t.length,n=arguments.length>2&&arguments[2]===!0;if(!n&&r===0)return 0;for(var l=!1;;)switch(e){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":return Fa(t).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return gp(t).length;default:if(l)return n?-1:Fa(t).length;e=(""+e).toLowerCase(),l=!0}}function n0(t,e,r){var n=!1;if((e===void 0||e<0)&&(e=0),e>this.length||((r===void 0||r>this.length)&&(r=this.length),r<=0)||(r>>>=0)<=(e>>>=0))return"";for(t||(t="utf8");;)switch(t){case"hex":return f0(this,e,r);case"utf8":case"utf-8":return pp(this,e,r);case"ascii":return c0(this,e,r);case"latin1":case"binary":return h0(this,e,r);case"base64":return u0(this,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return p0(this,e,r);default:if(n)throw new TypeError("Unknown encoding: "+t);t=(t+"").toLowerCase(),n=!0}}function zr(t,e,r){var n=t[e];t[e]=t[r],t[r]=n}function fc(t,e,r,n,l){if(t.length===0)return-1;if(typeof r=="string"?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),Ml(r=+r)&&(r=l?0:t.length-1),r<0&&(r=t.length+r),r>=t.length){if(l)return-1;r=t.length-1}else if(r<0){if(!l)return-1;r=0}if(typeof e=="string"&&(e=gt.from(e,n)),gt.isBuffer(e))return e.length===0?-1:pc(t,e,r,n,l);if(typeof e=="number")return e&=255,typeof Uint8Array.prototype.indexOf=="function"?l?Uint8Array.prototype.indexOf.call(t,e,r):Uint8Array.prototype.lastIndexOf.call(t,e,r):pc(t,[e],r,n,l);throw new TypeError("val must be string, number or Buffer")}function pc(t,e,r,n,l){var o,s=1,i=t.length,a=e.length;if(n!==void 0&&((n=String(n).toLowerCase())==="ucs2"||n==="ucs-2"||n==="utf16le"||n==="utf-16le")){if(t.length<2||e.length<2)return-1;s=2,i/=2,a/=2,r/=2}function u(g,b){return s===1?g[b]:g.readUInt16BE(b*s)}if(l){var c=-1;for(o=r;oi&&(r=i-a),o=r;o>=0;o--){for(var d=!0,f=0;fl&&(n=l):n=l;var o=e.length;n>o/2&&(n=o/2);for(var s=0;s>8,a=s%256,u.push(a),u.push(i);return u}(e,t.length-r),t,r,n)}function u0(t,e,r){return e===0&&r===t.length?fo.fromByteArray(t):fo.fromByteArray(t.slice(e,r))}function pp(t,e,r){r=Math.min(t.length,r);for(var n=[],l=e;l239?4:u>223?3:u>191?2:1;if(l+d<=r)switch(d){case 1:u<128&&(c=u);break;case 2:(192&(o=t[l+1]))==128&&(a=(31&u)<<6|63&o)>127&&(c=a);break;case 3:o=t[l+1],s=t[l+2],(192&o)==128&&(192&s)==128&&(a=(15&u)<<12|(63&o)<<6|63&s)>2047&&(a<55296||a>57343)&&(c=a);break;case 4:o=t[l+1],s=t[l+2],i=t[l+3],(192&o)==128&&(192&s)==128&&(192&i)==128&&(a=(15&u)<<18|(63&o)<<12|(63&s)<<6|63&i)>65535&&a<1114112&&(c=a)}c===null?(c=65533,d=1):c>65535&&(c-=65536,n.push(c>>>10&1023|55296),c=56320|1023&c),n.push(c),l+=d}return function(f){var g=f.length;if(g<=4096)return String.fromCharCode.apply(String,f);for(var b="",_=0;_n)&&(r=n);for(var l="",o=e;or)throw new RangeError("Trying to access beyond buffer length")}function ge(t,e,r,n,l,o){if(!gt.isBuffer(t))throw new TypeError('"buffer" argument must be a Buffer instance');if(e>l||et.length)throw new RangeError("Index out of range")}function dp(t,e,r,n,l,o){if(r+n>t.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function dc(t,e,r,n,l){return e=+e,r>>>=0,l||dp(t,0,r,4),tn.write(t,e,r,n,23,4),r+4}function gc(t,e,r,n,l){return e=+e,r>>>=0,l||dp(t,0,r,8),tn.write(t,e,r,n,52,8),r+8}function Fa(t,e){var r;e=e||1/0;for(var n=t.length,l=null,o=[],s=0;s55295&&r<57344){if(!l){if(r>56319){(e-=3)>-1&&o.push(239,191,189);continue}if(s+1===n){(e-=3)>-1&&o.push(239,191,189);continue}l=r;continue}if(r<56320){(e-=3)>-1&&o.push(239,191,189),l=r;continue}r=65536+(l-55296<<10|r-56320)}else l&&(e-=3)>-1&&o.push(239,191,189);if(l=null,r<128){if((e-=1)<0)break;o.push(r)}else if(r<2048){if((e-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((e-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((e-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function gp(t){return fo.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(mp,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(t))}function Mo(t,e,r,n){for(var l=0;l=e.length||l>=t.length);++l)e[l+r]=t[l];return l}function cr(t,e){return t instanceof e||t!=null&&t.constructor!=null&&t.constructor.name!=null&&t.constructor.name===e.name}function Ml(t){return t!=t}function mc(t,e){for(var r in t)e[r]=t[r]}function Kr(t,e,r){return $e(t,e,r)}function qn(t){var e;switch(this.encoding=function(r){var n=function(l){if(!l)return"utf8";for(var o;;)switch(l){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return l;default:if(o)return;l=(""+l).toLowerCase(),o=!0}}(r);if(typeof n!="string"&&(po.isEncoding===Wa||!Wa(r)))throw new Error("Unknown encoding: "+r);return n||r}(t),this.encoding){case"utf16le":this.text=g0,this.end=m0,e=4;break;case"utf8":this.fillLast=d0,e=4;break;case"base64":this.text=y0,this.end=b0,e=3;break;default:return this.write=v0,void(this.end=w0)}this.lastNeed=0,this.lastTotal=0,this.lastChar=po.allocUnsafe(e)}function ds(t){return t<=127?0:t>>5==6?2:t>>4==14?3:t>>3==30?4:t>>6==2?-1:-2}function d0(t){var e=this.lastTotal-this.lastNeed,r=function(n,l){if((192&l[0])!=128)return n.lastNeed=0,"�";if(n.lastNeed>1&&l.length>1){if((192&l[1])!=128)return n.lastNeed=1,"�";if(n.lastNeed>2&&l.length>2&&(192&l[2])!=128)return n.lastNeed=2,"�"}}(this,t);return r!==void 0?r:this.lastNeed<=t.length?(t.copy(this.lastChar,e,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(t.copy(this.lastChar,e,0,t.length),void(this.lastNeed-=t.length))}function g0(t,e){if((t.length-e)%2==0){var r=t.toString("utf16le",e);if(r){var n=r.charCodeAt(r.length-1);if(n>=55296&&n<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1],r.slice(0,-1)}return r}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=t[t.length-1],t.toString("utf16le",e,t.length-1)}function m0(t){var e=t&&t.length?this.write(t):"";if(this.lastNeed){var r=this.lastTotal-this.lastNeed;return e+this.lastChar.toString("utf16le",0,r)}return e}function y0(t,e){var r=(t.length-e)%3;return r===0?t.toString("base64",e):(this.lastNeed=3-r,this.lastTotal=3,r===1?this.lastChar[0]=t[t.length-1]:(this.lastChar[0]=t[t.length-2],this.lastChar[1]=t[t.length-1]),t.toString("base64",e,t.length-r))}function b0(t){var e=t&&t.length?this.write(t):"";return this.lastNeed?e+this.lastChar.toString("base64",0,3-this.lastNeed):e}function v0(t){return t.toString(this.encoding)}function w0(t){return t&&t.length?this.write(t):""}var yc,je,Ee,bc,gs,Gr,vc,We,fo,tn,ms,mp,yp,Hn,Mi,$e,An,po,Wa,wc=ne(()=>{for(ht(),pt(),ft(),yc={byteLength:function(t){var e=hc(t),r=e[0],n=e[1];return 3*(r+n)/4-n},toByteArray:function(t){var e,r,n=hc(t),l=n[0],o=n[1],s=new bc(function(u,c,d){return 3*(c+d)/4-d}(0,l,o)),i=0,a=o>0?l-4:l;for(r=0;r>16&255,s[i++]=e>>8&255,s[i++]=255&e;return o===2&&(e=Ee[t.charCodeAt(r)]<<2|Ee[t.charCodeAt(r+1)]>>4,s[i++]=255&e),o===1&&(e=Ee[t.charCodeAt(r)]<<10|Ee[t.charCodeAt(r+1)]<<4|Ee[t.charCodeAt(r+2)]>>2,s[i++]=e>>8&255,s[i++]=255&e),s},fromByteArray:function(t){for(var e,r=t.length,n=r%3,l=[],o=0,s=r-n;os?s:o+16383));return n===1?(e=t[r-1],l.push(je[e>>2]+je[e<<4&63]+"==")):n===2&&(e=(t[r-2]<<8)+t[r-1],l.push(je[e>>10]+je[e>>4&63]+je[e<<2&63]+"=")),l.join("")}},je=[],Ee=[],bc=typeof Uint8Array<"u"?Uint8Array:Array,gs="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",Gr=0,64;Gr<64;++Gr)je[Gr]=gs[Gr],Ee[gs.charCodeAt(Gr)]=Gr;Ee[45]=62,Ee[95]=63,vc={read:function(t,e,r,n,l){var o,s,i=8*l-n-1,a=(1<>1,c=-7,d=r?l-1:0,f=r?-1:1,g=t[e+d];for(d+=f,o=g&(1<<-c)-1,g>>=-c,c+=i;c>0;o=256*o+t[e+d],d+=f,c-=8);for(s=o&(1<<-c)-1,o>>=-c,c+=n;c>0;s=256*s+t[e+d],d+=f,c-=8);if(o===0)o=1-u;else{if(o===a)return s?NaN:1/0*(g?-1:1);s+=Math.pow(2,n),o-=u}return(g?-1:1)*s*Math.pow(2,o-n)},write:function(t,e,r,n,l,o){var s,i,a,u=8*o-l-1,c=(1<>1,f=l===23?Math.pow(2,-24)-Math.pow(2,-77):0,g=n?0:o-1,b=n?1:-1,_=e<0||e===0&&1/e<0?1:0;for(e=Math.abs(e),isNaN(e)||e===1/0?(i=isNaN(e)?1:0,s=c):(s=Math.floor(Math.log(e)/Math.LN2),e*(a=Math.pow(2,-s))<1&&(s--,a*=2),(e+=s+d>=1?f/a:f*Math.pow(2,1-d))*a>=2&&(s++,a/=2),s+d>=c?(i=0,s=c):s+d>=1?(i=(e*a-1)*Math.pow(2,l),s+=d):(i=e*Math.pow(2,d-1)*Math.pow(2,l),s=0));l>=8;t[r+g]=255&i,g+=b,i/=256,l-=8);for(s=s<0;t[r+g]=255&s,g+=b,s/=256,u-=8);t[r+g-b]|=128*_}},We={},fo=yc,tn=vc,ms=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null,We.Buffer=gt,We.SlowBuffer=function(t){return+t!=t&&(t=0),gt.alloc(+t)},We.INSPECT_MAX_BYTES=50,We.kMaxLength=2147483647,gt.TYPED_ARRAY_SUPPORT=function(){try{var t=new Uint8Array(1),e={foo:function(){return 42}};return Object.setPrototypeOf(e,Uint8Array.prototype),Object.setPrototypeOf(t,e),t.foo()===42}catch{return!1}}(),gt.TYPED_ARRAY_SUPPORT||typeof console>"u",Object.defineProperty(gt.prototype,"parent",{enumerable:!0,get:function(){if(gt.isBuffer(this))return this.buffer}}),Object.defineProperty(gt.prototype,"offset",{enumerable:!0,get:function(){if(gt.isBuffer(this))return this.byteOffset}}),gt.poolSize=8192,gt.from=function(t,e,r){return up(t,e,r)},Object.setPrototypeOf(gt.prototype,Uint8Array.prototype),Object.setPrototypeOf(gt,Uint8Array),gt.alloc=function(t,e,r){return l=e,o=r,cp(n=t),n<=0?ur(n):l!==void 0?typeof o=="string"?ur(n).fill(l,o):ur(n).fill(l):ur(n);var n,l,o},gt.allocUnsafe=function(t){return Da(t)},gt.allocUnsafeSlow=function(t){return Da(t)},gt.isBuffer=function(t){return t!=null&&t._isBuffer===!0&&t!==gt.prototype},gt.compare=function(t,e){if(cr(t,Uint8Array)&&(t=gt.from(t,t.offset,t.byteLength)),cr(e,Uint8Array)&&(e=gt.from(e,e.offset,e.byteLength)),!gt.isBuffer(t)||!gt.isBuffer(e))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(t===e)return 0;for(var r=t.length,n=e.length,l=0,o=Math.min(r,n);le&&(t+=" ... "),""},ms&&(gt.prototype[ms]=gt.prototype.inspect),gt.prototype.compare=function(t,e,r,n,l){if(cr(t,Uint8Array)&&(t=gt.from(t,t.offset,t.byteLength)),!gt.isBuffer(t))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof t);if(e===void 0&&(e=0),r===void 0&&(r=t?t.length:0),n===void 0&&(n=0),l===void 0&&(l=this.length),e<0||r>t.length||n<0||l>this.length)throw new RangeError("out of range index");if(n>=l&&e>=r)return 0;if(n>=l)return-1;if(e>=r)return 1;if(this===t)return 0;for(var o=(l>>>=0)-(n>>>=0),s=(r>>>=0)-(e>>>=0),i=Math.min(o,s),a=this.slice(n,l),u=t.slice(e,r),c=0;c>>=0,isFinite(r)?(r>>>=0,n===void 0&&(n="utf8")):(n=r,r=void 0)}var l=this.length-e;if((r===void 0||r>l)&&(r=l),t.length>0&&(r<0||e<0)||e>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return i0(this,t,e,r);case"utf8":case"utf-8":return o0(this,t,e,r);case"ascii":return fp(this,t,e,r);case"latin1":case"binary":return s0(this,t,e,r);case"base64":return a0(this,t,e,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return l0(this,t,e,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},gt.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}},gt.prototype.slice=function(t,e){var r=this.length;(t=~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),(e=e===void 0?r:~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),e>>=0,e>>>=0,r||Zt(t,e,this.length);for(var n=this[t],l=1,o=0;++o>>=0,e>>>=0,r||Zt(t,e,this.length);for(var n=this[t+--e],l=1;e>0&&(l*=256);)n+=this[t+--e]*l;return n},gt.prototype.readUInt8=function(t,e){return t>>>=0,e||Zt(t,1,this.length),this[t]},gt.prototype.readUInt16LE=function(t,e){return t>>>=0,e||Zt(t,2,this.length),this[t]|this[t+1]<<8},gt.prototype.readUInt16BE=function(t,e){return t>>>=0,e||Zt(t,2,this.length),this[t]<<8|this[t+1]},gt.prototype.readUInt32LE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),(this[t]|this[t+1]<<8|this[t+2]<<16)+16777216*this[t+3]},gt.prototype.readUInt32BE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),16777216*this[t]+(this[t+1]<<16|this[t+2]<<8|this[t+3])},gt.prototype.readIntLE=function(t,e,r){t>>>=0,e>>>=0,r||Zt(t,e,this.length);for(var n=this[t],l=1,o=0;++o=(l*=128)&&(n-=Math.pow(2,8*e)),n},gt.prototype.readIntBE=function(t,e,r){t>>>=0,e>>>=0,r||Zt(t,e,this.length);for(var n=e,l=1,o=this[t+--n];n>0&&(l*=256);)o+=this[t+--n]*l;return o>=(l*=128)&&(o-=Math.pow(2,8*e)),o},gt.prototype.readInt8=function(t,e){return t>>>=0,e||Zt(t,1,this.length),128&this[t]?-1*(255-this[t]+1):this[t]},gt.prototype.readInt16LE=function(t,e){t>>>=0,e||Zt(t,2,this.length);var r=this[t]|this[t+1]<<8;return 32768&r?4294901760|r:r},gt.prototype.readInt16BE=function(t,e){t>>>=0,e||Zt(t,2,this.length);var r=this[t+1]|this[t]<<8;return 32768&r?4294901760|r:r},gt.prototype.readInt32LE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),this[t]|this[t+1]<<8|this[t+2]<<16|this[t+3]<<24},gt.prototype.readInt32BE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),this[t]<<24|this[t+1]<<16|this[t+2]<<8|this[t+3]},gt.prototype.readFloatLE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),tn.read(this,t,!0,23,4)},gt.prototype.readFloatBE=function(t,e){return t>>>=0,e||Zt(t,4,this.length),tn.read(this,t,!1,23,4)},gt.prototype.readDoubleLE=function(t,e){return t>>>=0,e||Zt(t,8,this.length),tn.read(this,t,!0,52,8)},gt.prototype.readDoubleBE=function(t,e){return t>>>=0,e||Zt(t,8,this.length),tn.read(this,t,!1,52,8)},gt.prototype.writeUIntLE=function(t,e,r,n){t=+t,e>>>=0,r>>>=0,n||ge(this,t,e,r,Math.pow(2,8*r)-1,0);var l=1,o=0;for(this[e]=255&t;++o>>=0,r>>>=0,n||ge(this,t,e,r,Math.pow(2,8*r)-1,0);var l=r-1,o=1;for(this[e+l]=255&t;--l>=0&&(o*=256);)this[e+l]=t/o&255;return e+r},gt.prototype.writeUInt8=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,1,255,0),this[e]=255&t,e+1},gt.prototype.writeUInt16LE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,2,65535,0),this[e]=255&t,this[e+1]=t>>>8,e+2},gt.prototype.writeUInt16BE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,2,65535,0),this[e]=t>>>8,this[e+1]=255&t,e+2},gt.prototype.writeUInt32LE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,4,4294967295,0),this[e+3]=t>>>24,this[e+2]=t>>>16,this[e+1]=t>>>8,this[e]=255&t,e+4},gt.prototype.writeUInt32BE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,4,4294967295,0),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},gt.prototype.writeIntLE=function(t,e,r,n){if(t=+t,e>>>=0,!n){var l=Math.pow(2,8*r-1);ge(this,t,e,r,l-1,-l)}var o=0,s=1,i=0;for(this[e]=255&t;++o>>=0,!n){var l=Math.pow(2,8*r-1);ge(this,t,e,r,l-1,-l)}var o=r-1,s=1,i=0;for(this[e+o]=255&t;--o>=0&&(s*=256);)t<0&&i===0&&this[e+o+1]!==0&&(i=1),this[e+o]=(t/s|0)-i&255;return e+r},gt.prototype.writeInt8=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,1,127,-128),t<0&&(t=255+t+1),this[e]=255&t,e+1},gt.prototype.writeInt16LE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,2,32767,-32768),this[e]=255&t,this[e+1]=t>>>8,e+2},gt.prototype.writeInt16BE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,2,32767,-32768),this[e]=t>>>8,this[e+1]=255&t,e+2},gt.prototype.writeInt32LE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,4,2147483647,-2147483648),this[e]=255&t,this[e+1]=t>>>8,this[e+2]=t>>>16,this[e+3]=t>>>24,e+4},gt.prototype.writeInt32BE=function(t,e,r){return t=+t,e>>>=0,r||ge(this,t,e,4,2147483647,-2147483648),t<0&&(t=4294967295+t+1),this[e]=t>>>24,this[e+1]=t>>>16,this[e+2]=t>>>8,this[e+3]=255&t,e+4},gt.prototype.writeFloatLE=function(t,e,r){return dc(this,t,e,!0,r)},gt.prototype.writeFloatBE=function(t,e,r){return dc(this,t,e,!1,r)},gt.prototype.writeDoubleLE=function(t,e,r){return gc(this,t,e,!0,r)},gt.prototype.writeDoubleBE=function(t,e,r){return gc(this,t,e,!1,r)},gt.prototype.copy=function(t,e,r,n){if(!gt.isBuffer(t))throw new TypeError("argument should be a Buffer");if(r||(r=0),n||n===0||(n=this.length),e>=t.length&&(e=t.length),e||(e=0),n>0&&n=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),t.length-e=0;--o)t[o+e]=this[o+r];else Uint8Array.prototype.set.call(t,this.subarray(r,n),e);return l},gt.prototype.fill=function(t,e,r,n){if(typeof t=="string"){if(typeof e=="string"?(n=e,e=0,r=this.length):typeof r=="string"&&(n=r,r=this.length),n!==void 0&&typeof n!="string")throw new TypeError("encoding must be a string");if(typeof n=="string"&&!gt.isEncoding(n))throw new TypeError("Unknown encoding: "+n);if(t.length===1){var l=t.charCodeAt(0);(n==="utf8"&&l<128||n==="latin1")&&(t=l)}}else typeof t=="number"?t&=255:typeof t=="boolean"&&(t=Number(t));if(e<0||this.length>>=0,r=r===void 0?this.length:r>>>0,t||(t=0),typeof t=="number")for(o=e;o=0?(a>0&&(l.lastNeed=a-1),a):--i=0?(a>0&&(l.lastNeed=a-2),a):--i=0?(a>0&&(a===2?a=0:l.lastNeed=a-3),a):0}(this,t,e);if(!this.lastNeed)return t.toString("utf8",e);this.lastTotal=r;var n=t.length-(r-this.lastNeed);return t.copy(this.lastChar,0,n),t.toString("utf8",e,n)},qn.prototype.fillLast=function(t){if(this.lastNeed<=t.length)return t.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);t.copy(this.lastChar,this.lastTotal-this.lastNeed,0,t.length),this.lastNeed-=t.length},An.StringDecoder,An.StringDecoder}),bp={};fn(bp,{StringDecoder:()=>vp,default:()=>An});var vp,_0=ne(()=>{ht(),pt(),ft(),wc(),wc(),vp=An.StringDecoder}),wp=mt((t,e)=>{ht(),pt(),ft();var r=pn(),{PromisePrototypeThen:n,SymbolAsyncIterator:l,SymbolIterator:o}=Xt(),{Buffer:s}=(oe(),Dt(ie)),{ERR_INVALID_ARG_TYPE:i,ERR_STREAM_NULL_VALUES:a}=we().codes;e.exports=function(u,c,d){let f,g;if(typeof c=="string"||c instanceof s)return new u({objectMode:!0,...d,read(){this.push(c),this.push(null)}});if(c&&c[l])g=!0,f=c[l]();else{if(!c||!c[o])throw new i("iterable",["Iterable"],c);g=!1,f=c[o]()}let b=new u({objectMode:!0,highWaterMark:1,...d}),_=!1;return b._read=function(){_||(_=!0,async function(){for(;;){try{let{value:S,done:U}=g?await f.next():f.next();if(U)b.push(null);else{let D=S&&typeof S.then=="function"?await S:S;if(D===null)throw _=!1,new a;if(b.push(D))continue;_=!1}}catch(S){b.destroy(S)}break}}())},b._destroy=function(S,U){n(async function(D){let G=D!=null,$=typeof f.throw=="function";if(G&&$){let{value:X,done:B}=await f.throw(D);if(await X,B)return}if(typeof f.return=="function"){let{value:X}=await f.return();await X}}(S),()=>r.nextTick(U,S),D=>r.nextTick(U,D||S))},b}}),Lo=mt((t,e)=>{ht(),pt(),ft();var r=pn(),{ArrayPrototypeIndexOf:n,NumberIsInteger:l,NumberIsNaN:o,NumberParseInt:s,ObjectDefineProperties:i,ObjectKeys:a,ObjectSetPrototypeOf:u,Promise:c,SafeSet:d,SymbolAsyncIterator:f,Symbol:g}=Xt();e.exports=q,q.ReadableState=k;var{EventEmitter:b}=(Un(),Dt(dn)),{Stream:_,prependListener:S}=Rl(),{Buffer:U}=(oe(),Dt(ie)),{addAbortSignal:D}=Bo(),G=Dr(),$=fr().debuglog("stream",m=>{$=m}),X=e0(),B=jn(),{getHighWaterMark:A,getDefaultHighWaterMark:E}=Pl(),{aggregateTwoErrors:w,codes:{ERR_INVALID_ARG_TYPE:M,ERR_METHOD_NOT_IMPLEMENTED:R,ERR_OUT_OF_RANGE:nt,ERR_STREAM_PUSH_AFTER_EOF:st,ERR_STREAM_UNSHIFT_AFTER_END_EVENT:Z}}=we(),{validateObject:j}=Po(),N=g("kPaused"),{StringDecoder:F}=(_0(),Dt(bp)),it=wp();u(q.prototype,_.prototype),u(q,_);var J=()=>{},{errorOrDestroy:Y}=B;function k(m,y,T){typeof T!="boolean"&&(T=y instanceof pr()),this.objectMode=!(!m||!m.objectMode),T&&(this.objectMode=this.objectMode||!(!m||!m.readableObjectMode)),this.highWaterMark=m?A(this,m,"readableHighWaterMark",T):E(!1),this.buffer=new X,this.length=0,this.pipes=[],this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.constructed=!0,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this[N]=null,this.errorEmitted=!1,this.emitClose=!m||m.emitClose!==!1,this.autoDestroy=!m||m.autoDestroy!==!1,this.destroyed=!1,this.errored=null,this.closed=!1,this.closeEmitted=!1,this.defaultEncoding=m&&m.defaultEncoding||"utf8",this.awaitDrainWriters=null,this.multiAwaitDrain=!1,this.readingMore=!1,this.dataEmitted=!1,this.decoder=null,this.encoding=null,m&&m.encoding&&(this.decoder=new F(m.encoding),this.encoding=m.encoding)}function q(m){if(!(this instanceof q))return new q(m);let y=this instanceof pr();this._readableState=new k(m,this,y),m&&(typeof m.read=="function"&&(this._read=m.read),typeof m.destroy=="function"&&(this._destroy=m.destroy),typeof m.construct=="function"&&(this._construct=m.construct),m.signal&&!y&&D(m.signal,this)),_.call(this,m),B.construct(this,()=>{this._readableState.needReadable&&rt(this,this._readableState)})}function et(m,y,T,L){$("readableAddChunk",y);let z,V=m._readableState;if(V.objectMode||(typeof y=="string"?(T=T||V.defaultEncoding,V.encoding!==T&&(L&&V.encoding?y=U.from(y,T).toString(V.encoding):(y=U.from(y,T),T=""))):y instanceof U?T="":_._isUint8Array(y)?(y=_._uint8ArrayToBuffer(y),T=""):y!=null&&(z=new M("chunk",["string","Buffer","Uint8Array"],y))),z)Y(m,z);else if(y===null)V.reading=!1,function(lt,ct){if($("onEofChunk"),!ct.ended){if(ct.decoder){let yt=ct.decoder.end();yt&&yt.length&&(ct.buffer.push(yt),ct.length+=ct.objectMode?1:yt.length)}ct.ended=!0,ct.sync?dt(lt):(ct.needReadable=!1,ct.emittedReadable=!0,H(lt))}}(m,V);else if(V.objectMode||y&&y.length>0)if(L)if(V.endEmitted)Y(m,new Z);else{if(V.destroyed||V.errored)return!1;ot(m,V,y,!0)}else if(V.ended)Y(m,new st);else{if(V.destroyed||V.errored)return!1;V.reading=!1,V.decoder&&!T?(y=V.decoder.write(y),V.objectMode||y.length!==0?ot(m,V,y,!1):rt(m,V)):ot(m,V,y,!1)}else L||(V.reading=!1,rt(m,V));return!V.ended&&(V.length0?(y.multiAwaitDrain?y.awaitDrainWriters.clear():y.awaitDrainWriters=null,y.dataEmitted=!0,m.emit("data",T)):(y.length+=y.objectMode?1:T.length,L?y.buffer.unshift(T):y.buffer.push(T),y.needReadable&&dt(m)),rt(m,y)}q.prototype.destroy=B.destroy,q.prototype._undestroy=B.undestroy,q.prototype._destroy=function(m,y){y(m)},q.prototype[b.captureRejectionSymbol]=function(m){this.destroy(m)},q.prototype.push=function(m,y){return et(this,m,y,!1)},q.prototype.unshift=function(m,y){return et(this,m,y,!0)},q.prototype.isPaused=function(){let m=this._readableState;return m[N]===!0||m.flowing===!1},q.prototype.setEncoding=function(m){let y=new F(m);this._readableState.decoder=y,this._readableState.encoding=this._readableState.decoder.encoding;let T=this._readableState.buffer,L="";for(let z of T)L+=y.write(z);return T.clear(),L!==""&&T.push(L),this._readableState.length=L.length,this};var at;function ut(m,y){return m<=0||y.length===0&&y.ended?0:y.objectMode?1:o(m)?y.flowing&&y.length?y.buffer.first().length:y.length:m<=y.length?m:y.ended?y.length:0}function dt(m){let y=m._readableState;$("emitReadable",y.needReadable,y.emittedReadable),y.needReadable=!1,y.emittedReadable||($("emitReadable",y.flowing),y.emittedReadable=!0,r.nextTick(H,m))}function H(m){let y=m._readableState;$("emitReadable_",y.destroyed,y.length,y.ended),!y.destroyed&&!y.errored&&(y.length||y.ended)&&(m.emit("readable"),y.emittedReadable=!1),y.needReadable=!y.flowing&&!y.ended&&y.length<=y.highWaterMark,Q(m)}function rt(m,y){!y.readingMore&&y.constructed&&(y.readingMore=!0,r.nextTick(tt,m,y))}function tt(m,y){for(;!y.reading&&!y.ended&&(y.length0,y.resumeScheduled&&y[N]===!1?y.flowing=!0:m.listenerCount("data")>0?m.resume():y.readableListening||(y.flowing=null)}function W(m){$("readable nexttick read 0"),m.read(0)}function C(m,y){$("resume",y.reading),y.reading||m.read(0),y.resumeScheduled=!1,m.emit("resume"),Q(m),y.flowing&&!y.reading&&m.read(0)}function Q(m){let y=m._readableState;for($("flow",y.flowing);y.flowing&&m.read()!==null;);}function h(m,y){typeof m.read!="function"&&(m=q.wrap(m,{objectMode:!0}));let T=async function*(L,z){let V=J;function lt(bt){this===L?(V(),V=J):V=bt}L.on("readable",lt);let ct,yt=G(L,{writable:!1},bt=>{ct=bt?w(ct,bt):null,V(),V=J});try{for(;;){let bt=L.destroyed?null:L.read();if(bt!==null)yield bt;else{if(ct)throw ct;if(ct===null)return;await new c(lt)}}}catch(bt){throw ct=w(ct,bt),ct}finally{!ct&&(z==null?void 0:z.destroyOnReturn)===!1||ct!==void 0&&!L._readableState.autoDestroy?(L.off("readable",lt),yt()):B.destroyer(L,null)}}(m,y);return T.stream=m,T}function p(m,y){if(y.length===0)return null;let T;return y.objectMode?T=y.buffer.shift():!m||m>=y.length?(T=y.decoder?y.buffer.join(""):y.buffer.length===1?y.buffer.first():y.buffer.concat(y.length),y.buffer.clear()):T=y.buffer.consume(m,y.decoder),T}function v(m){let y=m._readableState;$("endReadable",y.endEmitted),y.endEmitted||(y.ended=!0,r.nextTick(O,y,m))}function O(m,y){if($("endReadableNT",m.endEmitted,m.length),!m.errored&&!m.closeEmitted&&!m.endEmitted&&m.length===0){if(m.endEmitted=!0,y.emit("end"),y.writable&&y.allowHalfOpen===!1)r.nextTick(x,y);else if(m.autoDestroy){let T=y._writableState;(!T||T.autoDestroy&&(T.finished||T.writable===!1))&&y.destroy()}}}function x(m){m.writable&&!m.writableEnded&&!m.destroyed&&m.end()}function I(){return at===void 0&&(at={}),at}q.prototype.read=function(m){$("read",m),m===void 0?m=NaN:l(m)||(m=s(m,10));let y=this._readableState,T=m;if(m>y.highWaterMark&&(y.highWaterMark=function(V){if(V>1073741824)throw new nt("size","<= 1GiB",V);return V--,V|=V>>>1,V|=V>>>2,V|=V>>>4,V|=V>>>8,V|=V>>>16,++V}(m)),m!==0&&(y.emittedReadable=!1),m===0&&y.needReadable&&((y.highWaterMark!==0?y.length>=y.highWaterMark:y.length>0)||y.ended))return $("read: emitReadable",y.length,y.ended),y.length===0&&y.ended?v(this):dt(this),null;if((m=ut(m,y))===0&&y.ended)return y.length===0&&v(this),null;let L,z=y.needReadable;if($("need readable",z),(y.length===0||y.length-m0?p(m,y):null,L===null?(y.needReadable=y.length<=y.highWaterMark,m=0):(y.length-=m,y.multiAwaitDrain?y.awaitDrainWriters.clear():y.awaitDrainWriters=null),y.length===0&&(y.ended||(y.needReadable=!0),T!==m&&y.ended&&v(this)),L!==null&&!y.errorEmitted&&!y.closeEmitted&&(y.dataEmitted=!0,this.emit("data",L)),L},q.prototype._read=function(m){throw new R("_read()")},q.prototype.pipe=function(m,y){let T=this,L=this._readableState;L.pipes.length===1&&(L.multiAwaitDrain||(L.multiAwaitDrain=!0,L.awaitDrainWriters=new d(L.awaitDrainWriters?[L.awaitDrainWriters]:[]))),L.pipes.push(m),$("pipe count=%d opts=%j",L.pipes.length,y);let z=y&&y.end===!1||m===r.stdout||m===r.stderr?Tt:lt;function V(kt,vt){$("onunpipe"),kt===T&&vt&&vt.hasUnpiped===!1&&(vt.hasUnpiped=!0,$("cleanup"),m.removeListener("close",xt),m.removeListener("finish",_t),ct&&m.removeListener("drain",ct),m.removeListener("error",Ot),m.removeListener("unpipe",V),T.removeListener("end",lt),T.removeListener("end",Tt),T.removeListener("data",wt),yt=!0,ct&&L.awaitDrainWriters&&(!m._writableState||m._writableState.needDrain)&&ct())}function lt(){$("onend"),m.end()}L.endEmitted?r.nextTick(z):T.once("end",z),m.on("unpipe",V);let ct,yt=!1;function bt(){yt||(L.pipes.length===1&&L.pipes[0]===m?($("false write response, pause",0),L.awaitDrainWriters=m,L.multiAwaitDrain=!1):L.pipes.length>1&&L.pipes.includes(m)&&($("false write response, pause",L.awaitDrainWriters.size),L.awaitDrainWriters.add(m)),T.pause()),ct||(ct=function(kt,vt){return function(){let Ct=kt._readableState;Ct.awaitDrainWriters===vt?($("pipeOnDrain",1),Ct.awaitDrainWriters=null):Ct.multiAwaitDrain&&($("pipeOnDrain",Ct.awaitDrainWriters.size),Ct.awaitDrainWriters.delete(vt)),(!Ct.awaitDrainWriters||Ct.awaitDrainWriters.size===0)&&kt.listenerCount("data")&&kt.resume()}}(T,m),m.on("drain",ct))}function wt(kt){$("ondata");let vt=m.write(kt);$("dest.write",vt),vt===!1&&bt()}function Ot(kt){if($("onerror",kt),Tt(),m.removeListener("error",Ot),m.listenerCount("error")===0){let vt=m._writableState||m._readableState;vt&&!vt.errorEmitted?Y(m,kt):m.emit("error",kt)}}function xt(){m.removeListener("finish",_t),Tt()}function _t(){$("onfinish"),m.removeListener("close",xt),Tt()}function Tt(){$("unpipe"),T.unpipe(m)}return T.on("data",wt),S(m,"error",Ot),m.once("close",xt),m.once("finish",_t),m.emit("pipe",T),m.writableNeedDrain===!0?L.flowing&&bt():L.flowing||($("pipe resume"),T.resume()),m},q.prototype.unpipe=function(m){let y=this._readableState;if(y.pipes.length===0)return this;if(!m){let L=y.pipes;y.pipes=[],this.pause();for(let z=0;z0,L.flowing!==!1&&this.resume()):m==="readable"&&!L.endEmitted&&!L.readableListening&&(L.readableListening=L.needReadable=!0,L.flowing=!1,L.emittedReadable=!1,$("on readable",L.length,L.reading),L.length?dt(this):L.reading||r.nextTick(W,this)),T},q.prototype.addListener=q.prototype.on,q.prototype.removeListener=function(m,y){let T=_.prototype.removeListener.call(this,m,y);return m==="readable"&&r.nextTick(P,this),T},q.prototype.off=q.prototype.removeListener,q.prototype.removeAllListeners=function(m){let y=_.prototype.removeAllListeners.apply(this,arguments);return(m==="readable"||m===void 0)&&r.nextTick(P,this),y},q.prototype.resume=function(){let m=this._readableState;return m.flowing||($("resume"),m.flowing=!m.readableListening,function(y,T){T.resumeScheduled||(T.resumeScheduled=!0,r.nextTick(C,y,T))}(this,m)),m[N]=!1,this},q.prototype.pause=function(){return $("call pause flowing=%j",this._readableState.flowing),this._readableState.flowing!==!1&&($("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState[N]=!0,this},q.prototype.wrap=function(m){let y=!1;m.on("data",L=>{!this.push(L)&&m.pause&&(y=!0,m.pause())}),m.on("end",()=>{this.push(null)}),m.on("error",L=>{Y(this,L)}),m.on("close",()=>{this.destroy()}),m.on("destroy",()=>{this.destroy()}),this._read=()=>{y&&m.resume&&(y=!1,m.resume())};let T=a(m);for(let L=1;L{ht(),pt(),ft();var r=pn(),{ArrayPrototypeSlice:n,Error:l,FunctionPrototypeSymbolHasInstance:o,ObjectDefineProperty:s,ObjectDefineProperties:i,ObjectSetPrototypeOf:a,StringPrototypeToLowerCase:u,Symbol:c,SymbolHasInstance:d}=Xt();e.exports=F,F.WritableState=j;var{EventEmitter:f}=(Un(),Dt(dn)),g=Rl().Stream,{Buffer:b}=(oe(),Dt(ie)),_=jn(),{addAbortSignal:S}=Bo(),{getHighWaterMark:U,getDefaultHighWaterMark:D}=Pl(),{ERR_INVALID_ARG_TYPE:G,ERR_METHOD_NOT_IMPLEMENTED:$,ERR_MULTIPLE_CALLBACK:X,ERR_STREAM_CANNOT_PIPE:B,ERR_STREAM_DESTROYED:A,ERR_STREAM_ALREADY_FINISHED:E,ERR_STREAM_NULL_VALUES:w,ERR_STREAM_WRITE_AFTER_END:M,ERR_UNKNOWN_ENCODING:R}=we().codes,{errorOrDestroy:nt}=_;function st(){}a(F.prototype,g.prototype),a(F,g);var Z=c("kOnFinished");function j(C,Q,h){typeof h!="boolean"&&(h=Q instanceof pr()),this.objectMode=!(!C||!C.objectMode),h&&(this.objectMode=this.objectMode||!(!C||!C.writableObjectMode)),this.highWaterMark=C?U(this,C,"writableHighWaterMark",h):D(!1),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;let p=!(!C||C.decodeStrings!==!1);this.decodeStrings=!p,this.defaultEncoding=C&&C.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=k.bind(void 0,Q),this.writecb=null,this.writelen=0,this.afterWriteTickInfo=null,N(this),this.pendingcb=0,this.constructed=!0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!C||C.emitClose!==!1,this.autoDestroy=!C||C.autoDestroy!==!1,this.errored=null,this.closed=!1,this.closeEmitted=!1,this[Z]=[]}function N(C){C.buffered=[],C.bufferedIndex=0,C.allBuffers=!0,C.allNoop=!0}function F(C){let Q=this instanceof pr();if(!Q&&!o(F,this))return new F(C);this._writableState=new j(C,this,Q),C&&(typeof C.write=="function"&&(this._write=C.write),typeof C.writev=="function"&&(this._writev=C.writev),typeof C.destroy=="function"&&(this._destroy=C.destroy),typeof C.final=="function"&&(this._final=C.final),typeof C.construct=="function"&&(this._construct=C.construct),C.signal&&S(C.signal,this)),g.call(this,C),_.construct(this,()=>{let h=this._writableState;h.writing||at(this,h),H(this,h)})}function it(C,Q,h,p){let v,O=C._writableState;if(typeof h=="function")p=h,h=O.defaultEncoding;else{if(h){if(h!=="buffer"&&!b.isEncoding(h))throw new R(h)}else h=O.defaultEncoding;typeof p!="function"&&(p=st)}if(Q===null)throw new w;if(!O.objectMode)if(typeof Q=="string")O.decodeStrings!==!1&&(Q=b.from(Q,h),h="buffer");else if(Q instanceof b)h="buffer";else{if(!g._isUint8Array(Q))throw new G("chunk",["string","Buffer","Uint8Array"],Q);Q=g._uint8ArrayToBuffer(Q),h="buffer"}return O.ending?v=new M:O.destroyed&&(v=new A("write")),v?(r.nextTick(p,v),nt(C,v,!0),v):(O.pendingcb++,function(x,I,m,y,T){let L=I.objectMode?1:m.length;I.length+=L;let z=I.lengthh.bufferedIndex&&at(C,h),p?h.afterWriteTickInfo!==null&&h.afterWriteTickInfo.cb===v?h.afterWriteTickInfo.count++:(h.afterWriteTickInfo={count:1,cb:v,stream:C,state:h},r.nextTick(q,h.afterWriteTickInfo)):et(C,h,1,v))):nt(C,new X)}function q({stream:C,state:Q,count:h,cb:p}){return Q.afterWriteTickInfo=null,et(C,Q,h,p)}function et(C,Q,h,p){for(!Q.ending&&!C.destroyed&&Q.length===0&&Q.needDrain&&(Q.needDrain=!1,C.emit("drain"));h-- >0;)Q.pendingcb--,p();Q.destroyed&&ot(Q),H(C,Q)}function ot(C){if(C.writing)return;for(let v=C.bufferedIndex;v1&&C._writev){Q.pendingcb-=O-1;let I=Q.allNoop?st:y=>{for(let T=x;T256?(h.splice(0,x),Q.bufferedIndex=0):Q.bufferedIndex=x}Q.bufferProcessing=!1}function ut(C){return C.ending&&!C.destroyed&&C.constructed&&C.length===0&&!C.errored&&C.buffered.length===0&&!C.finished&&!C.writing&&!C.errorEmitted&&!C.closeEmitted}function dt(C,Q){!Q.prefinished&&!Q.finalCalled&&(typeof C._final!="function"||Q.destroyed?(Q.prefinished=!0,C.emit("prefinish")):(Q.finalCalled=!0,function(h,p){let v=!1;function O(x){if(v)nt(h,x??X());else if(v=!0,p.pendingcb--,x){let I=p[Z].splice(0);for(let m=0;m{ut(v)?rt(p,v):v.pendingcb--},C,Q)):ut(Q)&&(Q.pendingcb++,rt(C,Q))))}function rt(C,Q){Q.pendingcb--,Q.finished=!0;let h=Q[Z].splice(0);for(let p=0;p{ht(),pt(),ft();var r=pn(),n=(oe(),Dt(ie)),{isReadable:l,isWritable:o,isIterable:s,isNodeStream:i,isReadableNodeStream:a,isWritableNodeStream:u,isDuplexNodeStream:c}=yr(),d=Dr(),{AbortError:f,codes:{ERR_INVALID_ARG_TYPE:g,ERR_INVALID_RETURN_VALUE:b}}=we(),{destroyer:_}=jn(),S=pr(),U=Lo(),{createDeferredPromise:D}=fr(),G=wp(),$=globalThis.Blob||n.Blob,X=typeof $<"u"?function(M){return M instanceof $}:function(M){return!1},B=globalThis.AbortController||Cl().AbortController,{FunctionPrototypeCall:A}=Xt(),E=class extends S{constructor(M){super(M),(M==null?void 0:M.readable)===!1&&(this._readableState.readable=!1,this._readableState.ended=!0,this._readableState.endEmitted=!0),(M==null?void 0:M.writable)===!1&&(this._writableState.writable=!1,this._writableState.ending=!0,this._writableState.ended=!0,this._writableState.finished=!0)}};function w(M){let R,nt,st,Z,j,N=M.readable&&typeof M.readable.read!="function"?U.wrap(M.readable):M.readable,F=M.writable,it=!!l(N),J=!!o(F);function Y(k){let q=Z;Z=null,q?q(k):k&&j.destroy(k)}return j=new E({readableObjectMode:!(N==null||!N.readableObjectMode),writableObjectMode:!(F==null||!F.writableObjectMode),readable:it,writable:J}),J&&(d(F,k=>{J=!1,k&&_(N,k),Y(k)}),j._write=function(k,q,et){F.write(k,q)?et():R=et},j._final=function(k){F.end(),nt=k},F.on("drain",function(){if(R){let k=R;R=null,k()}}),F.on("finish",function(){if(nt){let k=nt;nt=null,k()}})),it&&(d(N,k=>{it=!1,k&&_(N,k),Y(k)}),N.on("readable",function(){if(st){let k=st;st=null,k()}}),N.on("end",function(){j.push(null)}),j._read=function(){for(;;){let k=N.read();if(k===null)return void(st=j._read);if(!j.push(k))return}}),j._destroy=function(k,q){!k&&Z!==null&&(k=new f),st=null,R=null,nt=null,Z===null?q(k):(Z=q,_(F,k),_(N,k))},j}e.exports=function M(R,nt){if(c(R))return R;if(a(R))return w({readable:R});if(u(R))return w({writable:R});if(i(R))return w({writable:!1,readable:!1});if(typeof R=="function"){let{value:Z,write:j,final:N,destroy:F}=function(J){let{promise:Y,resolve:k}=D(),q=new B,et=q.signal;return{value:J(async function*(){for(;;){let ot=Y;Y=null;let{chunk:at,done:ut,cb:dt}=await ot;if(r.nextTick(dt),ut)return;if(et.aborted)throw new f(void 0,{cause:et.reason});({promise:Y,resolve:k}=D()),yield at}}(),{signal:et}),write(ot,at,ut){let dt=k;k=null,dt({chunk:ot,done:!1,cb:ut})},final(ot){let at=k;k=null,at({done:!0,cb:ot})},destroy(ot,at){q.abort(),at(ot)}}}(R);if(s(Z))return G(E,Z,{objectMode:!0,write:j,final:N,destroy:F});let it=Z==null?void 0:Z.then;if(typeof it=="function"){let J,Y=A(it,Z,k=>{if(k!=null)throw new b("nully","body",k)},k=>{_(J,k)});return J=new E({objectMode:!0,readable:!1,write:j,final(k){N(async()=>{try{await Y,r.nextTick(k,null)}catch(q){r.nextTick(k,q)}})},destroy:F})}throw new b("Iterable, AsyncIterable or AsyncFunction",nt,Z)}if(X(R))return M(R.arrayBuffer());if(s(R))return G(E,R,{objectMode:!0,writable:!1});if(typeof(R==null?void 0:R.writable)=="object"||typeof(R==null?void 0:R.readable)=="object")return w({readable:R!=null&&R.readable?a(R==null?void 0:R.readable)?R==null?void 0:R.readable:M(R.readable):void 0,writable:R!=null&&R.writable?u(R==null?void 0:R.writable)?R==null?void 0:R.writable:M(R.writable):void 0});let st=R==null?void 0:R.then;if(typeof st=="function"){let Z;return A(st,R,j=>{j!=null&&Z.push(j),Z.push(null)},j=>{_(Z,j)}),Z=new E({objectMode:!0,writable:!1,read(){}})}throw new g(nt,["Blob","ReadableStream","WritableStream","Stream","Iterable","AsyncIterable","Function","{ readable, writable } pair","Promise"],R)}}),pr=mt((t,e)=>{ht(),pt(),ft();var{ObjectDefineProperties:r,ObjectGetOwnPropertyDescriptor:n,ObjectKeys:l,ObjectSetPrototypeOf:o}=Xt();e.exports=c;var s,i,a=Lo(),u=_p();o(c.prototype,a.prototype),o(c,a);{let f=l(u.prototype);for(let g=0;g{ht(),pt(),ft();var{ObjectSetPrototypeOf:r,Symbol:n}=Xt();e.exports=a;var{ERR_METHOD_NOT_IMPLEMENTED:l}=we().codes,o=pr(),{getHighWaterMark:s}=Pl();r(a.prototype,o.prototype),r(a,o);var i=n("kCallback");function a(d){if(!(this instanceof a))return new a(d);let f=d?s(this,d,"readableHighWaterMark",!0):null;f===0&&(d={...d,highWaterMark:null,readableHighWaterMark:f,writableHighWaterMark:d.writableHighWaterMark||0}),o.call(this,d),this._readableState.sync=!1,this[i]=null,d&&(typeof d.transform=="function"&&(this._transform=d.transform),typeof d.flush=="function"&&(this._flush=d.flush)),this.on("prefinish",c)}function u(d){typeof this._flush!="function"||this.destroyed?(this.push(null),d&&d()):this._flush((f,g)=>{f?d?d(f):this.destroy(f):(g!=null&&this.push(g),this.push(null),d&&d())})}function c(){this._final!==u&&u.call(this)}a.prototype._final=u,a.prototype._transform=function(d,f,g){throw new l("_transform()")},a.prototype._write=function(d,f,g){let b=this._readableState,_=this._writableState,S=b.length;this._transform(d,f,(U,D)=>{U?g(U):(D!=null&&this.push(D),_.ended||S===b.length||b.length{ht(),pt(),ft();var{ObjectSetPrototypeOf:r}=Xt();e.exports=l;var n=Ep();function l(o){if(!(this instanceof l))return new l(o);n.call(this,o)}r(l.prototype,n.prototype),r(l,n),l.prototype._transform=function(o,s,i){i(null,o)}}),Ll=mt((t,e)=>{ht(),pt(),ft();var r,n,l=pn(),{ArrayIsArray:o,Promise:s,SymbolAsyncIterator:i}=Xt(),a=Dr(),{once:u}=fr(),c=jn(),d=pr(),{aggregateTwoErrors:f,codes:{ERR_INVALID_ARG_TYPE:g,ERR_INVALID_RETURN_VALUE:b,ERR_MISSING_ARGS:_,ERR_STREAM_DESTROYED:S,ERR_STREAM_PREMATURE_CLOSE:U},AbortError:D}=we(),{validateFunction:G,validateAbortSignal:$}=Po(),{isIterable:X,isReadable:B,isReadableNodeStream:A,isNodeStream:E,isTransformStream:w,isWebStream:M,isReadableStream:R,isReadableEnded:nt}=yr(),st=globalThis.AbortController||Cl().AbortController;function Z(Y,k,q){let et=!1;return Y.on("close",()=>{et=!0}),{destroy:ot=>{et||(et=!0,c.destroyer(Y,ot||new S("pipe")))},cleanup:a(Y,{readable:k,writable:q},ot=>{et=!ot})}}function j(Y){if(X(Y))return Y;if(A(Y))return async function*(k){n||(n=Lo()),yield*n.prototype[i].call(k)}(Y);throw new g("val",["Readable","Iterable","AsyncIterable"],Y)}async function N(Y,k,q,{end:et}){let ot,at=null,ut=rt=>{if(rt&&(ot=rt),at){let tt=at;at=null,tt()}},dt=()=>new s((rt,tt)=>{ot?tt(ot):at=()=>{ot?tt(ot):rt()}});k.on("drain",ut);let H=a(k,{readable:!1},ut);try{k.writableNeedDrain&&await dt();for await(let rt of Y)k.write(rt)||await dt();et&&k.end(),await dt(),q()}catch(rt){q(ot!==rt?f(ot,rt):rt)}finally{H(),k.off("drain",ut)}}async function F(Y,k,q,{end:et}){w(k)&&(k=k.writable);let ot=k.getWriter();try{for await(let at of Y)await ot.ready,ot.write(at).catch(()=>{});await ot.ready,et&&await ot.close(),q()}catch(at){try{await ot.abort(at),q(at)}catch(ut){q(ut)}}}function it(Y,k,q){if(Y.length===1&&o(Y[0])&&(Y=Y[0]),Y.length<2)throw new _("streams");let et=new st,ot=et.signal,at=q==null?void 0:q.signal,ut=[];function dt(){Q(new D)}$(at,"options.signal"),at==null||at.addEventListener("abort",dt);let H,rt,tt,P=[],W=0;function C(v){Q(v,--W==0)}function Q(v,O){if(v&&(!H||H.code==="ERR_STREAM_PREMATURE_CLOSE")&&(H=v),H||O){for(;P.length;)P.shift()(H);at==null||at.removeEventListener("abort",dt),et.abort(),O&&(H||ut.forEach(x=>x()),l.nextTick(k,H,rt))}}for(let v=0;v0,m=x||(q==null?void 0:q.end)!==!1,y=v===Y.length-1;if(E(O)){let T=function(L){L&&L.name!=="AbortError"&&L.code!=="ERR_STREAM_PREMATURE_CLOSE"&&C(L)};if(m){let{destroy:L,cleanup:z}=Z(O,x,I);P.push(L),B(O)&&y&&ut.push(z)}O.on("error",T),B(O)&&y&&ut.push(()=>{O.removeListener("error",T)})}if(v===0)if(typeof O=="function"){if(tt=O({signal:ot}),!X(tt))throw new b("Iterable, AsyncIterable or Stream","source",tt)}else tt=X(O)||A(O)||w(O)?O:d.from(O);else if(typeof O=="function"){var h;if(w(tt)?tt=j((h=tt)===null||h===void 0?void 0:h.readable):tt=j(tt),tt=O(tt,{signal:ot}),x){if(!X(tt,!0))throw new b("AsyncIterable",`transform[${v-1}]`,tt)}else{var p;r||(r=Sp());let T=new r({objectMode:!0}),L=(p=tt)===null||p===void 0?void 0:p.then;if(typeof L=="function")W++,L.call(tt,lt=>{rt=lt,lt!=null&&T.write(lt),m&&T.end(),l.nextTick(C)},lt=>{T.destroy(lt),l.nextTick(C,lt)});else if(X(tt,!0))W++,N(tt,T,C,{end:m});else{if(!R(tt)&&!w(tt))throw new b("AsyncIterable or Promise","destination",tt);{let lt=tt.readable||tt;W++,N(lt,T,C,{end:m})}}tt=T;let{destroy:z,cleanup:V}=Z(tt,!1,!0);P.push(z),y&&ut.push(V)}}else if(E(O)){if(A(tt)){W+=2;let T=J(tt,O,C,{end:m});B(O)&&y&&ut.push(T)}else if(w(tt)||R(tt)){let T=tt.readable||tt;W++,N(T,O,C,{end:m})}else{if(!X(tt))throw new g("val",["Readable","Iterable","AsyncIterable","ReadableStream","TransformStream"],tt);W++,N(tt,O,C,{end:m})}tt=O}else if(M(O)){if(A(tt))W++,F(j(tt),O,C,{end:m});else if(R(tt)||X(tt))W++,F(tt,O,C,{end:m});else{if(!w(tt))throw new g("val",["Readable","Iterable","AsyncIterable","ReadableStream","TransformStream"],tt);W++,F(tt.readable,O,C,{end:m})}tt=O}else tt=d.from(O)}return(ot!=null&&ot.aborted||at!=null&&at.aborted)&&l.nextTick(dt),tt}function J(Y,k,q,{end:et}){let ot=!1;if(k.on("close",()=>{ot||q(new U)}),Y.pipe(k,{end:!1}),et){let at=function(){ot=!0,k.end()};nt(Y)?l.nextTick(at):Y.once("end",at)}else q();return a(Y,{readable:!0,writable:!1},at=>{let ut=Y._readableState;at&&at.code==="ERR_STREAM_PREMATURE_CLOSE"&&ut&&ut.ended&&!ut.errored&&!ut.errorEmitted?Y.once("end",q).once("error",q):q(at)}),a(k,{readable:!1,writable:!0},q)}e.exports={pipelineImpl:it,pipeline:function(...Y){return it(Y,u(function(k){return G(k[k.length-1],"streams[stream.length - 1]"),k.pop()}(Y)))}}}),Ap=mt((t,e)=>{ht(),pt(),ft();var{pipeline:r}=Ll(),n=pr(),{destroyer:l}=jn(),{isNodeStream:o,isReadable:s,isWritable:i,isWebStream:a,isTransformStream:u,isWritableStream:c,isReadableStream:d}=yr(),{AbortError:f,codes:{ERR_INVALID_ARG_VALUE:g,ERR_MISSING_ARGS:b}}=we(),_=Dr();e.exports=function(...S){if(S.length===0)throw new b("streams");if(S.length===1)return n.from(S[0]);let U,D,G,$,X,B=[...S];if(typeof S[0]=="function"&&(S[0]=n.from(S[0])),typeof S[S.length-1]=="function"){let R=S.length-1;S[R]=n.from(S[R])}for(let R=0;R0&&!(i(S[R])||c(S[R])||u(S[R])))throw new g(`streams[${R}]`,B[R],"must be writable")}let A=S[0],E=r(S,function(R){let nt=$;$=null,nt?nt(R):R?X.destroy(R):!M&&!w&&X.destroy()}),w=!!(i(A)||c(A)||u(A)),M=!!(s(E)||d(E)||u(E));if(X=new n({writableObjectMode:!(A==null||!A.writableObjectMode),readableObjectMode:!(E==null||!E.writableObjectMode),writable:w,readable:M}),w){if(o(A))X._write=function(nt,st,Z){A.write(nt,st)?Z():U=Z},X._final=function(nt){A.end(),D=nt},A.on("drain",function(){if(U){let nt=U;U=null,nt()}});else if(a(A)){let nt=(u(A)?A.writable:A).getWriter();X._write=async function(st,Z,j){try{await nt.ready,nt.write(st).catch(()=>{}),j()}catch(N){j(N)}},X._final=async function(st){try{await nt.ready,nt.close().catch(()=>{}),D=st}catch(Z){st(Z)}}}let R=u(E)?E.readable:E;_(R,()=>{if(D){let nt=D;D=null,nt()}})}if(M){if(o(E))E.on("readable",function(){if(G){let R=G;G=null,R()}}),E.on("end",function(){X.push(null)}),X._read=function(){for(;;){let R=E.read();if(R===null)return void(G=X._read);if(!X.push(R))return}};else if(a(E)){let R=(u(E)?E.readable:E).getReader();X._read=async function(){for(;;)try{let{value:nt,done:st}=await R.read();if(!X.push(nt))return;if(st)return void X.push(null)}catch{return}}}}return X._destroy=function(R,nt){!R&&$!==null&&(R=new f),G=null,U=null,D=null,$===null?nt(R):($=nt,o(E)&&l(E,R))},X}}),S0=mt((t,e)=>{ht(),pt(),ft();var r=globalThis.AbortController||Cl().AbortController,{codes:{ERR_INVALID_ARG_VALUE:n,ERR_INVALID_ARG_TYPE:l,ERR_MISSING_ARGS:o,ERR_OUT_OF_RANGE:s},AbortError:i}=we(),{validateAbortSignal:a,validateInteger:u,validateObject:c}=Po(),d=Xt().Symbol("kWeak"),{finished:f}=Dr(),g=Ap(),{addAbortSignalNoValidate:b}=Bo(),{isWritable:_,isNodeStream:S}=yr(),{ArrayPrototypePush:U,MathFloor:D,Number:G,NumberIsNaN:$,Promise:X,PromiseReject:B,PromisePrototypeThen:A,Symbol:E}=Xt(),w=E("kEmpty"),M=E("kEof");function R(N,F){if(typeof N!="function")throw new l("fn",["Function","AsyncFunction"],N);F!=null&&c(F,"options"),(F==null?void 0:F.signal)!=null&&a(F.signal,"options.signal");let it=1;return(F==null?void 0:F.concurrency)!=null&&(it=D(F.concurrency)),u(it,"concurrency",1),(async function*(){var J,Y;let k=new r,q=this,et=[],ot=k.signal,at={signal:ot},ut=()=>k.abort();F!=null&&(J=F.signal)!==null&&J!==void 0&&J.aborted&&ut(),F==null||(Y=F.signal)===null||Y===void 0||Y.addEventListener("abort",ut);let dt,H,rt=!1;function tt(){rt=!0}(async function(){try{for await(let C of q){var P;if(rt)return;if(ot.aborted)throw new i;try{C=N(C,at)}catch(Q){C=B(Q)}C!==w&&(typeof((P=C)===null||P===void 0?void 0:P.catch)=="function"&&C.catch(tt),et.push(C),dt&&(dt(),dt=null),!rt&&et.length&&et.length>=it&&await new X(Q=>{H=Q}))}et.push(M)}catch(C){let Q=B(C);A(Q,void 0,tt),et.push(Q)}finally{var W;rt=!0,dt&&(dt(),dt=null),F==null||(W=F.signal)===null||W===void 0||W.removeEventListener("abort",ut)}})();try{for(;;){for(;et.length>0;){let P=await et[0];if(P===M)return;if(ot.aborted)throw new i;P!==w&&(yield P),et.shift(),H&&(H(),H=null)}await new X(P=>{dt=P})}}finally{k.abort(),rt=!0,H&&(H(),H=null)}}).call(this)}async function nt(N,F=void 0){for await(let it of st.call(this,N,F))return!0;return!1}function st(N,F){if(typeof N!="function")throw new l("fn",["Function","AsyncFunction"],N);return R.call(this,async function(it,J){return await N(it,J)?it:w},F)}var Z=class extends o{constructor(){super("reduce"),this.message="Reduce of an empty stream requires an initial value"}};function j(N){if(N=G(N),$(N))return 0;if(N<0)throw new s("number",">= 0",N);return N}e.exports.streamReturningOperators={asIndexedPairs:function(N=void 0){return N!=null&&c(N,"options"),(N==null?void 0:N.signal)!=null&&a(N.signal,"options.signal"),(async function*(){let F=0;for await(let J of this){var it;if(N!=null&&(it=N.signal)!==null&&it!==void 0&&it.aborted)throw new i({cause:N.signal.reason});yield[F++,J]}}).call(this)},drop:function(N,F=void 0){return F!=null&&c(F,"options"),(F==null?void 0:F.signal)!=null&&a(F.signal,"options.signal"),N=j(N),(async function*(){var it;if(F!=null&&(it=F.signal)!==null&&it!==void 0&&it.aborted)throw new i;for await(let Y of this){var J;if(F!=null&&(J=F.signal)!==null&&J!==void 0&&J.aborted)throw new i;N--<=0&&(yield Y)}}).call(this)},filter:st,flatMap:function(N,F){let it=R.call(this,N,F);return(async function*(){for await(let J of it)yield*J}).call(this)},map:R,take:function(N,F=void 0){return F!=null&&c(F,"options"),(F==null?void 0:F.signal)!=null&&a(F.signal,"options.signal"),N=j(N),(async function*(){var it;if(F!=null&&(it=F.signal)!==null&&it!==void 0&&it.aborted)throw new i;for await(let Y of this){var J;if(F!=null&&(J=F.signal)!==null&&J!==void 0&&J.aborted)throw new i;if(!(N-- >0))return;yield Y}}).call(this)},compose:function(N,F){if(F!=null&&c(F,"options"),(F==null?void 0:F.signal)!=null&&a(F.signal,"options.signal"),S(N)&&!_(N))throw new n("stream",N,"must be writable");let it=g(this,N);return F!=null&&F.signal&&b(F.signal,it),it}},e.exports.promiseReturningOperators={every:async function(N,F=void 0){if(typeof N!="function")throw new l("fn",["Function","AsyncFunction"],N);return!await nt.call(this,async(...it)=>!await N(...it),F)},forEach:async function(N,F){if(typeof N!="function")throw new l("fn",["Function","AsyncFunction"],N);for await(let it of R.call(this,async function(J,Y){return await N(J,Y),w},F));},reduce:async function(N,F,it){var J;if(typeof N!="function")throw new l("reducer",["Function","AsyncFunction"],N);it!=null&&c(it,"options"),(it==null?void 0:it.signal)!=null&&a(it.signal,"options.signal");let Y=arguments.length>1;if(it!=null&&(J=it.signal)!==null&&J!==void 0&&J.aborted){let at=new i(void 0,{cause:it.signal.reason});throw this.once("error",()=>{}),await f(this.destroy(at)),at}let k=new r,q=k.signal;if(it!=null&&it.signal){let at={once:!0,[d]:this};it.signal.addEventListener("abort",()=>k.abort(),at)}let et=!1;try{for await(let at of this){var ot;if(et=!0,it!=null&&(ot=it.signal)!==null&&ot!==void 0&&ot.aborted)throw new i;Y?F=await N(F,at,{signal:q}):(F=at,Y=!0)}if(!et&&!Y)throw new Z}finally{k.abort()}return F},toArray:async function(N){N!=null&&c(N,"options"),(N==null?void 0:N.signal)!=null&&a(N.signal,"options.signal");let F=[];for await(let J of this){var it;if(N!=null&&(it=N.signal)!==null&&it!==void 0&&it.aborted)throw new i(void 0,{cause:N.signal.reason});U(F,J)}return F},some:nt,find:async function(N,F){for await(let it of st.call(this,N,F))return it}}}),kp=mt((t,e)=>{ht(),pt(),ft();var{ArrayPrototypePop:r,Promise:n}=Xt(),{isIterable:l,isNodeStream:o,isWebStream:s}=yr(),{pipelineImpl:i}=Ll(),{finished:a}=Dr();Ip(),e.exports={finished:a,pipeline:function(...u){return new n((c,d)=>{let f,g,b=u[u.length-1];if(b&&typeof b=="object"&&!o(b)&&!l(b)&&!s(b)){let _=r(u);f=_.signal,g=_.end}i(u,(_,S)=>{_?d(_):c(S)},{signal:f,end:g})})}}}),Ip=mt((t,e)=>{ht(),pt(),ft();var{Buffer:r}=(oe(),Dt(ie)),{ObjectDefineProperty:n,ObjectKeys:l,ReflectApply:o}=Xt(),{promisify:{custom:s}}=fr(),{streamReturningOperators:i,promiseReturningOperators:a}=S0(),{codes:{ERR_ILLEGAL_CONSTRUCTOR:u}}=we(),c=Ap(),{pipeline:d}=Ll(),{destroyer:f}=jn(),g=Dr(),b=kp(),_=yr(),S=e.exports=Rl().Stream;S.isDisturbed=_.isDisturbed,S.isErrored=_.isErrored,S.isReadable=_.isReadable,S.Readable=Lo();for(let D of l(i)){let G=function(...X){if(new.target)throw u();return S.Readable.from(o($,this,X))},$=i[D];n(G,"name",{__proto__:null,value:$.name}),n(G,"length",{__proto__:null,value:$.length}),n(S.Readable.prototype,D,{__proto__:null,value:G,enumerable:!1,configurable:!0,writable:!0})}for(let D of l(a)){let G=function(...X){if(new.target)throw u();return o($,this,X)},$=a[D];n(G,"name",{__proto__:null,value:$.name}),n(G,"length",{__proto__:null,value:$.length}),n(S.Readable.prototype,D,{__proto__:null,value:G,enumerable:!1,configurable:!0,writable:!0})}S.Writable=_p(),S.Duplex=pr(),S.Transform=Ep(),S.PassThrough=Sp(),S.pipeline=d;var{addAbortSignal:U}=Bo();S.addAbortSignal=U,S.finished=g,S.destroy=f,S.compose=c,n(S,"promises",{__proto__:null,configurable:!0,enumerable:!0,get:()=>b}),n(d,s,{__proto__:null,enumerable:!0,get:()=>b.pipeline}),n(g,s,{__proto__:null,enumerable:!0,get:()=>b.finished}),S.Stream=S,S._isUint8Array=function(D){return D instanceof Uint8Array},S._uint8ArrayToBuffer=function(D){return r.from(D.buffer,D.byteOffset,D.byteLength)}}),gn=mt((t,e)=>{ht(),pt(),ft();var r=Ip(),n=kp(),l=r.Readable.destroy;e.exports=r.Readable,e.exports._uint8ArrayToBuffer=r._uint8ArrayToBuffer,e.exports._isUint8Array=r._isUint8Array,e.exports.isDisturbed=r.isDisturbed,e.exports.isErrored=r.isErrored,e.exports.isReadable=r.isReadable,e.exports.Readable=r.Readable,e.exports.Writable=r.Writable,e.exports.Duplex=r.Duplex,e.exports.Transform=r.Transform,e.exports.PassThrough=r.PassThrough,e.exports.addAbortSignal=r.addAbortSignal,e.exports.finished=r.finished,e.exports.destroy=r.destroy,e.exports.destroy=l,e.exports.pipeline=r.pipeline,e.exports.compose=r.compose,Object.defineProperty(r,"promises",{configurable:!0,enumerable:!0,get:()=>n}),e.exports.Stream=r.Stream,e.exports.default=e.exports}),A0=mt((t,e)=>{ht(),pt(),ft(),typeof Object.create=="function"?e.exports=function(r,n){n&&(r.super_=n,r.prototype=Object.create(n.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(r,n){if(n){r.super_=n;var l=function(){};l.prototype=n.prototype,r.prototype=new l,r.prototype.constructor=r}}}),k0=mt((t,e)=>{ht(),pt(),ft();var{Buffer:r}=(oe(),Dt(ie)),n=Symbol.for("BufferList");function l(o){if(!(this instanceof l))return new l(o);l._init.call(this,o)}l._init=function(o){Object.defineProperty(this,n,{value:!0}),this._bufs=[],this.length=0,o&&this.append(o)},l.prototype._new=function(o){return new l(o)},l.prototype._offset=function(o){if(o===0)return[0,0];let s=0;for(let i=0;ithis.length||o<0)return;let s=this._offset(o);return this._bufs[s[0]][s[1]]},l.prototype.slice=function(o,s){return typeof o=="number"&&o<0&&(o+=this.length),typeof s=="number"&&s<0&&(s+=this.length),this.copy(null,0,o,s)},l.prototype.copy=function(o,s,i,a){if((typeof i!="number"||i<0)&&(i=0),(typeof a!="number"||a>this.length)&&(a=this.length),i>=this.length||a<=0)return o||r.alloc(0);let u=!!o,c=this._offset(i),d=a-i,f=d,g=u&&s||0,b=c[1];if(i===0&&a===this.length){if(!u)return this._bufs.length===1?this._bufs[0]:r.concat(this._bufs,this.length);for(let _=0;_S)){this._bufs[_].copy(o,g,b,b+f),g+=S;break}this._bufs[_].copy(o,g,b),g+=S,f-=S,b&&(b=0)}return o.length>g?o.slice(0,g):o},l.prototype.shallowSlice=function(o,s){if(o=o||0,s=typeof s!="number"?this.length:s,o<0&&(o+=this.length),s<0&&(s+=this.length),o===s)return this._new();let i=this._offset(o),a=this._offset(s),u=this._bufs.slice(i[0],a[0]+1);return a[1]===0?u.pop():u[u.length-1]=u[u.length-1].slice(0,a[1]),i[1]!==0&&(u[0]=u[0].slice(i[1])),this._new(u)},l.prototype.toString=function(o,s,i){return this.slice(s,i).toString(o)},l.prototype.consume=function(o){if(o=Math.trunc(o),Number.isNaN(o)||o<=0)return this;for(;this._bufs.length;){if(!(o>=this._bufs[0].length)){this._bufs[0]=this._bufs[0].slice(o),this.length-=o;break}o-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift()}return this},l.prototype.duplicate=function(){let o=this._new();for(let s=0;sthis.length?this.length:s;let a=this._offset(s),u=a[0],c=a[1];for(;u=o.length){let f=d.indexOf(o,c);if(f!==-1)return this._reverseOffset([u,f]);c=d.length-o.length+1}else{let f=this._reverseOffset([u,c]);if(this._match(f,o))return f;c++}c=0}return-1},l.prototype._match=function(o,s){if(this.length-o{ht(),pt(),ft();var r=gn().Duplex,n=A0(),l=k0();function o(s){if(!(this instanceof o))return new o(s);if(typeof s=="function"){this._callback=s;let i=(function(a){this._callback&&(this._callback(a),this._callback=null)}).bind(this);this.on("pipe",function(a){a.on("error",i)}),this.on("unpipe",function(a){a.removeListener("error",i)}),s=null}l._init.call(this,s),r.call(this)}n(o,r),Object.assign(o.prototype,l.prototype),o.prototype._new=function(s){return new o(s)},o.prototype._write=function(s,i,a){this._appendBuffer(s),typeof a=="function"&&a()},o.prototype._read=function(s){if(!this.length)return this.push(null);s=Math.min(s,this.length),this.push(this.slice(0,s)),this.consume(s)},o.prototype.end=function(s){r.prototype.end.call(this,s),this._callback&&(this._callback(null,this.slice()),this._callback=null)},o.prototype._destroy=function(s,i){this._bufs.length=0,this.length=0,i(s)},o.prototype._isBufferList=function(s){return s instanceof o||s instanceof l||o.isBufferList(s)},o.isBufferList=l.isBufferList,e.exports=o,e.exports.BufferListStream=o,e.exports.BufferList=l}),T0=mt((t,e)=>{ht(),pt(),ft(),e.exports=class{constructor(){this.cmd=null,this.retain=!1,this.qos=0,this.dup=!1,this.length=-1,this.topic=null,this.payload=null}}}),Tp=mt((t,e)=>{ht(),pt(),ft();var r=e.exports,{Buffer:n}=(oe(),Dt(ie));r.types={0:"reserved",1:"connect",2:"connack",3:"publish",4:"puback",5:"pubrec",6:"pubrel",7:"pubcomp",8:"subscribe",9:"suback",10:"unsubscribe",11:"unsuback",12:"pingreq",13:"pingresp",14:"disconnect",15:"auth"},r.requiredHeaderFlags={1:0,2:0,4:0,5:0,6:2,7:0,8:2,9:0,10:2,11:0,12:0,13:0,14:0,15:0},r.requiredHeaderFlagsErrors={};for(let o in r.requiredHeaderFlags){let s=r.requiredHeaderFlags[o];r.requiredHeaderFlagsErrors[o]="Invalid header flag bits, must be 0x"+s.toString(16)+" for "+r.types[o]+" packet"}r.codes={};for(let o in r.types){let s=r.types[o];r.codes[s]=o}r.CMD_SHIFT=4,r.CMD_MASK=240,r.DUP_MASK=8,r.QOS_MASK=3,r.QOS_SHIFT=1,r.RETAIN_MASK=1,r.VARBYTEINT_MASK=127,r.VARBYTEINT_FIN_MASK=128,r.VARBYTEINT_MAX=268435455,r.SESSIONPRESENT_MASK=1,r.SESSIONPRESENT_HEADER=n.from([r.SESSIONPRESENT_MASK]),r.CONNACK_HEADER=n.from([r.codes.connack<[0,1].map(i=>[0,1].map(a=>{let u=n.alloc(1);return u.writeUInt8(r.codes[o]<n.from([o])),r.EMPTY={pingreq:n.from([r.codes.pingreq<<4,0]),pingresp:n.from([r.codes.pingresp<<4,0]),disconnect:n.from([r.codes.disconnect<<4,0])},r.MQTT5_PUBACK_PUBREC_CODES={0:"Success",16:"No matching subscribers",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",144:"Topic Name invalid",145:"Packet identifier in use",151:"Quota exceeded",153:"Payload format invalid"},r.MQTT5_PUBREL_PUBCOMP_CODES={0:"Success",146:"Packet Identifier not found"},r.MQTT5_SUBACK_CODES={0:"Granted QoS 0",1:"Granted QoS 1",2:"Granted QoS 2",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",143:"Topic Filter invalid",145:"Packet Identifier in use",151:"Quota exceeded",158:"Shared Subscriptions not supported",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},r.MQTT5_UNSUBACK_CODES={0:"Success",17:"No subscription existed",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",143:"Topic Filter invalid",145:"Packet Identifier in use"},r.MQTT5_DISCONNECT_CODES={0:"Normal disconnection",4:"Disconnect with Will Message",128:"Unspecified error",129:"Malformed Packet",130:"Protocol Error",131:"Implementation specific error",135:"Not authorized",137:"Server busy",139:"Server shutting down",141:"Keep Alive timeout",142:"Session taken over",143:"Topic Filter invalid",144:"Topic Name invalid",147:"Receive Maximum exceeded",148:"Topic Alias invalid",149:"Packet too large",150:"Message rate too high",151:"Quota exceeded",152:"Administrative action",153:"Payload format invalid",154:"Retain not supported",155:"QoS not supported",156:"Use another server",157:"Server moved",158:"Shared Subscriptions not supported",159:"Connection rate exceeded",160:"Maximum connect time",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},r.MQTT5_AUTH_CODES={0:"Success",24:"Continue authentication",25:"Re-authenticate"}}),O0=mt((t,e)=>{ht(),pt(),ft();var r=1e3,n=6e4,l=60*n,o=24*l,s=7*o,i=365.25*o;function a(u,c,d,f){var g=c>=1.5*d;return Math.round(u/d)+" "+f+(g?"s":"")}e.exports=function(u,c){c=c||{};var d=typeof u;if(d==="string"&&u.length>0)return function(f){if(f=String(f),!(f.length>100)){var g=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(f);if(g){var b=parseFloat(g[1]);switch((g[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return b*i;case"weeks":case"week":case"w":return b*s;case"days":case"day":case"d":return b*o;case"hours":case"hour":case"hrs":case"hr":case"h":return b*l;case"minutes":case"minute":case"mins":case"min":case"m":return b*n;case"seconds":case"second":case"secs":case"sec":case"s":return b*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return b;default:return}}}}(u);if(d==="number"&&isFinite(u))return c.long?function(f){var g=Math.abs(f);return g>=o?a(f,g,o,"day"):g>=l?a(f,g,l,"hour"):g>=n?a(f,g,n,"minute"):g>=r?a(f,g,r,"second"):f+" ms"}(u):function(f){var g=Math.abs(f);return g>=o?Math.round(f/o)+"d":g>=l?Math.round(f/l)+"h":g>=n?Math.round(f/n)+"m":g>=r?Math.round(f/r)+"s":f+"ms"}(u);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(u))}}),x0=mt((t,e)=>{ht(),pt(),ft(),e.exports=function(r){function n(s){let i,a,u,c=null;function d(...f){if(!d.enabled)return;let g=d,b=Number(new Date),_=b-(i||b);g.diff=_,g.prev=i,g.curr=b,i=b,f[0]=n.coerce(f[0]),typeof f[0]!="string"&&f.unshift("%O");let S=0;f[0]=f[0].replace(/%([a-zA-Z%])/g,(U,D)=>{if(U==="%%")return"%";S++;let G=n.formatters[D];if(typeof G=="function"){let $=f[S];U=G.call(g,$),f.splice(S,1),S--}return U}),n.formatArgs.call(g,f),(g.log||n.log).apply(g,f)}return d.namespace=s,d.useColors=n.useColors(),d.color=n.selectColor(s),d.extend=l,d.destroy=n.destroy,Object.defineProperty(d,"enabled",{enumerable:!0,configurable:!1,get:()=>c!==null?c:(a!==n.namespaces&&(a=n.namespaces,u=n.enabled(s)),u),set:f=>{c=f}}),typeof n.init=="function"&&n.init(d),d}function l(s,i){let a=n(this.namespace+(typeof i>"u"?":":i)+s);return a.log=this.log,a}function o(s){return s.toString().substring(2,s.toString().length-2).replace(/\.\*\?$/,"*")}return n.debug=n,n.default=n,n.coerce=function(s){return s instanceof Error?s.stack||s.message:s},n.disable=function(){let s=[...n.names.map(o),...n.skips.map(o).map(i=>"-"+i)].join(",");return n.enable(""),s},n.enable=function(s){n.save(s),n.namespaces=s,n.names=[],n.skips=[];let i,a=(typeof s=="string"?s:"").split(/[\s,]+/),u=a.length;for(i=0;i{n[s]=r[s]}),n.names=[],n.skips=[],n.formatters={},n.selectColor=function(s){let i=0;for(let a=0;a{ht(),pt(),ft(),t.formatArgs=function(n){if(n[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+n[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;let l="color: "+this.color;n.splice(1,0,l,"color: inherit");let o=0,s=0;n[0].replace(/%[a-zA-Z%]/g,i=>{i!=="%%"&&(o++,i==="%c"&&(s=o))}),n.splice(s,0,l)},t.save=function(n){try{n?t.storage.setItem("debug",n):t.storage.removeItem("debug")}catch{}},t.load=function(){let n;try{n=t.storage.getItem("debug")}catch{}return!n&&typeof Nt<"u"&&"env"in Nt&&(n=Nt.env.DEBUG),n},t.useColors=function(){return!(!(typeof window<"u"&&window.process)||window.process.type!=="renderer"&&!window.process.__nwjs)||!(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))&&(typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/))},t.storage=function(){try{return localStorage}catch{}}(),t.destroy=(()=>{let n=!1;return()=>{n||(n=!0)}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=x0()(t);var{formatters:r}=e.exports;r.j=function(n){try{return JSON.stringify(n)}catch(l){return"[UnexpectedJSONParseError]: "+l.message}}}),C0=mt((t,e)=>{ht(),pt(),ft();var r=I0(),{EventEmitter:n}=(Un(),Dt(dn)),l=T0(),o=Tp(),s=dr()("mqtt-packet:parser");e.exports=class $a extends n{constructor(){super(),this.parser=this.constructor.parser}static parser(a){return this instanceof $a?(this.settings=a||{},this._states=["_parseHeader","_parseLength","_parsePayload","_newPacket"],this._resetState(),this):new $a().parser(a)}_resetState(){s("_resetState: resetting packet, error, _list, and _stateCounter"),this.packet=new l,this.error=null,this._list=r(),this._stateCounter=0}parse(a){for(this.error&&this._resetState(),this._list.append(a),s("parse: current state: %s",this._states[this._stateCounter]);(this.packet.length!==-1||this._list.length>0)&&this[this._states[this._stateCounter]]()&&!this.error;)this._stateCounter++,s("parse: state complete. _stateCounter is now: %d",this._stateCounter),s("parse: packet.length: %d, buffer list length: %d",this.packet.length,this._list.length),this._stateCounter>=this._states.length&&(this._stateCounter=0);return s("parse: exited while loop. packet: %d, buffer list length: %d",this.packet.length,this._list.length),this._list.length}_parseHeader(){let a=this._list.readUInt8(0),u=a>>o.CMD_SHIFT;this.packet.cmd=o.types[u];let c=15&a,d=o.requiredHeaderFlags[u];return d!=null&&c!==d?this._emitError(new Error(o.requiredHeaderFlagsErrors[u])):(this.packet.retain=!!(a&o.RETAIN_MASK),this.packet.qos=a>>o.QOS_SHIFT&o.QOS_MASK,this.packet.qos>2?this._emitError(new Error("Packet must not have both QoS bits set to 1")):(this.packet.dup=!!(a&o.DUP_MASK),s("_parseHeader: packet: %o",this.packet),this._list.consume(1),!0))}_parseLength(){let a=this._parseVarByteNum(!0);return a&&(this.packet.length=a.value,this._list.consume(a.bytes)),s("_parseLength %d",a.value),!!a}_parsePayload(){s("_parsePayload: payload %O",this._list);let a=!1;if(this.packet.length===0||this._list.length>=this.packet.length){switch(this._pos=0,this.packet.cmd){case"connect":this._parseConnect();break;case"connack":this._parseConnack();break;case"publish":this._parsePublish();break;case"puback":case"pubrec":case"pubrel":case"pubcomp":this._parseConfirmation();break;case"subscribe":this._parseSubscribe();break;case"suback":this._parseSuback();break;case"unsubscribe":this._parseUnsubscribe();break;case"unsuback":this._parseUnsuback();break;case"pingreq":case"pingresp":break;case"disconnect":this._parseDisconnect();break;case"auth":this._parseAuth();break;default:this._emitError(new Error("Not supported"))}a=!0}return s("_parsePayload complete result: %s",a),a}_parseConnect(){s("_parseConnect");let a,u,c,d,f={},g=this.packet,b=this._parseString();if(b===null)return this._emitError(new Error("Cannot parse protocolId"));if(b!=="MQTT"&&b!=="MQIsdp")return this._emitError(new Error("Invalid protocolId"));if(g.protocolId=b,this._pos>=this._list.length)return this._emitError(new Error("Packet too short"));if(g.protocolVersion=this._list.readUInt8(this._pos),g.protocolVersion>=128&&(g.bridgeMode=!0,g.protocolVersion=g.protocolVersion-128),g.protocolVersion!==3&&g.protocolVersion!==4&&g.protocolVersion!==5)return this._emitError(new Error("Invalid protocol version"));if(this._pos++,this._pos>=this._list.length)return this._emitError(new Error("Packet too short"));if(1&this._list.readUInt8(this._pos))return this._emitError(new Error("Connect flag bit 0 must be 0, but got 1"));f.username=this._list.readUInt8(this._pos)&o.USERNAME_MASK,f.password=this._list.readUInt8(this._pos)&o.PASSWORD_MASK,f.will=this._list.readUInt8(this._pos)&o.WILL_FLAG_MASK;let _=!!(this._list.readUInt8(this._pos)&o.WILL_RETAIN_MASK),S=(this._list.readUInt8(this._pos)&o.WILL_QOS_MASK)>>o.WILL_QOS_SHIFT;if(f.will)g.will={},g.will.retain=_,g.will.qos=S;else{if(_)return this._emitError(new Error("Will Retain Flag must be set to zero when Will Flag is set to 0"));if(S)return this._emitError(new Error("Will QoS must be set to zero when Will Flag is set to 0"))}if(g.clean=!!(this._list.readUInt8(this._pos)&o.CLEAN_SESSION_MASK),this._pos++,g.keepalive=this._parseNum(),g.keepalive===-1)return this._emitError(new Error("Packet too short"));if(g.protocolVersion===5){let D=this._parseProperties();Object.getOwnPropertyNames(D).length&&(g.properties=D)}let U=this._parseString();if(U===null)return this._emitError(new Error("Packet too short"));if(g.clientId=U,s("_parseConnect: packet.clientId: %s",g.clientId),f.will){if(g.protocolVersion===5){let D=this._parseProperties();Object.getOwnPropertyNames(D).length&&(g.will.properties=D)}if(a=this._parseString(),a===null)return this._emitError(new Error("Cannot parse will topic"));if(g.will.topic=a,s("_parseConnect: packet.will.topic: %s",g.will.topic),u=this._parseBuffer(),u===null)return this._emitError(new Error("Cannot parse will payload"));g.will.payload=u,s("_parseConnect: packet.will.paylaod: %s",g.will.payload)}if(f.username){if(d=this._parseString(),d===null)return this._emitError(new Error("Cannot parse username"));g.username=d,s("_parseConnect: packet.username: %s",g.username)}if(f.password){if(c=this._parseBuffer(),c===null)return this._emitError(new Error("Cannot parse password"));g.password=c}return this.settings=g,s("_parseConnect: complete"),g}_parseConnack(){s("_parseConnack");let a=this.packet;if(this._list.length<1)return null;let u=this._list.readUInt8(this._pos++);if(u>1)return this._emitError(new Error("Invalid connack flags, bits 7-1 must be set to 0"));if(a.sessionPresent=!!(u&o.SESSIONPRESENT_MASK),this.settings.protocolVersion===5)this._list.length>=2?a.reasonCode=this._list.readUInt8(this._pos++):a.reasonCode=0;else{if(this._list.length<2)return null;a.returnCode=this._list.readUInt8(this._pos++)}if(a.returnCode===-1||a.reasonCode===-1)return this._emitError(new Error("Cannot parse return code"));if(this.settings.protocolVersion===5){let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(a.properties=c)}s("_parseConnack: complete")}_parsePublish(){s("_parsePublish");let a=this.packet;if(a.topic=this._parseString(),a.topic===null)return this._emitError(new Error("Cannot parse topic"));if(!(a.qos>0)||this._parseMessageId()){if(this.settings.protocolVersion===5){let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(a.properties=u)}a.payload=this._list.slice(this._pos,a.length),s("_parsePublish: payload from buffer list: %o",a.payload)}}_parseSubscribe(){s("_parseSubscribe");let a,u,c,d,f,g,b,_=this.packet;if(_.subscriptions=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let S=this._parseProperties();Object.getOwnPropertyNames(S).length&&(_.properties=S)}if(_.length<=0)return this._emitError(new Error("Malformed subscribe, no payload specified"));for(;this._pos<_.length;){if(a=this._parseString(),a===null)return this._emitError(new Error("Cannot parse topic"));if(this._pos>=_.length)return this._emitError(new Error("Malformed Subscribe Payload"));if(u=this._parseByte(),this.settings.protocolVersion===5){if(192&u)return this._emitError(new Error("Invalid subscribe topic flag bits, bits 7-6 must be 0"))}else if(252&u)return this._emitError(new Error("Invalid subscribe topic flag bits, bits 7-2 must be 0"));if(c=u&o.SUBSCRIBE_OPTIONS_QOS_MASK,c>2)return this._emitError(new Error("Invalid subscribe QoS, must be <= 2"));if(g=!!(u>>o.SUBSCRIBE_OPTIONS_NL_SHIFT&o.SUBSCRIBE_OPTIONS_NL_MASK),f=!!(u>>o.SUBSCRIBE_OPTIONS_RAP_SHIFT&o.SUBSCRIBE_OPTIONS_RAP_MASK),d=u>>o.SUBSCRIBE_OPTIONS_RH_SHIFT&o.SUBSCRIBE_OPTIONS_RH_MASK,d>2)return this._emitError(new Error("Invalid retain handling, must be <= 2"));b={topic:a,qos:c},this.settings.protocolVersion===5?(b.nl=g,b.rap=f,b.rh=d):this.settings.bridgeMode&&(b.rh=0,b.rap=!0,b.nl=!0),s("_parseSubscribe: push subscription `%s` to subscription",b),_.subscriptions.push(b)}}}_parseSuback(){s("_parseSuback");let a=this.packet;if(this.packet.granted=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(a.properties=u)}if(a.length<=0)return this._emitError(new Error("Malformed suback, no payload specified"));for(;this._pos2&&u!==128)return this._emitError(new Error("Invalid suback QoS, must be 0, 1, 2 or 128"));this.packet.granted.push(u)}}}_parseUnsubscribe(){s("_parseUnsubscribe");let a=this.packet;if(a.unsubscriptions=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(a.properties=u)}if(a.length<=0)return this._emitError(new Error("Malformed unsubscribe, no payload specified"));for(;this._pos2){switch(a.reasonCode=this._parseByte(),this.packet.cmd){case"puback":case"pubrec":if(!o.MQTT5_PUBACK_PUBREC_CODES[a.reasonCode])return this._emitError(new Error("Invalid "+this.packet.cmd+" reason code"));break;case"pubrel":case"pubcomp":if(!o.MQTT5_PUBREL_PUBCOMP_CODES[a.reasonCode])return this._emitError(new Error("Invalid "+this.packet.cmd+" reason code"))}s("_parseConfirmation: packet.reasonCode `%d`",a.reasonCode)}else a.reasonCode=0;if(a.length>3){let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(a.properties=u)}}return!0}_parseDisconnect(){let a=this.packet;if(s("_parseDisconnect"),this.settings.protocolVersion===5){this._list.length>0?(a.reasonCode=this._parseByte(),o.MQTT5_DISCONNECT_CODES[a.reasonCode]||this._emitError(new Error("Invalid disconnect reason code"))):a.reasonCode=0;let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(a.properties=u)}return s("_parseDisconnect result: true"),!0}_parseAuth(){s("_parseAuth");let a=this.packet;if(this.settings.protocolVersion!==5)return this._emitError(new Error("Not supported auth packet for this version MQTT"));if(a.reasonCode=this._parseByte(),!o.MQTT5_AUTH_CODES[a.reasonCode])return this._emitError(new Error("Invalid auth reason code"));let u=this._parseProperties();return Object.getOwnPropertyNames(u).length&&(a.properties=u),s("_parseAuth: result: true"),!0}_parseMessageId(){let a=this.packet;return a.messageId=this._parseNum(),a.messageId===null?(this._emitError(new Error("Cannot parse messageId")),!1):(s("_parseMessageId: packet.messageId %d",a.messageId),!0)}_parseString(a){let u=this._parseNum(),c=u+this._pos;if(u===-1||c>this._list.length||c>this.packet.length)return null;let d=this._list.toString("utf8",this._pos,c);return this._pos+=u,s("_parseString: result: %s",d),d}_parseStringPair(){return s("_parseStringPair"),{name:this._parseString(),value:this._parseString()}}_parseBuffer(){let a=this._parseNum(),u=a+this._pos;if(a===-1||u>this._list.length||u>this.packet.length)return null;let c=this._list.slice(this._pos,u);return this._pos+=a,s("_parseBuffer: result: %o",c),c}_parseNum(){if(this._list.length-this._pos<2)return-1;let a=this._list.readUInt16BE(this._pos);return this._pos+=2,s("_parseNum: result: %s",a),a}_parse4ByteNum(){if(this._list.length-this._pos<4)return-1;let a=this._list.readUInt32BE(this._pos);return this._pos+=4,s("_parse4ByteNum: result: %s",a),a}_parseVarByteNum(a){s("_parseVarByteNum");let u,c=0,d=1,f=0,g=!1,b=this._pos?this._pos:0;for(;c<4&&b+c=c&&this._emitError(new Error("Invalid variable byte integer")),b&&(this._pos+=c),g=!!g&&(a?{bytes:c,value:f}:f),s("_parseVarByteNum: result: %o",g),g}_parseByte(){let a;return this._pos{ht(),pt(),ft();var{Buffer:r}=(oe(),Dt(ie)),n={},l=r.isBuffer(r.from([1,2]).subarray(0,1));function o(s){let i=r.allocUnsafe(2);return i.writeUInt8(s>>8,0),i.writeUInt8(255&s,1),i}e.exports={cache:n,generateCache:function(){for(let s=0;s<65536;s++)n[s]=o(s)},generateNumber:o,genBufVariableByteInt:function(s){let i=0,a=0,u=r.allocUnsafe(4);do i=s%128|0,(s=s/128|0)>0&&(i|=128),u.writeUInt8(i,a++);while(s>0&&a<4);return s>0&&(a=0),l?u.subarray(0,a):u.slice(0,a)},generate4ByteBuffer:function(s){let i=r.allocUnsafe(4);return i.writeUInt32BE(s,0),i}}}),P0=mt((t,e)=>{ht(),pt(),ft(),typeof Nt>"u"||!Nt.version||Nt.version.indexOf("v0.")===0||Nt.version.indexOf("v1.")===0&&Nt.version.indexOf("v1.8.")!==0?e.exports={nextTick:function(r,n,l,o){if(typeof r!="function")throw new TypeError('"callback" argument must be a function');var s,i,a=arguments.length;switch(a){case 0:case 1:return Nt.nextTick(r);case 2:return Nt.nextTick(function(){r.call(null,n)});case 3:return Nt.nextTick(function(){r.call(null,n,l)});case 4:return Nt.nextTick(function(){r.call(null,n,l,o)});default:for(s=new Array(a-1),i=0;i{ht(),pt(),ft();var r=Tp(),{Buffer:n}=(oe(),Dt(ie)),l=n.allocUnsafe(0),o=n.from([0]),s=R0(),i=P0().nextTick,a=dr()("mqtt-packet:writeToStream"),u=s.cache,c=s.generateNumber,d=s.generateCache,f=s.genBufVariableByteInt,g=s.generate4ByteBuffer,b=B,_=!0;function S(j,N,F){switch(a("generate called"),N.cork&&(N.cork(),i(U,N)),_&&(_=!1,d()),a("generate: packet.cmd: %s",j.cmd),j.cmd){case"connect":return function(it,J){let Y=it||{},k=Y.protocolId||"MQTT",q=Y.protocolVersion||4,et=Y.will,ot=Y.clean,at=Y.keepalive||0,ut=Y.clientId||"",dt=Y.username,H=Y.password,rt=Y.properties;ot===void 0&&(ot=!0);let tt,P,W=0;if(typeof k!="string"&&!n.isBuffer(k))return J.destroy(new Error("Invalid protocolId")),!1;if(W+=k.length+2,q!==3&&q!==4&&q!==5)return J.destroy(new Error("Invalid protocol version")),!1;if(W+=1,(typeof ut=="string"||n.isBuffer(ut))&&(ut||q>=4)&&(ut||ot))W+=n.byteLength(ut)+2;else{if(q<4)return J.destroy(new Error("clientId must be supplied before 3.1.1")),!1;if(1*ot==0)return J.destroy(new Error("clientId must be given if cleanSession set to 0")),!1}if(typeof at!="number"||at<0||at>65535||at%1!=0)return J.destroy(new Error("Invalid keepalive")),!1;if(W+=2,W+=1,q===5){if(tt=w(J,rt),!tt)return!1;W+=tt.length}if(et){if(typeof et!="object")return J.destroy(new Error("Invalid will")),!1;if(!et.topic||typeof et.topic!="string")return J.destroy(new Error("Invalid will topic")),!1;if(W+=n.byteLength(et.topic)+2,W+=2,et.payload){if(!(et.payload.length>=0))return J.destroy(new Error("Invalid will payload")),!1;typeof et.payload=="string"?W+=n.byteLength(et.payload):W+=et.payload.length}if(P={},q===5){if(P=w(J,et.properties),!P)return!1;W+=P.length}}let C=!1;if(dt!=null){if(!Z(dt))return J.destroy(new Error("Invalid username")),!1;C=!0,W+=n.byteLength(dt)+2}if(H!=null){if(!C)return J.destroy(new Error("Username is required to use password")),!1;if(!Z(H))return J.destroy(new Error("Invalid password")),!1;W+=st(H)+2}J.write(r.CONNECT_HEADER),G(J,W),E(J,k),Y.bridgeMode&&(q+=128),J.write(q===131?r.VERSION131:q===132?r.VERSION132:q===4?r.VERSION4:q===5?r.VERSION5:r.VERSION3);let Q=0;return Q|=dt!=null?r.USERNAME_MASK:0,Q|=H!=null?r.PASSWORD_MASK:0,Q|=et&&et.retain?r.WILL_RETAIN_MASK:0,Q|=et&&et.qos?et.qos<0&&b(J,dt),tt==null||tt.write(),a("publish: payload: %o",ut),J.write(ut)}(j,N,F);case"puback":case"pubrec":case"pubrel":case"pubcomp":return function(it,J,Y){let k=Y?Y.protocolVersion:4,q=it||{},et=q.cmd||"puback",ot=q.messageId,at=q.dup&&et==="pubrel"?r.DUP_MASK:0,ut=0,dt=q.reasonCode,H=q.properties,rt=k===5?3:2;if(et==="pubrel"&&(ut=1),typeof ot!="number")return J.destroy(new Error("Invalid messageId")),!1;let tt=null;if(k===5&&typeof H=="object"){if(tt=M(J,H,Y,rt),!tt)return!1;rt+=tt.length}return J.write(r.ACKS[et][ut][at][0]),rt===3&&(rt+=dt!==0?1:-1),G(J,rt),b(J,ot),k===5&&rt!==2&&J.write(n.from([dt])),tt!==null?tt.write():rt===4&&J.write(n.from([0])),!0}(j,N,F);case"subscribe":return function(it,J,Y){a("subscribe: packet: ");let k=Y?Y.protocolVersion:4,q=it||{},et=q.dup?r.DUP_MASK:0,ot=q.messageId,at=q.subscriptions,ut=q.properties,dt=0;if(typeof ot!="number")return J.destroy(new Error("Invalid messageId")),!1;dt+=2;let H=null;if(k===5){if(H=w(J,ut),!H)return!1;dt+=H.length}if(typeof at!="object"||!at.length)return J.destroy(new Error("Invalid subscriptions")),!1;for(let tt=0;tt2)return J.destroy(new Error("Invalid subscriptions - invalid Retain Handling")),!1}dt+=n.byteLength(P)+2+1}a("subscribe: writing to stream: %o",r.SUBSCRIBE_HEADER),J.write(r.SUBSCRIBE_HEADER[1][et?1:0][0]),G(J,dt),b(J,ot),H!==null&&H.write();let rt=!0;for(let tt of at){let P,W=tt.topic,C=tt.qos,Q=+tt.nl,h=+tt.rap,p=tt.rh;$(J,W),P=r.SUBSCRIBE_OPTIONS_QOS[C],k===5&&(P|=Q?r.SUBSCRIBE_OPTIONS_NL:0,P|=h?r.SUBSCRIBE_OPTIONS_RAP:0,P|=p?r.SUBSCRIBE_OPTIONS_RH[p]:0),rt=J.write(n.from([P]))}return rt}(j,N,F);case"suback":return function(it,J,Y){let k=Y?Y.protocolVersion:4,q=it||{},et=q.messageId,ot=q.granted,at=q.properties,ut=0;if(typeof et!="number")return J.destroy(new Error("Invalid messageId")),!1;if(ut+=2,typeof ot!="object"||!ot.length)return J.destroy(new Error("Invalid qos vector")),!1;for(let H=0;Hb===B,set(j){j?((!u||Object.keys(u).length===0)&&(_=!0),b=B):(_=!1,b=A)}});var D={};function G(j,N){if(N>r.VARBYTEINT_MAX)return j.destroy(new Error(`Invalid variable byte integer: ${N}`)),!1;let F=D[N];return F||(F=f(N),N<16384&&(D[N]=F)),a("writeVarByteInt: writing to stream: %o",F),j.write(F)}function $(j,N){let F=n.byteLength(N);return b(j,F),a("writeString: %s",N),j.write(N,"utf8")}function X(j,N,F){$(j,N),$(j,F)}function B(j,N){return a("writeNumberCached: number: %d",N),a("writeNumberCached: %o",u[N]),j.write(u[N])}function A(j,N){let F=c(N);return a("writeNumberGenerated: %o",F),j.write(F)}function E(j,N){typeof N=="string"?$(j,N):N?(b(j,N.length),j.write(N)):b(j,0)}function w(j,N){if(typeof N!="object"||N.length!=null)return{length:1,write(){nt(j,{},0)}};let F=0;function it(J,Y){let k=0;switch(r.propertiesTypes[J]){case"byte":if(typeof Y!="boolean")return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=2;break;case"int8":if(typeof Y!="number"||Y<0||Y>255)return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=2;break;case"binary":if(Y&&Y===null)return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=1+n.byteLength(Y)+2;break;case"int16":if(typeof Y!="number"||Y<0||Y>65535)return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=3;break;case"int32":if(typeof Y!="number"||Y<0||Y>4294967295)return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=5;break;case"var":if(typeof Y!="number"||Y<0||Y>268435455)return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=1+n.byteLength(f(Y));break;case"string":if(typeof Y!="string")return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=3+n.byteLength(Y.toString());break;case"pair":if(typeof Y!="object")return j.destroy(new Error(`Invalid ${J}: ${Y}`)),!1;k+=Object.getOwnPropertyNames(Y).reduce((q,et)=>{let ot=Y[et];return Array.isArray(ot)?q+=ot.reduce((at,ut)=>at+=3+n.byteLength(et.toString())+2+n.byteLength(ut.toString()),0):q+=3+n.byteLength(et.toString())+2+n.byteLength(Y[et].toString()),q},0);break;default:return j.destroy(new Error(`Invalid property ${J}: ${Y}`)),!1}return k}if(N)for(let J in N){let Y=0,k=0,q=N[J];if(Array.isArray(q))for(let et=0;etY;){let q=J.shift();if(!q||!N[q])return!1;delete N[q],k=w(j,N)}return k}function R(j,N,F){switch(r.propertiesTypes[N]){case"byte":j.write(n.from([r.properties[N]])),j.write(n.from([+F]));break;case"int8":j.write(n.from([r.properties[N]])),j.write(n.from([F]));break;case"binary":j.write(n.from([r.properties[N]])),E(j,F);break;case"int16":j.write(n.from([r.properties[N]])),b(j,F);break;case"int32":j.write(n.from([r.properties[N]])),function(it,J){let Y=g(J);a("write4ByteNumber: %o",Y),it.write(Y)}(j,F);break;case"var":j.write(n.from([r.properties[N]])),G(j,F);break;case"string":j.write(n.from([r.properties[N]])),$(j,F);break;case"pair":Object.getOwnPropertyNames(F).forEach(it=>{let J=F[it];Array.isArray(J)?J.forEach(Y=>{j.write(n.from([r.properties[N]])),X(j,it.toString(),Y.toString())}):(j.write(n.from([r.properties[N]])),X(j,it.toString(),J.toString()))});break;default:return j.destroy(new Error(`Invalid property ${N} value: ${F}`)),!1}}function nt(j,N,F){G(j,F);for(let it in N)if(Object.prototype.hasOwnProperty.call(N,it)&&N[it]!==null){let J=N[it];if(Array.isArray(J))for(let Y=0;Y{ht(),pt(),ft();var r=Op(),{EventEmitter:n}=(Un(),Dt(dn)),{Buffer:l}=(oe(),Dt(ie)),o=class extends n{constructor(){super(),this._array=new Array(20),this._i=0}write(s){return this._array[this._i++]=s,!0}concat(){let s,i=0,a=new Array(this._array.length),u=this._array,c=0;for(s=0;s{ht(),pt(),ft(),t.parser=C0().parser,t.generate=B0(),t.writeToStream=Op()}),xp=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(){this.nextId=Math.max(1,Math.floor(65535*Math.random()))}allocate(){let e=this.nextId++;return this.nextId===65536&&(this.nextId=1),e}getLastAllocated(){return this.nextId===1?65535:this.nextId-1}register(e){return!0}deallocate(e){}clear(){}}}),L0=mt((t,e)=>{function r(n){return n instanceof ho?ho.from(n):new n.constructor(n.buffer.slice(),n.byteOffset,n.length)}ht(),pt(),ft(),e.exports=function(n){return(n=n||{}).circles?function(o){var s=[],i=[];return o.proto?function u(c){if(typeof c!="object"||c===null)return c;if(c instanceof Date)return new Date(c);if(Array.isArray(c))return a(c,u);if(c instanceof Map)return new Map(a(Array.from(c),u));if(c instanceof Set)return new Set(a(Array.from(c),u));var d={};for(var f in s.push(c),i.push(d),c){var g=c[f];if(typeof g!="object"||g===null)d[f]=g;else if(g instanceof Date)d[f]=new Date(g);else if(g instanceof Map)d[f]=new Map(a(Array.from(g),u));else if(g instanceof Set)d[f]=new Set(a(Array.from(g),u));else if(ArrayBuffer.isView(g))d[f]=r(g);else{var b=s.indexOf(g);d[f]=b!==-1?i[b]:u(g)}}return s.pop(),i.pop(),d}:function u(c){if(typeof c!="object"||c===null)return c;if(c instanceof Date)return new Date(c);if(Array.isArray(c))return a(c,u);if(c instanceof Map)return new Map(a(Array.from(c),u));if(c instanceof Set)return new Set(a(Array.from(c),u));var d={};for(var f in s.push(c),i.push(d),c)if(Object.hasOwnProperty.call(c,f)!==!1){var g=c[f];if(typeof g!="object"||g===null)d[f]=g;else if(g instanceof Date)d[f]=new Date(g);else if(g instanceof Map)d[f]=new Map(a(Array.from(g),u));else if(g instanceof Set)d[f]=new Set(a(Array.from(g),u));else if(ArrayBuffer.isView(g))d[f]=r(g);else{var b=s.indexOf(g);d[f]=b!==-1?i[b]:u(g)}}return s.pop(),i.pop(),d};function a(u,c){for(var d=Object.keys(u),f=new Array(d.length),g=0;g{ht(),pt(),ft(),e.exports=L0()()}),U0=mt(t=>{function e(r){let n=r.split("/");for(let l=0;l{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e=gn(),r={objectMode:!0},n={clean:!0};t.default=class{constructor(l){this.options=l||{},this.options=Object.assign(Object.assign({},n),l),this._inflights=new Map}put(l,o){return this._inflights.set(l.messageId,l),o&&o(),this}createStream(){let l=new e.Readable(r),o=[],s=!1,i=0;return this._inflights.forEach((a,u)=>{o.push(a)}),l._read=()=>{!s&&i{if(!s)return s=!0,setTimeout(()=>{l.emit("close")},0),l},l}del(l,o){let s=this._inflights.get(l.messageId);return s?(this._inflights.delete(l.messageId),o(null,s)):o&&o(new Error("missing packet")),this}get(l,o){let s=this._inflights.get(l.messageId);return s?o(null,s):o&&o(new Error("missing packet")),this}close(l){this.options.clean&&(this._inflights=null),l&&l()}}}),N0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e=[0,16,128,131,135,144,145,151,153];t.default=(r,n,l)=>{r.log("handlePublish: packet %o",n),l=typeof l<"u"?l:r.noop;let o=n.topic.toString(),s=n.payload,{qos:i}=n,{messageId:a}=n,{options:u}=r;if(r.options.protocolVersion===5){let c;if(n.properties&&(c=n.properties.topicAlias),typeof c<"u")if(o.length===0){if(!(c>0&&c<=65535))return r.log("handlePublish :: topic alias out of range. alias: %d",c),void r.emit("error",new Error("Received Topic Alias is out of range"));{let d=r.topicAliasRecv.getTopicByAlias(c);if(!d)return r.log("handlePublish :: unregistered topic alias. alias: %d",c),void r.emit("error",new Error("Received unregistered Topic Alias"));o=d,r.log("handlePublish :: topic complemented by alias. topic: %s - alias: %d",o,c)}}else{if(!r.topicAliasRecv.put(o,c))return r.log("handlePublish :: topic alias out of range. alias: %d",c),void r.emit("error",new Error("Received Topic Alias is out of range"));r.log("handlePublish :: registered topic: %s - alias: %d",o,c)}}switch(r.log("handlePublish: qos %d",i),i){case 2:u.customHandleAcks(o,s,n,(c,d)=>(typeof c=="number"&&(d=c,c=null),c?r.emit("error",c):e.indexOf(d)===-1?r.emit("error",new Error("Wrong reason code for pubrec")):void(d?r._sendPacket({cmd:"pubrec",messageId:a,reasonCode:d},l):r.incomingStore.put(n,()=>{r._sendPacket({cmd:"pubrec",messageId:a},l)}))));break;case 1:u.customHandleAcks(o,s,n,(c,d)=>(typeof c=="number"&&(d=c,c=null),c?r.emit("error",c):e.indexOf(d)===-1?r.emit("error",new Error("Wrong reason code for puback")):(d||r.emit("message",o,s,n),void r.handleMessage(n,f=>{if(f)return l&&l(f);r._sendPacket({cmd:"puback",messageId:a,reasonCode:d},l)}))));break;case 0:r.emit("message",o,s,n),r.handleMessage(n,l);break;default:r.log("handlePublish: unknown QoS. Doing nothing.")}}}),D0=mt((t,e)=>{e.exports={version:"5.10.3"}}),Nn=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.MQTTJS_VERSION=t.nextTick=t.applyMixin=t.ErrorWithReasonCode=void 0;var e=class Rp extends Error{constructor(n,l){super(n),this.code=l,Object.setPrototypeOf(this,Rp.prototype),Object.getPrototypeOf(this).name="ErrorWithReasonCode"}};t.ErrorWithReasonCode=e,t.applyMixin=function(r,n,l=!1){var o;let s=[n];for(;;){let i=s[0],a=Object.getPrototypeOf(i);if(!(a!=null&&a.prototype))break;s.unshift(a)}for(let i of s)for(let a of Object.getOwnPropertyNames(i.prototype))(l||a!=="constructor")&&Object.defineProperty(r.prototype,a,(o=Object.getOwnPropertyDescriptor(i.prototype,a))!==null&&o!==void 0?o:Object.create(null))},t.nextTick=typeof(Nt==null?void 0:Nt.nextTick)=="function"?Nt.nextTick:r=>{setTimeout(r,0)},t.MQTTJS_VERSION=D0().version}),jo=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.ReasonCodes=void 0;var e=Nn();t.ReasonCodes={0:"",1:"Unacceptable protocol version",2:"Identifier rejected",3:"Server unavailable",4:"Bad username or password",5:"Not authorized",16:"No matching subscribers",17:"No subscription existed",128:"Unspecified error",129:"Malformed Packet",130:"Protocol Error",131:"Implementation specific error",132:"Unsupported Protocol Version",133:"Client Identifier not valid",134:"Bad User Name or Password",135:"Not authorized",136:"Server unavailable",137:"Server busy",138:"Banned",139:"Server shutting down",140:"Bad authentication method",141:"Keep Alive timeout",142:"Session taken over",143:"Topic Filter invalid",144:"Topic Name invalid",145:"Packet identifier in use",146:"Packet Identifier not found",147:"Receive Maximum exceeded",148:"Topic Alias invalid",149:"Packet too large",150:"Message rate too high",151:"Quota exceeded",152:"Administrative action",153:"Payload format invalid",154:"Retain not supported",155:"QoS not supported",156:"Use another server",157:"Server moved",158:"Shared Subscriptions not supported",159:"Connection rate exceeded",160:"Maximum connect time",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},t.default=(r,n)=>{let{messageId:l}=n,o=n.cmd,s=null,i=r.outgoing[l]?r.outgoing[l].cb:null,a=null;if(i){switch(r.log("_handleAck :: packet type",o),o){case"pubcomp":case"puback":{let u=n.reasonCode;u&&u>0&&u!==16?(a=new e.ErrorWithReasonCode(`Publish error: ${t.ReasonCodes[u]}`,u),r._removeOutgoingAndStoreMessage(l,()=>{i(a,n)})):r._removeOutgoingAndStoreMessage(l,i);break}case"pubrec":{s={cmd:"pubrel",qos:2,messageId:l};let u=n.reasonCode;u&&u>0&&u!==16?(a=new e.ErrorWithReasonCode(`Publish error: ${t.ReasonCodes[u]}`,u),r._removeOutgoingAndStoreMessage(l,()=>{i(a,n)})):r._sendPacket(s);break}case"suback":{delete r.outgoing[l],r.messageIdProvider.deallocate(l);let u=n.granted;for(let c=0;c{delete r._resubscribeTopics[g]})}}delete r.messageIdToTopic[l],r._invokeStoreProcessingQueue(),i(a,n);break}case"unsuback":delete r.outgoing[l],r.messageIdProvider.deallocate(l),r._invokeStoreProcessingQueue(),i(null,n);break;default:r.emit("error",new Error("unrecognized packet type"))}r.disconnecting&&Object.keys(r.outgoing).length===0&&r.emit("outgoingEmpty")}else r.log("_handleAck :: Server sent an ack in error. Ignoring.")}}),F0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e=Nn(),r=jo();t.default=(n,l)=>{let{options:o}=n,s=o.protocolVersion,i=s===5?l.reasonCode:l.returnCode;if(s===5)n.handleAuth(l,(a,u)=>{if(a)n.emit("error",a);else if(i===24)n.reconnecting=!1,n._sendPacket(u);else{let c=new e.ErrorWithReasonCode(`Connection refused: ${r.ReasonCodes[i]}`,i);n.emit("error",c)}});else{let a=new e.ErrorWithReasonCode(`Protocol error: Auth packets are only supported in MQTT 5. Your version:${s}`,i);n.emit("error",a)}}}),W0=mt(t=>{var f,g,b,_,S,U,D,G,$,X,B,A,E,w,M,R,nt,st,Z,j,N,F,it,J,Y,k,Va,et,ot,at,ut,Pp,H,rt,tt,kr,Ir,qa,Ji,Xi,$t,Ha,Qn,x;ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.LRUCache=void 0;var e=typeof performance=="object"&&performance&&typeof performance.now=="function"?performance:Date,r=new Set,n=typeof Nt=="object"&&Nt?Nt:{},l=(I,m,y,T)=>{typeof n.emitWarning=="function"&&n.emitWarning(I,m,y,T)},o=globalThis.AbortController,s=globalThis.AbortSignal;if(typeof o>"u"){s=class{constructor(){Ht(this,"onabort");Ht(this,"_onabort",[]);Ht(this,"reason");Ht(this,"aborted",!1)}addEventListener(y,T){this._onabort.push(T)}},o=class{constructor(){Ht(this,"signal",new s);m()}abort(y){var T,L;if(!this.signal.aborted){this.signal.reason=y,this.signal.aborted=!0;for(let z of this.signal._onabort)z(y);(L=(T=this.signal).onabort)==null||L.call(T,y)}}};let I=((f=n.env)==null?void 0:f.LRU_CACHE_IGNORE_AC_WARNING)!=="1",m=()=>{I&&(I=!1,l("AbortController is not defined. If using lru-cache in node 14, load an AbortController polyfill from the `node-abort-controller` package. A minimal polyfill is provided for use by LRUCache.fetch(), but it should not be relied upon in other contexts (eg, passing it to other APIs that use AbortController/AbortSignal might have undesirable effects). You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.","NO_ABORT_CONTROLLER","ENOTSUP",m))}}var i=I=>I&&I===Math.floor(I)&&I>0&&isFinite(I),a=I=>i(I)?I<=Math.pow(2,8)?Uint8Array:I<=Math.pow(2,16)?Uint16Array:I<=Math.pow(2,32)?Uint32Array:I<=Number.MAX_SAFE_INTEGER?u:null:null,u=class extends Array{constructor(I){super(I),this.fill(0)}},c=(g=class{constructor(m,y){Ht(this,"heap");Ht(this,"length");if(!K(g,b))throw new TypeError("instantiate Stack using Stack.create(n)");this.heap=new y(m),this.length=0}static create(m){let y=a(m);if(!y)return[];St(g,b,!0);let T=new g(m,y);return St(g,b,!1),T}push(m){this.heap[this.length++]=m}pop(){return this.heap[--this.length]}},b=new WeakMap,Pt(g,b,!1),g),d=(x=class{constructor(m){Pt(this,k);Pt(this,_);Pt(this,S);Pt(this,U);Pt(this,D);Pt(this,G);Ht(this,"ttl");Ht(this,"ttlResolution");Ht(this,"ttlAutopurge");Ht(this,"updateAgeOnGet");Ht(this,"updateAgeOnHas");Ht(this,"allowStale");Ht(this,"noDisposeOnSet");Ht(this,"noUpdateTTL");Ht(this,"maxEntrySize");Ht(this,"sizeCalculation");Ht(this,"noDeleteOnFetchRejection");Ht(this,"noDeleteOnStaleGet");Ht(this,"allowStaleOnFetchAbort");Ht(this,"allowStaleOnFetchRejection");Ht(this,"ignoreFetchAbort");Pt(this,$);Pt(this,X);Pt(this,B);Pt(this,A);Pt(this,E);Pt(this,w);Pt(this,M);Pt(this,R);Pt(this,nt);Pt(this,st);Pt(this,Z);Pt(this,j);Pt(this,N);Pt(this,F);Pt(this,it);Pt(this,J);Pt(this,Y);Pt(this,et,()=>{});Pt(this,ot,()=>{});Pt(this,at,()=>{});Pt(this,ut,()=>!1);Pt(this,H,m=>{});Pt(this,rt,(m,y,T)=>{});Pt(this,tt,(m,y,T,L)=>{if(T||L)throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");return 0});let{max:y=0,ttl:T,ttlResolution:L=1,ttlAutopurge:z,updateAgeOnGet:V,updateAgeOnHas:lt,allowStale:ct,dispose:yt,disposeAfter:bt,noDisposeOnSet:wt,noUpdateTTL:Ot,maxSize:xt=0,maxEntrySize:_t=0,sizeCalculation:Tt,fetchMethod:kt,noDeleteOnFetchRejection:vt,noDeleteOnStaleGet:Ct,allowStaleOnFetchRejection:re,allowStaleOnFetchAbort:Yt,ignoreFetchAbort:se}=m;if(y!==0&&!i(y))throw new TypeError("max option must be a nonnegative integer");let Je=y?a(y):Array;if(!Je)throw new Error("invalid max value: "+y);if(St(this,_,y),St(this,S,xt),this.maxEntrySize=_t||K(this,S),this.sizeCalculation=Tt,this.sizeCalculation){if(!K(this,S)&&!this.maxEntrySize)throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");if(typeof this.sizeCalculation!="function")throw new TypeError("sizeCalculation set to non-function")}if(kt!==void 0&&typeof kt!="function")throw new TypeError("fetchMethod must be a function if specified");if(St(this,G,kt),St(this,J,!!kt),St(this,B,new Map),St(this,A,new Array(y).fill(void 0)),St(this,E,new Array(y).fill(void 0)),St(this,w,new Je(y)),St(this,M,new Je(y)),St(this,R,0),St(this,nt,0),St(this,st,c.create(y)),St(this,$,0),St(this,X,0),typeof yt=="function"&&St(this,U,yt),typeof bt=="function"?(St(this,D,bt),St(this,Z,[])):(St(this,D,void 0),St(this,Z,void 0)),St(this,it,!!K(this,U)),St(this,Y,!!K(this,D)),this.noDisposeOnSet=!!wt,this.noUpdateTTL=!!Ot,this.noDeleteOnFetchRejection=!!vt,this.allowStaleOnFetchRejection=!!re,this.allowStaleOnFetchAbort=!!Yt,this.ignoreFetchAbort=!!se,this.maxEntrySize!==0){if(K(this,S)!==0&&!i(K(this,S)))throw new TypeError("maxSize must be a positive integer if specified");if(!i(this.maxEntrySize))throw new TypeError("maxEntrySize must be a positive integer if specified");Et(this,k,Pp).call(this)}if(this.allowStale=!!ct,this.noDeleteOnStaleGet=!!Ct,this.updateAgeOnGet=!!V,this.updateAgeOnHas=!!lt,this.ttlResolution=i(L)||L===0?L:1,this.ttlAutopurge=!!z,this.ttl=T||0,this.ttl){if(!i(this.ttl))throw new TypeError("ttl must be a positive integer if specified");Et(this,k,Va).call(this)}if(K(this,_)===0&&this.ttl===0&&K(this,S)===0)throw new TypeError("At least one of max, maxSize, or ttl is required");if(!this.ttlAutopurge&&!K(this,_)&&!K(this,S)){let _e="LRU_CACHE_UNBOUNDED";(ki=>!r.has(ki))(_e)&&(r.add(_e),l("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.","UnboundedCacheWarning",_e,x))}}static unsafeExposeInternals(m){return{starts:K(m,N),ttls:K(m,F),sizes:K(m,j),keyMap:K(m,B),keyList:K(m,A),valList:K(m,E),next:K(m,w),prev:K(m,M),get head(){return K(m,R)},get tail(){return K(m,nt)},free:K(m,st),isBackgroundFetch:y=>{var T;return Et(T=m,k,$t).call(T,y)},backgroundFetch:(y,T,L,z)=>{var V;return Et(V=m,k,Xi).call(V,y,T,L,z)},moveToTail:y=>{var T;return Et(T=m,k,Qn).call(T,y)},indexes:y=>{var T;return Et(T=m,k,kr).call(T,y)},rindexes:y=>{var T;return Et(T=m,k,Ir).call(T,y)},isStale:y=>{var T;return K(T=m,ut).call(T,y)}}}get max(){return K(this,_)}get maxSize(){return K(this,S)}get calculatedSize(){return K(this,X)}get size(){return K(this,$)}get fetchMethod(){return K(this,G)}get dispose(){return K(this,U)}get disposeAfter(){return K(this,D)}getRemainingTTL(m){return K(this,B).has(m)?1/0:0}*entries(){for(let m of Et(this,k,kr).call(this))K(this,E)[m]!==void 0&&K(this,A)[m]!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield[K(this,A)[m],K(this,E)[m]])}*rentries(){for(let m of Et(this,k,Ir).call(this))K(this,E)[m]!==void 0&&K(this,A)[m]!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield[K(this,A)[m],K(this,E)[m]])}*keys(){for(let m of Et(this,k,kr).call(this)){let y=K(this,A)[m];y!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield y)}}*rkeys(){for(let m of Et(this,k,Ir).call(this)){let y=K(this,A)[m];y!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield y)}}*values(){for(let m of Et(this,k,kr).call(this))K(this,E)[m]!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield K(this,E)[m])}*rvalues(){for(let m of Et(this,k,Ir).call(this))K(this,E)[m]!==void 0&&!Et(this,k,$t).call(this,K(this,E)[m])&&(yield K(this,E)[m])}[Symbol.iterator](){return this.entries()}find(m,y={}){for(let T of Et(this,k,kr).call(this)){let L=K(this,E)[T],z=Et(this,k,$t).call(this,L)?L.__staleWhileFetching:L;if(z!==void 0&&m(z,K(this,A)[T],this))return this.get(K(this,A)[T],y)}}forEach(m,y=this){for(let T of Et(this,k,kr).call(this)){let L=K(this,E)[T],z=Et(this,k,$t).call(this,L)?L.__staleWhileFetching:L;z!==void 0&&m.call(y,z,K(this,A)[T],this)}}rforEach(m,y=this){for(let T of Et(this,k,Ir).call(this)){let L=K(this,E)[T],z=Et(this,k,$t).call(this,L)?L.__staleWhileFetching:L;z!==void 0&&m.call(y,z,K(this,A)[T],this)}}purgeStale(){let m=!1;for(let y of Et(this,k,Ir).call(this,{allowStale:!0}))K(this,ut).call(this,y)&&(this.delete(K(this,A)[y]),m=!0);return m}dump(){let m=[];for(let y of Et(this,k,kr).call(this,{allowStale:!0})){let T=K(this,A)[y],L=K(this,E)[y],z=Et(this,k,$t).call(this,L)?L.__staleWhileFetching:L;if(z===void 0||T===void 0)continue;let V={value:z};if(K(this,F)&&K(this,N)){V.ttl=K(this,F)[y];let lt=e.now()-K(this,N)[y];V.start=Math.floor(Date.now()-lt)}K(this,j)&&(V.size=K(this,j)[y]),m.unshift([T,V])}return m}load(m){this.clear();for(let[y,T]of m){if(T.start){let L=Date.now()-T.start;T.start=e.now()-L}this.set(y,T.value,T)}}set(m,y,T={}){var Ot,xt,_t,Tt,kt;if(y===void 0)return this.delete(m),this;let{ttl:L=this.ttl,start:z,noDisposeOnSet:V=this.noDisposeOnSet,sizeCalculation:lt=this.sizeCalculation,status:ct}=T,{noUpdateTTL:yt=this.noUpdateTTL}=T,bt=K(this,tt).call(this,m,y,T.size||0,lt);if(this.maxEntrySize&&bt>this.maxEntrySize)return ct&&(ct.set="miss",ct.maxEntrySizeExceeded=!0),this.delete(m),this;let wt=K(this,$)===0?void 0:K(this,B).get(m);if(wt===void 0)wt=K(this,$)===0?K(this,nt):K(this,st).length!==0?K(this,st).pop():K(this,$)===K(this,_)?Et(this,k,Ji).call(this,!1):K(this,$),K(this,A)[wt]=m,K(this,E)[wt]=y,K(this,B).set(m,wt),K(this,w)[K(this,nt)]=wt,K(this,M)[wt]=K(this,nt),St(this,nt,wt),Ii(this,$)._++,K(this,rt).call(this,wt,bt,ct),ct&&(ct.set="add"),yt=!1;else{Et(this,k,Qn).call(this,wt);let vt=K(this,E)[wt];if(y!==vt){if(K(this,J)&&Et(this,k,$t).call(this,vt)){vt.__abortController.abort(new Error("replaced"));let{__staleWhileFetching:Ct}=vt;Ct!==void 0&&!V&&(K(this,it)&&((Ot=K(this,U))==null||Ot.call(this,Ct,m,"set")),K(this,Y)&&((xt=K(this,Z))==null||xt.push([Ct,m,"set"])))}else V||(K(this,it)&&((_t=K(this,U))==null||_t.call(this,vt,m,"set")),K(this,Y)&&((Tt=K(this,Z))==null||Tt.push([vt,m,"set"])));if(K(this,H).call(this,wt),K(this,rt).call(this,wt,bt,ct),K(this,E)[wt]=y,ct){ct.set="replace";let Ct=vt&&Et(this,k,$t).call(this,vt)?vt.__staleWhileFetching:vt;Ct!==void 0&&(ct.oldValue=Ct)}}else ct&&(ct.set="update")}if(L!==0&&!K(this,F)&&Et(this,k,Va).call(this),K(this,F)&&(yt||K(this,at).call(this,wt,L,z),ct&&K(this,ot).call(this,ct,wt)),!V&&K(this,Y)&&K(this,Z)){let vt,Ct=K(this,Z);for(;vt=Ct==null?void 0:Ct.shift();)(kt=K(this,D))==null||kt.call(this,...vt)}return this}pop(){var m;try{for(;K(this,$);){let y=K(this,E)[K(this,R)];if(Et(this,k,Ji).call(this,!0),Et(this,k,$t).call(this,y)){if(y.__staleWhileFetching)return y.__staleWhileFetching}else if(y!==void 0)return y}}finally{if(K(this,Y)&&K(this,Z)){let y,T=K(this,Z);for(;y=T==null?void 0:T.shift();)(m=K(this,D))==null||m.call(this,...y)}}}has(m,y={}){let{updateAgeOnHas:T=this.updateAgeOnHas,status:L}=y,z=K(this,B).get(m);if(z!==void 0){let V=K(this,E)[z];if(Et(this,k,$t).call(this,V)&&V.__staleWhileFetching===void 0)return!1;if(!K(this,ut).call(this,z))return T&&K(this,et).call(this,z),L&&(L.has="hit",K(this,ot).call(this,L,z)),!0;L&&(L.has="stale",K(this,ot).call(this,L,z))}else L&&(L.has="miss");return!1}peek(m,y={}){let{allowStale:T=this.allowStale}=y,L=K(this,B).get(m);if(L!==void 0&&(T||!K(this,ut).call(this,L))){let z=K(this,E)[L];return Et(this,k,$t).call(this,z)?z.__staleWhileFetching:z}}async fetch(m,y={}){let{allowStale:T=this.allowStale,updateAgeOnGet:L=this.updateAgeOnGet,noDeleteOnStaleGet:z=this.noDeleteOnStaleGet,ttl:V=this.ttl,noDisposeOnSet:lt=this.noDisposeOnSet,size:ct=0,sizeCalculation:yt=this.sizeCalculation,noUpdateTTL:bt=this.noUpdateTTL,noDeleteOnFetchRejection:wt=this.noDeleteOnFetchRejection,allowStaleOnFetchRejection:Ot=this.allowStaleOnFetchRejection,ignoreFetchAbort:xt=this.ignoreFetchAbort,allowStaleOnFetchAbort:_t=this.allowStaleOnFetchAbort,context:Tt,forceRefresh:kt=!1,status:vt,signal:Ct}=y;if(!K(this,J))return vt&&(vt.fetch="get"),this.get(m,{allowStale:T,updateAgeOnGet:L,noDeleteOnStaleGet:z,status:vt});let re={allowStale:T,updateAgeOnGet:L,noDeleteOnStaleGet:z,ttl:V,noDisposeOnSet:lt,size:ct,sizeCalculation:yt,noUpdateTTL:bt,noDeleteOnFetchRejection:wt,allowStaleOnFetchRejection:Ot,allowStaleOnFetchAbort:_t,ignoreFetchAbort:xt,status:vt,signal:Ct},Yt=K(this,B).get(m);if(Yt===void 0){vt&&(vt.fetch="miss");let se=Et(this,k,Xi).call(this,m,Yt,re,Tt);return se.__returned=se}{let se=K(this,E)[Yt];if(Et(this,k,$t).call(this,se)){let ql=T&&se.__staleWhileFetching!==void 0;return vt&&(vt.fetch="inflight",ql&&(vt.returnedStale=!0)),ql?se.__staleWhileFetching:se.__returned=se}let Je=K(this,ut).call(this,Yt);if(!kt&&!Je)return vt&&(vt.fetch="hit"),Et(this,k,Qn).call(this,Yt),L&&K(this,et).call(this,Yt),vt&&K(this,ot).call(this,vt,Yt),se;let _e=Et(this,k,Xi).call(this,m,Yt,re,Tt),ki=_e.__staleWhileFetching!==void 0&&T;return vt&&(vt.fetch=Je?"stale":"refresh",ki&&Je&&(vt.returnedStale=!0)),ki?_e.__staleWhileFetching:_e.__returned=_e}}get(m,y={}){let{allowStale:T=this.allowStale,updateAgeOnGet:L=this.updateAgeOnGet,noDeleteOnStaleGet:z=this.noDeleteOnStaleGet,status:V}=y,lt=K(this,B).get(m);if(lt!==void 0){let ct=K(this,E)[lt],yt=Et(this,k,$t).call(this,ct);return V&&K(this,ot).call(this,V,lt),K(this,ut).call(this,lt)?(V&&(V.get="stale"),yt?(V&&T&&ct.__staleWhileFetching!==void 0&&(V.returnedStale=!0),T?ct.__staleWhileFetching:void 0):(z||this.delete(m),V&&T&&(V.returnedStale=!0),T?ct:void 0)):(V&&(V.get="hit"),yt?ct.__staleWhileFetching:(Et(this,k,Qn).call(this,lt),L&&K(this,et).call(this,lt),ct))}V&&(V.get="miss")}delete(m){var T,L,z,V;let y=!1;if(K(this,$)!==0){let lt=K(this,B).get(m);if(lt!==void 0)if(y=!0,K(this,$)===1)this.clear();else{K(this,H).call(this,lt);let ct=K(this,E)[lt];Et(this,k,$t).call(this,ct)?ct.__abortController.abort(new Error("deleted")):(K(this,it)||K(this,Y))&&(K(this,it)&&((T=K(this,U))==null||T.call(this,ct,m,"delete")),K(this,Y)&&((L=K(this,Z))==null||L.push([ct,m,"delete"]))),K(this,B).delete(m),K(this,A)[lt]=void 0,K(this,E)[lt]=void 0,lt===K(this,nt)?St(this,nt,K(this,M)[lt]):lt===K(this,R)?St(this,R,K(this,w)[lt]):(K(this,w)[K(this,M)[lt]]=K(this,w)[lt],K(this,M)[K(this,w)[lt]]=K(this,M)[lt]),Ii(this,$)._--,K(this,st).push(lt)}}if(K(this,Y)&&((z=K(this,Z))!=null&&z.length)){let lt,ct=K(this,Z);for(;lt=ct==null?void 0:ct.shift();)(V=K(this,D))==null||V.call(this,...lt)}return y}clear(){var m,y,T;for(let L of Et(this,k,Ir).call(this,{allowStale:!0})){let z=K(this,E)[L];if(Et(this,k,$t).call(this,z))z.__abortController.abort(new Error("deleted"));else{let V=K(this,A)[L];K(this,it)&&((m=K(this,U))==null||m.call(this,z,V,"delete")),K(this,Y)&&((y=K(this,Z))==null||y.push([z,V,"delete"]))}}if(K(this,B).clear(),K(this,E).fill(void 0),K(this,A).fill(void 0),K(this,F)&&K(this,N)&&(K(this,F).fill(0),K(this,N).fill(0)),K(this,j)&&K(this,j).fill(0),St(this,R,0),St(this,nt,0),K(this,st).length=0,St(this,X,0),St(this,$,0),K(this,Y)&&K(this,Z)){let L,z=K(this,Z);for(;L=z==null?void 0:z.shift();)(T=K(this,D))==null||T.call(this,...L)}}},_=new WeakMap,S=new WeakMap,U=new WeakMap,D=new WeakMap,G=new WeakMap,$=new WeakMap,X=new WeakMap,B=new WeakMap,A=new WeakMap,E=new WeakMap,w=new WeakMap,M=new WeakMap,R=new WeakMap,nt=new WeakMap,st=new WeakMap,Z=new WeakMap,j=new WeakMap,N=new WeakMap,F=new WeakMap,it=new WeakMap,J=new WeakMap,Y=new WeakMap,k=new WeakSet,Va=function(){let m=new u(K(this,_)),y=new u(K(this,_));St(this,F,m),St(this,N,y),St(this,at,(z,V,lt=e.now())=>{if(y[z]=V!==0?lt:0,m[z]=V,V!==0&&this.ttlAutopurge){let ct=setTimeout(()=>{K(this,ut).call(this,z)&&this.delete(K(this,A)[z])},V+1);ct.unref&&ct.unref()}}),St(this,et,z=>{y[z]=m[z]!==0?e.now():0}),St(this,ot,(z,V)=>{if(m[V]){let lt=m[V],ct=y[V];z.ttl=lt,z.start=ct,z.now=T||L();let yt=z.now-ct;z.remainingTTL=lt-yt}});let T=0,L=()=>{let z=e.now();if(this.ttlResolution>0){T=z;let V=setTimeout(()=>T=0,this.ttlResolution);V.unref&&V.unref()}return z};this.getRemainingTTL=z=>{let V=K(this,B).get(z);if(V===void 0)return 0;let lt=m[V],ct=y[V];return lt===0||ct===0?1/0:lt-((T||L())-ct)},St(this,ut,z=>m[z]!==0&&y[z]!==0&&(T||L())-y[z]>m[z])},et=new WeakMap,ot=new WeakMap,at=new WeakMap,ut=new WeakMap,Pp=function(){let m=new u(K(this,_));St(this,X,0),St(this,j,m),St(this,H,y=>{St(this,X,K(this,X)-m[y]),m[y]=0}),St(this,tt,(y,T,L,z)=>{if(Et(this,k,$t).call(this,T))return 0;if(!i(L)){if(!z)throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");if(typeof z!="function")throw new TypeError("sizeCalculation must be a function");if(L=z(T,y),!i(L))throw new TypeError("sizeCalculation return invalid (expect positive integer)")}return L}),St(this,rt,(y,T,L)=>{if(m[y]=T,K(this,S)){let z=K(this,S)-m[y];for(;K(this,X)>z;)Et(this,k,Ji).call(this,!0)}St(this,X,K(this,X)+m[y]),L&&(L.entrySize=T,L.totalCalculatedSize=K(this,X))})},H=new WeakMap,rt=new WeakMap,tt=new WeakMap,kr=function*({allowStale:m=this.allowStale}={}){if(K(this,$))for(let y=K(this,nt);Et(this,k,qa).call(this,y)&&((m||!K(this,ut).call(this,y))&&(yield y),y!==K(this,R));)y=K(this,M)[y]},Ir=function*({allowStale:m=this.allowStale}={}){if(K(this,$))for(let y=K(this,R);Et(this,k,qa).call(this,y)&&((m||!K(this,ut).call(this,y))&&(yield y),y!==K(this,nt));)y=K(this,w)[y]},qa=function(m){return m!==void 0&&K(this,B).get(K(this,A)[m])===m},Ji=function(m){var z,V;let y=K(this,R),T=K(this,A)[y],L=K(this,E)[y];return K(this,J)&&Et(this,k,$t).call(this,L)?L.__abortController.abort(new Error("evicted")):(K(this,it)||K(this,Y))&&(K(this,it)&&((z=K(this,U))==null||z.call(this,L,T,"evict")),K(this,Y)&&((V=K(this,Z))==null||V.push([L,T,"evict"]))),K(this,H).call(this,y),m&&(K(this,A)[y]=void 0,K(this,E)[y]=void 0,K(this,st).push(y)),K(this,$)===1?(St(this,R,St(this,nt,0)),K(this,st).length=0):St(this,R,K(this,w)[y]),K(this,B).delete(T),Ii(this,$)._--,y},Xi=function(m,y,T,L){let z=y===void 0?void 0:K(this,E)[y];if(Et(this,k,$t).call(this,z))return z;let V=new o,{signal:lt}=T;lt==null||lt.addEventListener("abort",()=>V.abort(lt.reason),{signal:V.signal});let ct={signal:V.signal,options:T,context:L},yt=(xt,_t=!1)=>{let{aborted:Tt}=V.signal,kt=T.ignoreFetchAbort&&xt!==void 0;if(T.status&&(Tt&&!_t?(T.status.fetchAborted=!0,T.status.fetchError=V.signal.reason,kt&&(T.status.fetchAbortIgnored=!0)):T.status.fetchResolved=!0),Tt&&!kt&&!_t)return bt(V.signal.reason);let vt=wt;return K(this,E)[y]===wt&&(xt===void 0?vt.__staleWhileFetching?K(this,E)[y]=vt.__staleWhileFetching:this.delete(m):(T.status&&(T.status.fetchUpdated=!0),this.set(m,xt,ct.options))),xt},bt=xt=>{let{aborted:_t}=V.signal,Tt=_t&&T.allowStaleOnFetchAbort,kt=Tt||T.allowStaleOnFetchRejection,vt=kt||T.noDeleteOnFetchRejection,Ct=wt;if(K(this,E)[y]===wt&&(vt&&Ct.__staleWhileFetching!==void 0?Tt||(K(this,E)[y]=Ct.__staleWhileFetching):this.delete(m)),kt)return T.status&&Ct.__staleWhileFetching!==void 0&&(T.status.returnedStale=!0),Ct.__staleWhileFetching;if(Ct.__returned===Ct)throw xt};T.status&&(T.status.fetchDispatched=!0);let wt=new Promise((xt,_t)=>{var kt;let Tt=(kt=K(this,G))==null?void 0:kt.call(this,m,z,ct);Tt&&Tt instanceof Promise&&Tt.then(vt=>xt(vt===void 0?void 0:vt),_t),V.signal.addEventListener("abort",()=>{(!T.ignoreFetchAbort||T.allowStaleOnFetchAbort)&&(xt(void 0),T.allowStaleOnFetchAbort&&(xt=vt=>yt(vt,!0)))})}).then(yt,xt=>(T.status&&(T.status.fetchRejected=!0,T.status.fetchError=xt),bt(xt))),Ot=Object.assign(wt,{__abortController:V,__staleWhileFetching:z,__returned:void 0});return y===void 0?(this.set(m,Ot,{...ct.options,status:void 0}),y=K(this,B).get(m)):K(this,E)[y]=Ot,Ot},$t=function(m){if(!K(this,J))return!1;let y=m;return!!y&&y instanceof Promise&&y.hasOwnProperty("__staleWhileFetching")&&y.__abortController instanceof o},Ha=function(m,y){K(this,M)[y]=m,K(this,w)[m]=y},Qn=function(m){m!==K(this,nt)&&(m===K(this,R)?St(this,R,K(this,w)[m]):Et(this,k,Ha).call(this,K(this,M)[m],K(this,w)[m]),Et(this,k,Ha).call(this,K(this,nt),m),St(this,nt,m))},x);t.LRUCache=d}),br=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.ContainerIterator=t.Container=t.Base=void 0,t.ContainerIterator=class{constructor(r=0){this.iteratorType=r}equals(r){return this.o===r.o}};var e=class{constructor(){this.i=0}get length(){return this.i}size(){return this.i}empty(){return this.i===0}};t.Base=e,t.Container=class extends e{}}),$0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=br(),r=class extends e.Base{constructor(n=[]){super(),this.S=[];let l=this;n.forEach(function(o){l.push(o)})}clear(){this.i=0,this.S=[]}push(n){return this.S.push(n),this.i+=1,this.i}pop(){if(this.i!==0)return this.i-=1,this.S.pop()}top(){return this.S[this.i-1]}};t.default=r}),V0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=br(),r=class extends e.Base{constructor(n=[]){super(),this.j=0,this.q=[];let l=this;n.forEach(function(o){l.push(o)})}clear(){this.q=[],this.i=this.j=0}push(n){let l=this.q.length;if(this.j/l>.5&&this.j+this.i>=l&&l>4096){let o=this.i;for(let s=0;s{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=br(),r=class extends e.Base{constructor(n=[],l=function(s,i){return s>i?-1:s>1;for(let i=this.i-1>>1;i>=0;--i)this.k(i,s)}m(n){let l=this.C[n];for(;n>0;){let o=n-1>>1,s=this.C[o];if(this.v(s,l)<=0)break;this.C[n]=s,n=o}this.C[n]=l}k(n,l){let o=this.C[n];for(;n0&&(s=i,a=this.C[i]),this.v(a,o)>=0)break;this.C[n]=a,n=s}this.C[n]=o}clear(){this.i=0,this.C.length=0}push(n){this.C.push(n),this.m(this.i),this.i+=1}pop(){if(this.i===0)return;let n=this.C[0],l=this.C.pop();return this.i-=1,this.i&&(this.C[0]=l,this.k(0,this.i>>1)),n}top(){return this.C[0]}find(n){return this.C.indexOf(n)>=0}remove(n){let l=this.C.indexOf(n);return!(l<0)&&(l===0?this.pop():l===this.i-1?(this.C.pop(),this.i-=1):(this.C.splice(l,1,this.C.pop()),this.i-=1,this.m(l),this.k(l,this.i>>1)),!0)}updateItem(n){let l=this.C.indexOf(n);return!(l<0)&&(this.m(l),this.k(l,this.i>>1),!0)}toArray(){return[...this.C]}};t.default=r}),jl=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=br(),r=class extends e.Container{};t.default=r}),vr=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.throwIteratorAccessError=function(){throw new RangeError("Iterator access denied!")}}),Bp=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.RandomIterator=void 0;var e=br(),r=vr(),n=class extends e.ContainerIterator{constructor(l,o){super(o),this.o=l,this.iteratorType===0?(this.pre=function(){return this.o===0&&(0,r.throwIteratorAccessError)(),this.o-=1,this},this.next=function(){return this.o===this.container.size()&&(0,r.throwIteratorAccessError)(),this.o+=1,this}):(this.pre=function(){return this.o===this.container.size()-1&&(0,r.throwIteratorAccessError)(),this.o+=1,this},this.next=function(){return this.o===-1&&(0,r.throwIteratorAccessError)(),this.o-=1,this})}get pointer(){return this.container.getElementByPos(this.o)}set pointer(l){this.container.setElementByPos(this.o,l)}};t.RandomIterator=n}),H0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=jl())&&e.t?e:{default:e},n=Bp(),l=class Mp extends n.RandomIterator{constructor(i,a,u){super(i,u),this.container=a}copy(){return new Mp(this.o,this.container,this.iteratorType)}},o=class extends r.default{constructor(s=[],i=!0){if(super(),Array.isArray(s))this.J=i?[...s]:s,this.i=s.length;else{this.J=[];let a=this;s.forEach(function(u){a.pushBack(u)})}}clear(){this.i=0,this.J.length=0}begin(){return new l(0,this)}end(){return new l(this.i,this)}rBegin(){return new l(this.i-1,this,1)}rEnd(){return new l(-1,this,1)}front(){return this.J[0]}back(){return this.J[this.i-1]}getElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;return this.J[s]}eraseElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;return this.J.splice(s,1),this.i-=1,this.i}eraseElementByValue(s){let i=0;for(let a=0;athis.i-1)throw new RangeError;this.J[s]=i}insert(s,i,a=1){if(s<0||s>this.i)throw new RangeError;return this.J.splice(s,0,...new Array(a).fill(i)),this.i+=a,this.i}find(s){for(let i=0;i{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=jl())&&e.t?e:{default:e},n=br(),l=vr(),o=class Lp extends n.ContainerIterator{constructor(a,u,c,d){super(d),this.o=a,this.h=u,this.container=c,this.iteratorType===0?(this.pre=function(){return this.o.L===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.L,this},this.next=function(){return this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.B,this}):(this.pre=function(){return this.o.B===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.B,this},this.next=function(){return this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.L,this})}get pointer(){return this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o.l}set pointer(a){this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o.l=a}copy(){return new Lp(this.o,this.h,this.container,this.iteratorType)}},s=class extends r.default{constructor(i=[]){super(),this.h={},this.p=this._=this.h.L=this.h.B=this.h;let a=this;i.forEach(function(u){a.pushBack(u)})}V(i){let{L:a,B:u}=i;a.B=u,u.L=a,i===this.p&&(this.p=u),i===this._&&(this._=a),this.i-=1}G(i,a){let u=a.B,c={l:i,L:a,B:u};a.B=c,u.L=c,a===this.h&&(this.p=c),u===this.h&&(this._=c),this.i+=1}clear(){this.i=0,this.p=this._=this.h.L=this.h.B=this.h}begin(){return new o(this.p,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this._,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){return this.p.l}back(){return this._.l}getElementByPos(i){if(i<0||i>this.i-1)throw new RangeError;let a=this.p;for(;i--;)a=a.B;return a.l}eraseElementByPos(i){if(i<0||i>this.i-1)throw new RangeError;let a=this.p;for(;i--;)a=a.B;return this.V(a),this.i}eraseElementByValue(i){let a=this.p;for(;a!==this.h;)a.l===i&&this.V(a),a=a.B;return this.i}eraseElementByIterator(i){let a=i.o;return a===this.h&&(0,l.throwIteratorAccessError)(),i=i.next(),this.V(a),i}pushBack(i){return this.G(i,this._),this.i}popBack(){if(this.i===0)return;let i=this._.l;return this.V(this._),i}pushFront(i){return this.G(i,this.h),this.i}popFront(){if(this.i===0)return;let i=this.p.l;return this.V(this.p),i}setElementByPos(i,a){if(i<0||i>this.i-1)throw new RangeError;let u=this.p;for(;i--;)u=u.B;u.l=a}insert(i,a,u=1){if(i<0||i>this.i)throw new RangeError;if(u<=0)return this.i;if(i===0)for(;u--;)this.pushFront(a);else if(i===this.i)for(;u--;)this.pushBack(a);else{let c=this.p;for(let f=1;f{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=jl())&&e.t?e:{default:e},n=Bp(),l=class jp extends n.RandomIterator{constructor(i,a,u){super(i,u),this.container=a}copy(){return new jp(this.o,this.container,this.iteratorType)}},o=class extends r.default{constructor(s=[],i=4096){super(),this.j=0,this.D=0,this.R=0,this.N=0,this.P=0,this.A=[];let a=(()=>{if(typeof s.length=="number")return s.length;if(typeof s.size=="number")return s.size;if(typeof s.size=="function")return s.size();throw new TypeError("Cannot get the length or size of the container")})();this.F=i,this.P=Math.max(Math.ceil(a/this.F),1);for(let d=0;d>1)-(u>>1),this.D=this.N=this.F-a%this.F>>1;let c=this;s.forEach(function(d){c.pushBack(d)})}T(){let s=[],i=Math.max(this.P>>1,1);for(let a=0;a>1}begin(){return new l(0,this)}end(){return new l(this.i,this)}rBegin(){return new l(this.i-1,this,1)}rEnd(){return new l(-1,this,1)}front(){if(this.i!==0)return this.A[this.j][this.D]}back(){if(this.i!==0)return this.A[this.R][this.N]}pushBack(s){return this.i&&(this.N0?this.N-=1:this.R>0?(this.R-=1,this.N=this.F-1):(this.R=this.P-1,this.N=this.F-1)),this.i-=1,s}pushFront(s){return this.i&&(this.D>0?this.D-=1:this.j>0?(this.j-=1,this.D=this.F-1):(this.j=this.P-1,this.D=this.F-1),this.j===this.R&&this.D===this.N&&this.T()),this.i+=1,this.A[this.j][this.D]=s,this.i}popFront(){if(this.i===0)return;let s=this.A[this.j][this.D];return this.i!==1&&(this.Dthis.i-1)throw new RangeError;let{curNodeBucketIndex:i,curNodePointerIndex:a}=this.O(s);return this.A[i][a]}setElementByPos(s,i){if(s<0||s>this.i-1)throw new RangeError;let{curNodeBucketIndex:a,curNodePointerIndex:u}=this.O(s);this.A[a][u]=i}insert(s,i,a=1){if(s<0||s>this.i)throw new RangeError;if(s===0)for(;a--;)this.pushFront(i);else if(s===this.i)for(;a--;)this.pushBack(i);else{let u=[];for(let c=s;cthis.i-1)throw new RangeError;if(s===0)this.popFront();else if(s===this.i-1)this.popBack();else{let i=[];for(let u=s+1;us;)this.popBack();return this.i}sort(s){let i=[];for(let a=0;a{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.TreeNodeEnableIndex=t.TreeNode=void 0;var e=class{constructor(r,n){this.ee=1,this.u=void 0,this.l=void 0,this.U=void 0,this.W=void 0,this.tt=void 0,this.u=r,this.l=n}L(){let r=this;if(r.ee===1&&r.tt.tt===r)r=r.W;else if(r.U)for(r=r.U;r.W;)r=r.W;else{let n=r.tt;for(;n.U===r;)r=n,n=r.tt;r=n}return r}B(){let r=this;if(r.W){for(r=r.W;r.U;)r=r.U;return r}{let n=r.tt;for(;n.W===r;)r=n,n=r.tt;return r.W!==n?n:r}}te(){let r=this.tt,n=this.W,l=n.U;return r.tt===this?r.tt=n:r.U===this?r.U=n:r.W=n,n.tt=r,n.U=this,this.tt=n,this.W=l,l&&(l.tt=this),n}se(){let r=this.tt,n=this.U,l=n.W;return r.tt===this?r.tt=n:r.U===this?r.U=n:r.W=n,n.tt=r,n.W=this,this.tt=n,this.U=l,l&&(l.tt=this),n}};t.TreeNode=e,t.TreeNodeEnableIndex=class extends e{constructor(){super(...arguments),this.rt=1}te(){let r=super.te();return this.ie(),r.ie(),r}se(){let r=super.se();return this.ie(),r.ie(),r}ie(){this.rt=1,this.U&&(this.rt+=this.U.rt),this.W&&(this.rt+=this.W.rt)}}}),Up=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=G0(),r=br(),n=vr(),l=class extends r.Container{constructor(s=function(a,u){return au?1:0},i=!1){super(),this.Y=void 0,this.v=s,i?(this.re=e.TreeNodeEnableIndex,this.M=function(a,u,c){let d=this.ne(a,u,c);if(d){let f=d.tt;for(;f!==this.h;)f.rt+=1,f=f.tt;let g=this.he(d);if(g){let{parentNode:b,grandParent:_,curNode:S}=g;b.ie(),_.ie(),S.ie()}}return this.i},this.V=function(a){let u=this.fe(a);for(;u!==this.h;)u.rt-=1,u=u.tt}):(this.re=e.TreeNode,this.M=function(a,u,c){let d=this.ne(a,u,c);return d&&this.he(d),this.i},this.V=this.fe),this.h=new this.re}X(s,i){let a=this.h;for(;s;){let u=this.v(s.u,i);if(u<0)s=s.W;else{if(!(u>0))return s;a=s,s=s.U}}return a}Z(s,i){let a=this.h;for(;s;)this.v(s.u,i)<=0?s=s.W:(a=s,s=s.U);return a}$(s,i){let a=this.h;for(;s;){let u=this.v(s.u,i);if(u<0)a=s,s=s.W;else{if(!(u>0))return s;s=s.U}}return a}rr(s,i){let a=this.h;for(;s;)this.v(s.u,i)<0?(a=s,s=s.W):s=s.U;return a}ue(s){for(;;){let i=s.tt;if(i===this.h)return;if(s.ee===1)return void(s.ee=0);if(s===i.U){let a=i.W;if(a.ee===1)a.ee=0,i.ee=1,i===this.Y?this.Y=i.te():i.te();else{if(a.W&&a.W.ee===1)return a.ee=i.ee,i.ee=0,a.W.ee=0,void(i===this.Y?this.Y=i.te():i.te());a.U&&a.U.ee===1?(a.ee=1,a.U.ee=0,a.se()):(a.ee=1,s=i)}}else{let a=i.U;if(a.ee===1)a.ee=0,i.ee=1,i===this.Y?this.Y=i.se():i.se();else{if(a.U&&a.U.ee===1)return a.ee=i.ee,i.ee=0,a.U.ee=0,void(i===this.Y?this.Y=i.se():i.se());a.W&&a.W.ee===1?(a.ee=1,a.W.ee=0,a.te()):(a.ee=1,s=i)}}}}fe(s){if(this.i===1)return this.clear(),this.h;let i=s;for(;i.U||i.W;){if(i.W)for(i=i.W;i.U;)i=i.U;else i=i.U;[s.u,i.u]=[i.u,s.u],[s.l,i.l]=[i.l,s.l],s=i}this.h.U===i?this.h.U=i.tt:this.h.W===i&&(this.h.W=i.tt),this.ue(i);let a=i.tt;return i===a.U?a.U=void 0:a.W=void 0,this.i-=1,this.Y.ee=0,a}oe(s,i){return s!==void 0&&(!(!this.oe(s.U,i)&&!i(s))||this.oe(s.W,i))}he(s){for(;;){let i=s.tt;if(i.ee===0)return;let a=i.tt;if(i===a.U){let u=a.W;if(u&&u.ee===1){if(u.ee=i.ee=0,a===this.Y)return;a.ee=1,s=a;continue}if(s===i.W){if(s.ee=0,s.U&&(s.U.tt=i),s.W&&(s.W.tt=a),i.W=s.U,a.U=s.W,s.U=i,s.W=a,a===this.Y)this.Y=s,this.h.tt=s;else{let c=a.tt;c.U===a?c.U=s:c.W=s}return s.tt=a.tt,i.tt=s,a.tt=s,a.ee=1,{parentNode:i,grandParent:a,curNode:s}}i.ee=0,a===this.Y?this.Y=a.se():a.se(),a.ee=1}else{let u=a.U;if(u&&u.ee===1){if(u.ee=i.ee=0,a===this.Y)return;a.ee=1,s=a;continue}if(s===i.U){if(s.ee=0,s.U&&(s.U.tt=a),s.W&&(s.W.tt=i),a.W=s.U,i.U=s.W,s.U=a,s.W=i,a===this.Y)this.Y=s,this.h.tt=s;else{let c=a.tt;c.U===a?c.U=s:c.W=s}return s.tt=a.tt,i.tt=s,a.tt=s,a.ee=1,{parentNode:i,grandParent:a,curNode:s}}i.ee=0,a===this.Y?this.Y=a.te():a.te(),a.ee=1}return}}ne(s,i,a){if(this.Y===void 0)return this.i+=1,this.Y=new this.re(s,i),this.Y.ee=0,this.Y.tt=this.h,this.h.tt=this.Y,this.h.U=this.Y,void(this.h.W=this.Y);let u,c=this.h.U,d=this.v(c.u,s);if(d!==0){if(d>0)c.U=new this.re(s,i),c.U.tt=c,u=c.U,this.h.U=u;else{let f=this.h.W,g=this.v(f.u,s);if(g===0)return void(f.l=i);if(g<0)f.W=new this.re(s,i),f.W.tt=f,u=f.W,this.h.W=u;else{if(a!==void 0){let b=a.o;if(b!==this.h){let _=this.v(b.u,s);if(_===0)return void(b.l=i);if(_>0){let S=b.L(),U=this.v(S.u,s);if(U===0)return void(S.l=i);U<0&&(u=new this.re(s,i),S.W===void 0?(S.W=u,u.tt=S):(b.U=u,u.tt=b))}}}if(u===void 0)for(u=this.Y;;){let b=this.v(u.u,s);if(b>0){if(u.U===void 0){u.U=new this.re(s,i),u.U.tt=u,u=u.U;break}u=u.U}else{if(!(b<0))return void(u.l=i);if(u.W===void 0){u.W=new this.re(s,i),u.W.tt=u,u=u.W;break}u=u.W}}}}return this.i+=1,u}c.l=i}I(s,i){for(;s;){let a=this.v(s.u,i);if(a<0)s=s.W;else{if(!(a>0))return s;s=s.U}}return s||this.h}clear(){this.i=0,this.Y=void 0,this.h.tt=void 0,this.h.U=this.h.W=void 0}updateKeyByIterator(s,i){let a=s.o;if(a===this.h&&(0,n.throwIteratorAccessError)(),this.i===1)return a.u=i,!0;if(a===this.h.U)return this.v(a.B().u,i)>0&&(a.u=i,!0);if(a===this.h.W)return this.v(a.L().u,i)<0&&(a.u=i,!0);let u=a.L().u;if(this.v(u,i)>=0)return!1;let c=a.B().u;return!(this.v(c,i)<=0)&&(a.u=i,!0)}eraseElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;let i=0,a=this;return this.oe(this.Y,function(u){return s===i?(a.V(u),!0):(i+=1,!1)}),this.i}eraseElementByKey(s){if(this.i===0)return!1;let i=this.I(this.Y,s);return i!==this.h&&(this.V(i),!0)}eraseElementByIterator(s){let i=s.o;i===this.h&&(0,n.throwIteratorAccessError)();let a=i.W===void 0;return s.iteratorType===0?a&&s.next():(!a||i.U===void 0)&&s.next(),this.V(i),s}forEach(s){let i=0;for(let a of this)s(a,i++,this)}getElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;let i,a=0;for(let u of this){if(a===s){i=u;break}a+=1}return i}getHeight(){if(this.i===0)return 0;let s=function(i){return i?Math.max(s(i.U),s(i.W))+1:0};return s(this.Y)}},o=l;t.default=o}),Np=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=br(),r=vr(),n=class extends e.ContainerIterator{constructor(l,o,s){super(s),this.o=l,this.h=o,this.iteratorType===0?(this.pre=function(){return this.o===this.h.U&&(0,r.throwIteratorAccessError)(),this.o=this.o.L(),this},this.next=function(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o=this.o.B(),this}):(this.pre=function(){return this.o===this.h.W&&(0,r.throwIteratorAccessError)(),this.o=this.o.B(),this},this.next=function(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o=this.o.L(),this})}get index(){let l=this.o,o=this.h.tt;if(l===this.h)return o?o.rt-1:0;let s=0;for(l.U&&(s+=l.U.rt);l!==o;){let i=l.tt;l===i.W&&(s+=1,i.U&&(s+=i.U.rt)),l=i}return s}};t.default=n}),Q0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=l(Up()),r=l(Np()),n=vr();function l(i){return i&&i.t?i:{default:i}}var o=class Dp extends r.default{constructor(a,u,c,d){super(a,u,d),this.container=c}get pointer(){return this.o===this.h&&(0,n.throwIteratorAccessError)(),this.o.u}copy(){return new Dp(this.o,this.h,this.container,this.iteratorType)}},s=class extends e.default{constructor(i=[],a,u){super(a,u);let c=this;i.forEach(function(d){c.insert(d)})}*K(i){i!==void 0&&(yield*this.K(i.U),yield i.u,yield*this.K(i.W))}begin(){return new o(this.h.U||this.h,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this.h.W||this.h,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){return this.h.U?this.h.U.u:void 0}back(){return this.h.W?this.h.W.u:void 0}insert(i,a){return this.M(i,void 0,a)}find(i){let a=this.I(this.Y,i);return new o(a,this.h,this)}lowerBound(i){let a=this.X(this.Y,i);return new o(a,this.h,this)}upperBound(i){let a=this.Z(this.Y,i);return new o(a,this.h,this)}reverseLowerBound(i){let a=this.$(this.Y,i);return new o(a,this.h,this)}reverseUpperBound(i){let a=this.rr(this.Y,i);return new o(a,this.h,this)}union(i){let a=this;return i.forEach(function(u){a.insert(u)}),this.i}[Symbol.iterator](){return this.K(this.Y)}};t.default=s}),Y0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=l(Up()),r=l(Np()),n=vr();function l(i){return i&&i.t?i:{default:i}}var o=class Fp extends r.default{constructor(a,u,c,d){super(a,u,d),this.container=c}get pointer(){this.o===this.h&&(0,n.throwIteratorAccessError)();let a=this;return new Proxy([],{get:(u,c)=>c==="0"?a.o.u:c==="1"?a.o.l:void 0,set(u,c,d){if(c!=="1")throw new TypeError("props must be 1");return a.o.l=d,!0}})}copy(){return new Fp(this.o,this.h,this.container,this.iteratorType)}},s=class extends e.default{constructor(i=[],a,u){super(a,u);let c=this;i.forEach(function(d){c.setElement(d[0],d[1])})}*K(i){i!==void 0&&(yield*this.K(i.U),yield[i.u,i.l],yield*this.K(i.W))}begin(){return new o(this.h.U||this.h,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this.h.W||this.h,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){if(this.i===0)return;let i=this.h.U;return[i.u,i.l]}back(){if(this.i===0)return;let i=this.h.W;return[i.u,i.l]}lowerBound(i){let a=this.X(this.Y,i);return new o(a,this.h,this)}upperBound(i){let a=this.Z(this.Y,i);return new o(a,this.h,this)}reverseLowerBound(i){let a=this.$(this.Y,i);return new o(a,this.h,this)}reverseUpperBound(i){let a=this.rr(this.Y,i);return new o(a,this.h,this)}setElement(i,a,u){return this.M(i,a,u)}find(i){let a=this.I(this.Y,i);return new o(a,this.h,this)}getElementByKey(i){return this.I(this.Y,i).l}union(i){let a=this;return i.forEach(function(u){a.setElement(u[0],u[1])}),this.i}[Symbol.iterator](){return this.K(this.Y)}};t.default=s}),Wp=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=function(e){let r=typeof e;return r==="object"&&e!==null||r==="function"}}),$p=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.HashContainerIterator=t.HashContainer=void 0;var e,r=br(),n=(e=Wp())&&e.t?e:{default:e},l=vr(),o=class extends r.ContainerIterator{constructor(i,a,u){super(u),this.o=i,this.h=a,this.iteratorType===0?(this.pre=function(){return this.o.L===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.L,this},this.next=function(){return this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.B,this}):(this.pre=function(){return this.o.B===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.B,this},this.next=function(){return this.o===this.h&&(0,l.throwIteratorAccessError)(),this.o=this.o.L,this})}};t.HashContainerIterator=o;var s=class extends r.Container{constructor(){super(),this.H=[],this.g={},this.HASH_TAG=Symbol("@@HASH_TAG"),Object.setPrototypeOf(this.g,null),this.h={},this.h.L=this.h.B=this.p=this._=this.h}V(i){let{L:a,B:u}=i;a.B=u,u.L=a,i===this.p&&(this.p=u),i===this._&&(this._=a),this.i-=1}M(i,a,u){let c;if(u===void 0&&(u=(0,n.default)(i)),u){let d=i[this.HASH_TAG];if(d!==void 0)return this.H[d].l=a,this.i;Object.defineProperty(i,this.HASH_TAG,{value:this.H.length,configurable:!0}),c={u:i,l:a,L:this._,B:this.h},this.H.push(c)}else{let d=this.g[i];if(d)return d.l=a,this.i;c={u:i,l:a,L:this._,B:this.h},this.g[i]=c}return this.i===0?(this.p=c,this.h.B=c):this._.B=c,this._=c,this.h.L=c,++this.i}I(i,a){if(a===void 0&&(a=(0,n.default)(i)),a){let u=i[this.HASH_TAG];return u===void 0?this.h:this.H[u]}return this.g[i]||this.h}clear(){let i=this.HASH_TAG;this.H.forEach(function(a){delete a.u[i]}),this.H=[],this.g={},Object.setPrototypeOf(this.g,null),this.i=0,this.p=this._=this.h.L=this.h.B=this.h}eraseElementByKey(i,a){let u;if(a===void 0&&(a=(0,n.default)(i)),a){let c=i[this.HASH_TAG];if(c===void 0)return!1;delete i[this.HASH_TAG],u=this.H[c],delete this.H[c]}else{if(u=this.g[i],u===void 0)return!1;delete this.g[i]}return this.V(u),!0}eraseElementByIterator(i){let a=i.o;return a===this.h&&(0,l.throwIteratorAccessError)(),this.V(a),i.next()}eraseElementByPos(i){if(i<0||i>this.i-1)throw new RangeError;let a=this.p;for(;i--;)a=a.B;return this.V(a),this.i}};t.HashContainer=s}),J0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=$p(),r=vr(),n=class Vp extends e.HashContainerIterator{constructor(s,i,a,u){super(s,i,u),this.container=a}get pointer(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o.u}copy(){return new Vp(this.o,this.h,this.container,this.iteratorType)}},l=class extends e.HashContainer{constructor(o=[]){super();let s=this;o.forEach(function(i){s.insert(i)})}begin(){return new n(this.p,this.h,this)}end(){return new n(this.h,this.h,this)}rBegin(){return new n(this._,this.h,this,1)}rEnd(){return new n(this.h,this.h,this,1)}front(){return this.p.u}back(){return this._.u}insert(o,s){return this.M(o,void 0,s)}getElementByPos(o){if(o<0||o>this.i-1)throw new RangeError;let s=this.p;for(;o--;)s=s.B;return s.u}find(o,s){let i=this.I(o,s);return new n(i,this.h,this)}forEach(o){let s=0,i=this.p;for(;i!==this.h;)o(i.u,s++,this),i=i.B}[Symbol.iterator](){return(function*(){let o=this.p;for(;o!==this.h;)yield o.u,o=o.B}).bind(this)()}};t.default=l}),X0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=$p(),n=(e=Wp())&&e.t?e:{default:e},l=vr(),o=class qp extends r.HashContainerIterator{constructor(a,u,c,d){super(a,u,d),this.container=c}get pointer(){this.o===this.h&&(0,l.throwIteratorAccessError)();let a=this;return new Proxy([],{get:(u,c)=>c==="0"?a.o.u:c==="1"?a.o.l:void 0,set(u,c,d){if(c!=="1")throw new TypeError("props must be 1");return a.o.l=d,!0}})}copy(){return new qp(this.o,this.h,this.container,this.iteratorType)}},s=class extends r.HashContainer{constructor(i=[]){super();let a=this;i.forEach(function(u){a.setElement(u[0],u[1])})}begin(){return new o(this.p,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this._,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){if(this.i!==0)return[this.p.u,this.p.l]}back(){if(this.i!==0)return[this._.u,this._.l]}setElement(i,a,u){return this.M(i,a,u)}getElementByKey(i,a){if(a===void 0&&(a=(0,n.default)(i)),a){let c=i[this.HASH_TAG];return c!==void 0?this.H[c].l:void 0}let u=this.g[i];return u?u.l:void 0}getElementByPos(i){if(i<0||i>this.i-1)throw new RangeError;let a=this.p;for(;i--;)a=a.B;return[a.u,a.l]}find(i,a){let u=this.I(i,a);return new o(u,this.h,this)}forEach(i){let a=0,u=this.p;for(;u!==this.h;)i([u.u,u.l],a++,this),u=u.B}[Symbol.iterator](){return(function*(){let i=this.p;for(;i!==this.h;)yield[i.u,i.l],i=i.B}).bind(this)()}};t.default=s}),Z0=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"t",{value:!0}),Object.defineProperty(t,"Deque",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"HashMap",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"HashSet",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"LinkList",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"OrderedMap",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"OrderedSet",{enumerable:!0,get:function(){return i.default}}),Object.defineProperty(t,"PriorityQueue",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(t,"Queue",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(t,"Stack",{enumerable:!0,get:function(){return e.default}}),Object.defineProperty(t,"Vector",{enumerable:!0,get:function(){return l.default}});var e=d($0()),r=d(V0()),n=d(q0()),l=d(H0()),o=d(z0()),s=d(K0()),i=d(Q0()),a=d(Y0()),u=d(J0()),c=d(X0());function d(f){return f&&f.t?f:{default:f}}}),ty=mt((t,e)=>{ht(),pt(),ft();var r=Z0().OrderedSet,n=dr()("number-allocator:trace"),l=dr()("number-allocator:error");function o(i,a){this.low=i,this.high=a}function s(i,a){if(!(this instanceof s))return new s(i,a);this.min=i,this.max=a,this.ss=new r([],(u,c)=>u.compare(c)),n("Create"),this.clear()}o.prototype.equals=function(i){return this.low===i.low&&this.high===i.high},o.prototype.compare=function(i){return this.lowi)&&(c===i?(this.ss.updateKeyByIterator(u,new o(c+1,d)),n("use():"+i),!0):d===i?(this.ss.updateKeyByIterator(u,new o(c,d-1)),n("use():"+i),!0):(this.ss.updateKeyByIterator(u,new o(i+1,d)),this.ss.insert(new o(c,i-1)),n("use():"+i),!0))}return n("use():failed"),!1},s.prototype.free=function(i){if(ithis.max)return void l("free():"+i+" is out of range");let a=new o(i,i),u=this.ss.upperBound(a);if(u.equals(this.ss.end())){if(u.equals(this.ss.begin()))return void this.ss.insert(a);u.pre();let c=u.pointer.high;u.pointer.high+1===i?this.ss.updateKeyByIterator(u,new o(c,i)):this.ss.insert(a)}else if(u.equals(this.ss.begin()))if(i+1===u.pointer.low){let c=u.pointer.high;this.ss.updateKeyByIterator(u,new o(i,c))}else this.ss.insert(a);else{let c=u.pointer.low,d=u.pointer.high;u.pre();let f=u.pointer.low;u.pointer.high+1===i?i+1===c?(this.ss.eraseElementByIterator(u),this.ss.updateKeyByIterator(u,new o(f,d))):this.ss.updateKeyByIterator(u,new o(f,i)):i+1===c?(this.ss.eraseElementByIterator(u.next()),this.ss.insert(new o(i,d))):this.ss.insert(a)}n("free():"+i)},s.prototype.clear=function(){n("clear()"),this.ss.clear(),this.ss.insert(new o(this.min,this.max))},s.prototype.intervalCount=function(){return this.ss.size()},s.prototype.dump=function(){for(let i of this.ss);},e.exports=s}),Hp=mt((t,e)=>{ht(),pt(),ft();var r=ty();e.exports.NumberAllocator=r}),ey=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e=W0(),r=Hp();t.default=class{constructor(n){n>0&&(this.aliasToTopic=new e.LRUCache({max:n}),this.topicToAlias={},this.numberAllocator=new r.NumberAllocator(1,n),this.max=n,this.length=0)}put(n,l){if(l===0||l>this.max)return!1;let o=this.aliasToTopic.get(l);return o&&delete this.topicToAlias[o],this.aliasToTopic.set(l,n),this.topicToAlias[n]=l,this.numberAllocator.use(l),this.length=this.aliasToTopic.size,!0}getTopicByAlias(n){return this.aliasToTopic.get(n)}getAliasByTopic(n){let l=this.topicToAlias[n];return typeof l<"u"&&this.aliasToTopic.get(l),l}clear(){this.aliasToTopic.clear(),this.topicToAlias={},this.numberAllocator.clear(),this.length=0}getLruAlias(){return this.numberAllocator.firstVacant()||[...this.aliasToTopic.keys()][this.aliasToTopic.size-1]}}}),ry=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t,"__esModule",{value:!0});var r=jo(),n=e(ey()),l=Nn();t.default=(o,s)=>{o.log("_handleConnack");let{options:i}=o,a=i.protocolVersion===5?s.reasonCode:s.returnCode;if(clearTimeout(o.connackTimer),delete o.topicAliasSend,s.properties){if(s.properties.topicAliasMaximum){if(s.properties.topicAliasMaximum>65535)return void o.emit("error",new Error("topicAliasMaximum from broker is out of range"));s.properties.topicAliasMaximum>0&&(o.topicAliasSend=new n.default(s.properties.topicAliasMaximum))}s.properties.serverKeepAlive&&i.keepalive&&(i.keepalive=s.properties.serverKeepAlive),s.properties.maximumPacketSize&&(i.properties||(i.properties={}),i.properties.maximumPacketSize=s.properties.maximumPacketSize)}if(a===0)o.reconnecting=!1,o._onConnect(s);else if(a>0){let u=new l.ErrorWithReasonCode(`Connection refused: ${r.ReasonCodes[a]}`,a);o.emit("error",u),o.options.reconnectOnConnackError&&o._cleanUp(!0)}}}),ny=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=(e,r,n)=>{e.log("handling pubrel packet");let l=typeof n<"u"?n:e.noop,{messageId:o}=r,s={cmd:"pubcomp",messageId:o};e.incomingStore.get(r,(i,a)=>{i?e._sendPacket(s,l):(e.emit("message",a.topic,a.payload,a),e.handleMessage(a,u=>{if(u)return l(u);e.incomingStore.del(a,e.noop),e._sendPacket(s,l)}))})}}),iy=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(i){return i&&i.__esModule?i:{default:i}};Object.defineProperty(t,"__esModule",{value:!0});var r=e(N0()),n=e(F0()),l=e(ry()),o=e(jo()),s=e(ny());t.default=(i,a,u)=>{let{options:c}=i;if(c.protocolVersion===5&&c.properties&&c.properties.maximumPacketSize&&c.properties.maximumPacketSize{ht(),pt(),ft();var e=t&&t.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t,"__esModule",{value:!0}),t.TypedEventEmitter=void 0;var r=e((Un(),Dt(dn))),n=Nn(),l=class{};t.TypedEventEmitter=l,(0,n.applyMixin)(l,r.default)}),Uo=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.isReactNativeBrowser=t.isWebWorker=void 0;var e,r=()=>{var o,s;return!(typeof self!="object"||(s=(o=self==null?void 0:self.constructor)===null||o===void 0?void 0:o.name)===null||s===void 0||!s.includes("WorkerGlobalScope"))},n=()=>typeof navigator<"u"&&navigator.product==="ReactNative",l=typeof window<"u"&&(typeof navigator<"u"&&((e=navigator.userAgent)===null||e===void 0?void 0:e.toLowerCase().indexOf(" electron/"))>-1&&Nt!=null&&Nt.versions?!Object.prototype.hasOwnProperty.call(Nt.versions,"electron"):typeof window.document<"u")||r()||n();t.isWebWorker=r(),t.isReactNativeBrowser=n(),t.default=l}),sy=mt((t,e)=>{var r,n;ht(),pt(),ft(),r=t,n=function(l){var o,s=Number.MAX_SAFE_INTEGER===void 0?9007199254740991:Number.MAX_SAFE_INTEGER,i=536870912,a=2*i,u=new WeakMap,c=function(f,g){return function(b){var _=g.get(b),S=_===void 0?b.size:_s)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;b.has(S);)S=Math.floor(Math.random()*s);return f(b,S)}}((o=u,function(f,g){return o.set(f,g),g}),u),d=function(f){return function(g){var b=f(g);return g.add(b),b}}(c);l.addUniqueNumber=d,l.generateUniqueNumber=c},typeof t=="object"&&typeof e<"u"?n(t):typeof define=="function"&&define.amd?define(["exports"],n):n((r=typeof globalThis<"u"?globalThis:r||self).fastUniqueNumbers={})}),ay=mt((t,e)=>{var r,n;ht(),pt(),ft(),r=t,n=function(l,o){l.load=function(s){var i=new Map([[0,function(){}]]),a=new Map([[0,function(){}]]),u=new Map,c=new Worker(s);return c.addEventListener("message",function(d){var f=d.data;if(function(w){return w.method!==void 0&&w.method==="call"}(f)){var g=f.params,b=g.timerId,_=g.timerType;if(_==="interval"){var S=i.get(b);if(typeof S=="number"){var U=u.get(S);if(U===void 0||U.timerId!==b||U.timerType!==_)throw new Error("The timer is in an undefined state.")}else{if(!(typeof S<"u"))throw new Error("The timer is in an undefined state.");S()}}else if(_==="timeout"){var D=a.get(b);if(typeof D=="number"){var G=u.get(D);if(G===void 0||G.timerId!==b||G.timerType!==_)throw new Error("The timer is in an undefined state.")}else{if(!(typeof D<"u"))throw new Error("The timer is in an undefined state.");D(),a.delete(b)}}}else{if(!function(w){return w.error===null&&typeof w.id=="number"}(f)){var $=f.error.message;throw new Error($)}var X=f.id,B=u.get(X);if(B===void 0)throw new Error("The timer is in an undefined state.");var A=B.timerId,E=B.timerType;u.delete(X),E==="interval"?i.delete(A):a.delete(A)}}),{clearInterval:function(d){var f=o.generateUniqueNumber(u);u.set(f,{timerId:d,timerType:"interval"}),i.set(d,f),c.postMessage({id:f,method:"clear",params:{timerId:d,timerType:"interval"}})},clearTimeout:function(d){var f=o.generateUniqueNumber(u);u.set(f,{timerId:d,timerType:"timeout"}),a.set(d,f),c.postMessage({id:f,method:"clear",params:{timerId:d,timerType:"timeout"}})},setInterval:function(d){var f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,g=o.generateUniqueNumber(i);return i.set(g,function(){d(),typeof i.get(g)=="function"&&c.postMessage({id:null,method:"set",params:{delay:f,now:performance.now(),timerId:g,timerType:"interval"}})}),c.postMessage({id:null,method:"set",params:{delay:f,now:performance.now(),timerId:g,timerType:"interval"}}),g},setTimeout:function(d){var f=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,g=o.generateUniqueNumber(a);return a.set(g,d),c.postMessage({id:null,method:"set",params:{delay:f,now:performance.now(),timerId:g,timerType:"timeout"}}),g}}}},typeof t=="object"&&typeof e<"u"?n(t,sy()):typeof define=="function"&&define.amd?define(["exports","fast-unique-numbers"],n):n((r=typeof globalThis<"u"?globalThis:r||self).workerTimersBroker={},r.fastUniqueNumbers)}),ly=mt((t,e)=>{var r,n;ht(),pt(),ft(),r=t,n=function(l,o){var s=function(i,a){var u=null;return function(){if(u!==null)return u;var c=new Blob([a],{type:"application/javascript; charset=utf-8"}),d=URL.createObjectURL(c);return u=i(d),setTimeout(function(){return URL.revokeObjectURL(d)}),u}}(o.load,`(()=>{var e={472:(e,t,r)=>{var o,i;void 0===(i="function"==typeof(o=function(){"use strict";var e=new Map,t=new Map,r=function(t){var r=e.get(t);if(void 0===r)throw new Error('There is no interval scheduled with the given id "'.concat(t,'".'));clearTimeout(r),e.delete(t)},o=function(e){var r=t.get(e);if(void 0===r)throw new Error('There is no timeout scheduled with the given id "'.concat(e,'".'));clearTimeout(r),t.delete(e)},i=function(e,t){var r,o=performance.now();return{expected:o+(r=e-Math.max(0,o-t)),remainingDelay:r}},n=function e(t,r,o,i){var n=performance.now();n>o?postMessage({id:null,method:"call",params:{timerId:r,timerType:i}}):t.set(r,setTimeout(e,o-n,t,r,o,i))},a=function(t,r,o){var a=i(t,o),s=a.expected,d=a.remainingDelay;e.set(r,setTimeout(n,d,e,r,s,"interval"))},s=function(e,r,o){var a=i(e,o),s=a.expected,d=a.remainingDelay;t.set(r,setTimeout(n,d,t,r,s,"timeout"))};addEventListener("message",(function(e){var t=e.data;try{if("clear"===t.method){var i=t.id,n=t.params,d=n.timerId,c=n.timerType;if("interval"===c)r(d),postMessage({error:null,id:i});else{if("timeout"!==c)throw new Error('The given type "'.concat(c,'" is not supported'));o(d),postMessage({error:null,id:i})}}else{if("set"!==t.method)throw new Error('The given method "'.concat(t.method,'" is not supported'));var u=t.params,l=u.delay,p=u.now,m=u.timerId,v=u.timerType;if("interval"===v)a(l,m,p);else{if("timeout"!==v)throw new Error('The given type "'.concat(v,'" is not supported'));s(l,m,p)}}}catch(e){postMessage({error:{message:e.message},id:t.id,result:null})}}))})?o.call(t,r,t,e):o)||(e.exports=i)}},t={};function r(o){var i=t[o];if(void 0!==i)return i.exports;var n=t[o]={exports:{}};return e[o](n,n.exports,r),n.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";r(472)})()})();`);l.clearInterval=function(i){return s().clearInterval(i)},l.clearTimeout=function(i){return s().clearTimeout(i)},l.setInterval=function(){var i;return(i=s()).setInterval.apply(i,arguments)},l.setTimeout=function(){var i;return(i=s()).setTimeout.apply(i,arguments)}},typeof t=="object"&&typeof e<"u"?n(t,ay()):typeof define=="function"&&define.amd?define(["exports","worker-timers-broker"],n):n((r=typeof globalThis<"u"?globalThis:r||self).workerTimers={},r.workerTimersBroker)}),uy=mt(t=>{ht(),pt(),ft();var e=t&&t.__createBinding||(Object.create?function(a,u,c,d){d===void 0&&(d=c);var f=Object.getOwnPropertyDescriptor(u,c);(!f||("get"in f?!u.__esModule:f.writable||f.configurable))&&(f={enumerable:!0,get:function(){return u[c]}}),Object.defineProperty(a,d,f)}:function(a,u,c,d){d===void 0&&(d=c),a[d]=u[c]}),r=t&&t.__setModuleDefault||(Object.create?function(a,u){Object.defineProperty(a,"default",{enumerable:!0,value:u})}:function(a,u){a.default=u}),n=t&&t.__importStar||function(a){if(a&&a.__esModule)return a;var u={};if(a!=null)for(var c in a)c!=="default"&&Object.prototype.hasOwnProperty.call(a,c)&&e(u,a,c);return r(u,a),u};Object.defineProperty(t,"__esModule",{value:!0});var l=n(Uo()),o=ly(),s={set:o.setInterval,clear:o.clearInterval},i={set:(a,u)=>setInterval(a,u),clear:a=>clearInterval(a)};t.default=a=>{switch(a){case"native":return i;case"worker":return s;default:return!l.default||l.isWebWorker||l.isReactNativeBrowser?i:s}}}),zp=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var r=e(uy());t.default=class{get keepaliveTimeoutTimestamp(){return this._keepaliveTimeoutTimestamp}get intervalEvery(){return this._intervalEvery}get keepalive(){return this._keepalive}constructor(n,l){this.destroyed=!1,this.client=n,this.timer=typeof l=="object"&&"set"in l&&"clear"in l?l:(0,r.default)(l),this.setKeepalive(n.options.keepalive)}clear(){this.timerId&&(this.timer.clear(this.timerId),this.timerId=null)}setKeepalive(n){if(n*=1e3,isNaN(n)||n<=0||n>2147483647)throw new Error(`Keepalive value must be an integer between 0 and 2147483647. Provided value is ${n}`);this._keepalive=n,this.reschedule(),this.client.log(`KeepaliveManager: set keepalive to ${n}ms`)}destroy(){this.clear(),this.destroyed=!0}reschedule(){if(this.destroyed)return;this.clear(),this.counter=0;let n=Math.ceil(1.5*this._keepalive);this._keepaliveTimeoutTimestamp=Date.now()+n,this._intervalEvery=Math.ceil(this._keepalive/2),this.timerId=this.timer.set(()=>{this.destroyed||(this.counter+=1,this.counter===2?this.client.sendPing():this.counter>2&&this.client.onKeepaliveTimeout())},this._intervalEvery)}}}),za=mt(t=>{ht(),pt(),ft();var e=t&&t.__createBinding||(Object.create?function(X,B,A,E){E===void 0&&(E=A);var w=Object.getOwnPropertyDescriptor(B,A);(!w||("get"in w?!B.__esModule:w.writable||w.configurable))&&(w={enumerable:!0,get:function(){return B[A]}}),Object.defineProperty(X,E,w)}:function(X,B,A,E){E===void 0&&(E=A),X[E]=B[A]}),r=t&&t.__setModuleDefault||(Object.create?function(X,B){Object.defineProperty(X,"default",{enumerable:!0,value:B})}:function(X,B){X.default=B}),n=t&&t.__importStar||function(X){if(X&&X.__esModule)return X;var B={};if(X!=null)for(var A in X)A!=="default"&&Object.prototype.hasOwnProperty.call(X,A)&&e(B,X,A);return r(B,X),B},l=t&&t.__importDefault||function(X){return X&&X.__esModule?X:{default:X}};Object.defineProperty(t,"__esModule",{value:!0});var o=l(Zm()),s=l(M0()),i=l(xp()),a=gn(),u=l(j0()),c=n(U0()),d=l(dr()),f=l(Cp()),g=l(iy()),b=Nn(),_=oy(),S=l(zp()),U=n(Uo()),D=globalThis.setImmediate||((...X)=>{let B=X.shift();(0,b.nextTick)(()=>{B(...X)})}),G={keepalive:60,reschedulePings:!0,protocolId:"MQTT",protocolVersion:4,reconnectPeriod:1e3,connectTimeout:3e4,clean:!0,resubscribe:!0,writeCache:!0,timerVariant:"auto"},$=class Ka extends _.TypedEventEmitter{static defaultId(){return`mqttjs_${Math.random().toString(16).substr(2,8)}`}constructor(B,A){super(),this.options=A||{};for(let E in G)typeof this.options[E]>"u"?this.options[E]=G[E]:this.options[E]=A[E];this.log=this.options.log||(0,d.default)("mqttjs:client"),this.noop=this._noop.bind(this),this.log("MqttClient :: version:",Ka.VERSION),U.isWebWorker?this.log("MqttClient :: environment","webworker"):this.log("MqttClient :: environment",U.default?"browser":"node"),this.log("MqttClient :: options.protocol",A.protocol),this.log("MqttClient :: options.protocolVersion",A.protocolVersion),this.log("MqttClient :: options.username",A.username),this.log("MqttClient :: options.keepalive",A.keepalive),this.log("MqttClient :: options.reconnectPeriod",A.reconnectPeriod),this.log("MqttClient :: options.rejectUnauthorized",A.rejectUnauthorized),this.log("MqttClient :: options.properties.topicAliasMaximum",A.properties?A.properties.topicAliasMaximum:void 0),this.options.clientId=typeof A.clientId=="string"?A.clientId:Ka.defaultId(),this.log("MqttClient :: clientId",this.options.clientId),this.options.customHandleAcks=A.protocolVersion===5&&A.customHandleAcks?A.customHandleAcks:(...E)=>{E[3](null,0)},this.options.writeCache||(s.default.writeToStream.cacheNumbers=!1),this.streamBuilder=B,this.messageIdProvider=typeof this.options.messageIdProvider>"u"?new i.default:this.options.messageIdProvider,this.outgoingStore=A.outgoingStore||new f.default,this.incomingStore=A.incomingStore||new f.default,this.queueQoSZero=A.queueQoSZero===void 0||A.queueQoSZero,this._resubscribeTopics={},this.messageIdToTopic={},this.keepaliveManager=null,this.connected=!1,this.disconnecting=!1,this.reconnecting=!1,this.queue=[],this.connackTimer=null,this.reconnectTimer=null,this._storeProcessing=!1,this._packetIdsDuringStoreProcessing={},this._storeProcessingQueue=[],this.outgoing={},this._firstConnection=!0,A.properties&&A.properties.topicAliasMaximum>0&&(A.properties.topicAliasMaximum>65535?this.log("MqttClient :: options.properties.topicAliasMaximum is out of range"):this.topicAliasRecv=new o.default(A.properties.topicAliasMaximum)),this.on("connect",()=>{let{queue:E}=this,w=()=>{let M=E.shift();this.log("deliver :: entry %o",M);let R=null;if(!M)return void this._resubscribe();R=M.packet,this.log("deliver :: call _sendPacket for %o",R);let nt=!0;R.messageId&&R.messageId!==0&&(this.messageIdProvider.register(R.messageId)||(nt=!1)),nt?this._sendPacket(R,st=>{M.cb&&M.cb(st),w()}):(this.log("messageId: %d has already used. The message is skipped and removed.",R.messageId),w())};this.log("connect :: sending queued packets"),w()}),this.on("close",()=>{this.log("close :: connected set to `false`"),this.connected=!1,this.log("close :: clearing connackTimer"),clearTimeout(this.connackTimer),this._destroyKeepaliveManager(),this.topicAliasRecv&&this.topicAliasRecv.clear(),this.log("close :: calling _setupReconnect"),this._setupReconnect()}),this.options.manualConnect||(this.log("MqttClient :: setting up stream"),this.connect())}handleAuth(B,A){A()}handleMessage(B,A){A()}_nextId(){return this.messageIdProvider.allocate()}getLastMessageId(){return this.messageIdProvider.getLastAllocated()}connect(){var B;let A=new a.Writable,E=s.default.parser(this.options),w=null,M=[];this.log("connect :: calling method to clear reconnect"),this._clearReconnect(),this.disconnected&&!this.reconnecting&&(this.incomingStore=this.options.incomingStore||new f.default,this.outgoingStore=this.options.outgoingStore||new f.default,this.disconnecting=!1,this.disconnected=!1),this.log("connect :: using streamBuilder provided to client to create stream"),this.stream=this.streamBuilder(this),E.on("packet",Z=>{this.log("parser :: on packet push to packets array."),M.push(Z)});let R=()=>{this.log("work :: getting next packet in queue");let Z=M.shift();if(Z)this.log("work :: packet pulled from queue"),(0,g.default)(this,Z,nt);else{this.log("work :: no packets in queue");let j=w;w=null,this.log("work :: done flag is %s",!!j),j&&j()}},nt=()=>{if(M.length)(0,b.nextTick)(R);else{let Z=w;w=null,Z()}};A._write=(Z,j,N)=>{w=N,this.log("writable stream :: parsing buffer"),E.parse(Z),R()},this.log("connect :: pipe stream to writable stream"),this.stream.pipe(A),this.stream.on("error",Z=>{this.log("streamErrorHandler :: error",Z.message),Z.code?(this.log("streamErrorHandler :: emitting error"),this.emit("error",Z)):this.noop(Z)}),this.stream.on("close",()=>{this.log("(%s)stream :: on close",this.options.clientId),this._flushVolatile(),this.log("stream: emit close to MqttClient"),this.emit("close")}),this.log("connect: sending packet `connect`");let st={cmd:"connect",protocolId:this.options.protocolId,protocolVersion:this.options.protocolVersion,clean:this.options.clean,clientId:this.options.clientId,keepalive:this.options.keepalive,username:this.options.username,password:this.options.password,properties:this.options.properties};if(this.options.will&&(st.will=Object.assign(Object.assign({},this.options.will),{payload:(B=this.options.will)===null||B===void 0?void 0:B.payload})),this.topicAliasRecv&&(st.properties||(st.properties={}),this.topicAliasRecv&&(st.properties.topicAliasMaximum=this.topicAliasRecv.max)),this._writePacket(st),E.on("error",this.emit.bind(this,"error")),this.options.properties){if(!this.options.properties.authenticationMethod&&this.options.properties.authenticationData)return this.end(()=>this.emit("error",new Error("Packet has no Authentication Method"))),this;if(this.options.properties.authenticationMethod&&this.options.authPacket&&typeof this.options.authPacket=="object"){let Z=Object.assign({cmd:"auth",reasonCode:0},this.options.authPacket);this._writePacket(Z)}}return this.stream.setMaxListeners(1e3),clearTimeout(this.connackTimer),this.connackTimer=setTimeout(()=>{this.log("!!connectTimeout hit!! Calling _cleanUp with force `true`"),this.emit("error",new Error("connack timeout")),this._cleanUp(!0)},this.options.connectTimeout),this}publish(B,A,E,w){this.log("publish :: message `%s` to topic `%s`",A,B);let{options:M}=this;typeof E=="function"&&(w=E,E=null),E=E||{},E=Object.assign(Object.assign({},{qos:0,retain:!1,dup:!1}),E);let{qos:R,retain:nt,dup:st,properties:Z,cbStorePut:j}=E;if(this._checkDisconnecting(w))return this;let N=()=>{let F=0;if((R===1||R===2)&&(F=this._nextId(),F===null))return this.log("No messageId left"),!1;let it={cmd:"publish",topic:B,payload:A,qos:R,retain:nt,messageId:F,dup:st};switch(M.protocolVersion===5&&(it.properties=Z),this.log("publish :: qos",R),R){case 1:case 2:this.outgoing[it.messageId]={volatile:!1,cb:w||this.noop},this.log("MqttClient:publish: packet cmd: %s",it.cmd),this._sendPacket(it,void 0,j);break;default:this.log("MqttClient:publish: packet cmd: %s",it.cmd),this._sendPacket(it,w,j)}return!0};return(this._storeProcessing||this._storeProcessingQueue.length>0||!N())&&this._storeProcessingQueue.push({invoke:N,cbStorePut:E.cbStorePut,callback:w}),this}publishAsync(B,A,E){return new Promise((w,M)=>{this.publish(B,A,E,(R,nt)=>{R?M(R):w(nt)})})}subscribe(B,A,E){let w=this.options.protocolVersion;typeof A=="function"&&(E=A),E=E||this.noop;let M=!1,R=[];typeof B=="string"?R=B=[B]:Array.isArray(B)?R=B:typeof B=="object"&&(M=B.resubscribe,delete B.resubscribe,R=Object.keys(B));let nt=c.validateTopics(R);if(nt!==null)return D(E,new Error(`Invalid topic ${nt}`)),this;if(this._checkDisconnecting(E))return this.log("subscribe: discconecting true"),this;let st={qos:0};w===5&&(st.nl=!1,st.rap=!1,st.rh=0);let Z=(A=Object.assign(Object.assign({},st),A)).properties,j=[],N=(it,J)=>{if(J=J||A,!Object.prototype.hasOwnProperty.call(this._resubscribeTopics,it)||this._resubscribeTopics[it].qos{this.log("subscribe: array topic %s",it),N(it)}):Object.keys(B).forEach(it=>{this.log("subscribe: object topic %s, %o",it,B[it]),N(it,B[it])}),!j.length)return E(null,[]),this;let F=()=>{let it=this._nextId();if(it===null)return this.log("No messageId left"),!1;let J={cmd:"subscribe",subscriptions:j,messageId:it};if(Z&&(J.properties=Z),this.options.resubscribe){this.log("subscribe :: resubscribe true");let Y=[];j.forEach(k=>{if(this.options.reconnectPeriod>0){let q={qos:k.qos};w===5&&(q.nl=k.nl||!1,q.rap=k.rap||!1,q.rh=k.rh||0,q.properties=k.properties),this._resubscribeTopics[k.topic]=q,Y.push(k.topic)}}),this.messageIdToTopic[J.messageId]=Y}return this.outgoing[J.messageId]={volatile:!0,cb(Y,k){if(!Y){let{granted:q}=k;for(let et=0;et0||!F())&&this._storeProcessingQueue.push({invoke:F,callback:E}),this}subscribeAsync(B,A){return new Promise((E,w)=>{this.subscribe(B,A,(M,R)=>{M?w(M):E(R)})})}unsubscribe(B,A,E){typeof B=="string"&&(B=[B]),typeof A=="function"&&(E=A),E=E||this.noop;let w=c.validateTopics(B);if(w!==null)return D(E,new Error(`Invalid topic ${w}`)),this;if(this._checkDisconnecting(E))return this;let M=()=>{let R=this._nextId();if(R===null)return this.log("No messageId left"),!1;let nt={cmd:"unsubscribe",messageId:R,unsubscriptions:[]};return typeof B=="string"?nt.unsubscriptions=[B]:Array.isArray(B)&&(nt.unsubscriptions=B),this.options.resubscribe&&nt.unsubscriptions.forEach(st=>{delete this._resubscribeTopics[st]}),typeof A=="object"&&A.properties&&(nt.properties=A.properties),this.outgoing[nt.messageId]={volatile:!0,cb:E},this.log("unsubscribe: call _sendPacket"),this._sendPacket(nt),!0};return(this._storeProcessing||this._storeProcessingQueue.length>0||!M())&&this._storeProcessingQueue.push({invoke:M,callback:E}),this}unsubscribeAsync(B,A){return new Promise((E,w)=>{this.unsubscribe(B,A,(M,R)=>{M?w(M):E(R)})})}end(B,A,E){this.log("end :: (%s)",this.options.clientId),(B==null||typeof B!="boolean")&&(E=E||A,A=B,B=!1),typeof A!="object"&&(E=E||A,A=null),this.log("end :: cb? %s",!!E),(!E||typeof E!="function")&&(E=this.noop);let w=()=>{this.log("end :: closeStores: closing incoming and outgoing stores"),this.disconnected=!0,this.incomingStore.close(R=>{this.outgoingStore.close(nt=>{if(this.log("end :: closeStores: emitting end"),this.emit("end"),E){let st=R||nt;this.log("end :: closeStores: invoking callback with args"),E(st)}})}),this._deferredReconnect?this._deferredReconnect():(this.options.reconnectPeriod===0||this.options.manualConnect)&&(this.disconnecting=!1)},M=()=>{this.log("end :: (%s) :: finish :: calling _cleanUp with force %s",this.options.clientId,B),this._cleanUp(B,()=>{this.log("end :: finish :: calling process.nextTick on closeStores"),(0,b.nextTick)(w)},A)};return this.disconnecting?(E(),this):(this._clearReconnect(),this.disconnecting=!0,!B&&Object.keys(this.outgoing).length>0?(this.log("end :: (%s) :: calling finish in 10ms once outgoing is empty",this.options.clientId),this.once("outgoingEmpty",setTimeout.bind(null,M,10))):(this.log("end :: (%s) :: immediately calling finish",this.options.clientId),M()),this)}endAsync(B,A){return new Promise((E,w)=>{this.end(B,A,M=>{M?w(M):E()})})}removeOutgoingMessage(B){if(this.outgoing[B]){let{cb:A}=this.outgoing[B];this._removeOutgoingAndStoreMessage(B,()=>{A(new Error("Message removed"))})}return this}reconnect(B){this.log("client reconnect");let A=()=>{B?(this.options.incomingStore=B.incomingStore,this.options.outgoingStore=B.outgoingStore):(this.options.incomingStore=null,this.options.outgoingStore=null),this.incomingStore=this.options.incomingStore||new f.default,this.outgoingStore=this.options.outgoingStore||new f.default,this.disconnecting=!1,this.disconnected=!1,this._deferredReconnect=null,this._reconnect()};return this.disconnecting&&!this.disconnected?this._deferredReconnect=A:A(),this}_flushVolatile(){this.outgoing&&(this.log("_flushVolatile :: deleting volatile messages from the queue and setting their callbacks as error function"),Object.keys(this.outgoing).forEach(B=>{this.outgoing[B].volatile&&typeof this.outgoing[B].cb=="function"&&(this.outgoing[B].cb(new Error("Connection closed")),delete this.outgoing[B])}))}_flush(){this.outgoing&&(this.log("_flush: queue exists? %b",!!this.outgoing),Object.keys(this.outgoing).forEach(B=>{typeof this.outgoing[B].cb=="function"&&(this.outgoing[B].cb(new Error("Connection closed")),delete this.outgoing[B])}))}_removeTopicAliasAndRecoverTopicName(B){let A;B.properties&&(A=B.properties.topicAlias);let E=B.topic.toString();if(this.log("_removeTopicAliasAndRecoverTopicName :: alias %d, topic %o",A,E),E.length===0){if(typeof A>"u")return new Error("Unregistered Topic Alias");if(E=this.topicAliasSend.getTopicByAlias(A),typeof E>"u")return new Error("Unregistered Topic Alias");B.topic=E}A&&delete B.properties.topicAlias}_checkDisconnecting(B){return this.disconnecting&&(B&&B!==this.noop?B(new Error("client disconnecting")):this.emit("error",new Error("client disconnecting"))),this.disconnecting}_reconnect(){this.log("_reconnect: emitting reconnect to client"),this.emit("reconnect"),this.connected?(this.end(()=>{this.connect()}),this.log("client already connected. disconnecting first.")):(this.log("_reconnect: calling connect"),this.connect())}_setupReconnect(){!this.disconnecting&&!this.reconnectTimer&&this.options.reconnectPeriod>0?(this.reconnecting||(this.log("_setupReconnect :: emit `offline` state"),this.emit("offline"),this.log("_setupReconnect :: set `reconnecting` to `true`"),this.reconnecting=!0),this.log("_setupReconnect :: setting reconnectTimer for %d ms",this.options.reconnectPeriod),this.reconnectTimer=setInterval(()=>{this.log("reconnectTimer :: reconnect triggered!"),this._reconnect()},this.options.reconnectPeriod)):this.log("_setupReconnect :: doing nothing...")}_clearReconnect(){this.log("_clearReconnect : clearing reconnect timer"),this.reconnectTimer&&(clearInterval(this.reconnectTimer),this.reconnectTimer=null)}_cleanUp(B,A,E={}){if(A&&(this.log("_cleanUp :: done callback provided for on stream close"),this.stream.on("close",A)),this.log("_cleanUp :: forced? %s",B),B)this.options.reconnectPeriod===0&&this.options.clean&&this._flush(),this.log("_cleanUp :: (%s) :: destroying stream",this.options.clientId),this.stream.destroy();else{let w=Object.assign({cmd:"disconnect"},E);this.log("_cleanUp :: (%s) :: call _sendPacket with disconnect packet",this.options.clientId),this._sendPacket(w,()=>{this.log("_cleanUp :: (%s) :: destroying stream",this.options.clientId),D(()=>{this.stream.end(()=>{this.log("_cleanUp :: (%s) :: stream destroyed",this.options.clientId)})})})}!this.disconnecting&&!this.reconnecting&&(this.log("_cleanUp :: client not disconnecting/reconnecting. Clearing and resetting reconnect."),this._clearReconnect(),this._setupReconnect()),this._destroyKeepaliveManager(),A&&!this.connected&&(this.log("_cleanUp :: (%s) :: removing stream `done` callback `close` listener",this.options.clientId),this.stream.removeListener("close",A),A())}_storeAndSend(B,A,E){this.log("storeAndSend :: store packet with cmd %s to outgoingStore",B.cmd);let w,M=B;if(M.cmd==="publish"&&(M=(0,u.default)(B),w=this._removeTopicAliasAndRecoverTopicName(M),w))return A&&A(w);this.outgoingStore.put(M,R=>{if(R)return A&&A(R);E(),this._writePacket(B,A)})}_applyTopicAlias(B){if(this.options.protocolVersion===5&&B.cmd==="publish"){let A;B.properties&&(A=B.properties.topicAlias);let E=B.topic.toString();if(this.topicAliasSend)if(A){if(E.length!==0&&(this.log("applyTopicAlias :: register topic: %s - alias: %d",E,A),!this.topicAliasSend.put(E,A)))return this.log("applyTopicAlias :: error out of range. topic: %s - alias: %d",E,A),new Error("Sending Topic Alias out of range")}else E.length!==0&&(this.options.autoAssignTopicAlias?(A=this.topicAliasSend.getAliasByTopic(E),A?(B.topic="",B.properties=Object.assign(Object.assign({},B.properties),{topicAlias:A}),this.log("applyTopicAlias :: auto assign(use) topic: %s - alias: %d",E,A)):(A=this.topicAliasSend.getLruAlias(),this.topicAliasSend.put(E,A),B.properties=Object.assign(Object.assign({},B.properties),{topicAlias:A}),this.log("applyTopicAlias :: auto assign topic: %s - alias: %d",E,A))):this.options.autoUseTopicAlias&&(A=this.topicAliasSend.getAliasByTopic(E),A&&(B.topic="",B.properties=Object.assign(Object.assign({},B.properties),{topicAlias:A}),this.log("applyTopicAlias :: auto use topic: %s - alias: %d",E,A))));else if(A)return this.log("applyTopicAlias :: error out of range. topic: %s - alias: %d",E,A),new Error("Sending Topic Alias out of range")}}_noop(B){this.log("noop ::",B)}_writePacket(B,A){this.log("_writePacket :: packet: %O",B),this.log("_writePacket :: emitting `packetsend`"),this.emit("packetsend",B),this.log("_writePacket :: writing to stream");let E=s.default.writeToStream(B,this.stream,this.options);this.log("_writePacket :: writeToStream result %s",E),!E&&A&&A!==this.noop?(this.log("_writePacket :: handle events on `drain` once through callback."),this.stream.once("drain",A)):A&&(this.log("_writePacket :: invoking cb"),A())}_sendPacket(B,A,E,w){this.log("_sendPacket :: (%s) :: start",this.options.clientId),E=E||this.noop,A=A||this.noop;let M=this._applyTopicAlias(B);if(M)A(M);else{if(!this.connected)return B.cmd==="auth"?void this._writePacket(B,A):(this.log("_sendPacket :: client not connected. Storing packet offline."),void this._storePacket(B,A,E));if(w)this._writePacket(B,A);else{switch(B.cmd){case"publish":break;case"pubrel":return void this._storeAndSend(B,A,E);default:return void this._writePacket(B,A)}switch(B.qos){case 2:case 1:this._storeAndSend(B,A,E);break;default:this._writePacket(B,A)}this.log("_sendPacket :: (%s) :: end",this.options.clientId)}}}_storePacket(B,A,E){this.log("_storePacket :: packet: %o",B),this.log("_storePacket :: cb? %s",!!A),E=E||this.noop;let w=B;if(w.cmd==="publish"){w=(0,u.default)(B);let R=this._removeTopicAliasAndRecoverTopicName(w);if(R)return A&&A(R)}let M=w.qos||0;M===0&&this.queueQoSZero||w.cmd!=="publish"?this.queue.push({packet:w,cb:A}):M>0?(A=this.outgoing[w.messageId]?this.outgoing[w.messageId].cb:null,this.outgoingStore.put(w,R=>{if(R)return A&&A(R);E()})):A&&A(new Error("No connection to broker"))}_setupKeepaliveManager(){this.log("_setupKeepaliveManager :: keepalive %d (seconds)",this.options.keepalive),!this.keepaliveManager&&this.options.keepalive&&(this.keepaliveManager=new S.default(this,this.options.timerVariant))}_destroyKeepaliveManager(){this.keepaliveManager&&(this.log("_destroyKeepaliveManager :: destroying keepalive manager"),this.keepaliveManager.destroy(),this.keepaliveManager=null)}reschedulePing(B=!1){this.keepaliveManager&&this.options.keepalive&&(B||this.options.reschedulePings)&&this._reschedulePing()}_reschedulePing(){this.log("_reschedulePing :: rescheduling ping"),this.keepaliveManager.reschedule()}sendPing(){this.log("_sendPing :: sending pingreq"),this._sendPacket({cmd:"pingreq"})}onKeepaliveTimeout(){this.emit("error",new Error("Keepalive timeout")),this.log("onKeepaliveTimeout :: calling _cleanUp with force true"),this._cleanUp(!0)}_resubscribe(){this.log("_resubscribe");let B=Object.keys(this._resubscribeTopics);if(!this._firstConnection&&(this.options.clean||this.options.protocolVersion>=4&&!this.connackPacket.sessionPresent)&&B.length>0)if(this.options.resubscribe)if(this.options.protocolVersion===5){this.log("_resubscribe: protocolVersion 5");for(let A=0;A{let E=this.outgoingStore.createStream(),w=()=>{E.destroy(),E=null,this._flushStoreProcessingQueue(),M()},M=()=>{this._storeProcessing=!1,this._packetIdsDuringStoreProcessing={}};this.once("close",w),E.on("error",nt=>{M(),this._flushStoreProcessingQueue(),this.removeListener("close",w),this.emit("error",nt)});let R=()=>{if(!E)return;let nt,st=E.read(1);st?(this._storeProcessing=!0,this._packetIdsDuringStoreProcessing[st.messageId]?R():this.disconnecting||this.reconnectTimer?E.destroy&&E.destroy():(nt=this.outgoing[st.messageId]?this.outgoing[st.messageId].cb:null,this.outgoing[st.messageId]={volatile:!1,cb(Z,j){nt&&nt(Z,j),R()}},this._packetIdsDuringStoreProcessing[st.messageId]=!0,this.messageIdProvider.register(st.messageId)?this._sendPacket(st,void 0,void 0,!0):this.log("messageId: %d has already used.",st.messageId))):E.once("readable",R)};E.on("end",()=>{let nt=!0;for(let st in this._packetIdsDuringStoreProcessing)if(!this._packetIdsDuringStoreProcessing[st]){nt=!1;break}this.removeListener("close",w),nt?(M(),this._invokeAllStoreProcessingQueue(),this.emit("connect",B)):A()}),R()};A()}_invokeStoreProcessingQueue(){if(!this._storeProcessing&&this._storeProcessingQueue.length>0){let B=this._storeProcessingQueue[0];if(B&&B.invoke())return this._storeProcessingQueue.shift(),!0}return!1}_invokeAllStoreProcessingQueue(){for(;this._invokeStoreProcessingQueue(););}_flushStoreProcessingQueue(){for(let B of this._storeProcessingQueue)B.cbStorePut&&B.cbStorePut(new Error("Connection closed")),B.callback&&B.callback(new Error("Connection closed"));this._storeProcessingQueue.splice(0)}_removeOutgoingAndStoreMessage(B,A){delete this.outgoing[B],this.outgoingStore.del({messageId:B},(E,w)=>{A(E,w),this.messageIdProvider.deallocate(B),this._invokeStoreProcessingQueue()})}};$.VERSION=b.MQTTJS_VERSION,t.default=$}),cy=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e=Hp();t.default=class{constructor(){this.numberAllocator=new e.NumberAllocator(1,65535)}allocate(){return this.lastId=this.numberAllocator.alloc(),this.lastId}getLastAllocated(){return this.lastId}register(r){return this.numberAllocator.use(r)}deallocate(r){this.numberAllocator.free(r)}clear(){this.numberAllocator.clear()}}});function Qr(t){throw new RangeError(Gp[t])}function _c(t,e){let r=t.split("@"),n="";return r.length>1&&(n=r[0]+"@",t=r[1]),n+function(l,o){let s=[],i=l.length;for(;i--;)s[i]=o(l[i]);return s}((t=t.replace(Kp,".")).split("."),e).join(".")}function Ec(t){let e=[],r=0,n=t.length;for(;r=55296&&l<=56319&&r{ht(),pt(),ft(),Sc=/^xn--/,Ac=/[^\0-\x7E]/,Kp=/[\x2E\u3002\uFF0E\uFF61]/g,Gp={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},Me=Math.floor,Li=String.fromCharCode,ys=function(t,e){return t+22+75*(t<26)-((e!=0)<<5)},bs=function(t,e,r){let n=0;for(t=r?Me(t/700):t>>1,t+=Me(t/e);t>455;n+=36)t=Me(t/35);return Me(n+36*t/(t+38))},vs=function(t){let e=[],r=t.length,n=0,l=128,o=72,s=t.lastIndexOf("-");s<0&&(s=0);for(let a=0;a=128&&Qr("not-basic"),e.push(t.charCodeAt(a));for(let a=s>0?s+1:0;a=r&&Qr("invalid-input");let g=(i=t.charCodeAt(a++))-48<10?i-22:i-65<26?i-65:i-97<26?i-97:36;(g>=36||g>Me((2147483647-n)/d))&&Qr("overflow"),n+=g*d;let b=f<=o?1:f>=o+26?26:f-o;if(gMe(2147483647/_)&&Qr("overflow"),d*=_}let c=e.length+1;o=bs(n-u,c,u==0),Me(n/c)>2147483647-l&&Qr("overflow"),l+=Me(n/c),n%=c,e.splice(n++,0,l)}var i;return String.fromCodePoint(...e)},ws=function(t){let e=[],r=(t=Ec(t)).length,n=128,l=0,o=72;for(let a of t)a<128&&e.push(Li(a));let s=e.length,i=s;for(s&&e.push("-");i=n&&cMe((2147483647-l)/u)&&Qr("overflow"),l+=(a-n)*u,n=a;for(let c of t)if(c2147483647&&Qr("overflow"),c==n){let d=l;for(let f=36;;f+=36){let g=f<=o?1:f>=o+26?26:f-o;if(dString.fromCodePoint(...t)},decode:vs,encode:ws,toASCII:function(t){return _c(t,function(e){return Ac.test(e)?"xn--"+ws(e):e})},toUnicode:function(t){return _c(t,function(e){return Sc.test(e)?vs(e.slice(4).toLowerCase()):e})}},Jr.encode,Jr.toASCII,Jr.toUnicode,Jr.ucs2,Jr.version});function fy(t,e){return Object.prototype.hasOwnProperty.call(t,e)}var kc,vn,Ic,nr,py=ne(()=>{ht(),pt(),ft(),kc=function(t,e,r,n){e=e||"&",r=r||"=";var l={};if(typeof t!="string"||t.length===0)return l;var o=/\+/g;t=t.split(e);var s=1e3;n&&typeof n.maxKeys=="number"&&(s=n.maxKeys);var i=t.length;s>0&&i>s&&(i=s);for(var a=0;a=0?(u=g.substr(0,b),c=g.substr(b+1)):(u=g,c=""),d=decodeURIComponent(u),f=decodeURIComponent(c),fy(l,d)?Array.isArray(l[d])?l[d].push(f):l[d]=[l[d],f]:l[d]=f}return l},vn=function(t){switch(typeof t){case"string":return t;case"boolean":return t?"true":"false";case"number":return isFinite(t)?t:"";default:return""}},Ic=function(t,e,r,n){return e=e||"&",r=r||"=",t===null&&(t=void 0),typeof t=="object"?Object.keys(t).map(function(l){var o=encodeURIComponent(vn(l))+r;return Array.isArray(t[l])?t[l].map(function(s){return o+encodeURIComponent(vn(s))}).join(e):o+encodeURIComponent(vn(t[l]))}).join(e):n?encodeURIComponent(vn(n))+r+encodeURIComponent(vn(t)):""},(nr={}).decode=nr.parse=kc,nr.encode=nr.stringify=Ic,nr.decode,nr.encode,nr.parse,nr.stringify});function Ga(){throw new Error("setTimeout has not been defined")}function Qa(){throw new Error("clearTimeout has not been defined")}function Qp(t){if(or===setTimeout)return setTimeout(t,0);if((or===Ga||!or)&&setTimeout)return or=setTimeout,setTimeout(t,0);try{return or(t,0)}catch{try{return or.call(null,t,0)}catch{return or.call(this||an,t,0)}}}function dy(){ln&&rn&&(ln=!1,rn.length?ze=rn.concat(ze):ai=-1,ze.length&&Yp())}function Yp(){if(!ln){var t=Qp(dy);ln=!0;for(var e=ze.length;e;){for(rn=ze,ze=[];++ai{ht(),pt(),ft(),an=typeof globalThis<"u"?globalThis:typeof self<"u"?self:to,Kt=Oc={},function(){try{or=typeof setTimeout=="function"?setTimeout:Ga}catch{or=Ga}try{sr=typeof clearTimeout=="function"?clearTimeout:Qa}catch{sr=Qa}}(),ze=[],ln=!1,ai=-1,Kt.nextTick=function(t){var e=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r1)for(var S=1;S{ht(),pt(),ft(),Zi={},Ya=!1,Xr=typeof globalThis<"u"?globalThis:typeof self<"u"?self:to,(zt=my()).platform="browser",zt.addListener,zt.argv,zt.binding,zt.browser,zt.chdir,zt.cwd,zt.emit,zt.env,zt.listeners,zt.nextTick,zt.off,zt.on,zt.once,zt.prependListener,zt.prependOnceListener,zt.removeAllListeners,zt.removeListener,zt.title,zt.umask,zt.version,zt.versions}),_s,Es,Ja,yy=ne(()=>{ht(),pt(),ft(),Jp(),_s={},Es=!1,Ja=function(){if(Es)return _s;Es=!0;var t=zt;function e(l){if(typeof l!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(l))}function r(l,o){for(var s,i="",a=0,u=-1,c=0,d=0;d<=l.length;++d){if(d2){var f=i.lastIndexOf("/");if(f!==i.length-1){f===-1?(i="",a=0):a=(i=i.slice(0,f)).length-1-i.lastIndexOf("/"),u=d,c=0;continue}}else if(i.length===2||i.length===1){i="",a=0,u=d,c=0;continue}}o&&(i.length>0?i+="/..":i="..",a=2)}else i.length>0?i+="/"+l.slice(u+1,d):i=l.slice(u+1,d),a=d-u-1;u=d,c=0}else s===46&&c!==-1?++c:c=-1}return i}var n={resolve:function(){for(var l,o="",s=!1,i=arguments.length-1;i>=-1&&!s;i--){var a;i>=0?a=arguments[i]:(l===void 0&&(l=t.cwd()),a=l),e(a),a.length!==0&&(o=a+"/"+o,s=a.charCodeAt(0)===47)}return o=r(o,!s),s?o.length>0?"/"+o:"/":o.length>0?o:"."},normalize:function(l){if(e(l),l.length===0)return".";var o=l.charCodeAt(0)===47,s=l.charCodeAt(l.length-1)===47;return(l=r(l,!o)).length===0&&!o&&(l="."),l.length>0&&s&&(l+="/"),o?"/"+l:l},isAbsolute:function(l){return e(l),l.length>0&&l.charCodeAt(0)===47},join:function(){if(arguments.length===0)return".";for(var l,o=0;o0&&(l===void 0?l=s:l+="/"+s)}return l===void 0?".":n.normalize(l)},relative:function(l,o){if(e(l),e(o),l===o||(l=n.resolve(l))===(o=n.resolve(o)))return"";for(var s=1;sd){if(o.charCodeAt(u+g)===47)return o.slice(u+g+1);if(g===0)return o.slice(u+g)}else a>d&&(l.charCodeAt(s+g)===47?f=g:g===0&&(f=0));break}var b=l.charCodeAt(s+g);if(b!==o.charCodeAt(u+g))break;b===47&&(f=g)}var _="";for(g=s+f+1;g<=i;++g)(g===i||l.charCodeAt(g)===47)&&(_.length===0?_+="..":_+="/..");return _.length>0?_+o.slice(u+f):(u+=f,o.charCodeAt(u)===47&&++u,o.slice(u))},_makeLong:function(l){return l},dirname:function(l){if(e(l),l.length===0)return".";for(var o=l.charCodeAt(0),s=o===47,i=-1,a=!0,u=l.length-1;u>=1;--u)if((o=l.charCodeAt(u))===47){if(!a){i=u;break}}else a=!1;return i===-1?s?"/":".":s&&i===1?"//":l.slice(0,i)},basename:function(l,o){if(o!==void 0&&typeof o!="string")throw new TypeError('"ext" argument must be a string');e(l);var s,i=0,a=-1,u=!0;if(o!==void 0&&o.length>0&&o.length<=l.length){if(o.length===l.length&&o===l)return"";var c=o.length-1,d=-1;for(s=l.length-1;s>=0;--s){var f=l.charCodeAt(s);if(f===47){if(!u){i=s+1;break}}else d===-1&&(u=!1,d=s+1),c>=0&&(f===o.charCodeAt(c)?--c==-1&&(a=s):(c=-1,a=d))}return i===a?a=d:a===-1&&(a=l.length),l.slice(i,a)}for(s=l.length-1;s>=0;--s)if(l.charCodeAt(s)===47){if(!u){i=s+1;break}}else a===-1&&(u=!1,a=s+1);return a===-1?"":l.slice(i,a)},extname:function(l){e(l);for(var o=-1,s=0,i=-1,a=!0,u=0,c=l.length-1;c>=0;--c){var d=l.charCodeAt(c);if(d!==47)i===-1&&(a=!1,i=c+1),d===46?o===-1?o=c:u!==1&&(u=1):o!==-1&&(u=-1);else if(!a){s=c+1;break}}return o===-1||i===-1||u===0||u===1&&o===i-1&&o===s+1?"":l.slice(o,i)},format:function(l){if(l===null||typeof l!="object")throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof l);return function(o,s){var i=s.dir||s.root,a=s.base||(s.name||"")+(s.ext||"");return i?i===s.root?i+a:i+o+a:a}("/",l)},parse:function(l){e(l);var o={root:"",dir:"",base:"",ext:"",name:""};if(l.length===0)return o;var s,i=l.charCodeAt(0),a=i===47;a?(o.root="/",s=1):s=0;for(var u=-1,c=0,d=-1,f=!0,g=l.length-1,b=0;g>=s;--g)if((i=l.charCodeAt(g))!==47)d===-1&&(f=!1,d=g+1),i===46?u===-1?u=g:b!==1&&(b=1):u!==-1&&(b=-1);else if(!f){c=g+1;break}return u===-1||d===-1||b===0||b===1&&u===d-1&&u===c+1?d!==-1&&(o.base=o.name=c===0&&a?l.slice(1,d):l.slice(c,d)):(c===0&&a?(o.name=l.slice(1,u),o.base=l.slice(1,d)):(o.name=l.slice(c,u),o.base=l.slice(c,d)),o.ext=l.slice(u,d)),c>0?o.dir=l.slice(0,c-1):a&&(o.dir="/"),o},sep:"/",delimiter:":",win32:null,posix:null};return n.posix=n,_s=n}()}),Xp={};function ke(){this.protocol=null,this.slashes=null,this.auth=null,this.host=null,this.port=null,this.hostname=null,this.hash=null,this.search=null,this.query=null,this.pathname=null,this.path=null,this.href=null}function zn(t,e,r){if(t&&Le.isObject(t)&&t instanceof ke)return t;var n=new ke;return n.parse(t,e,r),n}function by(t){if(typeof t=="string")t=new URL(t);else if(!(t instanceof URL))throw new Deno.errors.InvalidData("invalid argument path , must be a string or URL");if(t.protocol!=="file:")throw new Deno.errors.InvalidData("invalid url scheme");return go?function(e){let r=e.hostname,n=e.pathname;for(let l=0;lid||o!==":")throw new Deno.errors.InvalidData("file url path must be absolute");return n.slice(1)}}(t):function(e){if(e.hostname!=="")throw new Deno.errors.InvalidData("invalid file url hostname");let r=e.pathname;for(let n=0;nwd||o!==":")throw new Deno.errors.InvalidData("file url path must be absolute");return n.slice(1)}}(t):function(e){if(e.hostname!=="")throw new Deno.errors.InvalidData("invalid file url hostname");let r=e.pathname;for(let n=0;nmd,Url:()=>hd,default:()=>jt,fileURLToPath:()=>Zp,format:()=>fd,parse:()=>gd,pathToFileURL:()=>td,resolve:()=>pd,resolveObject:()=>dd});var jt,xc,Le,Cc,Rc,Pc,Bc,ji,Ss,As,ks,Mc,Lc,Ui,Yr,Ni,Is,Ts,Xa,jc,ed,rd,nd,id,go,od,sd,ad,ld,ud,cd,Uc,hd,fd,pd,dd,gd,md,yd,bd,vd,wd,mo,_d,Ed,Sd,Ad,kd,Id,wy=ne(()=>{ht(),pt(),ft(),hy(),py(),gy(),yy(),Jp(),xc=Jr,Le={isString:function(t){return typeof t=="string"},isObject:function(t){return typeof t=="object"&&t!==null},isNull:function(t){return t===null},isNullOrUndefined:function(t){return t==null}},(jt={}).parse=zn,jt.resolve=function(t,e){return zn(t,!1,!0).resolve(e)},jt.resolveObject=function(t,e){return t?zn(t,!1,!0).resolveObject(e):e},jt.format=function(t){return Le.isString(t)&&(t=zn(t)),t instanceof ke?t.format():ke.prototype.format.call(t)},jt.Url=ke,Cc=/^([a-z0-9.+-]+:)/i,Rc=/:[0-9]*$/,Pc=/^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,Bc=["{","}","|","\\","^","`"].concat(["<",">",'"',"`"," ","\r",` -`," "]),ji=["'"].concat(Bc),Ss=["%","/","?",";","#"].concat(ji),As=["/","?","#"],ks=/^[+a-z0-9A-Z_-]{0,63}$/,Mc=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,Lc={javascript:!0,"javascript:":!0},Ui={javascript:!0,"javascript:":!0},Yr={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},Ni=nr,ke.prototype.parse=function(t,e,r){if(!Le.isString(t))throw new TypeError("Parameter 'url' must be a string, not "+typeof t);var n=t.indexOf("?"),l=n!==-1&&n127?$+="x":$+=G[X];if(!$.match(ks)){var A=U.slice(0,b),E=U.slice(b+1),w=G.match(Mc);w&&(A.push(w[1]),E.unshift(w[2])),E.length&&(s="/"+E.join(".")+s),this.hostname=A.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),S||(this.hostname=xc.toASCII(this.hostname));var M=this.port?":"+this.port:"",R=this.hostname||"";this.host=R+M,this.href+=this.host,S&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),s[0]!=="/"&&(s="/"+s))}if(!Lc[u])for(b=0,D=ji.length;b0)&&r.host.split("@"))&&(r.auth=w.shift(),r.host=r.hostname=w.shift())),r.search=t.search,r.query=t.query,Le.isNull(r.pathname)&&Le.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.href=r.format(),r;if(!G.length)return r.pathname=null,r.search?r.path="/"+r.search:r.path=null,r.href=r.format(),r;for(var X=G.slice(-1)[0],B=(r.host||t.host||G.length>1)&&(X==="."||X==="..")||X==="",A=0,E=G.length;E>=0;E--)(X=G[E])==="."?G.splice(E,1):X===".."?(G.splice(E,1),A++):A&&(G.splice(E,1),A--);if(!U&&!D)for(;A--;A)G.unshift("..");!U||G[0]===""||G[0]&&G[0].charAt(0)==="/"||G.unshift(""),B&&G.join("/").substr(-1)!=="/"&&G.push("");var w,M=G[0]===""||G[0]&&G[0].charAt(0)==="/";return $&&(r.hostname=r.host=M?"":G.length?G.shift():"",(w=!!(r.host&&r.host.indexOf("@")>0)&&r.host.split("@"))&&(r.auth=w.shift(),r.host=r.hostname=w.shift())),(U=U||r.host&&G.length)&&!M&&G.unshift(""),G.length?r.pathname=G.join("/"):(r.pathname=null,r.path=null),Le.isNull(r.pathname)&&Le.isNull(r.search)||(r.path=(r.pathname?r.pathname:"")+(r.search?r.search:"")),r.auth=t.auth||r.auth,r.slashes=r.slashes||t.slashes,r.href=r.format(),r},ke.prototype.parseHost=function(){var t=this.host,e=Rc.exec(t);e&&((e=e[0])!==":"&&(this.port=e.substr(1)),t=t.substr(0,t.length-e.length)),t&&(this.hostname=t)},jt.Url,jt.format,jt.resolve,jt.resolveObject,Is={},Ts=!1,Xa=function(){if(Ts)return Is;Ts=!0;var t=Gt;function e(l){if(typeof l!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(l))}function r(l,o){for(var s,i="",a=0,u=-1,c=0,d=0;d<=l.length;++d){if(d2){var f=i.lastIndexOf("/");if(f!==i.length-1){f===-1?(i="",a=0):a=(i=i.slice(0,f)).length-1-i.lastIndexOf("/"),u=d,c=0;continue}}else if(i.length===2||i.length===1){i="",a=0,u=d,c=0;continue}}o&&(i.length>0?i+="/..":i="..",a=2)}else i.length>0?i+="/"+l.slice(u+1,d):i=l.slice(u+1,d),a=d-u-1;u=d,c=0}else s===46&&c!==-1?++c:c=-1}return i}var n={resolve:function(){for(var l,o="",s=!1,i=arguments.length-1;i>=-1&&!s;i--){var a;i>=0?a=arguments[i]:(l===void 0&&(l=t.cwd()),a=l),e(a),a.length!==0&&(o=a+"/"+o,s=a.charCodeAt(0)===47)}return o=r(o,!s),s?o.length>0?"/"+o:"/":o.length>0?o:"."},normalize:function(l){if(e(l),l.length===0)return".";var o=l.charCodeAt(0)===47,s=l.charCodeAt(l.length-1)===47;return(l=r(l,!o)).length===0&&!o&&(l="."),l.length>0&&s&&(l+="/"),o?"/"+l:l},isAbsolute:function(l){return e(l),l.length>0&&l.charCodeAt(0)===47},join:function(){if(arguments.length===0)return".";for(var l,o=0;o0&&(l===void 0?l=s:l+="/"+s)}return l===void 0?".":n.normalize(l)},relative:function(l,o){if(e(l),e(o),l===o||(l=n.resolve(l))===(o=n.resolve(o)))return"";for(var s=1;sd){if(o.charCodeAt(u+g)===47)return o.slice(u+g+1);if(g===0)return o.slice(u+g)}else a>d&&(l.charCodeAt(s+g)===47?f=g:g===0&&(f=0));break}var b=l.charCodeAt(s+g);if(b!==o.charCodeAt(u+g))break;b===47&&(f=g)}var _="";for(g=s+f+1;g<=i;++g)(g===i||l.charCodeAt(g)===47)&&(_.length===0?_+="..":_+="/..");return _.length>0?_+o.slice(u+f):(u+=f,o.charCodeAt(u)===47&&++u,o.slice(u))},_makeLong:function(l){return l},dirname:function(l){if(e(l),l.length===0)return".";for(var o=l.charCodeAt(0),s=o===47,i=-1,a=!0,u=l.length-1;u>=1;--u)if((o=l.charCodeAt(u))===47){if(!a){i=u;break}}else a=!1;return i===-1?s?"/":".":s&&i===1?"//":l.slice(0,i)},basename:function(l,o){if(o!==void 0&&typeof o!="string")throw new TypeError('"ext" argument must be a string');e(l);var s,i=0,a=-1,u=!0;if(o!==void 0&&o.length>0&&o.length<=l.length){if(o.length===l.length&&o===l)return"";var c=o.length-1,d=-1;for(s=l.length-1;s>=0;--s){var f=l.charCodeAt(s);if(f===47){if(!u){i=s+1;break}}else d===-1&&(u=!1,d=s+1),c>=0&&(f===o.charCodeAt(c)?--c==-1&&(a=s):(c=-1,a=d))}return i===a?a=d:a===-1&&(a=l.length),l.slice(i,a)}for(s=l.length-1;s>=0;--s)if(l.charCodeAt(s)===47){if(!u){i=s+1;break}}else a===-1&&(u=!1,a=s+1);return a===-1?"":l.slice(i,a)},extname:function(l){e(l);for(var o=-1,s=0,i=-1,a=!0,u=0,c=l.length-1;c>=0;--c){var d=l.charCodeAt(c);if(d!==47)i===-1&&(a=!1,i=c+1),d===46?o===-1?o=c:u!==1&&(u=1):o!==-1&&(u=-1);else if(!a){s=c+1;break}}return o===-1||i===-1||u===0||u===1&&o===i-1&&o===s+1?"":l.slice(o,i)},format:function(l){if(l===null||typeof l!="object")throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof l);return function(o,s){var i=s.dir||s.root,a=s.base||(s.name||"")+(s.ext||"");return i?i===s.root?i+a:i+o+a:a}("/",l)},parse:function(l){e(l);var o={root:"",dir:"",base:"",ext:"",name:""};if(l.length===0)return o;var s,i=l.charCodeAt(0),a=i===47;a?(o.root="/",s=1):s=0;for(var u=-1,c=0,d=-1,f=!0,g=l.length-1,b=0;g>=s;--g)if((i=l.charCodeAt(g))!==47)d===-1&&(f=!1,d=g+1),i===46?u===-1?u=g:b!==1&&(b=1):u!==-1&&(b=-1);else if(!f){c=g+1;break}return u===-1||d===-1||b===0||b===1&&u===d-1&&u===c+1?d!==-1&&(o.base=o.name=c===0&&a?l.slice(1,d):l.slice(c,d)):(c===0&&a?(o.name=l.slice(1,u),o.base=l.slice(1,d)):(o.name=l.slice(c,u),o.base=l.slice(c,d)),o.ext=l.slice(u,d)),c>0?o.dir=l.slice(0,c-1):a&&(o.dir="/"),o},sep:"/",delimiter:":",win32:null,posix:null};return n.posix=n,Is=n}(),jc=typeof Deno<"u"?Deno.build.os==="windows"?"win32":Deno.build.os:void 0,jt.URL=typeof URL<"u"?URL:null,jt.pathToFileURL=vy,jt.fileURLToPath=by,jt.Url,jt.format,jt.resolve,jt.resolveObject,jt.URL,ed=92,rd=47,nd=97,id=122,go=jc==="win32",od=/\//g,sd=/%/g,ad=/\\/g,ld=/\n/g,ud=/\r/g,cd=/\t/g,Uc=typeof Deno<"u"?Deno.build.os==="windows"?"win32":Deno.build.os:void 0,jt.URL=typeof URL<"u"?URL:null,jt.pathToFileURL=td,jt.fileURLToPath=Zp,hd=jt.Url,fd=jt.format,pd=jt.resolve,dd=jt.resolveObject,gd=jt.parse,md=jt.URL,yd=92,bd=47,vd=97,wd=122,mo=Uc==="win32",_d=/\//g,Ed=/%/g,Sd=/\\/g,Ad=/\n/g,kd=/\r/g,Id=/\t/g}),_y=mt((t,e)=>{ht(),pt(),ft(),e.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}}),Ul=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0}),t.BufferedDuplex=t.writev=void 0;var e=gn(),r=(oe(),Dt(ie));function n(o,s){let i=new Array(o.length);for(let a=0;a{this.destroyed||this.push(a)})}_read(o){this.proxy.read(o)}_write(o,s,i){this.isSocketOpen?this.writeToProxy(o,s,i):this.writeQueue.push({chunk:o,encoding:s,cb:i})}_final(o){this.writeQueue=[],this.proxy.end(o)}_destroy(o,s){this.writeQueue=[],this.proxy.destroy(),s(o)}socketReady(){this.emit("connect"),this.isSocketOpen=!0,this.processWriteQueue()}writeToProxy(o,s,i){this.proxy.write(o,s)===!1?this.proxy.once("drain",i):i()}processWriteQueue(){for(;this.writeQueue.length>0;){let{chunk:o,encoding:s,cb:i}=this.writeQueue.shift();this.writeToProxy(o,s,i)}}};t.BufferedDuplex=l}),Di=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(f){return f&&f.__esModule?f:{default:f}};Object.defineProperty(t,"__esModule",{value:!0}),t.streamBuilder=t.browserStreamBuilder=void 0;var r=(oe(),Dt(ie)),n=e(_y()),l=e(dr()),o=gn(),s=e(Uo()),i=Ul(),a=(0,l.default)("mqttjs:ws"),u=["rejectUnauthorized","ca","cert","key","pfx","passphrase"];function c(f,g){let b=`${f.protocol}://${f.hostname}:${f.port}${f.path}`;return typeof f.transformWsUrl=="function"&&(b=f.transformWsUrl(b,f,g)),b}function d(f){let g=f;return f.port||(f.protocol==="wss"?g.port=443:g.port=80),f.path||(g.path="/"),f.wsOptions||(g.wsOptions={}),!s.default&&!f.forceNativeWebSocket&&f.protocol==="wss"&&u.forEach(b=>{Object.prototype.hasOwnProperty.call(f,b)&&!Object.prototype.hasOwnProperty.call(f.wsOptions,b)&&(g.wsOptions[b]=f[b])}),g}t.streamBuilder=(f,g)=>{a("streamBuilder");let b=d(g);b.hostname=b.hostname||b.host||"localhost";let _=c(b,f),S=function(D,G,$){a("createWebSocket"),a(`protocol: ${$.protocolId} ${$.protocolVersion}`);let X,B=$.protocolId==="MQIsdp"&&$.protocolVersion===3?"mqttv3.1":"mqtt";return a(`creating new Websocket for url: ${G} and protocol: ${B}`),X=$.createWebsocket?$.createWebsocket(G,[B],$):new n.default(G,[B],$.wsOptions),X}(0,_,b),U=n.default.createWebSocketStream(S,b.wsOptions);return U.url=_,S.on("close",()=>{U.destroy()}),U},t.browserStreamBuilder=(f,g)=>{a("browserStreamBuilder");let b,_=function(w){let M=d(w);if(M.hostname||(M.hostname=M.host),!M.hostname){if(typeof document>"u")throw new Error("Could not determine host. Specify host manually.");let R=new URL(document.URL);M.hostname=R.hostname,M.port||(M.port=Number(R.port))}return M.objectMode===void 0&&(M.objectMode=!(M.binary===!0||M.binary===void 0)),M}(g).browserBufferSize||524288,S=g.browserBufferTimeout||1e3,U=!g.objectMode,D=function(w,M){let R,nt=M.protocolId==="MQIsdp"&&M.protocolVersion===3?"mqttv3.1":"mqtt",st=c(M,w);return R=M.createWebsocket?M.createWebsocket(st,[nt],M):new WebSocket(st,[nt]),R.binaryType="arraybuffer",R}(f,g),G=function(w,M,R){let nt=new o.Transform({objectMode:w.objectMode});return nt._write=M,nt._flush=R,nt}(g,function w(M,R,nt){if(D.bufferedAmount>_)return void setTimeout(w,S,M,R,nt);U&&typeof M=="string"&&(M=r.Buffer.from(M,"utf8"));try{D.send(M)}catch(st){return nt(st)}nt()},function(w){D.close(),w()});g.objectMode||(G._writev=i.writev.bind(G)),G.on("close",()=>{D.close()});let $=typeof D.addEventListener<"u";function X(){a("WebSocket onOpen"),b instanceof i.BufferedDuplex&&b.socketReady()}function B(w){a("WebSocket onClose",w),b.end(),b.destroy()}function A(w){a("WebSocket onError",w);let M=new Error("WebSocket error");M.event=w,b.destroy(M)}async function E(w){let{data:M}=w;M=M instanceof ArrayBuffer?r.Buffer.from(M):M instanceof Blob?r.Buffer.from(await new Response(M).arrayBuffer()):r.Buffer.from(M,"utf8"),G&&!G.destroyed&&G.push(M)}return D.readyState===D.OPEN?(b=G,b.socket=D):(b=new i.BufferedDuplex(g,G,D),$?D.addEventListener("open",X):D.onopen=X),$?(D.addEventListener("close",B),D.addEventListener("error",A),D.addEventListener("message",E)):(D.onclose=B,D.onerror=A,D.onmessage=E),b}}),Nl={};function Vt(){throw new Error("Node.js net module is not supported by JSPM core outside of Node.js")}fn(Nl,{Server:()=>Vt,Socket:()=>Vt,Stream:()=>Vt,_createServerHandle:()=>Vt,_normalizeArgs:()=>Vt,_setSimultaneousAccepts:()=>Vt,connect:()=>Vt,createConnection:()=>Vt,createServer:()=>Vt,default:()=>Td,isIP:()=>Vt,isIPv4:()=>Vt,isIPv6:()=>Vt});var Td,Od=ne(()=>{ht(),pt(),ft(),Td={_createServerHandle:Vt,_normalizeArgs:Vt,_setSimultaneousAccepts:Vt,connect:Vt,createConnection:Vt,createServer:Vt,isIP:Vt,isIPv4:Vt,isIPv6:Vt,Server:Vt,Socket:Vt,Stream:Vt}}),Nc=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(l){return l&&l.__esModule?l:{default:l}};Object.defineProperty(t,"__esModule",{value:!0});var r=e((Od(),Dt(Nl))),n=(0,e(dr()).default)("mqttjs:tcp");t.default=(l,o)=>{o.port=o.port||1883,o.hostname=o.hostname||o.host||"localhost";let{port:s,path:i}=o,a=o.hostname;return n("port %d and host %s",s,a),r.default.createConnection({port:s,host:a,path:i})}}),xd={};fn(xd,{default:()=>Cd});var Cd,Ey=ne(()=>{ht(),pt(),ft(),Cd={}}),Dc=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t,"__esModule",{value:!0});var r=e((Ey(),Dt(xd))),n=e((Od(),Dt(Nl))),l=(0,e(dr()).default)("mqttjs:tls");t.default=(o,s)=>{s.port=s.port||8883,s.host=s.hostname||s.host||"localhost",n.default.isIP(s.host)===0&&(s.servername=s.host),s.rejectUnauthorized=s.rejectUnauthorized!==!1,delete s.path,l("port %d host %s rejectUnauthorized %b",s.port,s.host,s.rejectUnauthorized);let i=r.default.connect(s);function a(u){s.rejectUnauthorized&&o.emit("error",u),i.end()}return i.on("secureConnect",()=>{s.rejectUnauthorized&&!i.authorized?i.emit("error",new Error("TLS not authorized")):i.removeListener("error",a)}),i.on("error",a),i}}),Fc=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e,r,n,l=(oe(),Dt(ie)),o=gn(),s=Ul();t.default=(i,a)=>{if(a.hostname=a.hostname||a.host,!a.hostname)throw new Error("Could not determine host. Specify host manually.");let u=a.protocolId==="MQIsdp"&&a.protocolVersion===3?"mqttv3.1":"mqtt";(function(f){f.hostname||(f.hostname="localhost"),f.path||(f.path="/"),f.wsOptions||(f.wsOptions={})})(a);let c=function(f,g){let b=f.protocol==="wxs"?"wss":"ws",_=`${b}://${f.hostname}${f.path}`;return f.port&&f.port!==80&&f.port!==443&&(_=`${b}://${f.hostname}:${f.port}${f.path}`),typeof f.transformWsUrl=="function"&&(_=f.transformWsUrl(_,f,g)),_}(a,i);e=wx.connectSocket({url:c,protocols:[u]}),r=function(){let f=new o.Transform;return f._write=(g,b,_)=>{e.send({data:g.buffer,success(){_()},fail(S){_(new Error(S))}})},f._flush=g=>{e.close({success(){g()}})},f}(),(n=new s.BufferedDuplex(a,r,e))._destroy=(f,g)=>{e.close({success(){g&&g(f)}})};let d=n.destroy;return n.destroy=(f,g)=>(n.destroy=d,setTimeout(()=>{e.close({fail(){n._destroy(f,g)}})},0),n),e.onOpen(()=>{n.socketReady()}),e.onMessage(f=>{let{data:g}=f;g=g instanceof ArrayBuffer?l.Buffer.from(g):l.Buffer.from(g,"utf8"),r.push(g)}),e.onClose(()=>{n.emit("close"),n.end(),n.destroy()}),e.onError(f=>{let g=new Error(f.errMsg);n.destroy(g)}),n}}),Wc=mt(t=>{ht(),pt(),ft(),Object.defineProperty(t,"__esModule",{value:!0});var e,r,n,l=(oe(),Dt(ie)),o=gn(),s=Ul(),i=!1;t.default=(a,u)=>{if(u.hostname=u.hostname||u.host,!u.hostname)throw new Error("Could not determine host. Specify host manually.");let c=u.protocolId==="MQIsdp"&&u.protocolVersion===3?"mqttv3.1":"mqtt";(function(f){f.hostname||(f.hostname="localhost"),f.path||(f.path="/"),f.wsOptions||(f.wsOptions={})})(u);let d=function(f,g){let b=f.protocol==="alis"?"wss":"ws",_=`${b}://${f.hostname}${f.path}`;return f.port&&f.port!==80&&f.port!==443&&(_=`${b}://${f.hostname}:${f.port}${f.path}`),typeof f.transformWsUrl=="function"&&(_=f.transformWsUrl(_,f,g)),_}(u,a);return(e=u.my).connectSocket({url:d,protocols:c}),r=function(){let f=new o.Transform;return f._write=(g,b,_)=>{e.sendSocketMessage({data:g.buffer,success(){_()},fail(){_(new Error)}})},f._flush=g=>{e.closeSocket({success(){g()}})},f}(),n=new s.BufferedDuplex(u,r,e),i||(i=!0,e.onSocketOpen(()=>{n.socketReady()}),e.onSocketMessage(f=>{if(typeof f.data=="string"){let g=l.Buffer.from(f.data,"base64");r.push(g)}else{let g=new FileReader;g.addEventListener("load",()=>{let b=g.result;b=b instanceof ArrayBuffer?l.Buffer.from(b):l.Buffer.from(b,"utf8"),r.push(b)}),g.readAsArrayBuffer(f.data)}}),e.onSocketClose(()=>{n.end(),n.destroy()}),e.onSocketError(f=>{n.destroy(f)})),n}}),Sy=mt(t=>{ht(),pt(),ft();var e=t&&t.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(t,"__esModule",{value:!0}),t.connectAsync=void 0;var r=e(dr()),n=e((wy(),Dt(Xp))),l=e(za()),o=e(Uo());typeof(Nt==null?void 0:Nt.nextTick)!="function"&&(Nt.nextTick=setImmediate);var s=(0,r.default)("mqttjs"),i=null;function a(u,c){var d,f,g,b;if(s("connecting to an MQTT broker..."),typeof u=="object"&&!c&&(c=u,u=""),c=c||{},u&&typeof u=="string"){let S=n.default.parse(u,!0),U={};if(S.port!=null&&(U.port=Number(S.port)),U.host=S.hostname,U.query=S.query,U.auth=S.auth,U.protocol=S.protocol,U.path=S.path,U.protocol=(d=U.protocol)===null||d===void 0?void 0:d.replace(/:$/,""),!(c=Object.assign(Object.assign({},U),c)).protocol)throw new Error("Missing protocol")}if(c.unixSocket=c.unixSocket||((f=c.protocol)===null||f===void 0?void 0:f.includes("+unix")),c.unixSocket?c.protocol=c.protocol.replace("+unix",""):(g=c.protocol)!==null&&g!==void 0&&g.startsWith("ws")||(b=c.protocol)!==null&&b!==void 0&&b.startsWith("wx")||delete c.path,function(S){let U;S.auth&&(U=S.auth.match(/^(.+):(.+)$/),U?(S.username=U[1],S.password=U[2]):S.username=S.auth)}(c),c.query&&typeof c.query.clientId=="string"&&(c.clientId=c.query.clientId),c.cert&&c.key){if(!c.protocol)throw new Error("Missing secure protocol key");if(["mqtts","wss","wxs","alis"].indexOf(c.protocol)===-1)switch(c.protocol){case"mqtt":c.protocol="mqtts";break;case"ws":c.protocol="wss";break;case"wx":c.protocol="wxs";break;case"ali":c.protocol="alis";break;default:throw new Error(`Unknown protocol for secure connection: "${c.protocol}"!`)}}if(i||(i={},o.default||c.forceNativeWebSocket?(i.ws=Di().browserStreamBuilder,i.wss=Di().browserStreamBuilder,i.wx=Fc().default,i.wxs=Fc().default,i.ali=Wc().default,i.alis=Wc().default):(i.ws=Di().streamBuilder,i.wss=Di().streamBuilder,i.mqtt=Nc().default,i.tcp=Nc().default,i.ssl=Dc().default,i.tls=i.ssl,i.mqtts=Dc().default)),!i[c.protocol]){let S=["mqtts","wss"].indexOf(c.protocol)!==-1;c.protocol=["mqtt","mqtts","ws","wss","wx","wxs","ali","alis"].filter((U,D)=>(!S||D%2!=0)&&typeof i[U]=="function")[0]}if(c.clean===!1&&!c.clientId)throw new Error("Missing clientId for unclean clients");c.protocol&&(c.defaultProtocol=c.protocol);let _=new l.default(function(S){return c.servers&&((!S._reconnectCount||S._reconnectCount===c.servers.length)&&(S._reconnectCount=0),c.host=c.servers[S._reconnectCount].host,c.port=c.servers[S._reconnectCount].port,c.protocol=c.servers[S._reconnectCount].protocol?c.servers[S._reconnectCount].protocol:c.defaultProtocol,c.hostname=c.host,S._reconnectCount++),s("calling streambuilder for",c.protocol),i[c.protocol](S,c)},c);return _.on("error",()=>{}),_}t.connectAsync=function(u,c,d=!0){return new Promise((f,g)=>{let b=a(u,c),_={connect:U=>{S(),f(b)},end:()=>{S(),f(b)},error:U=>{S(),b.end(),g(U)}};function S(){Object.keys(_).forEach(U=>{b.off(U,_[U])})}d===!1&&(_.close=()=>{_.error(new Error("Couldn't connect to server"))}),Object.keys(_).forEach(U=>{b.on(U,_[U])})})},t.default=a}),$c=mt(t=>{ht(),pt(),ft();var e=t&&t.__createBinding||(Object.create?function(g,b,_,S){S===void 0&&(S=_);var U=Object.getOwnPropertyDescriptor(b,_);(!U||("get"in U?!b.__esModule:U.writable||U.configurable))&&(U={enumerable:!0,get:function(){return b[_]}}),Object.defineProperty(g,S,U)}:function(g,b,_,S){S===void 0&&(S=_),g[S]=b[_]}),r=t&&t.__setModuleDefault||(Object.create?function(g,b){Object.defineProperty(g,"default",{enumerable:!0,value:b})}:function(g,b){g.default=b}),n=t&&t.__importStar||function(g){if(g&&g.__esModule)return g;var b={};if(g!=null)for(var _ in g)_!=="default"&&Object.prototype.hasOwnProperty.call(g,_)&&e(b,g,_);return r(b,g),b},l=t&&t.__exportStar||function(g,b){for(var _ in g)_!=="default"&&!Object.prototype.hasOwnProperty.call(b,_)&&e(b,g,_)},o=t&&t.__importDefault||function(g){return g&&g.__esModule?g:{default:g}};Object.defineProperty(t,"__esModule",{value:!0}),t.ReasonCodes=t.KeepaliveManager=t.UniqueMessageIdProvider=t.DefaultMessageIdProvider=t.Store=t.MqttClient=t.connectAsync=t.connect=t.Client=void 0;var s=o(za());t.MqttClient=s.default;var i=o(xp());t.DefaultMessageIdProvider=i.default;var a=o(cy());t.UniqueMessageIdProvider=a.default;var u=o(Cp());t.Store=u.default;var c=n(Sy());t.connect=c.default,Object.defineProperty(t,"connectAsync",{enumerable:!0,get:function(){return c.connectAsync}});var d=o(zp());t.KeepaliveManager=d.default,t.Client=s.default,l(za(),t),l(Nn(),t);var f=jo();Object.defineProperty(t,"ReasonCodes",{enumerable:!0,get:function(){return f.ReasonCodes}})});const lb=mt(t=>{ht(),pt(),ft();var e=t&&t.__createBinding||(Object.create?function(s,i,a,u){u===void 0&&(u=a);var c=Object.getOwnPropertyDescriptor(i,a);(!c||("get"in c?!i.__esModule:c.writable||c.configurable))&&(c={enumerable:!0,get:function(){return i[a]}}),Object.defineProperty(s,u,c)}:function(s,i,a,u){u===void 0&&(u=a),s[u]=i[a]}),r=t&&t.__setModuleDefault||(Object.create?function(s,i){Object.defineProperty(s,"default",{enumerable:!0,value:i})}:function(s,i){s.default=i}),n=t&&t.__importStar||function(s){if(s&&s.__esModule)return s;var i={};if(s!=null)for(var a in s)a!=="default"&&Object.prototype.hasOwnProperty.call(s,a)&&e(i,s,a);return r(i,s),i},l=t&&t.__exportStar||function(s,i){for(var a in s)a!=="default"&&!Object.prototype.hasOwnProperty.call(i,a)&&e(i,s,a)};Object.defineProperty(t,"__esModule",{value:!0});var o=n($c());t.default=o,l($c(),t)})();/*! Bundled license information: - -@jspm/core/nodelibs/browser/buffer.js: - (*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh *) -*/var be="top",Re="bottom",Pe="right",ve="left",Za="auto",Si=[be,Re,Pe,ve],Pn="start",bi="end",Rd="viewport",Kn="popper",Vc=Si.reduce(function(t,e){return t.concat([e+"-"+Pn,e+"-"+bi])},[]),Pd=[].concat(Si,[Za]).reduce(function(t,e){return t.concat([e,e+"-"+Pn,e+"-"+bi])},[]),Ay=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function Ye(t){return t?(t.nodeName||"").toLowerCase():null}function Be(t){if(t==null)return window;if(t.toString()!=="[object Window]"){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function hn(t){return t instanceof Be(t).Element||t instanceof Element}function Oe(t){return t instanceof Be(t).HTMLElement||t instanceof HTMLElement}function Dl(t){return typeof ShadowRoot<"u"&&(t instanceof Be(t).ShadowRoot||t instanceof ShadowRoot)}const ky={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach(function(r){var n=e.styles[r]||{},l=e.attributes[r]||{},o=e.elements[r];Oe(o)&&Ye(o)&&(Object.assign(o.style,n),Object.keys(l).forEach(function(s){var i=l[s];i===!1?o.removeAttribute(s):o.setAttribute(s,i===!0?"":i)}))})},effect:function(t){var e=t.state,r={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,r.popper),e.styles=r,e.elements.arrow&&Object.assign(e.elements.arrow.style,r.arrow),function(){Object.keys(e.elements).forEach(function(n){var l=e.elements[n],o=e.attributes[n]||{},s=Object.keys(e.styles.hasOwnProperty(n)?e.styles[n]:r[n]).reduce(function(i,a){return i[a]="",i},{});Oe(l)&&Ye(l)&&(Object.assign(l.style,s),Object.keys(o).forEach(function(i){l.removeAttribute(i)}))})}},requires:["computeStyles"]};function Ke(t){return t.split("-")[0]}var un=Math.max,yo=Math.min,Bn=Math.round;function tl(){var t=navigator.userAgentData;return t!=null&&t.brands?t.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function Bd(){return!/^((?!chrome|android).)*safari/i.test(tl())}function Mn(t,e,r){e===void 0&&(e=!1),r===void 0&&(r=!1);var n=t.getBoundingClientRect(),l=1,o=1;e&&Oe(t)&&(l=t.offsetWidth>0&&Bn(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Bn(n.height)/t.offsetHeight||1);var s=(hn(t)?Be(t):window).visualViewport,i=!Bd()&&r,a=(n.left+(i&&s?s.offsetLeft:0))/l,u=(n.top+(i&&s?s.offsetTop:0))/o,c=n.width/l,d=n.height/o;return{width:c,height:d,top:u,right:a+c,bottom:u+d,left:a,x:a,y:u}}function Fl(t){var e=Mn(t),r=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-r)<=1&&(r=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:r,height:n}}function Md(t,e){var r=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(r&&Dl(r)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function hr(t){return Be(t).getComputedStyle(t)}function Iy(t){return["table","td","th"].indexOf(Ye(t))>=0}function Mr(t){return((hn(t)?t.ownerDocument:t.document)||window.document).documentElement}function No(t){return Ye(t)==="html"?t:t.assignedSlot||t.parentNode||(Dl(t)?t.host:null)||Mr(t)}function qc(t){return Oe(t)&&hr(t).position!=="fixed"?t.offsetParent:null}function Ai(t){for(var e=Be(t),r=qc(t);r&&Iy(r)&&hr(r).position==="static";)r=qc(r);return r&&(Ye(r)==="html"||Ye(r)==="body"&&hr(r).position==="static")?e:r||function(n){var l=/firefox/i.test(tl());if(/Trident/i.test(tl())&&Oe(n)&&hr(n).position==="fixed")return null;var o=No(n);for(Dl(o)&&(o=o.host);Oe(o)&&["html","body"].indexOf(Ye(o))<0;){var s=hr(o);if(s.transform!=="none"||s.perspective!=="none"||s.contain==="paint"||["transform","perspective"].indexOf(s.willChange)!==-1||l&&s.willChange==="filter"||l&&s.filter&&s.filter!=="none")return o;o=o.parentNode}return null}(t)||e}function Wl(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Yn(t,e,r){return un(t,yo(e,r))}function Ld(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function jd(t,e){return e.reduce(function(r,n){return r[n]=t,r},{})}const Ty={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,r=t.state,n=t.name,l=t.options,o=r.elements.arrow,s=r.modifiersData.popperOffsets,i=Ke(r.placement),a=Wl(i),u=[ve,Pe].indexOf(i)>=0?"height":"width";if(o&&s){var c=function(E,w){return Ld(typeof(E=typeof E=="function"?E(Object.assign({},w.rects,{placement:w.placement})):E)!="number"?E:jd(E,Si))}(l.padding,r),d=Fl(o),f=a==="y"?be:ve,g=a==="y"?Re:Pe,b=r.rects.reference[u]+r.rects.reference[a]-s[a]-r.rects.popper[u],_=s[a]-r.rects.reference[a],S=Ai(o),U=S?a==="y"?S.clientHeight||0:S.clientWidth||0:0,D=b/2-_/2,G=c[f],$=U-d[u]-c[g],X=U/2-d[u]/2+D,B=Yn(G,X,$),A=a;r.modifiersData[n]=((e={})[A]=B,e.centerOffset=B-X,e)}},effect:function(t){var e=t.state,r=t.options.element,n=r===void 0?"[data-popper-arrow]":r;n!=null&&(typeof n!="string"||(n=e.elements.popper.querySelector(n)))&&Md(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Ln(t){return t.split("-")[1]}var Oy={top:"auto",right:"auto",bottom:"auto",left:"auto"};function Hc(t){var e,r=t.popper,n=t.popperRect,l=t.placement,o=t.variation,s=t.offsets,i=t.position,a=t.gpuAcceleration,u=t.adaptive,c=t.roundOffsets,d=t.isFixed,f=s.x,g=f===void 0?0:f,b=s.y,_=b===void 0?0:b,S=typeof c=="function"?c({x:g,y:_}):{x:g,y:_};g=S.x,_=S.y;var U=s.hasOwnProperty("x"),D=s.hasOwnProperty("y"),G=ve,$=be,X=window;if(u){var B=Ai(r),A="clientHeight",E="clientWidth";B===Be(r)&&hr(B=Mr(r)).position!=="static"&&i==="absolute"&&(A="scrollHeight",E="scrollWidth"),(l===be||(l===ve||l===Pe)&&o===bi)&&($=Re,_-=(d&&B===X&&X.visualViewport?X.visualViewport.height:B[A])-n.height,_*=a?1:-1),(l===ve||(l===be||l===Re)&&o===bi)&&(G=Pe,g-=(d&&B===X&&X.visualViewport?X.visualViewport.width:B[E])-n.width,g*=a?1:-1)}var w,M=Object.assign({position:i},u&&Oy),R=c===!0?function(nt){var st=nt.x,Z=nt.y,j=window.devicePixelRatio||1;return{x:Bn(st*j)/j||0,y:Bn(Z*j)/j||0}}({x:g,y:_}):{x:g,y:_};return g=R.x,_=R.y,a?Object.assign({},M,((w={})[$]=D?"0":"",w[G]=U?"0":"",w.transform=(X.devicePixelRatio||1)<=1?"translate("+g+"px, "+_+"px)":"translate3d("+g+"px, "+_+"px, 0)",w)):Object.assign({},M,((e={})[$]=D?_+"px":"",e[G]=U?g+"px":"",e.transform="",e))}var Fi={passive:!0},xy={left:"right",right:"left",bottom:"top",top:"bottom"};function Wi(t){return t.replace(/left|right|bottom|top/g,function(e){return xy[e]})}var Cy={start:"end",end:"start"};function zc(t){return t.replace(/start|end/g,function(e){return Cy[e]})}function $l(t){var e=Be(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function el(t){return Mn(Mr(t)).left+$l(t).scrollLeft}function Vl(t){var e=hr(t),r=e.overflow,n=e.overflowX,l=e.overflowY;return/auto|scroll|overlay|hidden/.test(r+l+n)}function Ud(t){return["html","body","#document"].indexOf(Ye(t))>=0?t.ownerDocument.body:Oe(t)&&Vl(t)?t:Ud(No(t))}function li(t,e){var r;e===void 0&&(e=[]);var n=Ud(t),l=n===((r=t.ownerDocument)==null?void 0:r.body),o=Be(n),s=l?[o].concat(o.visualViewport||[],Vl(n)?n:[]):n,i=e.concat(s);return l?i:i.concat(li(No(s)))}function rl(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function Kc(t,e,r){return e===Rd?rl(function(n,l){var o=Be(n),s=Mr(n),i=o.visualViewport,a=s.clientWidth,u=s.clientHeight,c=0,d=0;if(i){a=i.width,u=i.height;var f=Bd();(f||!f&&l==="fixed")&&(c=i.offsetLeft,d=i.offsetTop)}return{width:a,height:u,x:c+el(n),y:d}}(t,r)):hn(e)?function(n,l){var o=Mn(n,!1,l==="fixed");return o.top=o.top+n.clientTop,o.left=o.left+n.clientLeft,o.bottom=o.top+n.clientHeight,o.right=o.left+n.clientWidth,o.width=n.clientWidth,o.height=n.clientHeight,o.x=o.left,o.y=o.top,o}(e,r):rl(function(n){var l,o=Mr(n),s=$l(n),i=(l=n.ownerDocument)==null?void 0:l.body,a=un(o.scrollWidth,o.clientWidth,i?i.scrollWidth:0,i?i.clientWidth:0),u=un(o.scrollHeight,o.clientHeight,i?i.scrollHeight:0,i?i.clientHeight:0),c=-s.scrollLeft+el(n),d=-s.scrollTop;return hr(i||o).direction==="rtl"&&(c+=un(o.clientWidth,i?i.clientWidth:0)-a),{width:a,height:u,x:c,y:d}}(Mr(t)))}function Ry(t,e,r,n){var l=e==="clippingParents"?function(a){var u=li(No(a)),c=["absolute","fixed"].indexOf(hr(a).position)>=0&&Oe(a)?Ai(a):a;return hn(c)?u.filter(function(d){return hn(d)&&Md(d,c)&&Ye(d)!=="body"}):[]}(t):[].concat(e),o=[].concat(l,[r]),s=o[0],i=o.reduce(function(a,u){var c=Kc(t,u,n);return a.top=un(c.top,a.top),a.right=yo(c.right,a.right),a.bottom=yo(c.bottom,a.bottom),a.left=un(c.left,a.left),a},Kc(t,s,n));return i.width=i.right-i.left,i.height=i.bottom-i.top,i.x=i.left,i.y=i.top,i}function Nd(t){var e,r=t.reference,n=t.element,l=t.placement,o=l?Ke(l):null,s=l?Ln(l):null,i=r.x+r.width/2-n.width/2,a=r.y+r.height/2-n.height/2;switch(o){case be:e={x:i,y:r.y-n.height};break;case Re:e={x:i,y:r.y+r.height};break;case Pe:e={x:r.x+r.width,y:a};break;case ve:e={x:r.x-n.width,y:a};break;default:e={x:r.x,y:r.y}}var u=o?Wl(o):null;if(u!=null){var c=u==="y"?"height":"width";switch(s){case Pn:e[u]=e[u]-(r[c]/2-n[c]/2);break;case bi:e[u]=e[u]+(r[c]/2-n[c]/2)}}return e}function vi(t,e){e===void 0&&(e={});var r=e,n=r.placement,l=n===void 0?t.placement:n,o=r.strategy,s=o===void 0?t.strategy:o,i=r.boundary,a=i===void 0?"clippingParents":i,u=r.rootBoundary,c=u===void 0?Rd:u,d=r.elementContext,f=d===void 0?Kn:d,g=r.altBoundary,b=g!==void 0&&g,_=r.padding,S=_===void 0?0:_,U=Ld(typeof S!="number"?S:jd(S,Si)),D=f===Kn?"reference":Kn,G=t.rects.popper,$=t.elements[b?D:f],X=Ry(hn($)?$:$.contextElement||Mr(t.elements.popper),a,c,s),B=Mn(t.elements.reference),A=Nd({reference:B,element:G,placement:l}),E=rl(Object.assign({},G,A)),w=f===Kn?E:B,M={top:X.top-w.top+U.top,bottom:w.bottom-X.bottom+U.bottom,left:X.left-w.left+U.left,right:w.right-X.right+U.right},R=t.modifiersData.offset;if(f===Kn&&R){var nt=R[l];Object.keys(M).forEach(function(st){var Z=[Pe,Re].indexOf(st)>=0?1:-1,j=[be,Re].indexOf(st)>=0?"y":"x";M[st]+=nt[j]*Z})}return M}function Py(t,e){e===void 0&&(e={});var r=e,n=r.placement,l=r.boundary,o=r.rootBoundary,s=r.padding,i=r.flipVariations,a=r.allowedAutoPlacements,u=a===void 0?Pd:a,c=Ln(n),d=c?i?Vc:Vc.filter(function(b){return Ln(b)===c}):Si,f=d.filter(function(b){return u.indexOf(b)>=0});f.length===0&&(f=d);var g=f.reduce(function(b,_){return b[_]=vi(t,{placement:_,boundary:l,rootBoundary:o,padding:s})[Ke(_)],b},{});return Object.keys(g).sort(function(b,_){return g[b]-g[_]})}const By={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var l=r.mainAxis,o=l===void 0||l,s=r.altAxis,i=s===void 0||s,a=r.fallbackPlacements,u=r.padding,c=r.boundary,d=r.rootBoundary,f=r.altBoundary,g=r.flipVariations,b=g===void 0||g,_=r.allowedAutoPlacements,S=e.options.placement,U=Ke(S),D=a||(U===S||!b?[Wi(S)]:function(k){if(Ke(k)===Za)return[];var q=Wi(k);return[zc(k),q,zc(q)]}(S)),G=[S].concat(D).reduce(function(k,q){return k.concat(Ke(q)===Za?Py(e,{placement:q,boundary:c,rootBoundary:d,padding:u,flipVariations:b,allowedAutoPlacements:_}):q)},[]),$=e.rects.reference,X=e.rects.popper,B=new Map,A=!0,E=G[0],w=0;w=0,Z=st?"width":"height",j=vi(e,{placement:M,boundary:c,rootBoundary:d,altBoundary:f,padding:u}),N=st?nt?Pe:ve:nt?Re:be;$[Z]>X[Z]&&(N=Wi(N));var F=Wi(N),it=[];if(o&&it.push(j[R]<=0),i&&it.push(j[N]<=0,j[F]<=0),it.every(function(k){return k})){E=M,A=!1;break}B.set(M,it)}if(A)for(var J=function(k){var q=G.find(function(et){var ot=B.get(et);if(ot)return ot.slice(0,k).every(function(at){return at})});if(q)return E=q,"break"},Y=b?3:1;Y>0&&J(Y)!=="break";Y--);e.placement!==E&&(e.modifiersData[n]._skip=!0,e.placement=E,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function Gc(t,e,r){return r===void 0&&(r={x:0,y:0}),{top:t.top-e.height-r.y,right:t.right-e.width+r.x,bottom:t.bottom-e.height+r.y,left:t.left-e.width-r.x}}function Qc(t){return[be,Pe,Re,ve].some(function(e){return t[e]>=0})}const My={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,r=t.options,n=t.name,l=r.offset,o=l===void 0?[0,0]:l,s=Pd.reduce(function(c,d){return c[d]=function(f,g,b){var _=Ke(f),S=[ve,be].indexOf(_)>=0?-1:1,U=typeof b=="function"?b(Object.assign({},g,{placement:f})):b,D=U[0],G=U[1];return D=D||0,G=(G||0)*S,[ve,Pe].indexOf(_)>=0?{x:G,y:D}:{x:D,y:G}}(d,e.rects,o),c},{}),i=s[e.placement],a=i.x,u=i.y;e.modifiersData.popperOffsets!=null&&(e.modifiersData.popperOffsets.x+=a,e.modifiersData.popperOffsets.y+=u),e.modifiersData[n]=s}},Ly={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,n=t.name,l=r.mainAxis,o=l===void 0||l,s=r.altAxis,i=s!==void 0&&s,a=r.boundary,u=r.rootBoundary,c=r.altBoundary,d=r.padding,f=r.tether,g=f===void 0||f,b=r.tetherOffset,_=b===void 0?0:b,S=vi(e,{boundary:a,rootBoundary:u,padding:d,altBoundary:c}),U=Ke(e.placement),D=Ln(e.placement),G=!D,$=Wl(U),X=$==="x"?"y":"x",B=e.modifiersData.popperOffsets,A=e.rects.reference,E=e.rects.popper,w=typeof _=="function"?_(Object.assign({},e.rects,{placement:e.placement})):_,M=typeof w=="number"?{mainAxis:w,altAxis:w}:Object.assign({mainAxis:0,altAxis:0},w),R=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,nt={x:0,y:0};if(B){if(o){var st,Z=$==="y"?be:ve,j=$==="y"?Re:Pe,N=$==="y"?"height":"width",F=B[$],it=F+S[Z],J=F-S[j],Y=g?-E[N]/2:0,k=D===Pn?A[N]:E[N],q=D===Pn?-E[N]:-A[N],et=e.elements.arrow,ot=g&&et?Fl(et):{width:0,height:0},at=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},ut=at[Z],dt=at[j],H=Yn(0,A[N],ot[N]),rt=G?A[N]/2-Y-H-ut-M.mainAxis:k-H-ut-M.mainAxis,tt=G?-A[N]/2+Y+H+dt+M.mainAxis:q+H+dt+M.mainAxis,P=e.elements.arrow&&Ai(e.elements.arrow),W=P?$==="y"?P.clientTop||0:P.clientLeft||0:0,C=(st=R==null?void 0:R[$])!=null?st:0,Q=F+tt-C,h=Yn(g?yo(it,F+rt-C-W):it,F,g?un(J,Q):J);B[$]=h,nt[$]=h-F}if(i){var p,v=$==="x"?be:ve,O=$==="x"?Re:Pe,x=B[X],I=X==="y"?"height":"width",m=x+S[v],y=x-S[O],T=[be,ve].indexOf(U)!==-1,L=(p=R==null?void 0:R[X])!=null?p:0,z=T?m:x-A[I]-E[I]-L+M.altAxis,V=T?x+A[I]+E[I]-L-M.altAxis:y,lt=g&&T?function(ct,yt,bt){var wt=Yn(ct,yt,bt);return wt>bt?bt:wt}(z,x,V):Yn(g?z:m,x,g?V:y);B[X]=lt,nt[X]=lt-x}e.modifiersData[n]=nt}},requiresIfExists:["offset"]};function jy(t,e,r){r===void 0&&(r=!1);var n,l,o=Oe(e),s=Oe(e)&&function(d){var f=d.getBoundingClientRect(),g=Bn(f.width)/d.offsetWidth||1,b=Bn(f.height)/d.offsetHeight||1;return g!==1||b!==1}(e),i=Mr(e),a=Mn(t,s,r),u={scrollLeft:0,scrollTop:0},c={x:0,y:0};return(o||!o&&!r)&&((Ye(e)!=="body"||Vl(i))&&(u=(n=e)!==Be(n)&&Oe(n)?{scrollLeft:(l=n).scrollLeft,scrollTop:l.scrollTop}:$l(n)),Oe(e)?((c=Mn(e,!0)).x+=e.clientLeft,c.y+=e.clientTop):i&&(c.x=el(i))),{x:a.left+u.scrollLeft-c.x,y:a.top+u.scrollTop-c.y,width:a.width,height:a.height}}function Uy(t){var e=new Map,r=new Set,n=[];function l(o){r.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach(function(s){if(!r.has(s)){var i=e.get(s);i&&l(i)}}),n.push(o)}return t.forEach(function(o){e.set(o.name,o)}),t.forEach(function(o){r.has(o.name)||l(o)}),n}function Ny(t){var e;return function(){return e||(e=new Promise(function(r){Promise.resolve().then(function(){e=void 0,r(t())})})),e}}var Yc={placement:"bottom",modifiers:[],strategy:"absolute"};function Jc(){for(var t=arguments.length,e=new Array(t),r=0;r{throw TypeError(t)};var ux=(t,e,r)=>e in t?cx(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var _e=(t,e,r)=>ux(t,typeof e!="symbol"?e+"":e,r),ic=(t,e,r)=>e.has(t)||up("Cannot "+r);var re=(t,e,r)=>(ic(t,e,"read from private field"),r?r.call(t):e.get(t)),We=(t,e,r)=>e.has(t)?up("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(t):e.set(t,r),Te=(t,e,r,n)=>(ic(t,e,"write to private field"),n?n.call(t,r):e.set(t,r),r),Oe=(t,e,r)=>(ic(t,e,"access private method"),r);var Us=(t,e,r,n)=>({set _(i){Te(t,e,i,r)},get _(){return re(t,e,n)}});const lu=typeof global<"u"?global:typeof self<"u"?self:typeof window<"u"?window:{};/** +* @vue/shared v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**//*! #__NO_SIDE_EFFECTS__ */function nd(t){const e=Object.create(null);for(const r of t.split(","))e[r]=1;return r=>r in e}const Je={},Li=[],Ar=()=>{},hx=()=>!1,_l=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&(t.charCodeAt(2)>122||t.charCodeAt(2)<97),id=t=>t.startsWith("onUpdate:"),mt=Object.assign,od=(t,e)=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)},fx=Object.prototype.hasOwnProperty,Ke=(t,e)=>fx.call(t,e),Ie=Array.isArray,Bi=t=>xl(t)==="[object Map]",zb=t=>xl(t)==="[object Set]",Re=t=>typeof t=="function",ot=t=>typeof t=="string",Gr=t=>typeof t=="symbol",rt=t=>t!==null&&typeof t=="object",Hb=t=>(rt(t)||Re(t))&&Re(t.then)&&Re(t.catch),qb=Object.prototype.toString,xl=t=>qb.call(t),Yb=t=>xl(t)==="[object Object]",sd=t=>ot(t)&&t!=="NaN"&&t[0]!=="-"&&""+parseInt(t,10)===t,Fo=nd(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"),Sl=t=>{const e=Object.create(null);return r=>e[r]||(e[r]=t(r))},dx=/-(\w)/g,ir=Sl(t=>t.replace(dx,(e,r)=>r?r.toUpperCase():"")),px=/\B([A-Z])/g,jn=Sl(t=>t.replace(px,"-$1").toLowerCase()),kl=Sl(t=>t.charAt(0).toUpperCase()+t.slice(1)),oc=Sl(t=>t?`on${kl(t)}`:""),An=(t,e)=>!Object.is(t,e),sc=(t,...e)=>{for(let r=0;r{Object.defineProperty(t,e,{configurable:!0,enumerable:!1,writable:n,value:r})},gx=t=>{const e=parseFloat(t);return isNaN(e)?t:e};let hp;const Qa=()=>hp||(hp=typeof globalThis<"u"?globalThis:typeof self<"u"?self:typeof window<"u"?window:lu!==void 0?lu:{});function El(t){if(Ie(t)){const e={};for(let r=0;r{if(r){const n=r.split(yx);n.length>1&&(e[n[0].trim()]=n[1].trim())}}),e}function Ol(t){let e="";if(ot(t))e=t;else if(Ie(t))for(let r=0;r!(!t||t.__v_isRef!==!0),xx=t=>ot(t)?t:t==null?"":Ie(t)||rt(t)&&(t.toString===qb||!Re(t.toString))?Gb(t)?xx(t.value):JSON.stringify(t,Qb,2):String(t),Qb=(t,e)=>Gb(e)?Qb(t,e.value):Bi(e)?{[`Map(${e.size})`]:[...e.entries()].reduce((r,[n,i],o)=>(r[ac(n,o)+" =>"]=i,r),{})}:zb(e)?{[`Set(${e.size})`]:[...e.values()].map(r=>ac(r))}:Gr(e)?ac(e):!rt(e)||Ie(e)||Yb(e)?e:String(e),ac=(t,e="")=>{var r;return Gr(t)?`Symbol(${(r=t.description)!=null?r:e})`:t};function Sx(t){return t==null?"initial":typeof t=="string"?t===""?" ":t:String(t)}/** +* @vue/reactivity v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let Ot,et;class Zb{constructor(e=!1){this.detached=e,this._active=!0,this._on=0,this.effects=[],this.cleanups=[],this._isPaused=!1,this.parent=Ot,!e&&Ot&&(this.index=(Ot.scopes||(Ot.scopes=[])).push(this)-1)}get active(){return this._active}pause(){if(this._active){let e,r;if(this._isPaused=!0,this.scopes)for(e=0,r=this.scopes.length;e0&&--this._on===0&&(Ot=this.prevScope,this.prevScope=void 0)}stop(e){if(this._active){let r,n;for(this._active=!1,r=0,n=this.effects.length;r0)return;if(Uo){let e=Uo;for(Uo=void 0;e;){const r=e.next;e.next=void 0,e.flags&=-9,e=r}}let t;for(;Wo;){let e=Wo;for(Wo=void 0;e;){const r=e.next;if(e.next=void 0,e.flags&=-9,1&e.flags)try{e.trigger()}catch(n){t||(t=n)}e=r}}if(t)throw t}function n0(t){for(let e=t.deps;e;e=e.nextDep)e.version=-1,e.prevActiveLink=e.dep.activeLink,e.dep.activeLink=e}function i0(t){let e,r=t.depsTail,n=r;for(;n;){const i=n.prevDep;n.version===-1?(n===r&&(r=i),cd(n),kx(n)):e=n,n.dep.activeLink=n.prevActiveLink,n.prevActiveLink=void 0,n=i}t.deps=e,t.depsTail=r}function uu(t){for(let e=t.deps;e;e=e.nextDep)if(e.dep.version!==e.version||e.dep.computed&&(o0(e.dep.computed)||e.dep.version!==e.version))return!0;return!!t._dirty}function o0(t){if(4&t.flags&&!(16&t.flags)||(t.flags&=-17,t.globalVersion===ss)||(t.globalVersion=ss,!t.isSSR&&128&t.flags&&(!t.deps&&!t._dirty||!uu(t))))return;t.flags|=2;const e=t.dep,r=et,n=dr;et=t,dr=!0;try{n0(t);const i=t.fn(t._value);(e.version===0||An(i,t._value))&&(t.flags|=128,t._value=i,e.version++)}catch(i){throw e.version++,i}finally{et=r,dr=n,i0(t),t.flags&=-3}}function cd(t,e=!1){const{dep:r,prevSub:n,nextSub:i}=t;if(n&&(n.nextSub=i,t.prevSub=void 0),i&&(i.prevSub=n,t.nextSub=void 0),r.subs===t&&(r.subs=n,!n&&r.computed)){r.computed.flags&=-5;for(let o=r.computed.deps;o;o=o.nextDep)cd(o,!0)}e||--r.sc||!r.map||r.map.delete(r.key)}function kx(t){const{prevDep:e,nextDep:r}=t;e&&(e.nextDep=r,t.prevDep=void 0),r&&(r.prevDep=e,t.nextDep=void 0)}let dr=!0;const s0=[];function $r(){s0.push(dr),dr=!1}function zr(){const t=s0.pop();dr=t===void 0||t}function fp(t){const{cleanup:e}=t;if(t.cleanup=void 0,e){const r=et;et=void 0;try{e()}finally{et=r}}}let ss=0;class Ex{constructor(e,r){this.sub=e,this.dep=r,this.version=r.version,this.nextDep=this.prevDep=this.nextSub=this.prevSub=this.prevActiveLink=void 0}}class ud{constructor(e){this.computed=e,this.version=0,this.activeLink=void 0,this.subs=void 0,this.map=void 0,this.key=void 0,this.sc=0,this.__v_skip=!0}track(e){if(!et||!dr||et===this.computed)return;let r=this.activeLink;if(r===void 0||r.sub!==et)r=this.activeLink=new Ex(et,this),et.deps?(r.prevDep=et.depsTail,et.depsTail.nextDep=r,et.depsTail=r):et.deps=et.depsTail=r,a0(r);else if(r.version===-1&&(r.version=this.version,r.nextDep)){const n=r.nextDep;n.prevDep=r.prevDep,r.prevDep&&(r.prevDep.nextDep=n),r.prevDep=et.depsTail,r.nextDep=void 0,et.depsTail.nextDep=r,et.depsTail=r,et.deps===r&&(et.deps=n)}return r}trigger(e){this.version++,ss++,this.notify(e)}notify(e){ad();try{for(let r=this.subs;r;r=r.prevSub)r.sub.notify()&&r.sub.dep.notify()}finally{ld()}}}function a0(t){if(t.dep.sc++,4&t.sub.flags){const e=t.dep.computed;if(e&&!t.dep.subs){e.flags|=20;for(let n=e.deps;n;n=n.nextDep)a0(n)}const r=t.dep.subs;r!==t&&(t.prevSub=r,r&&(r.nextSub=t)),t.dep.subs=t}}const Za=new WeakMap,ti=Symbol(""),hu=Symbol(""),as=Symbol("");function At(t,e,r){if(dr&&et){let n=Za.get(t);n||Za.set(t,n=new Map);let i=n.get(r);i||(n.set(r,i=new ud),i.map=n,i.key=r),i.track()}}function Fr(t,e,r,n,i,o){const s=Za.get(t);if(!s)return void ss++;const a=l=>{l&&l.trigger()};if(ad(),e==="clear")s.forEach(a);else{const l=Ie(t),c=l&&sd(r);if(l&&r==="length"){const u=Number(n);s.forEach((h,f)=>{(f==="length"||f===as||!Gr(f)&&f>=u)&&a(h)})}else switch((r!==void 0||s.has(void 0))&&a(s.get(r)),c&&a(s.get(as)),e){case"add":l?c&&a(s.get("length")):(a(s.get(ti)),Bi(t)&&a(s.get(hu)));break;case"delete":l||(a(s.get(ti)),Bi(t)&&a(s.get(hu)));break;case"set":Bi(t)&&a(s.get(ti))}}ld()}function bi(t){const e=Le(t);return e===t?e:(At(e,0,as),nr(t)?e:e.map(wt))}function Ml(t){return At(t=Le(t),0,as),t}const Ox={__proto__:null,[Symbol.iterator](){return cc(this,Symbol.iterator,wt)},concat(...t){return bi(this).concat(...t.map(e=>Ie(e)?bi(e):e))},entries(){return cc(this,"entries",t=>(t[1]=wt(t[1]),t))},every(t,e){return Pr(this,"every",t,e,void 0,arguments)},filter(t,e){return Pr(this,"filter",t,e,r=>r.map(wt),arguments)},find(t,e){return Pr(this,"find",t,e,wt,arguments)},findIndex(t,e){return Pr(this,"findIndex",t,e,void 0,arguments)},findLast(t,e){return Pr(this,"findLast",t,e,wt,arguments)},findLastIndex(t,e){return Pr(this,"findLastIndex",t,e,void 0,arguments)},forEach(t,e){return Pr(this,"forEach",t,e,void 0,arguments)},includes(...t){return uc(this,"includes",t)},indexOf(...t){return uc(this,"indexOf",t)},join(t){return bi(this).join(t)},lastIndexOf(...t){return uc(this,"lastIndexOf",t)},map(t,e){return Pr(this,"map",t,e,void 0,arguments)},pop(){return fo(this,"pop")},push(...t){return fo(this,"push",t)},reduce(t,...e){return dp(this,"reduce",t,e)},reduceRight(t,...e){return dp(this,"reduceRight",t,e)},shift(){return fo(this,"shift")},some(t,e){return Pr(this,"some",t,e,void 0,arguments)},splice(...t){return fo(this,"splice",t)},toReversed(){return bi(this).toReversed()},toSorted(t){return bi(this).toSorted(t)},toSpliced(...t){return bi(this).toSpliced(...t)},unshift(...t){return fo(this,"unshift",t)},values(){return cc(this,"values",wt)}};function cc(t,e,r){const n=Ml(t),i=n[e]();return n===t||nr(t)||(i._next=i.next,i.next=()=>{const o=i._next();return o.value&&(o.value=r(o.value)),o}),i}const Mx=Array.prototype;function Pr(t,e,r,n,i,o){const s=Ml(t),a=s!==t&&!nr(t),l=s[e];if(l!==Mx[e]){const h=l.apply(t,o);return a?wt(h):h}let c=r;s!==t&&(a?c=function(h,f){return r.call(this,wt(h),f,t)}:r.length>2&&(c=function(h,f){return r.call(this,h,f,t)}));const u=l.call(s,c,n);return a&&i?i(u):u}function dp(t,e,r,n){const i=Ml(t);let o=r;return i!==t&&(nr(t)?r.length>3&&(o=function(s,a,l){return r.call(this,s,a,l,t)}):o=function(s,a,l){return r.call(this,s,wt(a),l,t)}),i[e](o,...n)}function uc(t,e,r){const n=Le(t);At(n,0,as);const i=n[e](...r);return i!==-1&&i!==!1||!Os(r[0])?i:(r[0]=Le(r[0]),n[e](...r))}function fo(t,e,r=[]){$r(),ad();const n=Le(t)[e].apply(t,r);return ld(),zr(),n}const Ax=nd("__proto__,__v_isRef,__isVue"),l0=new Set(Object.getOwnPropertyNames(Symbol).filter(t=>t!=="arguments"&&t!=="caller").map(t=>Symbol[t]).filter(Gr));function Tx(t){Gr(t)||(t=String(t));const e=Le(this);return At(e,0,t),e.hasOwnProperty(t)}class c0{constructor(e=!1,r=!1){this._isReadonly=e,this._isShallow=r}get(e,r,n){if(r==="__v_skip")return e.__v_skip;const i=this._isReadonly,o=this._isShallow;if(r==="__v_isReactive")return!i;if(r==="__v_isReadonly")return i;if(r==="__v_isShallow")return o;if(r==="__v_raw")return n===(i?o?Bx:d0:o?f0:h0).get(e)||Object.getPrototypeOf(e)===Object.getPrototypeOf(n)?e:void 0;const s=Ie(e);if(!i){let l;if(s&&(l=Ox[r]))return l;if(r==="hasOwnProperty")return Tx}const a=Reflect.get(e,r,ft(e)?e:n);return(Gr(r)?l0.has(r):Ax(r))?a:(i||At(e,0,r),o?a:ft(a)?s&&sd(r)?a:a.value:rt(a)?i?g0(a):Es(a):a)}}class u0 extends c0{constructor(e=!1){super(!1,e)}set(e,r,n,i){let o=e[r];if(!this._isShallow){const l=Rn(o);if(nr(n)||Rn(n)||(o=Le(o),n=Le(n)),!Ie(e)&&ft(o)&&!ft(n))return!l&&(o.value=n,!0)}const s=Ie(e)&&sd(r)?Number(r)t,Vs=t=>Reflect.getPrototypeOf(t);function $s(t){return function(...e){return t!=="delete"&&(t==="clear"?void 0:this)}}function Dx(t,e){const r={get(n){const i=this.__v_raw,o=Le(i),s=Le(n);t||(An(n,s)&&At(o,0,n),At(o,0,s));const{has:a}=Vs(o),l=e?hc:t?_a:wt;return a.call(o,n)?l(i.get(n)):a.call(o,s)?l(i.get(s)):void(i!==o&&i.get(n))},get size(){const n=this.__v_raw;return!t&&At(Le(n),0,ti),Reflect.get(n,"size",n)},has(n){const i=this.__v_raw,o=Le(i),s=Le(n);return t||(An(n,s)&&At(o,0,n),At(o,0,s)),n===s?i.has(n):i.has(n)||i.has(s)},forEach(n,i){const o=this,s=o.__v_raw,a=Le(s),l=e?hc:t?_a:wt;return!t&&At(a,0,ti),s.forEach((c,u)=>n.call(i,l(c),l(u),o))}};return mt(r,t?{add:$s("add"),set:$s("set"),delete:$s("delete"),clear:$s("clear")}:{add(n){e||nr(n)||Rn(n)||(n=Le(n));const i=Le(this);return Vs(i).has.call(i,n)||(i.add(n),Fr(i,"add",n,n)),this},set(n,i){e||nr(i)||Rn(i)||(i=Le(i));const o=Le(this),{has:s,get:a}=Vs(o);let l=s.call(o,n);l||(n=Le(n),l=s.call(o,n));const c=a.call(o,n);return o.set(n,i),l?An(i,c)&&Fr(o,"set",n,i):Fr(o,"add",n,i),this},delete(n){const i=Le(this),{has:o,get:s}=Vs(i);let a=o.call(i,n);a||(n=Le(n),a=o.call(i,n)),s&&s.call(i,n);const l=i.delete(n);return a&&Fr(i,"delete",n,void 0),l},clear(){const n=Le(this),i=n.size!==0,o=n.clear();return i&&Fr(n,"clear",void 0,void 0),o}}),["keys","values","entries",Symbol.iterator].forEach(n=>{r[n]=function(i,o,s){return function(...a){const l=this.__v_raw,c=Le(l),u=Bi(c),h=i==="entries"||i===Symbol.iterator&&u,f=i==="keys"&&u,d=l[i](...a),y=s?hc:o?_a:wt;return!o&&At(c,0,f?hu:ti),{next(){const{value:g,done:p}=d.next();return p?{value:g,done:p}:{value:h?[y(g[0]),y(g[1])]:y(g),done:p}},[Symbol.iterator](){return this}}}}(n,t,e)}),r}function hd(t,e){const r=Dx(t,e);return(n,i,o)=>i==="__v_isReactive"?!t:i==="__v_isReadonly"?t:i==="__v_raw"?n:Reflect.get(Ke(r,i)&&i in n?r:n,i,o)}const jx={get:hd(!1,!1)},Nx={get:hd(!1,!0)},Lx={get:hd(!0,!1)},h0=new WeakMap,f0=new WeakMap,d0=new WeakMap,Bx=new WeakMap;function Fx(t){return t.__v_skip||!Object.isExtensible(t)?0:function(e){switch(e){case"Object":case"Array":return 1;case"Map":case"Set":case"WeakMap":case"WeakSet":return 2;default:return 0}}((e=>xl(e).slice(8,-1))(t))}function Es(t){return Rn(t)?t:fd(t,!1,Px,jx,h0)}function p0(t){return fd(t,!1,Rx,Nx,f0)}function g0(t){return fd(t,!0,Cx,Lx,d0)}function fd(t,e,r,n,i){if(!rt(t)||t.__v_raw&&(!e||!t.__v_isReactive))return t;const o=Fx(t);if(o===0)return t;const s=i.get(t);if(s)return s;const a=new Proxy(t,o===2?n:r);return i.set(t,a),a}function Tn(t){return Rn(t)?Tn(t.__v_raw):!(!t||!t.__v_isReactive)}function Rn(t){return!(!t||!t.__v_isReadonly)}function nr(t){return!(!t||!t.__v_isShallow)}function Os(t){return!!t&&!!t.__v_raw}function Le(t){const e=t&&t.__v_raw;return e?Le(e):t}function dd(t){return!Ke(t,"__v_skip")&&Object.isExtensible(t)&&cu(t,"__v_skip",!0),t}const wt=t=>rt(t)?Es(t):t,_a=t=>rt(t)?g0(t):t;function ft(t){return!!t&&t.__v_isRef===!0}function Al(t){return m0(t,!1)}function pd(t){return m0(t,!0)}function m0(t,e){return ft(t)?t:new Wx(t,e)}class Wx{constructor(e,r){this.dep=new ud,this.__v_isRef=!0,this.__v_isShallow=!1,this._rawValue=r?e:Le(e),this._value=r?e:wt(e),this.__v_isShallow=r}get value(){return this.dep.track(),this._value}set value(e){const r=this._rawValue,n=this.__v_isShallow||nr(e)||Rn(e);e=n?e:Le(e),An(e,r)&&(this._rawValue=e,this._value=n?e:wt(e),this.dep.trigger())}}function Fi(t){return ft(t)?t.value:t}const Ux={get:(t,e,r)=>e==="__v_raw"?t:Fi(Reflect.get(t,e,r)),set:(t,e,r,n)=>{const i=t[e];return ft(i)&&!ft(r)?(i.value=r,!0):Reflect.set(t,e,r,n)}};function y0(t){return Tn(t)?t:new Proxy(t,Ux)}class Vx{constructor(e,r,n){this._object=e,this._key=r,this._defaultValue=n,this.__v_isRef=!0,this._value=void 0}get value(){const e=this._object[this._key];return this._value=e===void 0?this._defaultValue:e}set value(e){this._object[this._key]=e}get dep(){return function(e,r){const n=Za.get(e);return n&&n.get(r)}(Le(this._object),this._key)}}function $x(t,e,r){const n=t[e];return ft(n)?n:new Vx(t,e,r)}class zx{constructor(e,r,n){this.fn=e,this.setter=r,this._value=void 0,this.dep=new ud(this),this.__v_isRef=!0,this.deps=void 0,this.depsTail=void 0,this.flags=16,this.globalVersion=ss-1,this.next=void 0,this.effect=this,this.__v_isReadonly=!r,this.isSSR=n}notify(){if(this.flags|=16,!(8&this.flags)&&et!==this)return r0(this,!0),!0}get value(){const e=this.dep.track();return o0(this),e&&(e.version=this.dep.version),this._value}set value(e){this.setter&&this.setter(e)}}const zs={},Hs=new WeakMap;let Bn;function Hx(t,e,r=Je){const{immediate:n,deep:i,once:o,scheduler:s,augmentJob:a,call:l}=r,c=k=>i?k:nr(k)||i===!1||i===0?Wr(k,1):Wr(k);let u,h,f,d,y=!1,g=!1;if(ft(t)?(h=()=>t.value,y=nr(t)):Tn(t)?(h=()=>c(t),y=!0):Ie(t)?(g=!0,y=t.some(k=>Tn(k)||nr(k)),h=()=>t.map(k=>ft(k)?k.value:Tn(k)?c(k):Re(k)?l?l(k,2):k():void 0)):h=Re(t)?e?l?()=>l(t,2):t:()=>{if(f){$r();try{f()}finally{zr()}}const k=Bn;Bn=u;try{return l?l(t,3,[d]):t(d)}finally{Bn=k}}:Ar,e&&i){const k=h,M=i===!0?1/0:i;h=()=>Wr(k(),M)}const p=Jb(),b=()=>{u.stop(),p&&p.active&&od(p.effects,u)};if(o&&e){const k=e;e=(...M)=>{k(...M),b()}}let v=g?new Array(t.length).fill(zs):zs;const S=k=>{if(1&u.flags&&(u.dirty||k))if(e){const M=u.run();if(i||y||(g?M.some((A,I)=>An(A,v[I])):An(M,v))){f&&f();const A=Bn;Bn=u;try{const I=[M,v===zs?void 0:g&&v[0]===zs?[]:v,d];v=M,l?l(e,3,I):e(...I)}finally{Bn=A}}}else u.run()};return a&&a(S),u=new e0(h),u.scheduler=s?()=>s(S,!1):S,d=k=>function(M,A=!1,I=Bn){if(I){let T=Hs.get(I);T||Hs.set(I,T=[]),T.push(M)}}(k,!1,u),f=u.onStop=()=>{const k=Hs.get(u);if(k){if(l)l(k,4);else for(const M of k)M();Hs.delete(u)}},e?n?S(!0):v=u.run():s?s(S.bind(null,!0),!0):u.run(),b.pause=u.pause.bind(u),b.resume=u.resume.bind(u),b.stop=b,b}function Wr(t,e=1/0,r){if(e<=0||!rt(t)||t.__v_skip||(r=r||new Set).has(t))return t;if(r.add(t),e--,ft(t))Wr(t.value,e,r);else if(Ie(t))for(let n=0;n{Wr(n,e,r)});else if(Yb(t)){for(const n in t)Wr(t[n],e,r);for(const n of Object.getOwnPropertySymbols(t))Object.prototype.propertyIsEnumerable.call(t,n)&&Wr(t[n],e,r)}return t}/** +* @vue/runtime-core v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/function Ms(t,e,r,n){try{return n?t(...n):t()}catch(i){Tl(i,e,r)}}function pr(t,e,r,n){if(Re(t)){const i=Ms(t,e,r,n);return i&&Hb(i)&&i.catch(o=>{Tl(o,e,r)}),i}if(Ie(t)){const i=[];for(let o=0;o=Vo(r)?jt.push(t):jt.splice(function(n){let i=wr+1,o=jt.length;for(;i>>1,a=jt[s],l=Vo(a);lVo(r)-Vo(n));if(Wi.length=0,mn)return void mn.push(...e);for(mn=e,Oi=0;Oit.id==null?2&t.flags?-1:1/0:t.id;function x0(t){try{for(wr=0;wr{n._d&&Dp(-1);const o=Ja(e);let s;try{s=t(...i)}finally{Ja(o),n._d&&Dp(1)}return s};return n._n=!0,n._c=!0,n._d=!0,n}function PT(t,e){if(_t===null)return t;const r=Bl(_t),n=t.dirs||(t.dirs=[]);for(let i=0;it.__isTeleport,Ao=t=>t&&(t.disabled||t.disabled===""),gp=t=>t&&(t.defer||t.defer===""),mp=t=>typeof SVGElement<"u"&&t instanceof SVGElement,yp=t=>typeof MathMLElement=="function"&&t instanceof MathMLElement,fc=(t,e)=>{const r=t&&t.to;return ot(r)?e?e(r):null:r},O0={name:"Teleport",__isTeleport:!0,process(t,e,r,n,i,o,s,a,l,c){const{mc:u,pc:h,pbc:f,o:{insert:d,querySelector:y,createText:g,createComment:p}}=c,b=Ao(e.props);let{shapeFlag:v,children:S,dynamicChildren:k}=e;if(t==null){const M=e.el=g(""),A=e.anchor=g("");d(M,r,n),d(A,r,n);const I=(_,x)=>{16&v&&(i&&i.isCE&&(i.ce._teleportTarget=_),u(S,_,x,i,o,s,a,l))},T=()=>{const _=e.target=fc(e.props,y),x=bp(_,e,g,d);_&&(s!=="svg"&&mp(_)?s="svg":s!=="mathml"&&yp(_)&&(s="mathml"),b||(I(_,x),Ys(e,!1)))};b&&(I(r,A),Ys(e,!0)),gp(e.props)?(e.el.__isMounted=!1,Dt(()=>{T(),delete e.el.__isMounted},o)):T()}else{if(gp(e.props)&&t.el.__isMounted===!1)return void Dt(()=>{O0.process(t,e,r,n,i,o,s,a,l,c)},o);e.el=t.el,e.targetStart=t.targetStart;const M=e.anchor=t.anchor,A=e.target=t.target,I=e.targetAnchor=t.targetAnchor,T=Ao(t.props),_=T?r:A,x=T?M:I;if(s==="svg"||mp(A)?s="svg":(s==="mathml"||yp(A))&&(s="mathml"),k?(f(t.dynamicChildren,k,_,i,o,s,a),wd(t,e,!0)):l||h(t,e,_,x,i,o,s,a,!1),b)T?e.props&&t.props&&e.props.to!==t.props.to&&(e.props.to=t.props.to):qs(e,r,M,c,1);else if((e.props&&e.props.to)!==(t.props&&t.props.to)){const O=e.target=fc(e.props,y);O&&qs(e,O,null,c,0)}else T&&qs(e,A,I,c,1);Ys(e,b)}},remove(t,e,r,{um:n,o:{remove:i}},o){const{shapeFlag:s,children:a,anchor:l,targetStart:c,targetAnchor:u,target:h,props:f}=t;if(h&&(i(c),i(u)),o&&i(l),16&s){const d=o||!Ao(f);for(let y=0;y{const e=t.subTree;return e.component?A0(e.component):e},Yx={name:"BaseTransition",props:M0,setup(t,{slots:e}){const r=Ll(),n=function(){const i={isMounted:!1,isLeaving:!1,isUnmounting:!1,leavingVNodes:new Map};return Dl(()=>{i.isMounted=!0}),j0(()=>{i.isUnmounting=!0}),i}();return()=>{const i=e.default&&P0(e.default(),!0);if(!i||!i.length)return;const o=T0(i),s=Le(t),{mode:a}=s;if(n.isLeaving)return dc(o);const l=vp(o);if(!l)return dc(o);let c=fu(l,s,n,r,h=>c=h);l.type!==Tt&&ls(l,c);let u=r.subTree&&vp(r.subTree);if(u&&u.type!==Tt&&!Gn(l,u)&&A0(r).type!==Tt){let h=fu(u,s,n,r);if(ls(u,h),a==="out-in"&&l.type!==Tt)return n.isLeaving=!0,h.afterLeave=()=>{n.isLeaving=!1,8&r.job.flags||r.update(),delete h.afterLeave,u=void 0},dc(o);a==="in-out"&&l.type!==Tt?h.delayLeave=(f,d,y)=>{I0(n,u)[String(u.key)]=u,f[yn]=()=>{d(),f[yn]=void 0,delete c.delayedLeave,u=void 0},c.delayedLeave=()=>{y(),delete c.delayedLeave,u=void 0}}:u=void 0}else u&&(u=void 0);return o}}};function T0(t){let e=t[0];if(t.length>1){for(const r of t)if(r.type!==Tt){e=r;break}}return e}const Kx=Yx;function I0(t,e){const{leavingVNodes:r}=t;let n=r.get(e.type);return n||(n=Object.create(null),r.set(e.type,n)),n}function fu(t,e,r,n,i){const{appear:o,mode:s,persisted:a=!1,onBeforeEnter:l,onEnter:c,onAfterEnter:u,onEnterCancelled:h,onBeforeLeave:f,onLeave:d,onAfterLeave:y,onLeaveCancelled:g,onBeforeAppear:p,onAppear:b,onAfterAppear:v,onAppearCancelled:S}=e,k=String(t.key),M=I0(r,t),A=(_,x)=>{_&&pr(_,n,9,x)},I=(_,x)=>{const O=x[1];A(_,x),Ie(_)?_.every(N=>N.length<=1)&&O():_.length<=1&&O()},T={mode:s,persisted:a,beforeEnter(_){let x=l;if(!r.isMounted){if(!o)return;x=p||l}_[yn]&&_[yn](!0);const O=M[k];O&&Gn(t,O)&&O.el[yn]&&O.el[yn](),A(x,[_])},enter(_){let x=c,O=u,N=h;if(!r.isMounted){if(!o)return;x=b||c,O=v||u,N=S||h}let G=!1;const U=_[Ks]=z=>{G||(G=!0,A(z?N:O,[_]),T.delayedLeave&&T.delayedLeave(),_[Ks]=void 0)};x?I(x,[_,U]):U()},leave(_,x){const O=String(t.key);if(_[Ks]&&_[Ks](!0),r.isUnmounting)return x();A(f,[_]);let N=!1;const G=_[yn]=U=>{N||(N=!0,x(),A(U?g:y,[_]),_[yn]=void 0,M[O]===t&&delete M[O])};M[O]=t,d?I(d,[_,G]):G()},clone(_){const x=fu(_,e,r,n,i);return i&&i(x),x}};return T}function dc(t){if(Cl(t))return(t=Dn(t)).children=null,t}function vp(t){if(!Cl(t))return E0(t.type)&&t.children?T0(t.children):t;if(t.component)return t.component.subTree;const{shapeFlag:e,children:r}=t;if(r){if(16&e)return r[0];if(32&e&&Re(r.default))return r.default()}}function ls(t,e){6&t.shapeFlag&&t.component?(t.transition=e,ls(t.component.subTree,e)):128&t.shapeFlag?(t.ssContent.transition=e.clone(t.ssContent),t.ssFallback.transition=e.clone(t.ssFallback)):t.transition=e}function P0(t,e=!1,r){let n=[],i=0;for(let o=0;o1)for(let o=0;o$o(y,e&&(Ie(e)?e[g]:e),r,n,i));if(Ii(n)&&!i)return void(512&n.shapeFlag&&n.type.__asyncResolved&&n.component.subTree.component&&$o(t,e,r,n.component.subTree));const o=4&n.shapeFlag?Bl(n.component):n.el,s=i?null:o,{i:a,r:l}=t,c=e&&e.r,u=a.refs===Je?a.refs={}:a.refs,h=a.setupState,f=Le(h),d=h===Je?()=>!1:y=>Ke(f,y);if(c!=null&&c!==l&&(ot(c)?(u[c]=null,d(c)&&(h[c]=null)):ft(c)&&(c.value=null)),Re(l))Ms(l,a,12,[s,u]);else{const y=ot(l),g=ft(l);if(y||g){const p=()=>{if(t.f){const b=y?d(l)?h[l]:u[l]:l.value;i?Ie(b)&&od(b,o):Ie(b)?b.includes(o)||b.push(o):y?(u[l]=[o],d(l)&&(h[l]=u[l])):(l.value=[o],t.k&&(u[t.k]=l.value))}else y?(u[l]=s,d(l)&&(h[l]=s)):g&&(l.value=s,t.k&&(u[t.k]=s))};s?(p.id=-1,Dt(p,r)):p()}}}Qa().requestIdleCallback,Qa().cancelIdleCallback;const Ii=t=>!!t.type.__asyncLoader,Cl=t=>t.type.__isKeepAlive;function Gx(t,e){R0(t,"a",e)}function Qx(t,e){R0(t,"da",e)}function R0(t,e,r=It){const n=t.__wdc||(t.__wdc=()=>{let i=r;for(;i;){if(i.isDeactivated)return;i=i.parent}return t()});if(Rl(e,n,r),r){let i=r.parent;for(;i&&i.parent;)Cl(i.parent.vnode)&&Zx(n,e,r,i),i=i.parent}}function Zx(t,e,r,n){const i=Rl(e,t,n,!0);jl(()=>{od(n[e],i)},r)}function Rl(t,e,r=It,n=!1){if(r){const i=r[t]||(r[t]=[]),o=e.__weh||(e.__weh=(...s)=>{$r();const a=As(r),l=pr(e,r,t,s);return a(),zr(),l});return n?i.unshift(o):i.push(o),o}}const Qr=t=>(e,r=It)=>{hs&&t!=="sp"||Rl(t,(...n)=>e(...n),r)},Xx=Qr("bm"),Dl=Qr("m"),D0=Qr("bu"),Jx=Qr("u"),j0=Qr("bum"),jl=Qr("um"),eS=Qr("sp"),tS=Qr("rtg"),rS=Qr("rtc");function nS(t,e=It){Rl("ec",t,e)}const md="components";function RT(t,e){return yd(md,t,!0,e)||t}const N0=Symbol.for("v-ndc");function DT(t){return ot(t)?yd(md,t,!1)||t:t||N0}function jT(t){return yd("directives",t)}function yd(t,e,r=!0,n=!1){const i=_t||It;if(i){const o=i.type;if(t===md){const a=ES(o,!1);if(a&&(a===e||a===ir(e)||a===kl(ir(e))))return o}const s=wp(i[t]||o[t],e)||wp(i.appContext[t],e);return!s&&n?o:s}}function wp(t,e){return t&&(t[e]||t[ir(e)]||t[kl(ir(e))])}function NT(t,e,r,n){let i;const o=r,s=Ie(t);if(s||ot(t)){let a=!1,l=!1;s&&Tn(t)&&(a=!nr(t),l=Rn(t),t=Ml(t)),i=new Array(t.length);for(let c=0,u=t.length;ce(a,l,void 0,o));else{const a=Object.keys(t);i=new Array(a.length);for(let l=0,c=a.length;l{const o=n.fn(...i);return o&&(o.key=n.key),o}:n.fn)}return t}function BT(t,e,r={},n,i){if(_t.ce||_t.parent&&Ii(_t.parent)&&_t.parent.ce)return e!=="default"&&(r.name=e),yu(),bu(Ft,null,[Wt("slot",r,n&&n())],64);let o=t[e];o&&o._c&&(o._d=!1),yu();const s=o&&L0(o(r)),a=r.key||s&&s.key,l=bu(Ft,{key:(a&&!Gr(a)?a:`_${e}`)+(!s&&n?"_fb":"")},s||(n?n():[]),s&&t._===1?64:-2);return!i&&l.scopeId&&(l.slotScopeIds=[l.scopeId+"-s"]),o&&o._c&&(o._d=!0),l}function L0(t){return t.some(e=>!us(e)||e.type!==Tt&&!(e.type===Ft&&!L0(e.children)))?t:null}const du=t=>t?X0(t)?Bl(t):du(t.parent):null,zo=mt(Object.create(null),{$:t=>t,$el:t=>t.vnode.el,$data:t=>t.data,$props:t=>t.props,$attrs:t=>t.attrs,$slots:t=>t.slots,$refs:t=>t.refs,$parent:t=>du(t.parent),$root:t=>du(t.root),$host:t=>t.ce,$emit:t=>t.emit,$options:t=>F0(t),$forceUpdate:t=>t.f||(t.f=()=>{gd(t.update)}),$nextTick:t=>t.n||(t.n=Il.bind(t.proxy)),$watch:t=>pS.bind(t)}),pc=(t,e)=>t!==Je&&!t.__isScriptSetup&&Ke(t,e),iS={get({_:t},e){if(e==="__v_skip")return!0;const{ctx:r,setupState:n,data:i,props:o,accessCache:s,type:a,appContext:l}=t;let c;if(e[0]!=="$"){const d=s[e];if(d!==void 0)switch(d){case 1:return n[e];case 2:return i[e];case 4:return r[e];case 3:return o[e]}else{if(pc(n,e))return s[e]=1,n[e];if(i!==Je&&Ke(i,e))return s[e]=2,i[e];if((c=t.propsOptions[0])&&Ke(c,e))return s[e]=3,o[e];if(r!==Je&&Ke(r,e))return s[e]=4,r[e];pu&&(s[e]=0)}}const u=zo[e];let h,f;return u?(e==="$attrs"&&At(t.attrs,0,""),u(t)):(h=a.__cssModules)&&(h=h[e])?h:r!==Je&&Ke(r,e)?(s[e]=4,r[e]):(f=l.config.globalProperties,Ke(f,e)?f[e]:void 0)},set({_:t},e,r){const{data:n,setupState:i,ctx:o}=t;return pc(i,e)?(i[e]=r,!0):n!==Je&&Ke(n,e)?(n[e]=r,!0):!Ke(t.props,e)&&(e[0]!=="$"||!(e.slice(1)in t))&&(o[e]=r,!0)},has({_:{data:t,setupState:e,accessCache:r,ctx:n,appContext:i,propsOptions:o}},s){let a;return!!r[s]||t!==Je&&Ke(t,s)||pc(e,s)||(a=o[0])&&Ke(a,s)||Ke(n,s)||Ke(zo,s)||Ke(i.config.globalProperties,s)},defineProperty(t,e,r){return r.get!=null?t._.accessCache[e]=0:Ke(r,"value")&&this.set(t,e,r.value,null),Reflect.defineProperty(t,e,r)}};function _p(t){return Ie(t)?t.reduce((e,r)=>(e[r]=null,e),{}):t}let pu=!0;function oS(t){const e=F0(t),r=t.proxy,n=t.ctx;pu=!1,e.beforeCreate&&xp(e.beforeCreate,t,"bc");const{data:i,computed:o,methods:s,watch:a,provide:l,inject:c,created:u,beforeMount:h,mounted:f,beforeUpdate:d,updated:y,activated:g,deactivated:p,beforeDestroy:b,beforeUnmount:v,destroyed:S,unmounted:k,render:M,renderTracked:A,renderTriggered:I,errorCaptured:T,serverPrefetch:_,expose:x,inheritAttrs:O,components:N,directives:G,filters:U}=e;if(c&&function(q,B){Ie(q)&&(q=gu(q));for(const ne in q){const H=q[ne];let Q;Q=rt(H)?"default"in H?Tr(H.from||ne,H.default,!0):Tr(H.from||ne):Tr(H),ft(Q)?Object.defineProperty(B,ne,{enumerable:!0,configurable:!0,get:()=>Q.value,set:F=>Q.value=F}):B[ne]=Q}}(c,n,null),s)for(const q in s){const B=s[q];Re(B)&&(n[q]=B.bind(r))}if(i){const q=i.call(r,r);rt(q)&&(t.data=Es(q))}if(pu=!0,o)for(const q in o){const B=o[q],ne=Re(B)?B.bind(r,r):Re(B.get)?B.get.bind(r,r):Ar,H=!Re(B)&&Re(B.set)?B.set.bind(r):Ar,Q=Jt({get:ne,set:H});Object.defineProperty(n,q,{enumerable:!0,configurable:!0,get:()=>Q.value,set:F=>Q.value=F})}if(a)for(const q in a)B0(a[q],n,r,q);if(l){const q=Re(l)?l.call(r):l;Reflect.ownKeys(q).forEach(B=>{xa(B,q[B])})}function z(q,B){Ie(B)?B.forEach(ne=>q(ne.bind(r))):B&&q(B.bind(r))}if(u&&xp(u,t,"c"),z(Xx,h),z(Dl,f),z(D0,d),z(Jx,y),z(Gx,g),z(Qx,p),z(nS,T),z(rS,A),z(tS,I),z(j0,v),z(jl,k),z(eS,_),Ie(x))if(x.length){const q=t.exposed||(t.exposed={});x.forEach(B=>{Object.defineProperty(q,B,{get:()=>r[B],set:ne=>r[B]=ne,enumerable:!0})})}else t.exposed||(t.exposed={});M&&t.render===Ar&&(t.render=M),O!=null&&(t.inheritAttrs=O),N&&(t.components=N),G&&(t.directives=G),_&&C0(t)}function xp(t,e,r){pr(Ie(t)?t.map(n=>n.bind(e.proxy)):t.bind(e.proxy),e,r)}function B0(t,e,r,n){let i=n.includes(".")?q0(r,n):()=>r[n];if(ot(t)){const o=e[t];Re(o)&&ni(i,o)}else if(Re(t))ni(i,t.bind(r));else if(rt(t))if(Ie(t))t.forEach(o=>B0(o,e,r,n));else{const o=Re(t.handler)?t.handler.bind(r):e[t.handler];Re(o)&&ni(i,o,t)}}function F0(t){const e=t.type,{mixins:r,extends:n}=e,{mixins:i,optionsCache:o,config:{optionMergeStrategies:s}}=t.appContext,a=o.get(e);let l;return a?l=a:i.length||r||n?(l={},i.length&&i.forEach(c=>el(l,c,s,!0)),el(l,e,s)):l=e,rt(e)&&o.set(e,l),l}function el(t,e,r,n=!1){const{mixins:i,extends:o}=e;o&&el(t,o,r,!0),i&&i.forEach(s=>el(t,s,r,!0));for(const s in e)if(!(n&&s==="expose")){const a=sS[s]||r&&r[s];t[s]=a?a(t[s],e[s]):e[s]}return t}const sS={data:Sp,props:kp,emits:kp,methods:po,computed:po,beforeCreate:Ct,created:Ct,beforeMount:Ct,mounted:Ct,beforeUpdate:Ct,updated:Ct,beforeDestroy:Ct,beforeUnmount:Ct,destroyed:Ct,unmounted:Ct,activated:Ct,deactivated:Ct,errorCaptured:Ct,serverPrefetch:Ct,components:po,directives:po,watch:function(t,e){if(!t)return e;if(!e)return t;const r=mt(Object.create(null),t);for(const n in e)r[n]=Ct(t[n],e[n]);return r},provide:Sp,inject:function(t,e){return po(gu(t),gu(e))}};function Sp(t,e){return e?t?function(){return mt(Re(t)?t.call(this,this):t,Re(e)?e.call(this,this):e)}:e:t}function gu(t){if(Ie(t)){const e={};for(let r=0;r(o.has(c)||(c&&Re(c.install)?(o.add(c),c.install(l,...u)):Re(c)&&(o.add(c),c(l,...u))),l),mixin:c=>(i.mixins.includes(c)||i.mixins.push(c),l),component:(c,u)=>u?(i.components[c]=u,l):i.components[c],directive:(c,u)=>u?(i.directives[c]=u,l):i.directives[c],mount(c,u,h){if(!a){const f=l._ceVNode||Wt(r,n);return f.appContext=i,h===!0?h="svg":h===!1&&(h=void 0),t(f,c,h),a=!0,l._container=c,c.__vue_app__=l,Bl(f.component)}},onUnmount(c){s.push(c)},unmount(){a&&(pr(s,l._instance,16),t(null,l._container),delete l._container.__vue_app__)},provide:(c,u)=>(i.provides[c]=u,l),runWithContext(c){const u=ri;ri=l;try{return c()}finally{ri=u}}};return l}}let ri=null;function xa(t,e){if(It){let r=It.provides;const n=It.parent&&It.parent.provides;n===r&&(r=It.provides=Object.create(n)),r[t]=e}}function Tr(t,e,r=!1){const n=Ll();if(n||ri){let i=ri?ri._context.provides:n?n.parent==null||n.ce?n.vnode.appContext&&n.vnode.appContext.provides:n.parent.provides:void 0;if(i&&t in i)return i[t];if(arguments.length>1)return r&&Re(e)?e.call(n&&n.proxy):e}}const U0={},Ep=()=>Object.create(U0),V0=t=>Object.getPrototypeOf(t)===U0;function Op(t,e,r,n){const[i,o]=t.propsOptions;let s,a=!1;if(e)for(let l in e){if(Fo(l))continue;const c=e[l];let u;i&&Ke(i,u=ir(l))?o&&o.includes(u)?(s||(s={}))[u]=c:r[u]=c:tl(t.emitsOptions,l)||l in n&&c===n[l]||(n[l]=c,a=!0)}if(o){const l=Le(r),c=s||Je;for(let u=0;u{l=!0;const[f,d]=$0(h,e,!0);mt(s,f),d&&a.push(...d)};!r&&e.mixins.length&&e.mixins.forEach(u),t.extends&&u(t.extends),t.mixins&&t.mixins.forEach(u)}if(!o&&!l)return rt(t)&&n.set(t,Li),Li;if(Ie(o))for(let u=0;ut==="_"||t==="__"||t==="_ctx"||t==="$stable",vd=t=>Ie(t)?t.map(Sr):[Sr(t)],uS=(t,e,r)=>{if(e._n)return e;const n=qx((...i)=>vd(e(...i)),r);return n._c=!1,n},Ap=(t,e,r)=>{const n=t._ctx;for(const i in t){if(bd(i))continue;const o=t[i];if(Re(o))e[i]=uS(0,o,n);else if(o!=null){const s=vd(o);e[i]=()=>s}}},Tp=(t,e)=>{const r=vd(e);t.slots.default=()=>r},Ip=(t,e,r)=>{for(const n in e)!r&&bd(n)||(t[n]=e[n])},Dt=function(t,e){e&&e.pendingBranch?Ie(t)?e.effects.push(...t):e.effects.push(t):w0(t)};function hS(t){return function(e){Qa().__VUE__=!0;const{insert:r,remove:n,patchProp:i,createElement:o,createText:s,createComment:a,setText:l,setElementText:c,parentNode:u,nextSibling:h,setScopeId:f=Ar,insertStaticContent:d}=e,y=(j,X,R,te=null,m=null,w=null,E=void 0,D=null,W=!!X.dynamicChildren)=>{if(j===X)return;j&&!Gn(j,X)&&(te=le(j),F(j,m,w,!0),j=null),X.patchFlag===-2&&(W=!1,X.dynamicChildren=null);const{type:P,ref:J,shapeFlag:he}=X;switch(P){case Nl:g(j,X,R,te);break;case Tt:p(j,X,R,te);break;case Sa:j==null&&b(X,R,te,E);break;case Ft:O(j,X,R,te,m,w,E,D,W);break;default:1&he?k(j,X,R,te,m,w,E,D,W):6&he?N(j,X,R,te,m,w,E,D,W):(64&he||128&he)&&P.process(j,X,R,te,m,w,E,D,W,ie)}J!=null&&m?$o(J,j&&j.ref,w,X||j,!X):J==null&&j&&j.ref!=null&&$o(j.ref,null,w,j,!0)},g=(j,X,R,te)=>{if(j==null)r(X.el=s(X.children),R,te);else{const m=X.el=j.el;X.children!==j.children&&l(m,X.children)}},p=(j,X,R,te)=>{j==null?r(X.el=a(X.children||""),R,te):X.el=j.el},b=(j,X,R,te)=>{[j.el,j.anchor]=d(j.children,X,R,te,j.el,j.anchor)},v=({el:j,anchor:X},R,te)=>{let m;for(;j&&j!==X;)m=h(j),r(j,R,te),j=m;r(X,R,te)},S=({el:j,anchor:X})=>{let R;for(;j&&j!==X;)R=h(j),n(j),j=R;n(X)},k=(j,X,R,te,m,w,E,D,W)=>{X.type==="svg"?E="svg":X.type==="math"&&(E="mathml"),j==null?M(X,R,te,m,w,E,D,W):T(j,X,m,w,E,D,W)},M=(j,X,R,te,m,w,E,D)=>{let W,P;const{props:J,shapeFlag:he,transition:fe,dirs:de}=j;if(W=j.el=o(j.type,w,J&&J.is,J),8&he?c(W,j.children):16&he&&I(j.children,W,null,te,m,gc(j,w),E,D),de&&Fn(j,null,te,"created"),A(W,j,j.scopeId,E,te),J){for(const K in J)K==="value"||Fo(K)||i(W,K,null,J[K],w,te);"value"in J&&i(W,"value",null,J.value,w),(P=J.onVnodeBeforeMount)&&yr(P,te,j)}de&&Fn(j,null,te,"beforeMount");const pe=function(K,ee){return(!K||K&&!K.pendingBranch)&&ee&&!ee.persisted}(m,fe);pe&&fe.beforeEnter(W),r(W,X,R),((P=J&&J.onVnodeMounted)||pe||de)&&Dt(()=>{P&&yr(P,te,j),pe&&fe.enter(W),de&&Fn(j,null,te,"mounted")},m)},A=(j,X,R,te,m)=>{if(R&&f(j,R),te)for(let w=0;w{for(let P=W;P{const D=X.el=j.el;let{patchFlag:W,dynamicChildren:P,dirs:J}=X;W|=16&j.patchFlag;const he=j.props||Je,fe=X.props||Je;let de;if(R&&Wn(R,!1),(de=fe.onVnodeBeforeUpdate)&&yr(de,R,X,j),J&&Fn(X,j,R,"beforeUpdate"),R&&Wn(R,!0),(he.innerHTML&&fe.innerHTML==null||he.textContent&&fe.textContent==null)&&c(D,""),P?_(j.dynamicChildren,P,D,R,te,gc(X,m),w):E||B(j,X,D,null,R,te,gc(X,m),w,!1),W>0){if(16&W)x(D,he,fe,R,m);else if(2&W&&he.class!==fe.class&&i(D,"class",null,fe.class,m),4&W&&i(D,"style",he.style,fe.style,m),8&W){const pe=X.dynamicProps;for(let K=0;K{de&&yr(de,R,X,j),J&&Fn(X,j,R,"updated")},te)},_=(j,X,R,te,m,w,E)=>{for(let D=0;D{if(X!==R){if(X!==Je)for(const w in X)Fo(w)||w in R||i(j,w,X[w],null,m,te);for(const w in R){if(Fo(w))continue;const E=R[w],D=X[w];E!==D&&w!=="value"&&i(j,w,D,E,m,te)}"value"in R&&i(j,"value",X.value,R.value,m)}},O=(j,X,R,te,m,w,E,D,W)=>{const P=X.el=j?j.el:s(""),J=X.anchor=j?j.anchor:s("");let{patchFlag:he,dynamicChildren:fe,slotScopeIds:de}=X;de&&(D=D?D.concat(de):de),j==null?(r(P,R,te),r(J,R,te),I(X.children||[],R,J,m,w,E,D,W)):he>0&&64&he&&fe&&j.dynamicChildren?(_(j.dynamicChildren,fe,R,m,w,E,D),(X.key!=null||m&&X===m.subTree)&&wd(j,X,!0)):B(j,X,R,J,m,w,E,D,W)},N=(j,X,R,te,m,w,E,D,W)=>{X.slotScopeIds=D,j==null?512&X.shapeFlag?m.ctx.activate(X,R,te,E,W):G(X,R,te,m,w,E,W):U(j,X,W)},G=(j,X,R,te,m,w,E)=>{const D=j.component=function(W,P,J){const he=W.type,fe=(P?P.appContext:W.appContext)||xS,de={uid:SS++,vnode:W,type:he,parent:P,appContext:fe,root:null,next:null,subTree:null,effect:null,update:null,job:null,scope:new Zb(!0),render:null,proxy:null,exposed:null,exposeProxy:null,withProxy:null,provides:P?P.provides:Object.create(fe.provides),ids:P?P.ids:["",0,0],accessCache:null,renderCache:[],components:null,directives:null,propsOptions:$0(he,fe),emitsOptions:Y0(he,fe),emit:null,emitted:null,propsDefaults:Je,inheritAttrs:he.inheritAttrs,ctx:Je,data:Je,props:Je,attrs:Je,slots:Je,refs:Je,setupState:Je,setupContext:null,suspense:J,suspenseId:J?J.pendingId:0,asyncDep:null,asyncResolved:!1,isMounted:!1,isUnmounted:!1,isDeactivated:!1,bc:null,c:null,bm:null,m:null,bu:null,u:null,um:null,bum:null,da:null,a:null,rtg:null,rtc:null,ec:null,sp:null};return de.ctx={_:de},de.root=P?P.root:de,de.emit=mS.bind(null,de),W.ce&&W.ce(de),de}(j,te,m);if(Cl(j)&&(D.ctx.renderer=ie),function(W,P=!1,J=!1){P&&vu(P);const{props:he,children:fe}=W.vnode,de=X0(W);(function(K,ee,ce,C=!1){const L={},ae=Ep();K.propsDefaults=Object.create(null),Op(K,ee,L,ae);for(const ge in K.propsOptions[0])ge in L||(L[ge]=void 0);ce?K.props=C?L:p0(L):K.type.props?K.props=L:K.props=ae,K.attrs=ae})(W,he,de,P),((K,ee,ce)=>{const C=K.slots=Ep();if(32&K.vnode.shapeFlag){const L=ee.__;L&&cu(C,"__",L,!0);const ae=ee._;ae?(Ip(C,ee,ce),ce&&cu(C,"_",ae,!0)):Ap(ee,C)}else ee&&Tp(K,ee)})(W,fe,J||P);const pe=de?function(K,ee){const ce=K.type;K.accessCache=Object.create(null),K.proxy=new Proxy(K.ctx,iS);const{setup:C}=ce;if(C){$r();const L=K.setupContext=C.length>1?function(ve){const Ce=ke=>{ve.exposed=ke||{}};return{attrs:new Proxy(ve.attrs,kS),slots:ve.slots,emit:ve.emit,expose:Ce}}(K):null,ae=As(K),ge=Ms(C,K,0,[K.props,L]),xe=Hb(ge);if(zr(),ae(),!xe&&!K.sp||Ii(K)||C0(K),xe){if(ge.then(jp,jp),ee)return ge.then(ve=>{Np(K,ve)}).catch(ve=>{Tl(ve,K,0)});K.asyncDep=ge}else Np(K,ge)}else J0(K)}(W,P):void 0;P&&vu(!1)}(D,!1,E),D.asyncDep){if(m&&m.registerDep(D,z,E),!j.el){const W=D.subTree=Wt(Tt);p(null,W,X,R),j.placeholder=W.el}}else z(D,j,X,R,m,w,E)},U=(j,X,R)=>{const te=X.component=j.component;if(function(m,w,E){const{props:D,children:W,component:P}=m,{props:J,children:he,patchFlag:fe}=w,de=P.emitsOptions;if(w.dirs||w.transition)return!0;if(!(E&&fe>=0))return!(!W&&!he||he&&he.$stable)||D!==J&&(D?!J||Rp(D,J,de):!!J);if(1024&fe)return!0;if(16&fe)return D?Rp(D,J,de):!!J;if(8&fe){const pe=w.dynamicProps;for(let K=0;K{const D=()=>{if(j.isMounted){let{next:he,bu:fe,u:de,parent:pe,vnode:K}=j;{const ae=z0(j);if(ae)return he&&(he.el=K.el,q(j,he,E)),void ae.asyncDep.then(()=>{j.isUnmounted||D()})}let ee,ce=he;Wn(j,!1),he?(he.el=K.el,q(j,he,E)):he=K,fe&&sc(fe),(ee=he.props&&he.props.onVnodeBeforeUpdate)&&yr(ee,pe,he,K),Wn(j,!0);const C=Cp(j),L=j.subTree;j.subTree=C,y(L,C,u(L.el),le(L),j,m,w),he.el=C.el,ce===null&&function({vnode:ae,parent:ge},xe){for(;ge;){const ve=ge.subTree;if(ve.suspense&&ve.suspense.activeBranch===ae&&(ve.el=ae.el),ve!==ae)break;(ae=ge.vnode).el=xe,ge=ge.parent}}(j,C.el),de&&Dt(de,m),(ee=he.props&&he.props.onVnodeUpdated)&&Dt(()=>yr(ee,pe,he,K),m)}else{let he;const{el:fe,props:de}=X,{bm:pe,m:K,parent:ee,root:ce,type:C}=j,L=Ii(X);Wn(j,!1),pe&&sc(pe),!L&&(he=de&&de.onVnodeBeforeMount)&&yr(he,ee,X),Wn(j,!0);{ce.ce&&ce.ce._def.shadowRoot!==!1&&ce.ce._injectChildStyle(C);const ae=j.subTree=Cp(j);y(null,ae,R,te,j,m,w),X.el=ae.el}if(K&&Dt(K,m),!L&&(he=de&&de.onVnodeMounted)){const ae=X;Dt(()=>yr(he,ee,ae),m)}(256&X.shapeFlag||ee&&Ii(ee.vnode)&&256&ee.vnode.shapeFlag)&&j.a&&Dt(j.a,m),j.isMounted=!0,X=R=te=null}};j.scope.on();const W=j.effect=new e0(D);j.scope.off();const P=j.update=W.run.bind(W),J=j.job=W.runIfDirty.bind(W);J.i=j,J.id=j.uid,W.scheduler=()=>gd(J),Wn(j,!0),P()},q=(j,X,R)=>{X.component=j;const te=j.vnode.props;j.vnode=X,j.next=null,function(m,w,E,D){const{props:W,attrs:P,vnode:{patchFlag:J}}=m,he=Le(W),[fe]=m.propsOptions;let de=!1;if(!(D||J>0)||16&J){let pe;Op(m,w,W,P)&&(de=!0);for(const K in he)w&&(Ke(w,K)||(pe=jn(K))!==K&&Ke(w,pe))||(fe?!E||E[K]===void 0&&E[pe]===void 0||(W[K]=mu(fe,he,K,void 0,m,!0)):delete W[K]);if(P!==he)for(const K in P)w&&Ke(w,K)||(delete P[K],de=!0)}else if(8&J){const pe=m.vnode.dynamicProps;for(let K=0;K{const{vnode:D,slots:W}=m;let P=!0,J=Je;if(32&D.shapeFlag){const he=w._;he?E&&he===1?P=!1:Ip(W,w,E):(P=!w.$stable,Ap(w,W)),J=w}else w&&(Tp(m,w),J={default:1});if(P)for(const he in W)bd(he)||J[he]!=null||delete W[he]})(j,X.children,R),$r(),pp(j),zr()},B=(j,X,R,te,m,w,E,D,W=!1)=>{const P=j&&j.children,J=j?j.shapeFlag:0,he=X.children,{patchFlag:fe,shapeFlag:de}=X;if(fe>0){if(128&fe)return void H(P,he,R,te,m,w,E,D,W);if(256&fe)return void ne(P,he,R,te,m,w,E,D,W)}8&de?(16&J&&se(P,m,w),he!==P&&c(R,he)):16&J?16&de?H(P,he,R,te,m,w,E,D,W):se(P,m,w,!0):(8&J&&c(R,""),16&de&&I(he,R,te,m,w,E,D,W))},ne=(j,X,R,te,m,w,E,D,W)=>{X=X||Li;const P=(j=j||Li).length,J=X.length,he=Math.min(P,J);let fe;for(fe=0;feJ?se(j,m,w,!0,!1,he):I(X,R,te,m,w,E,D,W,he)},H=(j,X,R,te,m,w,E,D,W)=>{let P=0;const J=X.length;let he=j.length-1,fe=J-1;for(;P<=he&&P<=fe;){const de=j[P],pe=X[P]=W?bn(X[P]):Sr(X[P]);if(!Gn(de,pe))break;y(de,pe,R,null,m,w,E,D,W),P++}for(;P<=he&&P<=fe;){const de=j[he],pe=X[fe]=W?bn(X[fe]):Sr(X[fe]);if(!Gn(de,pe))break;y(de,pe,R,null,m,w,E,D,W),he--,fe--}if(P>he){if(P<=fe){const de=fe+1,pe=defe)for(;P<=he;)F(j[P],m,w,!0),P++;else{const de=P,pe=P,K=new Map;for(P=pe;P<=fe;P++){const ve=X[P]=W?bn(X[P]):Sr(X[P]);ve.key!=null&&K.set(ve.key,P)}let ee,ce=0;const C=fe-pe+1;let L=!1,ae=0;const ge=new Array(C);for(P=0;P=C){F(ve,m,w,!0);continue}let Ce;if(ve.key!=null)Ce=K.get(ve.key);else for(ee=pe;ee<=fe;ee++)if(ge[ee-pe]===0&&Gn(ve,X[ee])){Ce=ee;break}Ce===void 0?F(ve,m,w,!0):(ge[Ce-pe]=P+1,Ce>=ae?ae=Ce:L=!0,y(ve,X[Ce],R,null,m,w,E,D,W),ce++)}const xe=L?function(ve){const Ce=ve.slice(),ke=[0];let Be,De,Ve,Fe,ze;const Me=ve.length;for(Be=0;Be>1,ve[ke[ze]]0&&(Ce[Be]=ke[Ve-1]),ke[Ve]=Be)}}for(Ve=ke.length,Fe=ke[Ve-1];Ve-- >0;)ke[Ve]=Fe,Fe=Ce[Fe];return ke}(ge):Li;for(ee=xe.length-1,P=C-1;P>=0;P--){const ve=pe+P,Ce=X[ve],ke=X[ve+1],Be=ve+1{const{el:w,type:E,transition:D,children:W,shapeFlag:P}=j;if(6&P)return void Q(j.component.subTree,X,R,te);if(128&P)return void j.suspense.move(X,R,te);if(64&P)return void E.move(j,X,R,ie);if(E===Ft){r(w,X,R);for(let J=0;JD.enter(w),m);else{const{leave:J,delayLeave:he,afterLeave:fe}=D,de=()=>{j.ctx.isUnmounted?n(w):r(w,X,R)},pe=()=>{J(w,()=>{de(),fe&&fe()})};he?he(w,de,pe):pe()}else r(w,X,R)},F=(j,X,R,te=!1,m=!1)=>{const{type:w,props:E,ref:D,children:W,dynamicChildren:P,shapeFlag:J,patchFlag:he,dirs:fe,cacheIndex:de}=j;if(he===-2&&(m=!1),D!=null&&($r(),$o(D,null,R,j,!0),zr()),de!=null&&(X.renderCache[de]=void 0),256&J)return void X.ctx.deactivate(j);const pe=1&J&&fe,K=!Ii(j);let ee;if(K&&(ee=E&&E.onVnodeBeforeUnmount)&&yr(ee,X,j),6&J)$(j.component,R,te);else{if(128&J)return void j.suspense.unmount(R,te);pe&&Fn(j,null,X,"beforeUnmount"),64&J?j.type.remove(j,X,R,ie,te):P&&!P.hasOnce&&(w!==Ft||he>0&&64&he)?se(P,X,R,!1,!0):(w===Ft&&384&he||!m&&16&J)&&se(W,X,R),te&&V(j)}(K&&(ee=E&&E.onVnodeUnmounted)||pe)&&Dt(()=>{ee&&yr(ee,X,j),pe&&Fn(j,null,X,"unmounted")},R)},V=j=>{const{type:X,el:R,anchor:te,transition:m}=j;if(X===Ft)return void Z(R,te);if(X===Sa)return void S(j);const w=()=>{n(R),m&&!m.persisted&&m.afterLeave&&m.afterLeave()};if(1&j.shapeFlag&&m&&!m.persisted){const{leave:E,delayLeave:D}=m,W=()=>E(R,w);D?D(j.el,w,W):W()}else w()},Z=(j,X)=>{let R;for(;j!==X;)R=h(j),n(j),j=R;n(X)},$=(j,X,R)=>{const{bum:te,scope:m,job:w,subTree:E,um:D,m:W,a:P,parent:J,slots:{__:he}}=j;Pp(W),Pp(P),te&&sc(te),J&&Ie(he)&&he.forEach(fe=>{J.renderCache[fe]=void 0}),m.stop(),w&&(w.flags|=8,F(E,j,X,R)),D&&Dt(D,X),Dt(()=>{j.isUnmounted=!0},X),X&&X.pendingBranch&&!X.isUnmounted&&j.asyncDep&&!j.asyncResolved&&j.suspenseId===X.pendingId&&(X.deps--,X.deps===0&&X.resolve())},se=(j,X,R,te=!1,m=!1,w=0)=>{for(let E=w;E{if(6&j.shapeFlag)return le(j.component.subTree);if(128&j.shapeFlag)return j.suspense.next();const X=h(j.anchor||j.el),R=X&&X[k0];return R?h(R):X};let ue=!1;const Y=(j,X,R)=>{j==null?X._vnode&&F(X._vnode,null,null,!0):y(X._vnode||null,j,X,null,null,null,R),X._vnode=j,ue||(ue=!0,pp(),_0(),ue=!1)},ie={p:y,um:F,m:Q,r:V,mt:G,mc:I,pc:B,pbc:_,n:le,o:e};return{render:Y,hydrate:void 0,createApp:lS(Y)}}(t)}function gc({type:t,props:e},r){return r==="svg"&&t==="foreignObject"||r==="mathml"&&t==="annotation-xml"&&e&&e.encoding&&e.encoding.includes("html")?void 0:r}function Wn({effect:t,job:e},r){r?(t.flags|=32,e.flags|=4):(t.flags&=-33,e.flags&=-5)}function wd(t,e,r=!1){const n=t.children,i=e.children;if(Ie(n)&&Ie(i))for(let o=0;oTr(fS);function ni(t,e,r){return H0(t,e,r)}function H0(t,e,r=Je){const{immediate:n,deep:i,flush:o,once:s}=r,a=mt({},r),l=e&&n||!e&&o!=="post";let c;if(hs){if(o==="sync"){const d=dS();c=d.__watcherHandles||(d.__watcherHandles=[])}else if(!l){const d=()=>{};return d.stop=Ar,d.resume=Ar,d.pause=Ar,d}}const u=It;a.call=(d,y,g)=>pr(d,u,y,g);let h=!1;o==="post"?a.scheduler=d=>{Dt(d,u&&u.suspense)}:o!=="sync"&&(h=!0,a.scheduler=(d,y)=>{y?d():gd(d)}),a.augmentJob=d=>{e&&(d.flags|=4),h&&(d.flags|=2,u&&(d.id=u.uid,d.i=u))};const f=Hx(t,e,a);return hs&&(c?c.push(f):l&&f()),f}function pS(t,e,r){const n=this.proxy,i=ot(t)?t.includes(".")?q0(n,t):()=>n[t]:t.bind(n,n);let o;Re(e)?o=e:(o=e.handler,r=e);const s=As(this),a=H0(i,o.bind(n),r);return s(),a}function q0(t,e){const r=e.split(".");return()=>{let n=t;for(let i=0;ie==="modelValue"||e==="model-value"?t.modelModifiers:t[`${e}Modifiers`]||t[`${ir(e)}Modifiers`]||t[`${jn(e)}Modifiers`];function mS(t,e,...r){if(t.isUnmounted)return;const n=t.vnode.props||Je;let i=r;const o=e.startsWith("update:"),s=o&&gS(n,e.slice(7));let a;s&&(s.trim&&(i=r.map(u=>ot(u)?u.trim():u)),s.number&&(i=r.map(gx)));let l=n[a=oc(e)]||n[a=oc(ir(e))];!l&&o&&(l=n[a=oc(jn(e))]),l&&pr(l,t,6,i);const c=n[a+"Once"];if(c){if(t.emitted){if(t.emitted[a])return}else t.emitted={};t.emitted[a]=!0,pr(c,t,6,i)}}function Y0(t,e,r=!1){const n=e.emitsCache,i=n.get(t);if(i!==void 0)return i;const o=t.emits;let s={},a=!1;if(!Re(t)){const l=c=>{const u=Y0(c,e,!0);u&&(a=!0,mt(s,u))};!r&&e.mixins.length&&e.mixins.forEach(l),t.extends&&l(t.extends),t.mixins&&t.mixins.forEach(l)}return o||a?(Ie(o)?o.forEach(l=>s[l]=null):mt(s,o),rt(t)&&n.set(t,s),s):(rt(t)&&n.set(t,null),null)}function tl(t,e){return!(!t||!_l(e))&&(e=e.slice(2).replace(/Once$/,""),Ke(t,e[0].toLowerCase()+e.slice(1))||Ke(t,jn(e))||Ke(t,e))}function Cp(t){const{type:e,vnode:r,proxy:n,withProxy:i,propsOptions:[o],slots:s,attrs:a,emit:l,render:c,renderCache:u,props:h,data:f,setupState:d,ctx:y,inheritAttrs:g}=t,p=Ja(t);let b,v;try{if(4&r.shapeFlag){const k=i||n,M=k;b=Sr(c.call(M,k,u,h,d,f,y)),v=a}else{const k=e;b=Sr(k.length>1?k(h,{attrs:a,slots:s,emit:l}):k(h,null)),v=e.props?a:yS(a)}}catch(k){Ho.length=0,Tl(k,t,1),b=Wt(Tt)}let S=b;if(v&&g!==!1){const k=Object.keys(v),{shapeFlag:M}=S;k.length&&7&M&&(o&&k.some(id)&&(v=bS(v,o)),S=Dn(S,v,!1,!0))}return r.dirs&&(S=Dn(S,null,!1,!0),S.dirs=S.dirs?S.dirs.concat(r.dirs):r.dirs),r.transition&&ls(S,r.transition),b=S,Ja(p),b}const yS=t=>{let e;for(const r in t)(r==="class"||r==="style"||_l(r))&&((e||(e={}))[r]=t[r]);return e},bS=(t,e)=>{const r={};for(const n in t)id(n)&&n.slice(9)in e||(r[n]=t[n]);return r};function Rp(t,e,r){const n=Object.keys(e);if(n.length!==Object.keys(t).length)return!0;for(let i=0;it.__isSuspense,Ft=Symbol.for("v-fgt"),Nl=Symbol.for("v-txt"),Tt=Symbol.for("v-cmt"),Sa=Symbol.for("v-stc"),Ho=[];let Yt=null;function yu(t=!1){Ho.push(Yt=t?null:[])}let cs=1;function Dp(t,e=!1){cs+=t,t<0&&Yt&&e&&(Yt.hasOnce=!0)}function G0(t){return t.dynamicChildren=cs>0?Yt||Li:null,Ho.pop(),Yt=Ho[Ho.length-1]||null,cs>0&&Yt&&Yt.push(t),t}function FT(t,e,r,n,i,o){return G0(Z0(t,e,r,n,i,o,!0))}function bu(t,e,r,n,i){return G0(Wt(t,e,r,n,i,!0))}function us(t){return!!t&&t.__v_isVNode===!0}function Gn(t,e){return t.type===e.type&&t.key===e.key}const Q0=({key:t})=>t??null,ka=({ref:t,ref_key:e,ref_for:r})=>(typeof t=="number"&&(t=""+t),t!=null?ot(t)||ft(t)||Re(t)?{i:_t,r:t,k:e,f:!!r}:t:null);function Z0(t,e=null,r=null,n=0,i=null,o=t===Ft?0:1,s=!1,a=!1){const l={__v_isVNode:!0,__v_skip:!0,type:t,props:e,key:e&&Q0(e),ref:e&&ka(e),scopeId:S0,slotScopeIds:null,children:r,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetStart:null,targetAnchor:null,staticCount:0,shapeFlag:o,patchFlag:n,dynamicProps:i,dynamicChildren:null,appContext:null,ctx:_t};return a?(_d(l,r),128&o&&t.normalize(l)):r&&(l.shapeFlag|=ot(r)?8:16),cs>0&&!s&&Yt&&(l.patchFlag>0||6&o)&&l.patchFlag!==32&&Yt.push(l),l}const Wt=function(t,e=null,r=null,n=0,i=null,o=!1){if(t&&t!==N0||(t=Tt),us(t)){const l=Dn(t,e,!0);return r&&_d(l,r),cs>0&&!o&&Yt&&(6&l.shapeFlag?Yt[Yt.indexOf(t)]=l:Yt.push(l)),l.patchFlag=-2,l}s=t,Re(s)&&"__vccOpts"in s&&(t=t.__vccOpts);var s;if(e){e=vS(e);let{class:l,style:c}=e;l&&!ot(l)&&(e.class=Ol(l)),rt(c)&&(Os(c)&&!Ie(c)&&(c=mt({},c)),e.style=El(c))}const a=ot(t)?1:K0(t)?128:E0(t)?64:rt(t)?4:Re(t)?2:0;return Z0(t,e,r,n,i,a,o,!0)};function vS(t){return t?Os(t)||V0(t)?mt({},t):t:null}function Dn(t,e,r=!1,n=!1){const{props:i,ref:o,patchFlag:s,children:a,transition:l}=t,c=e?_S(i||{},e):i,u={__v_isVNode:!0,__v_skip:!0,type:t.type,props:c,key:c&&Q0(c),ref:e&&e.ref?r&&o?Ie(o)?o.concat(ka(e)):[o,ka(e)]:ka(e):o,scopeId:t.scopeId,slotScopeIds:t.slotScopeIds,children:a,target:t.target,targetStart:t.targetStart,targetAnchor:t.targetAnchor,staticCount:t.staticCount,shapeFlag:t.shapeFlag,patchFlag:e&&t.type!==Ft?s===-1?16:16|s:s,dynamicProps:t.dynamicProps,dynamicChildren:t.dynamicChildren,appContext:t.appContext,dirs:t.dirs,transition:l,component:t.component,suspense:t.suspense,ssContent:t.ssContent&&Dn(t.ssContent),ssFallback:t.ssFallback&&Dn(t.ssFallback),placeholder:t.placeholder,el:t.el,anchor:t.anchor,ctx:t.ctx,ce:t.ce};return l&&n&&ls(u,l.clone(u)),u}function wS(t=" ",e=0){return Wt(Nl,null,t,e)}function WT(t="",e=!1){return e?(yu(),bu(Tt,null,t)):Wt(Tt,null,t)}function Sr(t){return t==null||typeof t=="boolean"?Wt(Tt):Ie(t)?Wt(Ft,null,t.slice()):us(t)?bn(t):Wt(Nl,null,String(t))}function bn(t){return t.el===null&&t.patchFlag!==-1||t.memo?t:Dn(t)}function _d(t,e){let r=0;const{shapeFlag:n}=t;if(e==null)e=null;else if(Ie(e))r=16;else if(typeof e=="object"){if(65&n){const i=e.default;return void(i&&(i._c&&(i._d=!1),_d(t,i()),i._c&&(i._d=!0)))}{r=32;const i=e._;i||V0(e)?i===3&&_t&&(_t.slots._===1?e._=1:(e._=2,t.patchFlag|=1024)):e._ctx=_t}}else Re(e)?(e={default:e,_ctx:_t},r=32):(e=String(e),64&n?(r=16,e=[wS(e)]):r=8);t.children=e,t.shapeFlag|=r}function _S(...t){const e={};for(let r=0;rIt||_t;let rl,vu;{const t=Qa(),e=(r,n)=>{let i;return(i=t[r])||(i=t[r]=[]),i.push(n),o=>{i.length>1?i.forEach(s=>s(o)):i[0](o)}};rl=e("__VUE_INSTANCE_SETTERS__",r=>It=r),vu=e("__VUE_SSR_SETTERS__",r=>hs=r)}const As=t=>{const e=It;return rl(t),t.scope.on(),()=>{t.scope.off(),rl(e)}},jp=()=>{It&&It.scope.off(),rl(null)};function X0(t){return 4&t.vnode.shapeFlag}let hs=!1;function Np(t,e,r){Re(e)?t.type.__ssrInlineRender?t.ssrRender=e:t.render=e:rt(e)&&(t.setupState=y0(e)),J0(t)}function J0(t,e,r){const n=t.type;t.render||(t.render=n.render||Ar);{const i=As(t);$r();try{oS(t)}finally{zr(),i()}}}const kS={get:(t,e)=>(At(t,0,""),t[e])};function Bl(t){return t.exposed?t.exposeProxy||(t.exposeProxy=new Proxy(y0(dd(t.exposed)),{get:(e,r)=>r in e?e[r]:r in zo?zo[r](t):void 0,has:(e,r)=>r in e||r in zo})):t.proxy}function ES(t,e=!0){return Re(t)?t.displayName||t.name:t.name||e&&t.__name}const Jt=(t,e)=>function(n,i,o=!1){let s,a;return Re(n)?s=n:(s=n.get,a=n.set),new zx(s,a,o)}(t,0,hs);function Yi(t,e,r){const n=arguments.length;return n===2?rt(e)&&!Ie(e)?us(e)?Wt(t,null,[e]):Wt(t,e):Wt(t,null,e):(n>3?r=Array.prototype.slice.call(arguments,2):n===3&&us(r)&&(r=[r]),Wt(t,e,r))}const ev="3.5.18";/** +* @vue/runtime-dom v3.5.18 +* (c) 2018-present Yuxi (Evan) You and Vue contributors +* @license MIT +**/let wu;const Lp=typeof window<"u"&&window.trustedTypes;if(Lp)try{wu=Lp.createPolicy("vue",{createHTML:t=>t})}catch{}const tv=wu?t=>wu.createHTML(t):t=>t,Br=typeof document<"u"?document:null,Bp=Br&&Br.createElement("template"),OS={insert:(t,e,r)=>{e.insertBefore(t,r||null)},remove:t=>{const e=t.parentNode;e&&e.removeChild(t)},createElement:(t,e,r,n)=>{const i=e==="svg"?Br.createElementNS("http://www.w3.org/2000/svg",t):e==="mathml"?Br.createElementNS("http://www.w3.org/1998/Math/MathML",t):r?Br.createElement(t,{is:r}):Br.createElement(t);return t==="select"&&n&&n.multiple!=null&&i.setAttribute("multiple",n.multiple),i},createText:t=>Br.createTextNode(t),createComment:t=>Br.createComment(t),setText:(t,e)=>{t.nodeValue=e},setElementText:(t,e)=>{t.textContent=e},parentNode:t=>t.parentNode,nextSibling:t=>t.nextSibling,querySelector:t=>Br.querySelector(t),setScopeId(t,e){t.setAttribute(e,"")},insertStaticContent(t,e,r,n,i,o){const s=r?r.previousSibling:e.lastChild;if(i&&(i===o||i.nextSibling))for(;e.insertBefore(i.cloneNode(!0),r),i!==o&&(i=i.nextSibling););else{Bp.innerHTML=tv(n==="svg"?`${t}`:n==="mathml"?`${t}`:t);const a=Bp.content;if(n==="svg"||n==="mathml"){const l=a.firstChild;for(;l.firstChild;)a.appendChild(l.firstChild);a.removeChild(l)}e.insertBefore(a,r)}return[s?s.nextSibling:e.firstChild,r?r.previousSibling:e.lastChild]}},tn="transition",go="animation",fs=Symbol("_vtc"),rv={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String},MS=mt({},M0,rv),UT=(t=>(t.displayName="Transition",t.props=MS,t))((t,{slots:e})=>Yi(Kx,function(r){const n={};for(const U in r)U in rv||(n[U]=r[U]);if(r.css===!1)return n;const{name:i="v",type:o,duration:s,enterFromClass:a=`${i}-enter-from`,enterActiveClass:l=`${i}-enter-active`,enterToClass:c=`${i}-enter-to`,appearFromClass:u=a,appearActiveClass:h=l,appearToClass:f=c,leaveFromClass:d=`${i}-leave-from`,leaveActiveClass:y=`${i}-leave-active`,leaveToClass:g=`${i}-leave-to`}=r,p=function(U){if(U==null)return null;if(rt(U))return[mc(U.enter),mc(U.leave)];{const z=mc(U);return[z,z]}}(s),b=p&&p[0],v=p&&p[1],{onBeforeEnter:S,onEnter:k,onEnterCancelled:M,onLeave:A,onLeaveCancelled:I,onBeforeAppear:T=S,onAppear:_=k,onAppearCancelled:x=M}=n,O=(U,z,q,B)=>{U._enterCancelled=B,Vn(U,z?f:c),Vn(U,z?h:l),q&&q()},N=(U,z)=>{U._isLeaving=!1,Vn(U,d),Vn(U,g),Vn(U,y),z&&z()},G=U=>(z,q)=>{const B=U?_:k,ne=()=>O(z,U,q);Un(B,[z,ne]),Wp(()=>{Vn(z,U?u:a),Cr(z,U?f:c),Fp(B)||Up(z,o,b,ne)})};return mt(n,{onBeforeEnter(U){Un(S,[U]),Cr(U,a),Cr(U,l)},onBeforeAppear(U){Un(T,[U]),Cr(U,u),Cr(U,h)},onEnter:G(!1),onAppear:G(!0),onLeave(U,z){U._isLeaving=!0;const q=()=>N(U,z);Cr(U,d),U._enterCancelled?(Cr(U,y),zp()):(zp(),Cr(U,y)),Wp(()=>{U._isLeaving&&(Vn(U,d),Cr(U,g),Fp(A)||Up(U,o,v,q))}),Un(A,[U,q])},onEnterCancelled(U){O(U,!1,void 0,!0),Un(M,[U])},onAppearCancelled(U){O(U,!0,void 0,!0),Un(x,[U])},onLeaveCancelled(U){N(U),Un(I,[U])}})}(t),e)),Un=(t,e=[])=>{Ie(t)?t.forEach(r=>r(...e)):t&&t(...e)},Fp=t=>!!t&&(Ie(t)?t.some(e=>e.length>1):t.length>1);function mc(t){return(r=>{const n=ot(r)?Number(r):NaN;return isNaN(n)?r:n})(t)}function Cr(t,e){e.split(/\s+/).forEach(r=>r&&t.classList.add(r)),(t[fs]||(t[fs]=new Set)).add(e)}function Vn(t,e){e.split(/\s+/).forEach(n=>n&&t.classList.remove(n));const r=t[fs];r&&(r.delete(e),r.size||(t[fs]=void 0))}function Wp(t){requestAnimationFrame(()=>{requestAnimationFrame(t)})}let AS=0;function Up(t,e,r,n){const i=t._endId=++AS,o=()=>{i===t._endId&&n()};if(r!=null)return setTimeout(o,r);const{type:s,timeout:a,propCount:l}=function(d,y){const g=window.getComputedStyle(d),p=O=>(g[O]||"").split(", "),b=p(`${tn}Delay`),v=p(`${tn}Duration`),S=Vp(b,v),k=p(`${go}Delay`),M=p(`${go}Duration`),A=Vp(k,M);let I=null,T=0,_=0;y===tn?S>0&&(I=tn,T=S,_=v.length):y===go?A>0&&(I=go,T=A,_=M.length):(T=Math.max(S,A),I=T>0?S>A?tn:go:null,_=I?I===tn?v.length:M.length:0);const x=I===tn&&/\b(transform|all)(,|$)/.test(p(`${tn}Property`).toString());return{type:I,timeout:T,propCount:_,hasTransform:x}}(t,e);if(!s)return n();const c=s+"end";let u=0;const h=()=>{t.removeEventListener(c,f),o()},f=d=>{d.target===t&&++u>=l&&h()};setTimeout(()=>{u$p(r)+$p(t[n])))}function $p(t){return t==="auto"?0:1e3*Number(t.slice(0,-1).replace(",","."))}function zp(){return document.body.offsetHeight}const nl=Symbol("_vod"),nv=Symbol("_vsh"),VT={beforeMount(t,{value:e},{transition:r}){t[nl]=t.style.display==="none"?"":t.style.display,r&&e?r.beforeEnter(t):mo(t,e)},mounted(t,{value:e},{transition:r}){r&&e&&r.enter(t)},updated(t,{value:e,oldValue:r},{transition:n}){!e!=!r&&(n?e?(n.beforeEnter(t),mo(t,!0),n.enter(t)):n.leave(t,()=>{mo(t,!1)}):mo(t,e))},beforeUnmount(t,{value:e}){mo(t,e)}};function mo(t,e){t.style.display=e?t[nl]:"none",t[nv]=!e}const iv=Symbol("");function $T(t){const e=Ll();if(!e)return;const r=e.ut=(i=t(e.proxy))=>{Array.from(document.querySelectorAll(`[data-v-owner="${e.uid}"]`)).forEach(o=>il(o,i))},n=()=>{const i=t(e.proxy);e.ce?il(e.ce,i):_u(e.subTree,i),r(i)};D0(()=>{w0(n)}),Dl(()=>{ni(n,Ar,{flush:"post"});const i=new MutationObserver(n);i.observe(e.subTree.el.parentNode,{childList:!0}),jl(()=>i.disconnect())})}function _u(t,e){if(128&t.shapeFlag){const r=t.suspense;t=r.activeBranch,r.pendingBranch&&!r.isHydrating&&r.effects.push(()=>{_u(r.activeBranch,e)})}for(;t.component;)t=t.component.subTree;if(1&t.shapeFlag&&t.el)il(t.el,e);else if(t.type===Ft)t.children.forEach(r=>_u(r,e));else if(t.type===Sa){let{el:r,anchor:n}=t;for(;r&&(il(r,e),r!==n);)r=r.nextSibling}}function il(t,e){if(t.nodeType===1){const r=t.style;let n="";for(const i in e){const o=Sx(e[i]);r.setProperty(`--${i}`,o),n+=`--${i}: ${o};`}r[iv]=n}}const TS=/(^|;)\s*display\s*:/,Hp=/\s*!important$/;function Ea(t,e,r){if(Ie(r))r.forEach(n=>Ea(t,e,n));else if(r==null&&(r=""),e.startsWith("--"))t.setProperty(e,r);else{const n=function(i,o){const s=yc[o];if(s)return s;let a=ir(o);if(a!=="filter"&&a in i)return yc[o]=a;a=kl(a);for(let l=0;l{if(d._vts){if(d._vts<=f.attached)return}else d._vts=Date.now();pr(function(y,g){if(Ie(g)){const p=y.stopImmediatePropagation;return y.stopImmediatePropagation=()=>{p.call(y),y._stopped=!0},g.map(b=>v=>!v._stopped&&b&&b(v))}return g}(d,f.value),h,5,[d])};return f.value=u,f.attached=CS(),f}(n,i);(function(u,h,f,d){u.addEventListener(h,f,d)})(t,a,c,l)}else s&&(function(c,u,h,f){c.removeEventListener(u,h,f)}(t,a,s,l),o[e]=void 0)}}const Zp=/(?:Once|Passive|Capture)$/;let bc=0;const PS=Promise.resolve(),CS=()=>bc||(PS.then(()=>bc=0),bc=Date.now()),Xp=t=>t.charCodeAt(0)===111&&t.charCodeAt(1)===110&&t.charCodeAt(2)>96&&t.charCodeAt(2)<123,RS=["ctrl","shift","alt","meta"],DS={stop:t=>t.stopPropagation(),prevent:t=>t.preventDefault(),self:t=>t.target!==t.currentTarget,ctrl:t=>!t.ctrlKey,shift:t=>!t.shiftKey,alt:t=>!t.altKey,meta:t=>!t.metaKey,left:t=>"button"in t&&t.button!==0,middle:t=>"button"in t&&t.button!==1,right:t=>"button"in t&&t.button!==2,exact:(t,e)=>RS.some(r=>t[`${r}Key`]&&!e.includes(r))},zT=(t,e)=>{const r=t._withMods||(t._withMods={}),n=e.join(".");return r[n]||(r[n]=(i,...o)=>{for(let s=0;s{const r=t._withKeys||(t._withKeys={}),n=e.join(".");return r[n]||(r[n]=i=>{if(!("key"in i))return;const o=jn(i.key);return e.some(s=>s===o||jS[s]===o)?t(i):void 0})},NS=mt({patchProp:(t,e,r,n,i,o)=>{const s=i==="svg";e==="class"?function(a,l,c){const u=a[fs];u&&(l=(l?[l,...u]:[...u]).join(" ")),l==null?a.removeAttribute("class"):c?a.setAttribute("class",l):a.className=l}(t,n,s):e==="style"?function(a,l,c){const u=a.style,h=ot(c);let f=!1;if(c&&!h){if(l)if(ot(l))for(const d of l.split(";")){const y=d.slice(0,d.indexOf(":")).trim();c[y]==null&&Ea(u,y,"")}else for(const d in l)c[d]==null&&Ea(u,d,"");for(const d in c)d==="display"&&(f=!0),Ea(u,d,c[d])}else if(h){if(l!==c){const d=u[iv];d&&(c+=";"+d),u.cssText=c,f=TS.test(c)}}else l&&a.removeAttribute("style");nl in a&&(a[nl]=f?u.display:"",a[nv]&&(u.display="none"))}(t,r,n):_l(e)?id(e)||IS(t,e,0,n,o):(e[0]==="."?(e=e.slice(1),1):e[0]==="^"?(e=e.slice(1),0):function(a,l,c,u){if(u)return l==="innerHTML"||l==="textContent"||!!(l in a&&Xp(l)&&Re(c));if(l==="spellcheck"||l==="draggable"||l==="translate"||l==="autocorrect"||l==="form"||l==="list"&&a.tagName==="INPUT"||l==="type"&&a.tagName==="TEXTAREA")return!1;if(l==="width"||l==="height"){const h=a.tagName;if(h==="IMG"||h==="VIDEO"||h==="CANVAS"||h==="SOURCE")return!1}return Xp(l)&&ot(c)?!1:l in a}(t,e,n,s))?(Gp(t,e,n),t.tagName.includes("-")||e!=="value"&&e!=="checked"&&e!=="selected"||Kp(t,e,n,s,0,e!=="value")):!t._isVueCE||!/[A-Z]/.test(e)&&ot(n)?(e==="true-value"?t._trueValue=n:e==="false-value"&&(t._falseValue=n),Kp(t,e,n,s)):Gp(t,ir(e),n,0,e)}},OS);let Jp;const qT=(...t)=>{const e=(Jp||(Jp=hS(NS))).createApp(...t),{mount:r}=e;return e.mount=n=>{const i=function(a){return ot(a)?document.querySelector(a):a}(n);if(!i)return;const o=e._component;Re(o)||o.render||o.template||(o.template=i.innerHTML),i.nodeType===1&&(i.textContent="");const s=r(i,!1,function(a){if(a instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&a instanceof MathMLElement)return"mathml"}(i));return i instanceof Element&&(i.removeAttribute("v-cloak"),i.setAttribute("data-v-app","")),s},e};let ov;const Fl=t=>ov=t,sv=Symbol();function xu(t){return t&&typeof t=="object"&&Object.prototype.toString.call(t)==="[object Object]"&&typeof t.toJSON!="function"}var qo,vc;function YT(){const t=Xb(!0),e=t.run(()=>Al({}));let r=[],n=[];const i=dd({install(o){Fl(i),i._a=o,o.provide(sv,i),o.config.globalProperties.$pinia=i,n.forEach(s=>r.push(s)),n=[]},use(o){return this._a?r.push(o):n.push(o),this},_p:r,_a:null,_e:t,_s:new Map,state:e});return i}(vc=qo||(qo={})).direct="direct",vc.patchObject="patch object",vc.patchFunction="patch function";const av=()=>{};function eg(t,e,r,n=av){t.push(e);const i=()=>{const s=t.indexOf(e);s>-1&&(t.splice(s,1),n())};var o;return!r&&Jb()&&(o=i,Ot&&Ot.cleanups.push(o)),i}function vi(t,...e){t.slice().forEach(r=>{r(...e)})}const LS=t=>t(),tg=Symbol(),wc=Symbol();function Su(t,e){t instanceof Map&&e instanceof Map?e.forEach((r,n)=>t.set(n,r)):t instanceof Set&&e instanceof Set&&e.forEach(t.add,t);for(const r in e){if(!e.hasOwnProperty(r))continue;const n=e[r],i=t[r];xu(i)&&xu(n)&&t.hasOwnProperty(r)&&!ft(n)&&!Tn(n)?t[r]=Su(i,n):t[r]=n}return t}const BS=Symbol();function FS(t){return!xu(t)||!Object.prototype.hasOwnProperty.call(t,BS)}const{assign:an}=Object;function WS(t){return!(!ft(t)||!t.effect)}function US(t,e,r,n){const{state:i,actions:o,getters:s}=e,a=r.state.value[t];let l;return l=lv(t,function(){a||(r.state.value[t]=i?i():{});const c=function(u){const h=Ie(u)?new Array(u.length):{};for(const f in u)h[f]=$x(u,f);return h}(r.state.value[t]);return an(c,o,Object.keys(s||{}).reduce((u,h)=>(u[h]=dd(Jt(()=>{Fl(r);const f=r._s.get(t);return s[h].call(f,f)})),u),{}))},e,r,n,!0),l}function lv(t,e,r={},n,i,o){let s;const a=an({actions:{}},r),l={deep:!0};let c,u,h,f=[],d=[];const y=n.state.value[t];let g;function p(A){let I;c=u=!1,typeof A=="function"?(A(n.state.value[t]),I={type:qo.patchFunction,storeId:t,events:h}):(Su(n.state.value[t],A),I={type:qo.patchObject,payload:A,storeId:t,events:h});const T=g=Symbol();Il().then(()=>{g===T&&(c=!0)}),u=!0,vi(f,I,n.state.value[t])}o||y||(n.state.value[t]={}),Al({});const b=o?function(){const{state:A}=r,I=A?A():{};this.$patch(T=>{an(T,I)})}:av,v=(A,I="")=>{if(tg in A)return A[wc]=I,A;const T=function(){Fl(n);const _=Array.from(arguments),x=[],O=[];let N;vi(d,{args:_,name:T[wc],store:k,after:function(G){x.push(G)},onError:function(G){O.push(G)}});try{N=A.apply(this&&this.$id===t?this:k,_)}catch(G){throw vi(O,G),G}return N instanceof Promise?N.then(G=>(vi(x,G),G)).catch(G=>(vi(O,G),Promise.reject(G))):(vi(x,N),N)};return T[tg]=!0,T[wc]=I,T},S={_p:n,$id:t,$onAction:eg.bind(null,d),$patch:p,$reset:b,$subscribe(A,I={}){const T=eg(f,A,I.detached,()=>_()),_=s.run(()=>ni(()=>n.state.value[t],x=>{(I.flush==="sync"?u:c)&&A({storeId:t,type:qo.direct,events:h},x)},an({},l,I)));return T},$dispose:function(){s.stop(),f=[],d=[],n._s.delete(t)}},k=Es(S);n._s.set(t,k);const M=(n._a&&n._a.runWithContext||LS)(()=>n._e.run(()=>(s=Xb()).run(()=>e({action:v}))));for(const A in M){const I=M[A];if(ft(I)&&!WS(I)||Tn(I))o||(y&&FS(I)&&(ft(I)?I.value=y[A]:Su(I,y[A])),n.state.value[t][A]=I);else if(typeof I=="function"){const T=v(I,A);M[A]=T,a.actions[A]=I}}return an(k,M),an(Le(k),M),Object.defineProperty(k,"$state",{get:()=>n.state.value[t],set:A=>{p(I=>{an(I,A)})}}),n._p.forEach(A=>{an(k,s.run(()=>A({store:k,app:n._a,pinia:n,options:a})))}),y&&o&&r.hydrate&&r.hydrate(k.$state,y),c=!0,u=!0,k}/*! #__NO_SIDE_EFFECTS__ */function KT(t,e,r){let n;const i=typeof e=="function";function o(s,a){const l=!(!Ll()&&!ri);return(s=s||(l?Tr(sv,null):null))&&Fl(s),(s=ov)._s.has(t)||(i?lv(t,e,n,s):US(t,n,s)),s._s.get(t)}return n=i?r:e,o.$id=t,o}/*! + * vue-router v4.5.1 + * (c) 2025 Eduardo San Martin Morote + * @license MIT + */const Mi=typeof document<"u";function rg(t){return typeof t=="object"||"displayName"in t||"props"in t||"__vccOpts"in t}const Ye=Object.assign;function _c(t,e){const r={};for(const n in e){const i=e[n];r[n]=gr(i)?i.map(t):t(i)}return r}const Yo=()=>{},gr=Array.isArray,cv=/#/g,VS=/&/g,$S=/\//g,zS=/=/g,HS=/\?/g,uv=/\+/g,qS=/%5B/g,YS=/%5D/g,hv=/%5E/g,KS=/%60/g,fv=/%7B/g,GS=/%7C/g,dv=/%7D/g,QS=/%20/g;function xd(t){return encodeURI(""+t).replace(GS,"|").replace(qS,"[").replace(YS,"]")}function ku(t){return xd(t).replace(uv,"%2B").replace(QS,"+").replace(cv,"%23").replace(VS,"%26").replace(KS,"`").replace(fv,"{").replace(dv,"}").replace(hv,"^")}function ZS(t){return ku(t).replace(zS,"%3D")}function XS(t){return t==null?"":function(e){return xd(e).replace(cv,"%23").replace(HS,"%3F")}(t).replace($S,"%2F")}function ds(t){try{return decodeURIComponent(""+t)}catch{}return""+t}const JS=/\/$/;function xc(t,e,r="/"){let n,i={},o="",s="";const a=e.indexOf("#");let l=e.indexOf("?");return a=0&&(l=-1),l>-1&&(n=e.slice(0,l),o=e.slice(l+1,a>-1?a:e.length),i=t(o)),a>-1&&(n=n||e.slice(0,a),s=e.slice(a,e.length)),n=function(c,u){if(c.startsWith("/"))return c;if(!c)return u;const h=u.split("/"),f=c.split("/"),d=f[f.length-1];d!==".."&&d!=="."||f.push("");let y,g,p=h.length-1;for(y=0;y1&&p--}return h.slice(0,p).join("/")+"/"+f.slice(y).join("/")}(n??e,r),{fullPath:n+(o&&"?")+o+s,path:n,query:i,hash:ds(s)}}function ng(t,e){return e&&t.toLowerCase().startsWith(e.toLowerCase())?t.slice(e.length)||"/":t}function Ui(t,e){return(t.aliasOf||t)===(e.aliasOf||e)}function pv(t,e){if(Object.keys(t).length!==Object.keys(e).length)return!1;for(const r in t)if(!ek(t[r],e[r]))return!1;return!0}function ek(t,e){return gr(t)?ig(t,e):gr(e)?ig(e,t):t===e}function ig(t,e){return gr(e)?t.length===e.length&&t.every((r,n)=>r===e[n]):t.length===1&&t[0]===e}const rn={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0};var ps,og,Ko,Sc;function tk(t){if(!t)if(Mi){const e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^\w+:\/\/[^\/]+/,"")}else t="/";return t[0]!=="/"&&t[0]!=="#"&&(t="/"+t),t.replace(JS,"")}(og=ps||(ps={})).pop="pop",og.push="push",(Sc=Ko||(Ko={})).back="back",Sc.forward="forward",Sc.unknown="";const rk=/^[^#]+#/;function nk(t,e){return t.replace(rk,"#")+e}const Wl=()=>({left:window.scrollX,top:window.scrollY});function ik(t){let e;if("el"in t){const r=t.el,n=typeof r=="string"&&r.startsWith("#"),i=typeof r=="string"?n?document.getElementById(r.slice(1)):document.querySelector(r):r;if(!i)return;e=function(o,s){const a=document.documentElement.getBoundingClientRect(),l=o.getBoundingClientRect();return{behavior:s.behavior,left:l.left-a.left-(s.left||0),top:l.top-a.top-(s.top||0)}}(i,t)}else e=t;"scrollBehavior"in document.documentElement.style?window.scrollTo(e):window.scrollTo(e.left!=null?e.left:window.scrollX,e.top!=null?e.top:window.scrollY)}function sg(t,e){return(history.state?history.state.position-e:-1)+t}const kc=new Map;function gv(t,e){const{pathname:r,search:n,hash:i}=e,o=t.indexOf("#");if(o>-1){let s=i.includes(t.slice(o))?t.slice(o).length:1,a=i.slice(s);return a[0]!=="/"&&(a="/"+a),ng(a,"")}return ng(r,t)+n+i}function ag(t,e,r,n=!1,i=!1){return{back:t,current:e,forward:r,replaced:n,position:window.history.length,scroll:i?Wl():null}}function ok(t){const{history:e,location:r}=window,n={value:gv(t,r)},i={value:e.state};function o(s,a,l){const c=t.indexOf("#"),u=c>-1?(r.host&&document.querySelector("base")?t:t.slice(c))+s:location.protocol+"//"+location.host+t+s;try{e[l?"replaceState":"pushState"](a,"",u),i.value=a}catch{r[l?"replace":"assign"](u)}}return i.value||o(n.value,{back:null,current:n.value,forward:null,position:e.length-1,replaced:!0,scroll:null},!0),{location:n,state:i,push:function(s,a){const l=Ye({},i.value,e.state,{forward:s,scroll:Wl()});o(l.current,l,!0),o(s,Ye({},ag(n.value,s,null),{position:l.position+1},a),!1),n.value=s},replace:function(s,a){o(s,Ye({},e.state,ag(i.value.back,s,i.value.forward,!0),a,{position:i.value.position}),!0),n.value=s}}}function sk(t){const e=ok(t=tk(t)),r=function(i,o,s,a){let l=[],c=[],u=null;const h=({state:d})=>{const y=gv(i,location),g=s.value,p=o.value;let b=0;if(d){if(s.value=y,o.value=d,u&&u===g)return void(u=null);b=p?d.position-p.position:0}else a(y);l.forEach(v=>{v(s.value,g,{delta:b,type:ps.pop,direction:b?b>0?Ko.forward:Ko.back:Ko.unknown})})};function f(){const{history:d}=window;d.state&&d.replaceState(Ye({},d.state,{scroll:Wl()}),"")}return window.addEventListener("popstate",h),window.addEventListener("beforeunload",f,{passive:!0}),{pauseListeners:function(){u=s.value},listen:function(d){l.push(d);const y=()=>{const g=l.indexOf(d);g>-1&&l.splice(g,1)};return c.push(y),y},destroy:function(){for(const d of c)d();c=[],window.removeEventListener("popstate",h),window.removeEventListener("beforeunload",f)}}}(t,e.state,e.location,e.replace),n=Ye({location:"",base:t,go:function(i,o=!0){o||r.pauseListeners(),history.go(i)},createHref:nk.bind(null,t)},e,r);return Object.defineProperty(n,"location",{enumerable:!0,get:()=>e.location.value}),Object.defineProperty(n,"state",{enumerable:!0,get:()=>e.state.value}),n}function GT(t){return(t=location.host?t||location.pathname+location.search:"").includes("#")||(t+="#"),sk(t)}function mv(t){return typeof t=="string"||typeof t=="symbol"}const yv=Symbol("");var lg,wi;function Ki(t,e){return Ye(new Error,{type:t,[yv]:!0},e)}function Rr(t,e){return t instanceof Error&&yv in t&&(e==null||!!(t.type&e))}(wi=lg||(lg={}))[wi.aborted=4]="aborted",wi[wi.cancelled=8]="cancelled",wi[wi.duplicated=16]="duplicated";const cg="[^/]+?",ak={sensitive:!1,strict:!1,start:!0,end:!0},lk=/[.+*?^${}()[\]/\\]/g;function ck(t,e){let r=0;for(;re.length?e.length===1&&e[0]===80?1:-1:0}function ug(t,e){let r=0;const n=t.score,i=e.score;for(;r0&&e[e.length-1]<0}const uk={type:0,value:""},hk=/[a-zA-Z0-9_]/;function fk(t,e,r){const n=function(o,s){const a=Ye({},ak,s),l=[];let c=a.start?"^":"";const u=[];for(const f of o){const d=f.length?[]:[90];a.strict&&!f.length&&(c+="/");for(let y=0;y1&&(f==="*"||f==="+")&&s(`A repeatable param (${y}) must be alone in its segment. eg: '/:ids+.`),u.push({type:1,value:y,regexp:g,repeatable:f==="*"||f==="+",optional:f==="*"||f==="?"})):s("Invalid state to consume buffer"),y="")}function b(){y+=f}for(;d{o(g)}:Yo}function o(a){if(mv(a)){const l=n.get(a);l&&(n.delete(a),r.splice(r.indexOf(l),1),l.children.forEach(o),l.alias.forEach(o))}else{const l=r.indexOf(a);l>-1&&(r.splice(l,1),a.record.name&&n.delete(a.record.name),a.children.forEach(o),a.alias.forEach(o))}}function s(a){const l=function(c,u){let h=0,f=u.length;for(;h!==f;){const y=h+f>>1;ug(c,u[y])<0?f=y:h=y+1}const d=function(y){let g=y;for(;g=g.parent;)if(mg(g)&&ug(y,g)===0)return g}(c);return d&&(f=u.lastIndexOf(d,f-1)),f}(a,r);r.splice(l,0,a),a.record.name&&!pg(a)&&n.set(a.record.name,a)}return e=gg({strict:!1,end:!0,sensitive:!1},e),t.forEach(a=>i(a)),{addRoute:i,resolve:function(a,l){let c,u,h,f={};if("name"in a&&a.name){if(c=n.get(a.name),!c)throw Ki(1,{location:a});h=c.record.name,f=Ye(fg(l.params,c.keys.filter(g=>!g.optional).concat(c.parent?c.parent.keys.filter(g=>g.optional):[]).map(g=>g.name)),a.params&&fg(a.params,c.keys.map(g=>g.name))),u=c.stringify(f)}else if(a.path!=null)u=a.path,c=r.find(g=>g.re.test(u)),c&&(f=c.parse(u),h=c.record.name);else{if(c=l.name?n.get(l.name):r.find(g=>g.re.test(l.path)),!c)throw Ki(1,{location:a,currentLocation:l});h=c.record.name,f=Ye({},l.params,a.params),u=c.stringify(f)}const d=[];let y=c;for(;y;)d.unshift(y.record),y=y.parent;return{name:h,path:u,params:f,matched:d,meta:gk(d)}},removeRoute:o,clearRoutes:function(){r.length=0,n.clear()},getRoutes:function(){return r},getRecordMatcher:function(a){return n.get(a)}}}function fg(t,e){const r={};for(const n of e)n in t&&(r[n]=t[n]);return r}function dg(t){const e={path:t.path,redirect:t.redirect,name:t.name,meta:t.meta||{},aliasOf:t.aliasOf,beforeEnter:t.beforeEnter,props:pk(t),children:t.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in t?t.components||null:t.component&&{default:t.component}};return Object.defineProperty(e,"mods",{value:{}}),e}function pk(t){const e={},r=t.props||!1;if("component"in t)e.default=r;else for(const n in t.components)e[n]=typeof r=="object"?r[n]:r;return e}function pg(t){for(;t;){if(t.record.aliasOf)return!0;t=t.parent}return!1}function gk(t){return t.reduce((e,r)=>Ye(e,r.meta),{})}function gg(t,e){const r={};for(const n in t)r[n]=n in e?e[n]:t[n];return r}function mg({record:t}){return!!(t.name||t.components&&Object.keys(t.components).length||t.redirect)}function mk(t){const e={};if(t===""||t==="?")return e;const r=(t[0]==="?"?t.slice(1):t).split("&");for(let n=0;ni&&ku(i)):[n&&ku(n)]).forEach(i=>{i!==void 0&&(e+=(e.length?"&":"")+r,i!=null&&(e+="="+i))})}return e}function yk(t){const e={};for(const r in t){const n=t[r];n!==void 0&&(e[r]=gr(n)?n.map(i=>i==null?null:""+i):n==null?n:""+n)}return e}const bk=Symbol(""),bg=Symbol(""),Sd=Symbol(""),bv=Symbol(""),Eu=Symbol("");function yo(){let t=[];return{add:function(e){return t.push(e),()=>{const r=t.indexOf(e);r>-1&&t.splice(r,1)}},list:()=>t.slice(),reset:function(){t=[]}}}function vn(t,e,r,n,i,o=s=>s()){const s=n&&(n.enterCallbacks[i]=n.enterCallbacks[i]||[]);return()=>new Promise((a,l)=>{const c=f=>{var d;f===!1?l(Ki(4,{from:r,to:e})):f instanceof Error?l(f):typeof(d=f)=="string"||d&&typeof d=="object"?l(Ki(2,{from:e,to:f})):(s&&n.enterCallbacks[i]===s&&typeof f=="function"&&s.push(f),a())},u=o(()=>t.call(n&&n.instances[i],e,r,c));let h=Promise.resolve(u);t.length<3&&(h=h.then(c)),h.catch(f=>l(f))})}function Ec(t,e,r,n,i=o=>o()){const o=[];for(const s of t)for(const a in s.components){let l=s.components[a];if(e==="beforeRouteEnter"||s.instances[a])if(rg(l)){const c=(l.__vccOpts||l)[e];c&&o.push(vn(c,r,n,s,a,i))}else{let c=l();o.push(()=>c.then(u=>{if(!u)throw new Error(`Couldn't resolve component "${a}" at "${s.path}"`);const h=(f=u).__esModule||f[Symbol.toStringTag]==="Module"||f.default&&rg(f.default)?u.default:u;var f;s.mods[a]=u,s.components[a]=h;const d=(h.__vccOpts||h)[e];return d&&vn(d,r,n,s,a,i)()}))}}return o}function vg(t){const e=Tr(Sd),r=Tr(bv),n=Jt(()=>{const a=Fi(t.to);return e.resolve(a)}),i=Jt(()=>{const{matched:a}=n.value,{length:l}=a,c=a[l-1],u=r.matched;if(!c||!u.length)return-1;const h=u.findIndex(Ui.bind(null,c));if(h>-1)return h;const f=wg(a[l-2]);return l>1&&wg(c)===f&&u[u.length-1].path!==f?u.findIndex(Ui.bind(null,a[l-2])):h}),o=Jt(()=>i.value>-1&&function(a,l){for(const c in l){const u=l[c],h=a[c];if(typeof u=="string"){if(u!==h)return!1}else if(!gr(h)||h.length!==u.length||u.some((f,d)=>f!==h[d]))return!1}return!0}(r.params,n.value.params)),s=Jt(()=>i.value>-1&&i.value===r.matched.length-1&&pv(r.params,n.value.params));return{route:n,href:Jt(()=>n.value.href),isActive:o,isExactActive:s,navigate:function(a={}){if(function(l){if(!(l.metaKey||l.altKey||l.ctrlKey||l.shiftKey)&&!l.defaultPrevented&&!(l.button!==void 0&&l.button!==0)){if(l.currentTarget&&l.currentTarget.getAttribute){const c=l.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(c))return}return l.preventDefault&&l.preventDefault(),!0}}(a)){const l=e[Fi(t.replace)?"replace":"push"](Fi(t.to)).catch(Yo);return t.viewTransition&&typeof document<"u"&&"startViewTransition"in document&&document.startViewTransition(()=>l),l}return Promise.resolve()}}}const vk=Pl({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"},viewTransition:Boolean},useLink:vg,setup(t,{slots:e}){const r=Es(vg(t)),{options:n}=Tr(Sd),i=Jt(()=>({[_g(t.activeClass,n.linkActiveClass,"router-link-active")]:r.isActive,[_g(t.exactActiveClass,n.linkExactActiveClass,"router-link-exact-active")]:r.isExactActive}));return()=>{const o=e.default&&((s=e.default(r)).length===1?s[0]:s);var s;return t.custom?o:Yi("a",{"aria-current":r.isExactActive?t.ariaCurrentValue:null,href:r.href,onClick:r.navigate,class:i.value},o)}}});function wg(t){return t?t.aliasOf?t.aliasOf.path:t.path:""}const _g=(t,e,r)=>t??e??r,wk=Pl({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(t,{attrs:e,slots:r}){const n=Tr(Eu),i=Jt(()=>t.route||n.value),o=Tr(bg,0),s=Jt(()=>{let c=Fi(o);const{matched:u}=i.value;let h;for(;(h=u[c])&&!h.components;)c++;return c}),a=Jt(()=>i.value.matched[s.value]);xa(bg,Jt(()=>s.value+1)),xa(bk,a),xa(Eu,i);const l=Al();return ni(()=>[l.value,a.value,t.name],([c,u,h],[f,d,y])=>{u&&(u.instances[h]=c,d&&d!==u&&c&&c===f&&(u.leaveGuards.size||(u.leaveGuards=d.leaveGuards),u.updateGuards.size||(u.updateGuards=d.updateGuards))),!c||!u||d&&Ui(u,d)&&f||(u.enterCallbacks[h]||[]).forEach(g=>g(c))},{flush:"post"}),()=>{const c=i.value,u=t.name,h=a.value,f=h&&h.components[u];if(!f)return xg(r.default,{Component:f,route:c});const d=h.props[u],y=d?d===!0?c.params:typeof d=="function"?d(c):d:null,g=Yi(f,Ye({},y,e,{onVnodeUnmounted:p=>{p.component.isUnmounted&&(h.instances[u]=null)},ref:l}));return xg(r.default,{Component:g,route:c})||g}}});function xg(t,e){if(!t)return null;const r=t(e);return r.length===1?r[0]:r}const _k=wk;function QT(t){const e=dk(t.routes,t),r=t.parseQuery||mk,n=t.stringifyQuery||yg,i=t.history,o=yo(),s=yo(),a=yo(),l=pd(rn);let c=rn;Mi&&t.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=_c.bind(null,F=>""+F),h=_c.bind(null,XS),f=_c.bind(null,ds);function d(F,V){if(V=Ye({},V||l.value),typeof F=="string"){const ie=xc(r,F,V.path),oe=e.resolve({path:ie.path},V),j=i.createHref(ie.fullPath);return Ye(ie,oe,{params:f(oe.params),hash:ds(ie.hash),redirectedFrom:void 0,href:j})}let Z;if(F.path!=null)Z=Ye({},F,{path:xc(r,F.path,V.path).path});else{const ie=Ye({},F.params);for(const oe in ie)ie[oe]==null&&delete ie[oe];Z=Ye({},F,{params:h(ie)}),V.params=h(V.params)}const $=e.resolve(Z,V),se=F.hash||"";$.params=u(f($.params));const le=function(ie,oe){const j=oe.query?ie(oe.query):"";return oe.path+(j&&"?")+j+(oe.hash||"")}(n,Ye({},F,{hash:(ue=se,xd(ue).replace(fv,"{").replace(dv,"}").replace(hv,"^")),path:$.path}));var ue;const Y=i.createHref(le);return Ye({fullPath:le,hash:se,query:n===yg?yk(F.query):F.query||{}},$,{redirectedFrom:void 0,href:Y})}function y(F){return typeof F=="string"?xc(r,F,l.value.path):Ye({},F)}function g(F,V){if(c!==F)return Ki(8,{from:V,to:F})}function p(F){return v(F)}function b(F){const V=F.matched[F.matched.length-1];if(V&&V.redirect){const{redirect:Z}=V;let $=typeof Z=="function"?Z(F):Z;return typeof $=="string"&&($=$.includes("?")||$.includes("#")?$=y($):{path:$},$.params={}),Ye({query:F.query,hash:F.hash,params:$.path!=null?{}:F.params},$)}}function v(F,V){const Z=c=d(F),$=l.value,se=F.state,le=F.force,ue=F.replace===!0,Y=b(Z);if(Y)return v(Ye(y(Y),{state:typeof Y=="object"?Ye({},se,Y.state):se,force:le,replace:ue}),V||Z);const ie=Z;let oe;return ie.redirectedFrom=V,!le&&function(j,X,R){const te=X.matched.length-1,m=R.matched.length-1;return te>-1&&te===m&&Ui(X.matched[te],R.matched[m])&&pv(X.params,R.params)&&j(X.query)===j(R.query)&&X.hash===R.hash}(n,$,Z)&&(oe=Ki(16,{to:ie,from:$}),z($,$,!0,!1)),(oe?Promise.resolve(oe):M(ie,$)).catch(j=>Rr(j)?Rr(j,2)?j:U(j):G(j,ie,$)).then(j=>{if(j){if(Rr(j,2))return v(Ye({replace:ue},y(j.to),{state:typeof j.to=="object"?Ye({},se,j.to.state):se,force:le}),V||ie)}else j=I(ie,$,!0,ue,se);return A(ie,$,j),j})}function S(F,V){const Z=g(F,V);return Z?Promise.reject(Z):Promise.resolve()}function k(F){const V=ne.values().next().value;return V&&typeof V.runWithContext=="function"?V.runWithContext(F):F()}function M(F,V){let Z;const[$,se,le]=function(Y,ie){const oe=[],j=[],X=[],R=Math.max(ie.matched.length,Y.matched.length);for(let te=0;teUi(E,m))?j.push(m):oe.push(m));const w=Y.matched[te];w&&(ie.matched.find(E=>Ui(E,w))||X.push(w))}return[oe,j,X]}(F,V);Z=Ec($.reverse(),"beforeRouteLeave",F,V);for(const Y of $)Y.leaveGuards.forEach(ie=>{Z.push(vn(ie,F,V))});const ue=S.bind(null,F,V);return Z.push(ue),Q(Z).then(()=>{Z=[];for(const Y of o.list())Z.push(vn(Y,F,V));return Z.push(ue),Q(Z)}).then(()=>{Z=Ec(se,"beforeRouteUpdate",F,V);for(const Y of se)Y.updateGuards.forEach(ie=>{Z.push(vn(ie,F,V))});return Z.push(ue),Q(Z)}).then(()=>{Z=[];for(const Y of le)if(Y.beforeEnter)if(gr(Y.beforeEnter))for(const ie of Y.beforeEnter)Z.push(vn(ie,F,V));else Z.push(vn(Y.beforeEnter,F,V));return Z.push(ue),Q(Z)}).then(()=>(F.matched.forEach(Y=>Y.enterCallbacks={}),Z=Ec(le,"beforeRouteEnter",F,V,k),Z.push(ue),Q(Z))).then(()=>{Z=[];for(const Y of s.list())Z.push(vn(Y,F,V));return Z.push(ue),Q(Z)}).catch(Y=>Rr(Y,8)?Y:Promise.reject(Y))}function A(F,V,Z){a.list().forEach($=>k(()=>$(F,V,Z)))}function I(F,V,Z,$,se){const le=g(F,V);if(le)return le;const ue=V===rn,Y=Mi?history.state:{};Z&&($||ue?i.replace(F.fullPath,Ye({scroll:ue&&Y&&Y.scroll},se)):i.push(F.fullPath,se)),l.value=F,z(F,V,Z,ue),U()}let T;function _(){T||(T=i.listen((F,V,Z)=>{if(!H.listening)return;const $=d(F),se=b($);if(se)return void v(Ye(se,{replace:!0,force:!0}),$).catch(Yo);c=$;const le=l.value;var ue,Y;Mi&&(ue=sg(le.fullPath,Z.delta),Y=Wl(),kc.set(ue,Y)),M($,le).catch(ie=>Rr(ie,12)?ie:Rr(ie,2)?(v(Ye(y(ie.to),{force:!0}),$).then(oe=>{Rr(oe,20)&&!Z.delta&&Z.type===ps.pop&&i.go(-1,!1)}).catch(Yo),Promise.reject()):(Z.delta&&i.go(-Z.delta,!1),G(ie,$,le))).then(ie=>{(ie=ie||I($,le,!1))&&(Z.delta&&!Rr(ie,8)?i.go(-Z.delta,!1):Z.type===ps.pop&&Rr(ie,20)&&i.go(-1,!1)),A($,le,ie)}).catch(Yo)}))}let x,O=yo(),N=yo();function G(F,V,Z){U(F);const $=N.list();return $.length&&$.forEach(se=>se(F,V,Z)),Promise.reject(F)}function U(F){return x||(x=!F,_(),O.list().forEach(([V,Z])=>F?Z(F):V()),O.reset()),F}function z(F,V,Z,$){const{scrollBehavior:se}=t;if(!Mi||!se)return Promise.resolve();const le=!Z&&function(ue){const Y=kc.get(ue);return kc.delete(ue),Y}(sg(F.fullPath,0))||($||!Z)&&history.state&&history.state.scroll||null;return Il().then(()=>se(F,V,le)).then(ue=>ue&&ik(ue)).catch(ue=>G(ue,F,V))}const q=F=>i.go(F);let B;const ne=new Set,H={currentRoute:l,listening:!0,addRoute:function(F,V){let Z,$;return mv(F)?(Z=e.getRecordMatcher(F),$=V):$=F,e.addRoute($,Z)},removeRoute:function(F){const V=e.getRecordMatcher(F);V&&e.removeRoute(V)},clearRoutes:e.clearRoutes,hasRoute:function(F){return!!e.getRecordMatcher(F)},getRoutes:function(){return e.getRoutes().map(F=>F.record)},resolve:d,options:t,push:p,replace:function(F){return p(Ye(y(F),{replace:!0}))},go:q,back:()=>q(-1),forward:()=>q(1),beforeEach:o.add,beforeResolve:s.add,afterEach:a.add,onError:N.add,isReady:function(){return x&&l.value!==rn?Promise.resolve():new Promise((F,V)=>{O.add([F,V])})},install(F){F.component("RouterLink",vk),F.component("RouterView",_k),F.config.globalProperties.$router=this,Object.defineProperty(F.config.globalProperties,"$route",{enumerable:!0,get:()=>Fi(l)}),Mi&&!B&&l.value===rn&&(B=!0,p(i.location).catch($=>{}));const V={};for(const $ in rn)Object.defineProperty(V,$,{get:()=>l.value[$],enumerable:!0});F.provide(Sd,this),F.provide(bv,p0(V)),F.provide(Eu,l);const Z=F.unmount;ne.add(F),F.unmount=function(){ne.delete(F),ne.size<1&&(c=rn,T&&T(),T=null,l.value=rn,B=!1,x=!1),Z()}}};function Q(F){return F.reduce((V,Z)=>V.then(()=>k(Z)),Promise.resolve())}return H}var Ou=Object.defineProperty,xk=Object.getOwnPropertyDescriptor,Sk=Object.getOwnPropertyNames,kk=Object.prototype.hasOwnProperty,Ht=(t,e)=>()=>(t&&(e=t(t=0)),e),we=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),no=(t,e)=>{for(var r in e)Ou(t,r,{get:e[r],enumerable:!0})},Xe=t=>((e,r,n,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of Sk(r))!kk.call(e,o)&&o!==n&&Ou(e,o,{get:()=>r[o],enumerable:!(i=xk(r,o))||i.enumerable});return e})(Ou({},"__esModule",{value:!0}),t),me=Ht(()=>{}),$e={};function kd(t){throw new Error("Node.js process "+t+" is not supported by JSPM core outside of Node.js")}function Ek(){!ii||!Zn||(ii=!1,Zn.length?kr=Zn.concat(kr):Go=-1,kr.length&&vv())}function vv(){if(!ii){var t=setTimeout(Ek,0);ii=!0;for(var e=kr.length;e;){for(Zn=kr,kr=[];++Go1)for(var r=1;rsh,_debugProcess:()=>oh,_events:()=>Pv,_eventsCount:()=>Cv,_exiting:()=>Hu,_fatalExceptions:()=>rh,_getActiveHandles:()=>Ev,_getActiveRequests:()=>kv,_kill:()=>Ku,_linkedBinding:()=>xv,_maxListeners:()=>Iv,_preload_modules:()=>bh,_rawDebug:()=>Vu,_startProfilerIdleNotifier:()=>ah,_stopProfilerIdleNotifier:()=>lh,_tickCallback:()=>ih,abort:()=>fh,addListener:()=>Rv,allowedNodeEnvironmentFlags:()=>eh,arch:()=>Au,argv:()=>Pu,argv0:()=>yh,assert:()=>Ov,binding:()=>Nu,browser:()=>Uu,chdir:()=>Fu,config:()=>qu,cpuUsage:()=>Ma,cwd:()=>Bu,debugPort:()=>mh,default:()=>Ed,dlopen:()=>Sv,domain:()=>zu,emit:()=>Bv,emitWarning:()=>ju,env:()=>Iu,execArgv:()=>Cu,execPath:()=>gh,exit:()=>Xu,features:()=>th,hasUncaughtExceptionCaptureCallback:()=>Mv,hrtime:()=>Oa,kill:()=>Zu,listeners:()=>Tv,memoryUsage:()=>Qu,moduleLoadList:()=>$u,nextTick:()=>wv,off:()=>jv,on:()=>Nr,once:()=>Dv,openStdin:()=>Ju,pid:()=>dh,platform:()=>Tu,ppid:()=>ph,prependListener:()=>Fv,prependOnceListener:()=>Wv,reallyExit:()=>Yu,release:()=>Wu,removeAllListeners:()=>Lv,removeListener:()=>Nv,resourceUsage:()=>Gu,setSourceMapsEnabled:()=>vh,setUncaughtExceptionCaptureCallback:()=>nh,stderr:()=>uh,stdin:()=>hh,stdout:()=>ch,title:()=>Mu,umask:()=>Lu,uptime:()=>Av,version:()=>Ru,versions:()=>Du});var kr,ii,Zn,Go,Mu,Au,Tu,Iu,Pu,Cu,Ru,Du,ju,Nu,Lu,Bu,Fu,Wu,Uu,Vu,$u,zu,Hu,qu,Yu,Ku,Ma,Gu,Qu,Zu,Xu,Ju,eh,th,rh,nh,ih,oh,sh,ah,lh,ch,uh,hh,fh,dh,ph,gh,mh,yh,bh,vh,_n,Oc,Aa,Iv,Pv,Cv,Rv,Dv,jv,Nv,Lv,Bv,Fv,Wv,Ed,Ok=Ht(()=>{me(),be(),ye(),kr=[],ii=!1,Go=-1,_v.prototype.run=function(){this.fun.apply(null,this.array)},Mu="browser",Au="x64",Tu="browser",Iu={PATH:"/usr/bin",LANG:typeof navigator<"u"?navigator.language+".UTF-8":void 0,PWD:"/",HOME:"/home",TMP:"/tmp"},Pu=["/usr/bin/node"],Cu=[],Ru="v16.8.0",Du={},ju=function(t,e){},Nu=function(t){kd("binding")},Lu=function(t){return 0},Bu=function(){return"/"},Fu=function(t){},Wu={name:"node",sourceUrl:"",headersUrl:"",libUrl:""},Uu=!0,Vu=St,$u=[],zu={},Hu=!1,qu={},Yu=St,Ku=St,Gu=Ma=function(){return{}},Qu=Ma,Zu=St,Xu=St,Ju=St,eh={},th={inspector:!1,debug:!1,uv:!1,ipv6:!1,tls_alpn:!1,tls_sni:!1,tls_ocsp:!1,tls:!1,cached_builtins:!0},rh=St,nh=St,ih=St,oh=St,sh=St,ah=St,lh=St,ch=void 0,uh=void 0,hh=void 0,fh=St,dh=2,ph=1,gh="/bin/usr/node",mh=9229,yh="node",bh=[],vh=St,(_n={now:typeof performance<"u"?performance.now.bind(performance):void 0,timing:typeof performance<"u"?performance.timing:void 0}).now===void 0&&(Oc=Date.now(),_n.timing&&_n.timing.navigationStart&&(Oc=_n.timing.navigationStart),_n.now=()=>Date.now()-Oc),Aa=1e9,Oa.bigint=function(t){var e=Oa(t);return typeof BigInt>"u"?e[0]*Aa+e[1]:BigInt(e[0]*Aa)+BigInt(e[1])},Ed={version:Ru,versions:Du,arch:Au,platform:Tu,browser:Uu,release:Wu,_rawDebug:Vu,moduleLoadList:$u,binding:Nu,_linkedBinding:xv,_events:Pv={},_eventsCount:Cv=0,_maxListeners:Iv=10,on:Nr,addListener:Rv=Nr,once:Dv=Nr,off:jv=Nr,removeListener:Nv=Nr,removeAllListeners:Lv=Nr,emit:Bv=St,prependListener:Fv=Nr,prependOnceListener:Wv=Nr,listeners:Tv,domain:zu,_exiting:Hu,config:qu,dlopen:Sv,uptime:Av,_getActiveRequests:kv,_getActiveHandles:Ev,reallyExit:Yu,_kill:Ku,cpuUsage:Ma,resourceUsage:Gu,memoryUsage:Qu,kill:Zu,exit:Xu,openStdin:Ju,allowedNodeEnvironmentFlags:eh,assert:Ov,features:th,_fatalExceptions:rh,setUncaughtExceptionCaptureCallback:nh,hasUncaughtExceptionCaptureCallback:Mv,emitWarning:ju,nextTick:wv,_tickCallback:ih,_debugProcess:oh,_debugEnd:sh,_startProfilerIdleNotifier:ah,_stopProfilerIdleNotifier:lh,stdout:ch,stdin:hh,stderr:uh,abort:fh,umask:Lu,chdir:Fu,cwd:Bu,env:Iu,title:Mu,argv:Pu,execArgv:Cu,pid:dh,ppid:ph,execPath:gh,debugPort:mh,hrtime:Oa,argv0:yh,_preload_modules:bh,setSourceMapsEnabled:vh}}),ye=Ht(()=>{Ok()});function Mk(){if(xh)return ln;xh=!0;let t=function(){if(wh)return Ai;wh=!0,Ai.byteLength=function(fe){var de=P(fe),pe=de[0],K=de[1];return 3*(pe+K)/4-K},Ai.toByteArray=function(fe){var de,pe,K=P(fe),ee=K[0],ce=K[1],C=new E(function(ge,xe,ve){return 3*(xe+ve)/4-ve}(0,ee,ce)),L=0,ae=ce>0?ee-4:ee;for(pe=0;pe>16&255,C[L++]=de>>8&255,C[L++]=255&de;return ce===2&&(de=w[fe.charCodeAt(pe)]<<2|w[fe.charCodeAt(pe+1)]>>4,C[L++]=255&de),ce===1&&(de=w[fe.charCodeAt(pe)]<<10|w[fe.charCodeAt(pe+1)]<<4|w[fe.charCodeAt(pe+2)]>>2,C[L++]=de>>8&255,C[L++]=255&de),C},Ai.fromByteArray=function(fe){for(var de,pe=fe.length,K=pe%3,ee=[],ce=16383,C=0,L=pe-K;CL?L:C+ce));return K===1?(de=fe[pe-1],ee.push(m[de>>2]+m[de<<4&63]+"==")):K===2&&(de=(fe[pe-2]<<8)+fe[pe-1],ee.push(m[de>>10]+m[de>>4&63]+m[de<<2&63]+"=")),ee.join("")};for(var m=[],w=[],E=typeof Uint8Array<"u"?Uint8Array:Array,D="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",W=0;W<64;++W)m[W]=D[W],w[D.charCodeAt(W)]=W;function P(fe){var de=fe.length;if(de%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var pe=fe.indexOf("=");return pe===-1&&(pe=de),[pe,pe===de?0:4-pe%4]}function J(fe){return m[fe>>18&63]+m[fe>>12&63]+m[fe>>6&63]+m[63&fe]}function he(fe,de,pe){for(var K,ee=[],ce=de;ce>1,pe=-7,K=E?W-1:0,ee=E?-1:1,ce=m[w+K];for(K+=ee,P=ce&(1<<-pe)-1,ce>>=-pe,pe+=he;pe>0;P=256*P+m[w+K],K+=ee,pe-=8);for(J=P&(1<<-pe)-1,P>>=-pe,pe+=D;pe>0;J=256*J+m[w+K],K+=ee,pe-=8);if(P===0)P=1-de;else{if(P===fe)return J?NaN:1/0*(ce?-1:1);J+=Math.pow(2,D),P-=de}return(ce?-1:1)*J*Math.pow(2,P-D)},Ta.write=function(m,w,E,D,W,P){var J,he,fe,de=8*P-W-1,pe=(1<>1,ee=W===23?Math.pow(2,-24)-Math.pow(2,-77):0,ce=D?0:P-1,C=D?1:-1,L=w<0||w===0&&1/w<0?1:0;for(w=Math.abs(w),isNaN(w)||w===1/0?(he=isNaN(w)?1:0,J=pe):(J=Math.floor(Math.log(w)/Math.LN2),w*(fe=Math.pow(2,-J))<1&&(J--,fe*=2),(w+=J+K>=1?ee/fe:ee*Math.pow(2,1-K))*fe>=2&&(J++,fe/=2),J+K>=pe?(he=0,J=pe):J+K>=1?(he=(w*fe-1)*Math.pow(2,W),J+=K):(he=w*Math.pow(2,K-1)*Math.pow(2,W),J=0));W>=8;m[E+ce]=255&he,ce+=C,he/=256,W-=8);for(J=J<0;m[E+ce]=255&J,ce+=C,J/=256,de-=8);m[E+ce-C]|=128*L}),Ta),r=typeof Symbol=="function"&&typeof Symbol.for=="function"?Symbol.for("nodejs.util.inspect.custom"):null;ln.Buffer=o,ln.SlowBuffer=function(m){return+m!=m&&(m=0),o.alloc(+m)},ln.INSPECT_MAX_BYTES=50;let n=2147483647;function i(m){if(m>n)throw new RangeError('The value "'+m+'" is invalid for option "size"');let w=new Uint8Array(m);return Object.setPrototypeOf(w,o.prototype),w}function o(m,w,E){if(typeof m=="number"){if(typeof w=="string")throw new TypeError('The "string" argument must be of type string. Received type number');return l(m)}return s(m,w,E)}function s(m,w,E){if(typeof m=="string")return function(P,J){if((typeof J!="string"||J==="")&&(J="utf8"),!o.isEncoding(J))throw new TypeError("Unknown encoding: "+J);let he=0|f(P,J),fe=i(he),de=fe.write(P,J);return de!==he&&(fe=fe.slice(0,de)),fe}(m,w);if(ArrayBuffer.isView(m))return function(P){if(oe(P,Uint8Array)){let J=new Uint8Array(P);return u(J.buffer,J.byteOffset,J.byteLength)}return c(P)}(m);if(m==null)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof m);if(oe(m,ArrayBuffer)||m&&oe(m.buffer,ArrayBuffer)||typeof SharedArrayBuffer<"u"&&(oe(m,SharedArrayBuffer)||m&&oe(m.buffer,SharedArrayBuffer)))return u(m,w,E);if(typeof m=="number")throw new TypeError('The "value" argument must not be of type number. Received type number');let D=m.valueOf&&m.valueOf();if(D!=null&&D!==m)return o.from(D,w,E);let W=function(P){if(o.isBuffer(P)){let J=0|h(P.length),he=i(J);return he.length===0||P.copy(he,0,0,J),he}if(P.length!==void 0)return typeof P.length!="number"||j(P.length)?i(0):c(P);if(P.type==="Buffer"&&Array.isArray(P.data))return c(P.data)}(m);if(W)return W;if(typeof Symbol<"u"&&Symbol.toPrimitive!=null&&typeof m[Symbol.toPrimitive]=="function")return o.from(m[Symbol.toPrimitive]("string"),w,E);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof m)}function a(m){if(typeof m!="number")throw new TypeError('"size" argument must be of type number');if(m<0)throw new RangeError('The value "'+m+'" is invalid for option "size"')}function l(m){return a(m),i(m<0?0:0|h(m))}function c(m){let w=m.length<0?0:0|h(m.length),E=i(w);for(let D=0;D=n)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+n.toString(16)+" bytes");return 0|m}function f(m,w){if(o.isBuffer(m))return m.length;if(ArrayBuffer.isView(m)||oe(m,ArrayBuffer))return m.byteLength;if(typeof m!="string")throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof m);let E=m.length,D=arguments.length>2&&arguments[2]===!0;if(!D&&E===0)return 0;let W=!1;for(;;)switch(w){case"ascii":case"latin1":case"binary":return E;case"utf8":case"utf-8":return ue(m).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*E;case"hex":return E>>>1;case"base64":return Y(m).length;default:if(W)return D?-1:ue(m).length;w=(""+w).toLowerCase(),W=!0}}function d(m,w,E){let D=!1;if((w===void 0||w<0)&&(w=0),w>this.length||((E===void 0||E>this.length)&&(E=this.length),E<=0)||(E>>>=0)<=(w>>>=0))return"";for(m||(m="utf8");;)switch(m){case"hex":return O(this,w,E);case"utf8":case"utf-8":return I(this,w,E);case"ascii":return _(this,w,E);case"latin1":case"binary":return x(this,w,E);case"base64":return A(this,w,E);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return N(this,w,E);default:if(D)throw new TypeError("Unknown encoding: "+m);m=(m+"").toLowerCase(),D=!0}}function y(m,w,E){let D=m[w];m[w]=m[E],m[E]=D}function g(m,w,E,D,W){if(m.length===0)return-1;if(typeof E=="string"?(D=E,E=0):E>2147483647?E=2147483647:E<-2147483648&&(E=-2147483648),j(E=+E)&&(E=W?0:m.length-1),E<0&&(E=m.length+E),E>=m.length){if(W)return-1;E=m.length-1}else if(E<0){if(!W)return-1;E=0}if(typeof w=="string"&&(w=o.from(w,D)),o.isBuffer(w))return w.length===0?-1:p(m,w,E,D,W);if(typeof w=="number")return w&=255,typeof Uint8Array.prototype.indexOf=="function"?W?Uint8Array.prototype.indexOf.call(m,w,E):Uint8Array.prototype.lastIndexOf.call(m,w,E):p(m,[w],E,D,W);throw new TypeError("val must be string, number or Buffer")}function p(m,w,E,D,W){let P,J=1,he=m.length,fe=w.length;if(D!==void 0&&((D=String(D).toLowerCase())==="ucs2"||D==="ucs-2"||D==="utf16le"||D==="utf-16le")){if(m.length<2||w.length<2)return-1;J=2,he/=2,fe/=2,E/=2}function de(pe,K){return J===1?pe[K]:pe.readUInt16BE(K*J)}if(W){let pe=-1;for(P=E;Phe&&(E=he-fe),P=E;P>=0;P--){let pe=!0;for(let K=0;KW&&(D=W):D=W;let P,J=w.length;for(D>J/2&&(D=J/2),P=0;P>8,fe=J%256,de.push(fe),de.push(he);return de}(w,m.length-E),m,E,D)}function A(m,w,E){return w===0&&E===m.length?t.fromByteArray(m):t.fromByteArray(m.slice(w,E))}function I(m,w,E){E=Math.min(m.length,E);let D=[],W=w;for(;W239?4:P>223?3:P>191?2:1;if(W+he<=E){let fe,de,pe,K;switch(he){case 1:P<128&&(J=P);break;case 2:fe=m[W+1],(192&fe)==128&&(K=(31&P)<<6|63&fe,K>127&&(J=K));break;case 3:fe=m[W+1],de=m[W+2],(192&fe)==128&&(192&de)==128&&(K=(15&P)<<12|(63&fe)<<6|63&de,K>2047&&(K<55296||K>57343)&&(J=K));break;case 4:fe=m[W+1],de=m[W+2],pe=m[W+3],(192&fe)==128&&(192&de)==128&&(192&pe)==128&&(K=(15&P)<<18|(63&fe)<<12|(63&de)<<6|63&pe,K>65535&&K<1114112&&(J=K))}}J===null?(J=65533,he=1):J>65535&&(J-=65536,D.push(J>>>10&1023|55296),J=56320|1023&J),D.push(J),W+=he}return function(P){let J=P.length;if(J<=T)return String.fromCharCode.apply(String,P);let he="",fe=0;for(;feD.length?(o.isBuffer(P)||(P=o.from(P)),P.copy(D,W)):Uint8Array.prototype.set.call(D,P,W);else{if(!o.isBuffer(P))throw new TypeError('"list" argument must be an Array of Buffers');P.copy(D,W)}W+=P.length}return D},o.byteLength=f,o.prototype._isBuffer=!0,o.prototype.swap16=function(){let m=this.length;if(m%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let w=0;ww&&(m+=" ... "),""},r&&(o.prototype[r]=o.prototype.inspect),o.prototype.compare=function(m,w,E,D,W){if(oe(m,Uint8Array)&&(m=o.from(m,m.offset,m.byteLength)),!o.isBuffer(m))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof m);if(w===void 0&&(w=0),E===void 0&&(E=m?m.length:0),D===void 0&&(D=0),W===void 0&&(W=this.length),w<0||E>m.length||D<0||W>this.length)throw new RangeError("out of range index");if(D>=W&&w>=E)return 0;if(D>=W)return-1;if(w>=E)return 1;if(this===m)return 0;let P=(W>>>=0)-(D>>>=0),J=(E>>>=0)-(w>>>=0),he=Math.min(P,J),fe=this.slice(D,W),de=m.slice(w,E);for(let pe=0;pe>>=0,isFinite(E)?(E>>>=0,D===void 0&&(D="utf8")):(D=E,E=void 0)}let W=this.length-w;if((E===void 0||E>W)&&(E=W),m.length>0&&(E<0||w<0)||w>this.length)throw new RangeError("Attempt to write outside buffer bounds");D||(D="utf8");let P=!1;for(;;)switch(D){case"hex":return b(this,m,w,E);case"utf8":case"utf-8":return v(this,m,w,E);case"ascii":case"latin1":case"binary":return S(this,m,w,E);case"base64":return k(this,m,w,E);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return M(this,m,w,E);default:if(P)throw new TypeError("Unknown encoding: "+D);D=(""+D).toLowerCase(),P=!0}},o.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};let T=4096;function _(m,w,E){let D="";E=Math.min(m.length,E);for(let W=w;WD)&&(E=D);let W="";for(let P=w;PE)throw new RangeError("Trying to access beyond buffer length")}function U(m,w,E,D,W,P){if(!o.isBuffer(m))throw new TypeError('"buffer" argument must be a Buffer instance');if(w>W||wm.length)throw new RangeError("Index out of range")}function z(m,w,E,D,W){Z(w,D,W,m,E,7);let P=Number(w&BigInt(4294967295));m[E++]=P,P>>=8,m[E++]=P,P>>=8,m[E++]=P,P>>=8,m[E++]=P;let J=Number(w>>BigInt(32)&BigInt(4294967295));return m[E++]=J,J>>=8,m[E++]=J,J>>=8,m[E++]=J,J>>=8,m[E++]=J,E}function q(m,w,E,D,W){Z(w,D,W,m,E,7);let P=Number(w&BigInt(4294967295));m[E+7]=P,P>>=8,m[E+6]=P,P>>=8,m[E+5]=P,P>>=8,m[E+4]=P;let J=Number(w>>BigInt(32)&BigInt(4294967295));return m[E+3]=J,J>>=8,m[E+2]=J,J>>=8,m[E+1]=J,J>>=8,m[E]=J,E+8}function B(m,w,E,D,W,P){if(E+D>m.length)throw new RangeError("Index out of range");if(E<0)throw new RangeError("Index out of range")}function ne(m,w,E,D,W){return w=+w,E>>>=0,W||B(m,0,E,4),e.write(m,w,E,D,23,4),E+4}function H(m,w,E,D,W){return w=+w,E>>>=0,W||B(m,0,E,8),e.write(m,w,E,D,52,8),E+8}o.prototype.slice=function(m,w){let E=this.length;(m=~~m)<0?(m+=E)<0&&(m=0):m>E&&(m=E),(w=w===void 0?E:~~w)<0?(w+=E)<0&&(w=0):w>E&&(w=E),w>>=0,w>>>=0,E||G(m,w,this.length);let D=this[m],W=1,P=0;for(;++P>>=0,w>>>=0,E||G(m,w,this.length);let D=this[m+--w],W=1;for(;w>0&&(W*=256);)D+=this[m+--w]*W;return D},o.prototype.readUint8=o.prototype.readUInt8=function(m,w){return m>>>=0,w||G(m,1,this.length),this[m]},o.prototype.readUint16LE=o.prototype.readUInt16LE=function(m,w){return m>>>=0,w||G(m,2,this.length),this[m]|this[m+1]<<8},o.prototype.readUint16BE=o.prototype.readUInt16BE=function(m,w){return m>>>=0,w||G(m,2,this.length),this[m]<<8|this[m+1]},o.prototype.readUint32LE=o.prototype.readUInt32LE=function(m,w){return m>>>=0,w||G(m,4,this.length),(this[m]|this[m+1]<<8|this[m+2]<<16)+16777216*this[m+3]},o.prototype.readUint32BE=o.prototype.readUInt32BE=function(m,w){return m>>>=0,w||G(m,4,this.length),16777216*this[m]+(this[m+1]<<16|this[m+2]<<8|this[m+3])},o.prototype.readBigUInt64LE=R(function(m){$(m>>>=0,"offset");let w=this[m],E=this[m+7];(w===void 0||E===void 0)&&se(m,this.length-8);let D=w+256*this[++m]+65536*this[++m]+this[++m]*2**24,W=this[++m]+256*this[++m]+65536*this[++m]+E*2**24;return BigInt(D)+(BigInt(W)<>>=0,"offset");let w=this[m],E=this[m+7];(w===void 0||E===void 0)&&se(m,this.length-8);let D=w*2**24+65536*this[++m]+256*this[++m]+this[++m],W=this[++m]*2**24+65536*this[++m]+256*this[++m]+E;return(BigInt(D)<>>=0,w>>>=0,E||G(m,w,this.length);let D=this[m],W=1,P=0;for(;++P=W&&(D-=Math.pow(2,8*w)),D},o.prototype.readIntBE=function(m,w,E){m>>>=0,w>>>=0,E||G(m,w,this.length);let D=w,W=1,P=this[m+--D];for(;D>0&&(W*=256);)P+=this[m+--D]*W;return W*=128,P>=W&&(P-=Math.pow(2,8*w)),P},o.prototype.readInt8=function(m,w){return m>>>=0,w||G(m,1,this.length),128&this[m]?-1*(255-this[m]+1):this[m]},o.prototype.readInt16LE=function(m,w){m>>>=0,w||G(m,2,this.length);let E=this[m]|this[m+1]<<8;return 32768&E?4294901760|E:E},o.prototype.readInt16BE=function(m,w){m>>>=0,w||G(m,2,this.length);let E=this[m+1]|this[m]<<8;return 32768&E?4294901760|E:E},o.prototype.readInt32LE=function(m,w){return m>>>=0,w||G(m,4,this.length),this[m]|this[m+1]<<8|this[m+2]<<16|this[m+3]<<24},o.prototype.readInt32BE=function(m,w){return m>>>=0,w||G(m,4,this.length),this[m]<<24|this[m+1]<<16|this[m+2]<<8|this[m+3]},o.prototype.readBigInt64LE=R(function(m){$(m>>>=0,"offset");let w=this[m],E=this[m+7];(w===void 0||E===void 0)&&se(m,this.length-8);let D=this[m+4]+256*this[m+5]+65536*this[m+6]+(E<<24);return(BigInt(D)<>>=0,"offset");let w=this[m],E=this[m+7];(w===void 0||E===void 0)&&se(m,this.length-8);let D=(w<<24)+65536*this[++m]+256*this[++m]+this[++m];return(BigInt(D)<>>=0,w||G(m,4,this.length),e.read(this,m,!0,23,4)},o.prototype.readFloatBE=function(m,w){return m>>>=0,w||G(m,4,this.length),e.read(this,m,!1,23,4)},o.prototype.readDoubleLE=function(m,w){return m>>>=0,w||G(m,8,this.length),e.read(this,m,!0,52,8)},o.prototype.readDoubleBE=function(m,w){return m>>>=0,w||G(m,8,this.length),e.read(this,m,!1,52,8)},o.prototype.writeUintLE=o.prototype.writeUIntLE=function(m,w,E,D){m=+m,w>>>=0,E>>>=0,!D&&U(this,m,w,E,Math.pow(2,8*E)-1,0);let W=1,P=0;for(this[w]=255&m;++P>>=0,E>>>=0,!D&&U(this,m,w,E,Math.pow(2,8*E)-1,0);let W=E-1,P=1;for(this[w+W]=255&m;--W>=0&&(P*=256);)this[w+W]=m/P&255;return w+E},o.prototype.writeUint8=o.prototype.writeUInt8=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,1,255,0),this[w]=255&m,w+1},o.prototype.writeUint16LE=o.prototype.writeUInt16LE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,2,65535,0),this[w]=255&m,this[w+1]=m>>>8,w+2},o.prototype.writeUint16BE=o.prototype.writeUInt16BE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,2,65535,0),this[w]=m>>>8,this[w+1]=255&m,w+2},o.prototype.writeUint32LE=o.prototype.writeUInt32LE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,4,4294967295,0),this[w+3]=m>>>24,this[w+2]=m>>>16,this[w+1]=m>>>8,this[w]=255&m,w+4},o.prototype.writeUint32BE=o.prototype.writeUInt32BE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,4,4294967295,0),this[w]=m>>>24,this[w+1]=m>>>16,this[w+2]=m>>>8,this[w+3]=255&m,w+4},o.prototype.writeBigUInt64LE=R(function(m,w=0){return z(this,m,w,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeBigUInt64BE=R(function(m,w=0){return q(this,m,w,BigInt(0),BigInt("0xffffffffffffffff"))}),o.prototype.writeIntLE=function(m,w,E,D){if(m=+m,w>>>=0,!D){let he=Math.pow(2,8*E-1);U(this,m,w,E,he-1,-he)}let W=0,P=1,J=0;for(this[w]=255&m;++W>>=0,!D){let he=Math.pow(2,8*E-1);U(this,m,w,E,he-1,-he)}let W=E-1,P=1,J=0;for(this[w+W]=255&m;--W>=0&&(P*=256);)m<0&&J===0&&this[w+W+1]!==0&&(J=1),this[w+W]=(m/P|0)-J&255;return w+E},o.prototype.writeInt8=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,1,127,-128),m<0&&(m=255+m+1),this[w]=255&m,w+1},o.prototype.writeInt16LE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,2,32767,-32768),this[w]=255&m,this[w+1]=m>>>8,w+2},o.prototype.writeInt16BE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,2,32767,-32768),this[w]=m>>>8,this[w+1]=255&m,w+2},o.prototype.writeInt32LE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,4,2147483647,-2147483648),this[w]=255&m,this[w+1]=m>>>8,this[w+2]=m>>>16,this[w+3]=m>>>24,w+4},o.prototype.writeInt32BE=function(m,w,E){return m=+m,w>>>=0,E||U(this,m,w,4,2147483647,-2147483648),m<0&&(m=4294967295+m+1),this[w]=m>>>24,this[w+1]=m>>>16,this[w+2]=m>>>8,this[w+3]=255&m,w+4},o.prototype.writeBigInt64LE=R(function(m,w=0){return z(this,m,w,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),o.prototype.writeBigInt64BE=R(function(m,w=0){return q(this,m,w,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))}),o.prototype.writeFloatLE=function(m,w,E){return ne(this,m,w,!0,E)},o.prototype.writeFloatBE=function(m,w,E){return ne(this,m,w,!1,E)},o.prototype.writeDoubleLE=function(m,w,E){return H(this,m,w,!0,E)},o.prototype.writeDoubleBE=function(m,w,E){return H(this,m,w,!1,E)},o.prototype.copy=function(m,w,E,D){if(!o.isBuffer(m))throw new TypeError("argument should be a Buffer");if(E||(E=0),!D&&D!==0&&(D=this.length),w>=m.length&&(w=m.length),w||(w=0),D>0&&D=this.length)throw new RangeError("Index out of range");if(D<0)throw new RangeError("sourceEnd out of bounds");D>this.length&&(D=this.length),m.length-w>>=0,E=E===void 0?this.length:E>>>0,m||(m=0),typeof m=="number")for(W=w;W=D+4;E-=3)w=`_${m.slice(E-3,E)}${w}`;return`${m.slice(0,E)}${w}`}function Z(m,w,E,D,W,P){if(m>E||m= 0${he} and < 2${he} ** ${8*(P+1)}${he}`:`>= -(2${he} ** ${8*(P+1)-1}${he}) and < 2 ** ${8*(P+1)-1}${he}`,new Q.ERR_OUT_OF_RANGE("value",J,m)}(function(J,he,fe){$(he,"offset"),(J[he]===void 0||J[he+fe]===void 0)&&se(he,J.length-(fe+1))})(D,W,P)}function $(m,w){if(typeof m!="number")throw new Q.ERR_INVALID_ARG_TYPE(w,"number",m)}function se(m,w,E){throw Math.floor(m)!==m?($(m,E),new Q.ERR_OUT_OF_RANGE("offset","an integer",m)):w<0?new Q.ERR_BUFFER_OUT_OF_BOUNDS:new Q.ERR_OUT_OF_RANGE("offset",`>= 0 and <= ${w}`,m)}F("ERR_BUFFER_OUT_OF_BOUNDS",function(m){return m?`${m} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"},RangeError),F("ERR_INVALID_ARG_TYPE",function(m,w){return`The "${m}" argument must be of type number. Received type ${typeof w}`},TypeError),F("ERR_OUT_OF_RANGE",function(m,w,E){let D=`The value of "${m}" is out of range.`,W=E;return Number.isInteger(E)&&Math.abs(E)>2**32?W=V(String(E)):typeof E=="bigint"&&(W=String(E),(E>BigInt(2)**BigInt(32)||E<-(BigInt(2)**BigInt(32)))&&(W=V(W)),W+="n"),D+=` It must be ${w}. Received ${W}`,D},RangeError);let le=/[^+/0-9A-Za-z-_]/g;function ue(m,w){w=w||1/0;let E,D=m.length,W=null,P=[];for(let J=0;J55295&&E<57344){if(!W){if(E>56319){(w-=3)>-1&&P.push(239,191,189);continue}if(J+1===D){(w-=3)>-1&&P.push(239,191,189);continue}W=E;continue}if(E<56320){(w-=3)>-1&&P.push(239,191,189),W=E;continue}E=65536+(W-55296<<10|E-56320)}else W&&(w-=3)>-1&&P.push(239,191,189);if(W=null,E<128){if((w-=1)<0)break;P.push(E)}else if(E<2048){if((w-=2)<0)break;P.push(E>>6|192,63&E|128)}else if(E<65536){if((w-=3)<0)break;P.push(E>>12|224,E>>6&63|128,63&E|128)}else{if(!(E<1114112))throw new Error("Invalid code point");if((w-=4)<0)break;P.push(E>>18|240,E>>12&63|128,E>>6&63|128,63&E|128)}}return P}function Y(m){return t.toByteArray(function(w){if((w=(w=w.split("=")[0]).trim().replace(le,"")).length<2)return"";for(;w.length%4!=0;)w+="=";return w}(m))}function ie(m,w,E,D){let W;for(W=0;W=w.length||W>=m.length);++W)w[W+E]=m[W];return W}function oe(m,w){return m instanceof w||m!=null&&m.constructor!=null&&m.constructor.name!=null&&m.constructor.name===w.name}function j(m){return m!=m}let X=function(){let m="0123456789abcdef",w=new Array(256);for(let E=0;E<16;++E){let D=16*E;for(let W=0;W<16;++W)w[D+W]=m[E]+m[W]}return w}();function R(m){return typeof BigInt>"u"?te:m}function te(){throw new Error("BigInt not supported")}return ln}var Ai,wh,Ta,_h,ln,xh,Ak=Ht(()=>{me(),be(),ye(),Ai={},wh=!1,Ta={},_h=!1,ln={},xh=!1}),bt={};no(bt,{Buffer:()=>ol,INSPECT_MAX_BYTES:()=>Uv,default:()=>cn,kMaxLength:()=>Vv});var cn,ol,Uv,Vv,vt=Ht(()=>{me(),be(),ye(),Ak(),(cn=Mk()).Buffer,cn.SlowBuffer,cn.INSPECT_MAX_BYTES,cn.kMaxLength,ol=cn.Buffer,Uv=cn.INSPECT_MAX_BYTES,Vv=cn.kMaxLength}),be=Ht(()=>{vt()}),at=we((t,e)=>{me(),be(),ye();var r=class extends Error{constructor(n){if(!Array.isArray(n))throw new TypeError("Expected input to be an Array, got "+typeof n);let i="";for(let o=0;oArray.isArray(n),ArrayPrototypeIncludes:(n,i)=>n.includes(i),ArrayPrototypeIndexOf:(n,i)=>n.indexOf(i),ArrayPrototypeJoin:(n,i)=>n.join(i),ArrayPrototypeMap:(n,i)=>n.map(i),ArrayPrototypePop:(n,i)=>n.pop(i),ArrayPrototypePush:(n,i)=>n.push(i),ArrayPrototypeSlice:(n,i,o)=>n.slice(i,o),Error,FunctionPrototypeCall:(n,i,...o)=>n.call(i,...o),FunctionPrototypeSymbolHasInstance:(n,i)=>Function.prototype[Symbol.hasInstance].call(n,i),MathFloor:Math.floor,Number,NumberIsInteger:Number.isInteger,NumberIsNaN:Number.isNaN,NumberMAX_SAFE_INTEGER:Number.MAX_SAFE_INTEGER,NumberMIN_SAFE_INTEGER:Number.MIN_SAFE_INTEGER,NumberParseInt:Number.parseInt,ObjectDefineProperties:(n,i)=>Object.defineProperties(n,i),ObjectDefineProperty:(n,i,o)=>Object.defineProperty(n,i,o),ObjectGetOwnPropertyDescriptor:(n,i)=>Object.getOwnPropertyDescriptor(n,i),ObjectKeys:n=>Object.keys(n),ObjectSetPrototypeOf:(n,i)=>Object.setPrototypeOf(n,i),Promise,PromisePrototypeCatch:(n,i)=>n.catch(i),PromisePrototypeThen:(n,i,o)=>n.then(i,o),PromiseReject:n=>Promise.reject(n),PromiseResolve:n=>Promise.resolve(n),ReflectApply:Reflect.apply,RegExpPrototypeTest:(n,i)=>n.test(i),SafeSet:Set,String,StringPrototypeSlice:(n,i,o)=>n.slice(i,o),StringPrototypeToLowerCase:n=>n.toLowerCase(),StringPrototypeToUpperCase:n=>n.toUpperCase(),StringPrototypeTrim:n=>n.trim(),Symbol,SymbolFor:Symbol.for,SymbolAsyncIterator:Symbol.asyncIterator,SymbolHasInstance:Symbol.hasInstance,SymbolIterator:Symbol.iterator,SymbolDispose:Symbol.dispose||Symbol("Symbol.dispose"),SymbolAsyncDispose:Symbol.asyncDispose||Symbol("Symbol.asyncDispose"),TypedArrayPrototypeSet:(n,i,o)=>n.set(i,o),Boolean,Uint8Array}}),$v=we((t,e)=>{me(),be(),ye(),e.exports={format:(r,...n)=>r.replace(/%([sdifj])/g,function(...[i,o]){let s=n.shift();return o==="f"?s.toFixed(6):o==="j"?JSON.stringify(s):o==="s"&&typeof s=="object"?`${s.constructor!==Object?s.constructor.name:""} {}`.trim():s.toString()}),inspect(r){switch(typeof r){case"string":if(r.includes("'")){if(!r.includes('"'))return`"${r}"`;if(!r.includes("`")&&!r.includes("${"))return`\`${r}\``}return`'${r}'`;case"number":return isNaN(r)?"NaN":Object.is(r,-0)?String(r):r;case"bigint":return`${String(r)}n`;case"boolean":case"undefined":return String(r);case"object":return"{}"}}}}),Lt=we((t,e)=>{me(),be(),ye();var{format:r,inspect:n}=$v(),{AggregateError:i}=at(),o=globalThis.AggregateError||i,s=Symbol("kIsNodeError"),a=["string","function","number","object","Function","Object","boolean","bigint","symbol"],l=/^([A-Z][a-z0-9]*)+$/,c={};function u(g,p){if(!g)throw new c.ERR_INTERNAL_ASSERTION(p)}function h(g){let p="",b=g.length,v=g[0]==="-"?1:0;for(;b>=v+4;b-=3)p=`_${g.slice(b-3,b)}${p}`;return`${g.slice(0,b)}${p}`}function f(g,p,b){b||(b=Error);class v extends b{constructor(...k){super(function(M,A,I){if(typeof A=="function")return u(A.length<=I.length,`Code: ${M}; The provided arguments length (${I.length}) does not match the required ones (${A.length}).`),A(...I);let T=(A.match(/%[dfijoOs]/g)||[]).length;return u(T===I.length,`Code: ${M}; The provided arguments length (${I.length}) does not match the required ones (${T}).`),I.length===0?A:r(A,...I)}(g,p,k))}toString(){return`${this.name} [${g}]: ${this.message}`}}Object.defineProperties(v.prototype,{name:{value:b.name,writable:!0,enumerable:!1,configurable:!0},toString:{value(){return`${this.name} [${g}]: ${this.message}`},writable:!0,enumerable:!1,configurable:!0}}),v.prototype.code=g,v.prototype[s]=!0,c[g]=v}function d(g){let p="__node_internal_"+g.name;return Object.defineProperty(g,"name",{value:p}),g}var y=class extends Error{constructor(g="The operation was aborted",p=void 0){if(p!==void 0&&typeof p!="object")throw new c.ERR_INVALID_ARG_TYPE("options","Object",p);super(g,p),this.code="ABORT_ERR",this.name="AbortError"}};f("ERR_ASSERTION","%s",Error),f("ERR_INVALID_ARG_TYPE",(g,p,b)=>{u(typeof g=="string","'name' must be a string"),Array.isArray(p)||(p=[p]);let v="The ";g.endsWith(" argument")?v+=`${g} `:v+=`"${g}" ${g.includes(".")?"property":"argument"} `,v+="must be ";let S=[],k=[],M=[];for(let I of p)u(typeof I=="string","All expected entries have to be of type string"),a.includes(I)?S.push(I.toLowerCase()):l.test(I)?k.push(I):(u(I!=="object",'The value "object" should be written as "Object"'),M.push(I));if(k.length>0){let I=S.indexOf("object");I!==-1&&(S.splice(S,I,1),k.push("Object"))}if(S.length>0){switch(S.length){case 1:v+=`of type ${S[0]}`;break;case 2:v+=`one of type ${S[0]} or ${S[1]}`;break;default:{let I=S.pop();v+=`one of type ${S.join(", ")}, or ${I}`}}(k.length>0||M.length>0)&&(v+=" or ")}if(k.length>0){switch(k.length){case 1:v+=`an instance of ${k[0]}`;break;case 2:v+=`an instance of ${k[0]} or ${k[1]}`;break;default:{let I=k.pop();v+=`an instance of ${k.join(", ")}, or ${I}`}}M.length>0&&(v+=" or ")}switch(M.length){case 0:break;case 1:M[0].toLowerCase()!==M[0]&&(v+="an "),v+=`${M[0]}`;break;case 2:v+=`one of ${M[0]} or ${M[1]}`;break;default:{let I=M.pop();v+=`one of ${M.join(", ")}, or ${I}`}}if(b==null)v+=`. Received ${b}`;else if(typeof b=="function"&&b.name)v+=`. Received function ${b.name}`;else if(typeof b=="object"){var A;(A=b.constructor)!==null&&A!==void 0&&A.name?v+=`. Received an instance of ${b.constructor.name}`:v+=`. Received ${n(b,{depth:-1})}`}else{let I=n(b,{colors:!1});I.length>25&&(I=`${I.slice(0,25)}...`),v+=`. Received type ${typeof b} (${I})`}return v},TypeError),f("ERR_INVALID_ARG_VALUE",(g,p,b="is invalid")=>{let v=n(p);return v.length>128&&(v=v.slice(0,128)+"..."),`The ${g.includes(".")?"property":"argument"} '${g}' ${b}. Received ${v}`},TypeError),f("ERR_INVALID_RETURN_VALUE",(g,p,b)=>{var v;return`Expected ${g} to be returned from the "${p}" function but got ${b!=null&&(v=b.constructor)!==null&&v!==void 0&&v.name?`instance of ${b.constructor.name}`:"type "+typeof b}.`},TypeError),f("ERR_MISSING_ARGS",(...g)=>{u(g.length>0,"At least one arg needs to be specified");let p,b=g.length;switch(g=(Array.isArray(g)?g:[g]).map(v=>`"${v}"`).join(" or "),b){case 1:p+=`The ${g[0]} argument`;break;case 2:p+=`The ${g[0]} and ${g[1]} arguments`;break;default:{let v=g.pop();p+=`The ${g.join(", ")}, and ${v} arguments`}}return`${p} must be specified`},TypeError),f("ERR_OUT_OF_RANGE",(g,p,b)=>{let v;if(u(p,'Missing "range" argument'),Number.isInteger(b)&&Math.abs(b)>2**32)v=h(String(b));else if(typeof b=="bigint"){v=String(b);let S=BigInt(2)**BigInt(32);(b>S||b<-S)&&(v=h(v)),v+="n"}else v=n(b);return`The value of "${g}" is out of range. It must be ${p}. Received ${v}`},RangeError),f("ERR_MULTIPLE_CALLBACK","Callback called multiple times",Error),f("ERR_METHOD_NOT_IMPLEMENTED","The %s method is not implemented",Error),f("ERR_STREAM_ALREADY_FINISHED","Cannot call %s after a stream was finished",Error),f("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable",Error),f("ERR_STREAM_DESTROYED","Cannot call %s after a stream was destroyed",Error),f("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError),f("ERR_STREAM_PREMATURE_CLOSE","Premature close",Error),f("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF",Error),f("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event",Error),f("ERR_STREAM_WRITE_AFTER_END","write after end",Error),f("ERR_UNKNOWN_ENCODING","Unknown encoding: %s",TypeError),e.exports={AbortError:y,aggregateTwoErrors:d(function(g,p){if(g&&p&&g!==p){if(Array.isArray(p.errors))return p.errors.push(g),p;let b=new o([p,g],p.message);return b.code=p.code,b}return g||p}),hideStackFrames:d,codes:c}}),gs=we((t,e)=>{me(),be(),ye();var{AbortController:r,AbortSignal:n}=typeof self<"u"?self:typeof window<"u"?window:void 0;e.exports=r,e.exports.AbortSignal=n,e.exports.default=r}),Nn={};function Tk(){if(Sh)return Ia;Sh=!0;var t,e=typeof Reflect=="object"?Reflect:null,r=e&&typeof e.apply=="function"?e.apply:function(g,p,b){return Function.prototype.apply.call(g,p,b)};t=e&&typeof e.ownKeys=="function"?e.ownKeys:Object.getOwnPropertySymbols?function(g){return Object.getOwnPropertyNames(g).concat(Object.getOwnPropertySymbols(g))}:function(g){return Object.getOwnPropertyNames(g)};var n=Number.isNaN||function(g){return g!=g};function i(){i.init.call(this)}(Ia=i).once=function(g,p){return new Promise(function(b,v){function S(M){g.removeListener(p,k),v(M)}function k(){typeof g.removeListener=="function"&&g.removeListener("error",S),b([].slice.call(arguments))}y(g,p,k,{once:!0}),p!=="error"&&function(M,A,I){typeof M.on=="function"&&y(M,"error",A,I)}(g,S,{once:!0})})},i.EventEmitter=i,i.prototype._events=void 0,i.prototype._eventsCount=0,i.prototype._maxListeners=void 0;var o=10;function s(g){if(typeof g!="function")throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof g)}function a(g){return g._maxListeners===void 0?i.defaultMaxListeners:g._maxListeners}function l(g,p,b,v){var S,k,M;if(s(b),(k=g._events)===void 0?(k=g._events=Object.create(null),g._eventsCount=0):(k.newListener!==void 0&&(g.emit("newListener",p,b.listener?b.listener:b),k=g._events),M=k[p]),M===void 0)M=k[p]=b,++g._eventsCount;else if(typeof M=="function"?M=k[p]=v?[b,M]:[M,b]:v?M.unshift(b):M.push(b),(S=a(g))>0&&M.length>S&&!M.warned){M.warned=!0;var A=new Error("Possible EventEmitter memory leak detected. "+M.length+" "+String(p)+" listeners added. Use emitter.setMaxListeners() to increase limit");A.name="MaxListenersExceededWarning",A.emitter=g,A.type=p,A.count=M.length}return g}function c(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,arguments.length===0?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function u(g,p,b){var v={fired:!1,wrapFn:void 0,target:g,type:p,listener:b},S=c.bind(v);return S.listener=b,v.wrapFn=S,S}function h(g,p,b){var v=g._events;if(v===void 0)return[];var S=v[p];return S===void 0?[]:typeof S=="function"?b?[S.listener||S]:[S]:b?function(k){for(var M=new Array(k.length),A=0;A0&&(k=p[0]),k instanceof Error)throw k;var M=new Error("Unhandled error."+(k?" ("+k.message+")":""));throw M.context=k,M}var A=S[g];if(A===void 0)return!1;if(typeof A=="function")r(A,this,p);else{var I=A.length,T=d(A,I);for(b=0;b=0;k--)if(b[k]===p||b[k].listener===p){M=b[k].listener,S=k;break}if(S<0)return this;S===0?b.shift():function(A,I){for(;I+1=0;v--)this.removeListener(g,p[v]);return this},i.prototype.listeners=function(g){return h(this,g,!0)},i.prototype.rawListeners=function(g){return h(this,g,!1)},i.listenerCount=function(g,p){return typeof g.listenerCount=="function"?g.listenerCount(p):f.call(g,p)},i.prototype.listenerCount=f,i.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]},Ia}no(Nn,{EventEmitter:()=>zv,default:()=>To,defaultMaxListeners:()=>Hv,init:()=>qv,listenerCount:()=>Yv,on:()=>Kv,once:()=>Gv});var Ia,Sh,To,zv,Hv,qv,Yv,Kv,Gv,fi=Ht(()=>{me(),be(),ye(),Ia={},Sh=!1,(To=Tk()).once,To.once=function(t,e){return new Promise((r,n)=>{function i(...s){o!==void 0&&t.removeListener("error",o),r(s)}let o;e!=="error"&&(o=s=>{t.removeListener(name,i),n(s)},t.once("error",o)),t.once(e,i)})},To.on=function(t,e){let r=[],n=[],i=null,o=!1,s={async next(){let c=r.shift();if(c)return createIterResult(c,!1);if(i){let u=Promise.reject(i);return i=null,u}return o?createIterResult(void 0,!0):new Promise((u,h)=>n.push({resolve:u,reject:h}))},async return(){t.removeListener(e,a),t.removeListener("error",l),o=!0;for(let c of n)c.resolve(createIterResult(void 0,!0));return createIterResult(void 0,!0)},throw(c){i=c,t.removeListener(e,a),t.removeListener("error",l)},[Symbol.asyncIterator](){return this}};return t.on(e,a),t.on("error",l),s;function a(...c){let u=n.shift();u?u.resolve(createIterResult(c,!1)):r.push(c)}function l(c){o=!0;let u=n.shift();u?u.reject(c):i=c,s.return()}},{EventEmitter:zv,defaultMaxListeners:Hv,init:qv,listenerCount:Yv,on:Kv,once:Gv}=To}),Vt=we((t,e)=>{me(),be(),ye();var r=(vt(),Xe(bt)),{format:n,inspect:i}=$v(),{codes:{ERR_INVALID_ARG_TYPE:o}}=Lt(),{kResistStopPropagation:s,AggregateError:a,SymbolDispose:l}=at(),c=globalThis.AbortSignal||gs().AbortSignal,u=globalThis.AbortController||gs().AbortController,h=Object.getPrototypeOf(async function(){}).constructor,f=globalThis.Blob||r.Blob,d=typeof f<"u"?function(g){return g instanceof f}:function(g){return!1},y=(g,p)=>{if(g!==void 0&&(g===null||typeof g!="object"||!("aborted"in g)))throw new o(p,"AbortSignal",g)};e.exports={AggregateError:a,kEmptyObject:Object.freeze({}),once(g){let p=!1;return function(...b){p||(p=!0,g.apply(this,b))}},createDeferredPromise:function(){let g,p;return{promise:new Promise((b,v)=>{g=b,p=v}),resolve:g,reject:p}},promisify:g=>new Promise((p,b)=>{g((v,...S)=>v?b(v):p(...S))}),debuglog:()=>function(){},format:n,inspect:i,types:{isAsyncFunction:g=>g instanceof h,isArrayBufferView:g=>ArrayBuffer.isView(g)},isBlob:d,deprecate:(g,p)=>g,addAbortListener:(fi(),Xe(Nn)).addAbortListener||function(g,p){if(g===void 0)throw new o("signal","AbortSignal",g);let b;return y(g,"signal"),((v,S)=>{if(typeof v!="function")throw new o(S,"Function",v)})(p,"listener"),g.aborted?queueMicrotask(()=>p()):(g.addEventListener("abort",p,{__proto__:null,once:!0,[s]:!0}),b=()=>{g.removeEventListener("abort",p)}),{__proto__:null,[l](){var v;(v=b)===null||v===void 0||v()}}},AbortSignalAny:c.any||function(g){if(g.length===1)return g[0];let p=new u,b=()=>p.abort();return g.forEach(v=>{y(v,"signals"),v.addEventListener("abort",b,{once:!0})}),p.signal.addEventListener("abort",()=>{g.forEach(v=>v.removeEventListener("abort",b))},{once:!0}),p.signal}},e.exports.promisify.custom=Symbol.for("nodejs.util.promisify.custom")}),Ts=we((t,e)=>{me(),be(),ye();var{ArrayIsArray:r,ArrayPrototypeIncludes:n,ArrayPrototypeJoin:i,ArrayPrototypeMap:o,NumberIsInteger:s,NumberIsNaN:a,NumberMAX_SAFE_INTEGER:l,NumberMIN_SAFE_INTEGER:c,NumberParseInt:u,ObjectPrototypeHasOwnProperty:h,RegExpPrototypeExec:f,String:d,StringPrototypeToUpperCase:y,StringPrototypeTrim:g}=at(),{hideStackFrames:p,codes:{ERR_SOCKET_BAD_PORT:b,ERR_INVALID_ARG_TYPE:v,ERR_INVALID_ARG_VALUE:S,ERR_OUT_OF_RANGE:k,ERR_UNKNOWN_SIGNAL:M}}=Lt(),{normalizeEncoding:A}=Vt(),{isAsyncFunction:I,isArrayBufferView:T}=Vt().types,_={},x=/^[0-7]+$/,O=p((Y,ie,oe=c,j=l)=>{if(typeof Y!="number")throw new v(ie,"number",Y);if(!s(Y))throw new k(ie,"an integer",Y);if(Yj)throw new k(ie,`>= ${oe} && <= ${j}`,Y)}),N=p((Y,ie,oe=-2147483648,j=2147483647)=>{if(typeof Y!="number")throw new v(ie,"number",Y);if(!s(Y))throw new k(ie,"an integer",Y);if(Yj)throw new k(ie,`>= ${oe} && <= ${j}`,Y)}),G=p((Y,ie,oe=!1)=>{if(typeof Y!="number")throw new v(ie,"number",Y);if(!s(Y))throw new k(ie,"an integer",Y);let j=oe?1:0,X=4294967295;if(YX)throw new k(ie,`>= ${j} && <= ${X}`,Y)});function U(Y,ie){if(typeof Y!="string")throw new v(ie,"string",Y)}var z=p((Y,ie,oe)=>{if(!n(oe,Y)){let j="must be one of: "+i(o(oe,X=>typeof X=="string"?`'${X}'`:d(X)),", ");throw new S(ie,Y,j)}});function q(Y,ie){if(typeof Y!="boolean")throw new v(ie,"boolean",Y)}function B(Y,ie,oe){return Y!=null&&h(Y,ie)?Y[ie]:oe}var ne=p((Y,ie,oe=null)=>{let j=B(oe,"allowArray",!1),X=B(oe,"allowFunction",!1);if(!B(oe,"nullable",!1)&&Y===null||!j&&r(Y)||typeof Y!="object"&&(!X||typeof Y!="function"))throw new v(ie,"Object",Y)}),H=p((Y,ie)=>{if(Y!=null&&typeof Y!="object"&&typeof Y!="function")throw new v(ie,"a dictionary",Y)}),Q=p((Y,ie,oe=0)=>{if(!r(Y))throw new v(ie,"Array",Y);if(Y.length{if(!T(Y))throw new v(ie,["Buffer","TypedArray","DataView"],Y)}),V=p((Y,ie)=>{if(Y!==void 0&&(Y===null||typeof Y!="object"||!("aborted"in Y)))throw new v(ie,"AbortSignal",Y)}),Z=p((Y,ie)=>{if(typeof Y!="function")throw new v(ie,"Function",Y)}),$=p((Y,ie)=>{if(typeof Y!="function"||I(Y))throw new v(ie,"Function",Y)}),se=p((Y,ie)=>{if(Y!==void 0)throw new v(ie,"undefined",Y)}),le=/^(?:<[^>]*>)(?:\s*;\s*[^;"\s]+(?:=(")?[^;"\s]*\1)?)*$/;function ue(Y,ie){if(typeof Y>"u"||!f(le,Y))throw new S(ie,Y,'must be an array or string of format "; rel=preload; as=style"')}e.exports={isInt32:function(Y){return Y===(0|Y)},isUint32:function(Y){return Y===Y>>>0},parseFileMode:function(Y,ie,oe){if(typeof Y>"u"&&(Y=oe),typeof Y=="string"){if(f(x,Y)===null)throw new S(ie,Y,"must be a 32-bit unsigned integer or an octal string");Y=u(Y,8)}return G(Y,ie),Y},validateArray:Q,validateStringArray:function(Y,ie){Q(Y,ie);for(let oe=0;oej||(oe!=null||j!=null)&&a(Y))throw new k(ie,`${oe!=null?`>= ${oe}`:""}${oe!=null&&j!=null?" && ":""}${j!=null?`<= ${j}`:""}`,Y)},validateObject:ne,validateOneOf:z,validatePlainFunction:$,validatePort:function(Y,ie="Port",oe=!0){if(typeof Y!="number"&&typeof Y!="string"||typeof Y=="string"&&g(Y).length===0||+Y!==+Y>>>0||Y>65535||Y===0&&!oe)throw new b(ie,Y,oe);return 0|Y},validateSignalName:function(Y,ie="signal"){if(U(Y,ie),_[Y]===void 0)throw _[y(Y)]!==void 0?new M(Y+" (signals must use all capital letters)"):new M(Y)},validateString:U,validateUint32:G,validateUndefined:se,validateUnion:function(Y,ie,oe){if(!n(oe,Y))throw new v(ie,`('${i(oe,"|")}')`,Y)},validateAbortSignal:V,validateLinkHeaderValue:function(Y){if(typeof Y=="string")return ue(Y,"hints"),Y;if(r(Y)){let ie=Y.length,oe="";if(ie===0)return oe;for(let j=0;j; rel=preload; as=style"')}}}),di=we((t,e)=>{me(),be(),ye();var r,n,i=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function a(p){if(r===setTimeout)return setTimeout(p,0);if((r===o||!r)&&setTimeout)return r=setTimeout,setTimeout(p,0);try{return r(p,0)}catch{try{return r.call(null,p,0)}catch{return r.call(this,p,0)}}}(function(){try{r=typeof setTimeout=="function"?setTimeout:o}catch{r=o}try{n=typeof clearTimeout=="function"?clearTimeout:s}catch{n=s}})();var l,c=[],u=!1,h=-1;function f(){!u||!l||(u=!1,l.length?c=l.concat(c):h=-1,c.length&&d())}function d(){if(!u){var p=a(f);u=!0;for(var b=c.length;b;){for(l=c,c=[];++h1)for(var v=1;v{me(),be(),ye();var{SymbolAsyncIterator:r,SymbolIterator:n,SymbolFor:i}=at(),o=i("nodejs.stream.destroyed"),s=i("nodejs.stream.errored"),a=i("nodejs.stream.readable"),l=i("nodejs.stream.writable"),c=i("nodejs.stream.disturbed"),u=i("nodejs.webstream.isClosedPromise"),h=i("nodejs.webstream.controllerErrorFunction");function f(_,x=!1){var O;return!(!_||typeof _.pipe!="function"||typeof _.on!="function"||x&&(typeof _.pause!="function"||typeof _.resume!="function")||_._writableState&&((O=_._readableState)===null||O===void 0?void 0:O.readable)===!1||_._writableState&&!_._readableState)}function d(_){var x;return!(!_||typeof _.write!="function"||typeof _.on!="function"||_._readableState&&((x=_._writableState)===null||x===void 0?void 0:x.writable)===!1)}function y(_){return _&&(_._readableState||_._writableState||typeof _.write=="function"&&typeof _.on=="function"||typeof _.pipe=="function"&&typeof _.on=="function")}function g(_){return!(!_||y(_)||typeof _.pipeThrough!="function"||typeof _.getReader!="function"||typeof _.cancel!="function")}function p(_){return!(!_||y(_)||typeof _.getWriter!="function"||typeof _.abort!="function")}function b(_){return!(!_||y(_)||typeof _.readable!="object"||typeof _.writable!="object")}function v(_){if(!y(_))return null;let x=_._writableState,O=_._readableState,N=x||O;return!!(_.destroyed||_[o]||N!=null&&N.destroyed)}function S(_){if(!d(_))return null;if(_.writableEnded===!0)return!0;let x=_._writableState;return(x==null||!x.errored)&&(typeof(x==null?void 0:x.ended)!="boolean"?null:x.ended)}function k(_,x){if(!f(_))return null;let O=_._readableState;return(O==null||!O.errored)&&(typeof(O==null?void 0:O.endEmitted)!="boolean"?null:!!(O.endEmitted||x===!1&&O.ended===!0&&O.length===0))}function M(_){return _&&_[a]!=null?_[a]:typeof(_==null?void 0:_.readable)!="boolean"?null:!v(_)&&f(_)&&_.readable&&!k(_)}function A(_){return _&&_[l]!=null?_[l]:typeof(_==null?void 0:_.writable)!="boolean"?null:!v(_)&&d(_)&&_.writable&&!S(_)}function I(_){return typeof _._closed=="boolean"&&typeof _._defaultKeepAlive=="boolean"&&typeof _._removedConnection=="boolean"&&typeof _._removedContLen=="boolean"}function T(_){return typeof _._sent100=="boolean"&&I(_)}e.exports={isDestroyed:v,kIsDestroyed:o,isDisturbed:function(_){var x;return!(!_||!((x=_[c])!==null&&x!==void 0?x:_.readableDidRead||_.readableAborted))},kIsDisturbed:c,isErrored:function(_){var x,O,N,G,U,z,q,B,ne,H;return!(!_||!((x=(O=(N=(G=(U=(z=_[s])!==null&&z!==void 0?z:_.readableErrored)!==null&&U!==void 0?U:_.writableErrored)!==null&&G!==void 0?G:(q=_._readableState)===null||q===void 0?void 0:q.errorEmitted)!==null&&N!==void 0?N:(B=_._writableState)===null||B===void 0?void 0:B.errorEmitted)!==null&&O!==void 0?O:(ne=_._readableState)===null||ne===void 0?void 0:ne.errored)!==null&&x!==void 0?x:(H=_._writableState)!==null&&H!==void 0&&H.errored))},kIsErrored:s,isReadable:M,kIsReadable:a,kIsClosedPromise:u,kControllerErrorFunction:h,kIsWritable:l,isClosed:function(_){if(!y(_))return null;if(typeof _.closed=="boolean")return _.closed;let x=_._writableState,O=_._readableState;return typeof(x==null?void 0:x.closed)=="boolean"||typeof(O==null?void 0:O.closed)=="boolean"?(x==null?void 0:x.closed)||(O==null?void 0:O.closed):typeof _._closed=="boolean"&&I(_)?_._closed:null},isDuplexNodeStream:function(_){return!(!_||typeof _.pipe!="function"||!_._readableState||typeof _.on!="function"||typeof _.write!="function")},isFinished:function(_,x){return y(_)?!!v(_)||!((x==null?void 0:x.readable)!==!1&&M(_)||(x==null?void 0:x.writable)!==!1&&A(_)):null},isIterable:function(_,x){return _!=null&&(x===!0?typeof _[r]=="function":x===!1?typeof _[n]=="function":typeof _[r]=="function"||typeof _[n]=="function")},isReadableNodeStream:f,isReadableStream:g,isReadableEnded:function(_){if(!f(_))return null;if(_.readableEnded===!0)return!0;let x=_._readableState;return!(!x||x.errored)&&(typeof(x==null?void 0:x.ended)!="boolean"?null:x.ended)},isReadableFinished:k,isReadableErrored:function(_){var x,O;return y(_)?_.readableErrored?_.readableErrored:(x=(O=_._readableState)===null||O===void 0?void 0:O.errored)!==null&&x!==void 0?x:null:null},isNodeStream:y,isWebStream:function(_){return g(_)||p(_)||b(_)},isWritable:A,isWritableNodeStream:d,isWritableStream:p,isWritableEnded:S,isWritableFinished:function(_,x){if(!d(_))return null;if(_.writableFinished===!0)return!0;let O=_._writableState;return(O==null||!O.errored)&&(typeof(O==null?void 0:O.finished)!="boolean"?null:!!(O.finished||x===!1&&O.ended===!0&&O.length===0))},isWritableErrored:function(_){var x,O;return y(_)?_.writableErrored?_.writableErrored:(x=(O=_._writableState)===null||O===void 0?void 0:O.errored)!==null&&x!==void 0?x:null:null},isServerRequest:function(_){var x;return typeof _._consuming=="boolean"&&typeof _._dumped=="boolean"&&((x=_.req)===null||x===void 0?void 0:x.upgradeOrConnect)===void 0},isServerResponse:T,willEmitClose:function(_){if(!y(_))return null;let x=_._writableState,O=_._readableState,N=x||O;return!N&&T(_)||!!(N&&N.autoDestroy&&N.emitClose&&N.closed===!1)},isTransformStream:b}}),Ln=we((t,e)=>{me(),be(),ye();var r,n=di(),{AbortError:i,codes:o}=Lt(),{ERR_INVALID_ARG_TYPE:s,ERR_STREAM_PREMATURE_CLOSE:a}=o,{kEmptyObject:l,once:c}=Vt(),{validateAbortSignal:u,validateFunction:h,validateObject:f,validateBoolean:d}=Ts(),{Promise:y,PromisePrototypeThen:g,SymbolDispose:p}=at(),{isClosed:b,isReadable:v,isReadableNodeStream:S,isReadableStream:k,isReadableFinished:M,isReadableErrored:A,isWritable:I,isWritableNodeStream:T,isWritableStream:_,isWritableFinished:x,isWritableErrored:O,isNodeStream:N,willEmitClose:G,kIsClosedPromise:U}=Zr(),z=()=>{};function q(B,ne,H){var Q,F;if(arguments.length===2?(H=ne,ne=l):ne==null?ne=l:f(ne,"options"),h(H,"callback"),u(ne.signal,"options.signal"),H=c(H),k(B)||_(B))return function(D,W,P){let J=!1,he=z;if(W.signal)if(he=()=>{J=!0,P.call(D,new i(void 0,{cause:W.signal.reason}))},W.signal.aborted)n.nextTick(he);else{let de=(r=r||Vt().addAbortListener)(W.signal,he),pe=P;P=c((...K)=>{de[p](),pe.apply(D,K)})}let fe=(...de)=>{J||n.nextTick(()=>P.apply(D,de))};return g(D[U].promise,fe,fe),z}(B,ne,H);if(!N(B))throw new s("stream",["ReadableStream","WritableStream","Stream"],B);let V=(Q=ne.readable)!==null&&Q!==void 0?Q:S(B),Z=(F=ne.writable)!==null&&F!==void 0?F:T(B),$=B._writableState,se=B._readableState,le=()=>{B.writable||ie()},ue=G(B)&&S(B)===V&&T(B)===Z,Y=x(B,!1),ie=()=>{Y=!0,B.destroyed&&(ue=!1),(!ue||B.readable&&!V)&&(!V||oe)&&H.call(B)},oe=M(B,!1),j=()=>{oe=!0,B.destroyed&&(ue=!1),(!ue||B.writable&&!Z)&&(!Z||Y)&&H.call(B)},X=D=>{H.call(B,D)},R=b(B),te=()=>{R=!0;let D=O(B)||A(B);return D&&typeof D!="boolean"?H.call(B,D):V&&!oe&&S(B,!0)&&!M(B,!1)?H.call(B,new a):!Z||Y||x(B,!1)?void H.call(B):H.call(B,new a)},m=()=>{R=!0;let D=O(B)||A(B);if(D&&typeof D!="boolean")return H.call(B,D);H.call(B)},w=()=>{B.req.on("finish",ie)};(function(D){return D.setHeader&&typeof D.abort=="function"})(B)?(B.on("complete",ie),ue||B.on("abort",te),B.req?w():B.on("request",w)):Z&&!$&&(B.on("end",le),B.on("close",le)),!ue&&typeof B.aborted=="boolean"&&B.on("aborted",te),B.on("end",j),B.on("finish",ie),ne.error!==!1&&B.on("error",X),B.on("close",te),R?n.nextTick(te):$!=null&&$.errorEmitted||se!=null&&se.errorEmitted?ue||n.nextTick(m):(!V&&(!ue||v(B))&&(Y||I(B)===!1)||!Z&&(!ue||I(B))&&(oe||v(B)===!1)||se&&B.req&&B.aborted)&&n.nextTick(m);let E=()=>{H=z,B.removeListener("aborted",te),B.removeListener("complete",ie),B.removeListener("abort",te),B.removeListener("request",w),B.req&&B.req.removeListener("finish",ie),B.removeListener("end",le),B.removeListener("close",le),B.removeListener("finish",ie),B.removeListener("end",j),B.removeListener("error",X),B.removeListener("close",te)};if(ne.signal&&!R){let D=()=>{let W=H;E(),W.call(B,new i(void 0,{cause:ne.signal.reason}))};if(ne.signal.aborted)n.nextTick(D);else{let W=(r=r||Vt().addAbortListener)(ne.signal,D),P=H;H=c((...J)=>{W[p](),P.apply(B,J)})}}return E}e.exports=q,e.exports.finished=function(B,ne){var H;let Q=!1;return ne===null&&(ne=l),(H=ne)!==null&&H!==void 0&&H.cleanup&&(d(ne.cleanup,"cleanup"),Q=ne.cleanup),new y((F,V)=>{let Z=q(B,ne,$=>{Q&&Z(),$?V($):F()})})}}),io=we((t,e)=>{me(),be(),ye();var r=di(),{aggregateTwoErrors:n,codes:{ERR_MULTIPLE_CALLBACK:i},AbortError:o}=Lt(),{Symbol:s}=at(),{kIsDestroyed:a,isDestroyed:l,isFinished:c,isServerRequest:u}=Zr(),h=s("kDestroy"),f=s("kConstruct");function d(T,_,x){T&&(T.stack,_&&!_.errored&&(_.errored=T),x&&!x.errored&&(x.errored=T))}function y(T,_,x){let O=!1;function N(G){if(O)return;O=!0;let U=T._readableState,z=T._writableState;d(G,z,U),z&&(z.closed=!0),U&&(U.closed=!0),typeof x=="function"&&x(G),G?r.nextTick(g,T,G):r.nextTick(p,T)}try{T._destroy(_||null,N)}catch(G){N(G)}}function g(T,_){b(T,_),p(T)}function p(T){let _=T._readableState,x=T._writableState;x&&(x.closeEmitted=!0),_&&(_.closeEmitted=!0),(x!=null&&x.emitClose||_!=null&&_.emitClose)&&T.emit("close")}function b(T,_){let x=T._readableState,O=T._writableState;O!=null&&O.errorEmitted||x!=null&&x.errorEmitted||(O&&(O.errorEmitted=!0),x&&(x.errorEmitted=!0),T.emit("error",_))}function v(T,_,x){let O=T._readableState,N=T._writableState;if(N!=null&&N.destroyed||O!=null&&O.destroyed)return this;O!=null&&O.autoDestroy||N!=null&&N.autoDestroy?T.destroy(_):_&&(_.stack,N&&!N.errored&&(N.errored=_),O&&!O.errored&&(O.errored=_),x?r.nextTick(b,T,_):b(T,_))}function S(T){let _=!1;function x(O){if(_)return void v(T,O??new i);_=!0;let N=T._readableState,G=T._writableState,U=G||N;N&&(N.constructed=!0),G&&(G.constructed=!0),U.destroyed?T.emit(h,O):O?v(T,O,!0):r.nextTick(k,T)}try{T._construct(O=>{r.nextTick(x,O)})}catch(O){r.nextTick(x,O)}}function k(T){T.emit(f)}function M(T){return(T==null?void 0:T.setHeader)&&typeof T.abort=="function"}function A(T){T.emit("close")}function I(T,_){T.emit("error",_),r.nextTick(A,T)}e.exports={construct:function(T,_){if(typeof T._construct!="function")return;let x=T._readableState,O=T._writableState;x&&(x.constructed=!1),O&&(O.constructed=!1),T.once(f,_),!(T.listenerCount(f)>1)&&r.nextTick(S,T)},destroyer:function(T,_){!T||l(T)||(!_&&!c(T)&&(_=new o),u(T)?(T.socket=null,T.destroy(_)):M(T)?T.abort():M(T.req)?T.req.abort():typeof T.destroy=="function"?T.destroy(_):typeof T.close=="function"?T.close():_?r.nextTick(I,T,_):r.nextTick(A,T),T.destroyed||(T[a]=!0))},destroy:function(T,_){let x=this._readableState,O=this._writableState,N=O||x;return O!=null&&O.destroyed||x!=null&&x.destroyed?(typeof _=="function"&&_(),this):(d(T,O,x),O&&(O.destroyed=!0),x&&(x.destroyed=!0),N.constructed?y(this,T,_):this.once(h,function(G){y(this,n(G,T),_)}),this)},undestroy:function(){let T=this._readableState,_=this._writableState;T&&(T.constructed=!0,T.closed=!1,T.closeEmitted=!1,T.destroyed=!1,T.errored=null,T.errorEmitted=!1,T.reading=!1,T.ended=T.readable===!1,T.endEmitted=T.readable===!1),_&&(_.constructed=!0,_.destroyed=!1,_.closed=!1,_.closeEmitted=!1,_.errored=null,_.errorEmitted=!1,_.finalCalled=!1,_.prefinished=!1,_.ended=_.writable===!1,_.ending=_.writable===!1,_.finished=_.writable===!1)},errorOrDestroy:v}}),Od=we((t,e)=>{me(),be(),ye();var{ArrayIsArray:r,ObjectSetPrototypeOf:n}=at(),{EventEmitter:i}=(fi(),Xe(Nn));function o(a){i.call(this,a)}function s(a,l,c){if(typeof a.prependListener=="function")return a.prependListener(l,c);a._events&&a._events[l]?r(a._events[l])?a._events[l].unshift(c):a._events[l]=[c,a._events[l]]:a.on(l,c)}n(o.prototype,i.prototype),n(o,i),o.prototype.pipe=function(a,l){let c=this;function u(b){a.writable&&a.write(b)===!1&&c.pause&&c.pause()}function h(){c.readable&&c.resume&&c.resume()}c.on("data",u),a.on("drain",h),!a._isStdio&&(!l||l.end!==!1)&&(c.on("end",d),c.on("close",y));let f=!1;function d(){f||(f=!0,a.end())}function y(){f||(f=!0,typeof a.destroy=="function"&&a.destroy())}function g(b){p(),i.listenerCount(this,"error")===0&&this.emit("error",b)}function p(){c.removeListener("data",u),a.removeListener("drain",h),c.removeListener("end",d),c.removeListener("close",y),c.removeListener("error",g),a.removeListener("error",g),c.removeListener("end",p),c.removeListener("close",p),a.removeListener("close",p)}return s(c,"error",g),s(a,"error",g),c.on("end",p),c.on("close",p),a.on("close",p),a.emit("pipe",c),a},e.exports={Stream:o,prependListener:s}}),Ul=we((t,e)=>{me(),be(),ye();var r,{SymbolDispose:n}=at(),{AbortError:i,codes:o}=Lt(),{isNodeStream:s,isWebStream:a,kControllerErrorFunction:l}=Zr(),c=Ln(),{ERR_INVALID_ARG_TYPE:u}=o;e.exports.addAbortSignal=function(h,f){if(((d,y)=>{if(typeof d!="object"||!("aborted"in d))throw new u(y,"AbortSignal",d)})(h,"signal"),!s(f)&&!a(f))throw new u("stream",["ReadableStream","WritableStream","Stream"],f);return e.exports.addAbortSignalNoValidate(h,f)},e.exports.addAbortSignalNoValidate=function(h,f){if(typeof h!="object"||!("aborted"in h))return f;let d=s(f)?()=>{f.destroy(new i(void 0,{cause:h.reason}))}:()=>{f[l](new i(void 0,{cause:h.reason}))};if(h.aborted)d();else{let y=(r=r||Vt().addAbortListener)(h,d);c(f,y[n])}return f}}),Ik=we((t,e)=>{me(),be(),ye();var{StringPrototypeSlice:r,SymbolIterator:n,TypedArrayPrototypeSet:i,Uint8Array:o}=at(),{Buffer:s}=(vt(),Xe(bt)),{inspect:a}=Vt();e.exports=class{constructor(){this.head=null,this.tail=null,this.length=0}push(l){let c={data:l,next:null};this.length>0?this.tail.next=c:this.head=c,this.tail=c,++this.length}unshift(l){let c={data:l,next:this.head};this.length===0&&(this.tail=c),this.head=c,++this.length}shift(){if(this.length===0)return;let l=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,l}clear(){this.head=this.tail=null,this.length=0}join(l){if(this.length===0)return"";let c=this.head,u=""+c.data;for(;(c=c.next)!==null;)u+=l+c.data;return u}concat(l){if(this.length===0)return s.alloc(0);let c=s.allocUnsafe(l>>>0),u=this.head,h=0;for(;u;)i(c,u.data,h),h+=u.data.length,u=u.next;return c}consume(l,c){let u=this.head.data;if(lf.length)){l===f.length?(c+=f,++h,u.next?this.head=u.next:this.head=this.tail=null):(c+=r(f,0,l),this.head=u,u.data=r(f,l));break}c+=f,l-=f.length,++h}while((u=u.next)!==null);return this.length-=h,c}_getBuffer(l){let c=s.allocUnsafe(l),u=l,h=this.head,f=0;do{let d=h.data;if(!(l>d.length)){l===d.length?(i(c,d,u-l),++f,h.next?this.head=h.next:this.head=this.tail=null):(i(c,new o(d.buffer,d.byteOffset,l),u-l),this.head=h,h.data=d.slice(l));break}i(c,d,u-l),l-=d.length,++f}while((h=h.next)!==null);return this.length-=f,c}[Symbol.for("nodejs.util.inspect.custom")](l,c){return a(this,{...c,depth:0,customInspect:!1})}}}),Vl=we((t,e)=>{me(),be(),ye();var{MathFloor:r,NumberIsInteger:n}=at(),{validateInteger:i}=Ts(),{ERR_INVALID_ARG_VALUE:o}=Lt().codes,s=16384,a=16;function l(c){return c?a:s}e.exports={getHighWaterMark:function(c,u,h,f){let d=function(y,g,p){return y.highWaterMark!=null?y.highWaterMark:g?y[p]:null}(u,f,h);if(d!=null){if(!n(d)||d<0)throw new o(f?`options.${h}`:"options.highWaterMark",d);return r(d)}return l(c.objectMode)},getDefaultHighWaterMark:l,setDefaultHighWaterMark:function(c,u){i(u,"value",0),c?a=u:s=u}}}),Pk=we((t,e)=>{me(),be(),ye();var r=(vt(),Xe(bt)),n=r.Buffer;function i(s,a){for(var l in s)a[l]=s[l]}function o(s,a,l){return n(s,a,l)}n.from&&n.alloc&&n.allocUnsafe&&n.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=o),o.prototype=Object.create(n.prototype),i(n,o),o.from=function(s,a,l){if(typeof s=="number")throw new TypeError("Argument must not be a number");return n(s,a,l)},o.alloc=function(s,a,l){if(typeof s!="number")throw new TypeError("Argument must be a number");var c=n(s);return a!==void 0?typeof l=="string"?c.fill(a,l):c.fill(a):c.fill(0),c},o.allocUnsafe=function(s){if(typeof s!="number")throw new TypeError("Argument must be a number");return n(s)},o.allocUnsafeSlow=function(s){if(typeof s!="number")throw new TypeError("Argument must be a number");return r.SlowBuffer(s)}}),Ck=we(t=>{me(),be(),ye();var e=Pk().Buffer,r=e.isEncoding||function(f){switch((f=""+f)&&f.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function n(f){var d;switch(this.encoding=function(y){var g=function(p){if(!p)return"utf8";for(var b;;)switch(p){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return p;default:if(b)return;p=(""+p).toLowerCase(),b=!0}}(y);if(typeof g!="string"&&(e.isEncoding===r||!r(y)))throw new Error("Unknown encoding: "+y);return g||y}(f),this.encoding){case"utf16le":this.text=s,this.end=a,d=4;break;case"utf8":this.fillLast=o,d=4;break;case"base64":this.text=l,this.end=c,d=3;break;default:return this.write=u,void(this.end=h)}this.lastNeed=0,this.lastTotal=0,this.lastChar=e.allocUnsafe(d)}function i(f){return f<=127?0:f>>5==6?2:f>>4==14?3:f>>3==30?4:f>>6==2?-1:-2}function o(f){var d=this.lastTotal-this.lastNeed,y=function(g,p){if((192&p[0])!=128)return g.lastNeed=0,"�";if(g.lastNeed>1&&p.length>1){if((192&p[1])!=128)return g.lastNeed=1,"�";if(g.lastNeed>2&&p.length>2&&(192&p[2])!=128)return g.lastNeed=2,"�"}}(this,f);return y!==void 0?y:this.lastNeed<=f.length?(f.copy(this.lastChar,d,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(f.copy(this.lastChar,d,0,f.length),void(this.lastNeed-=f.length))}function s(f,d){if((f.length-d)%2==0){var y=f.toString("utf16le",d);if(y){var g=y.charCodeAt(y.length-1);if(g>=55296&&g<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=f[f.length-2],this.lastChar[1]=f[f.length-1],y.slice(0,-1)}return y}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=f[f.length-1],f.toString("utf16le",d,f.length-1)}function a(f){var d=f&&f.length?this.write(f):"";if(this.lastNeed){var y=this.lastTotal-this.lastNeed;return d+this.lastChar.toString("utf16le",0,y)}return d}function l(f,d){var y=(f.length-d)%3;return y===0?f.toString("base64",d):(this.lastNeed=3-y,this.lastTotal=3,y===1?this.lastChar[0]=f[f.length-1]:(this.lastChar[0]=f[f.length-2],this.lastChar[1]=f[f.length-1]),f.toString("base64",d,f.length-y))}function c(f){var d=f&&f.length?this.write(f):"";return this.lastNeed?d+this.lastChar.toString("base64",0,3-this.lastNeed):d}function u(f){return f.toString(this.encoding)}function h(f){return f&&f.length?this.write(f):""}t.StringDecoder=n,n.prototype.write=function(f){if(f.length===0)return"";var d,y;if(this.lastNeed){if((d=this.fillLast(f))===void 0)return"";y=this.lastNeed,this.lastNeed=0}else y=0;return y=0?(k>0&&(p.lastNeed=k-1),k):--S=0?(k>0&&(p.lastNeed=k-2),k):--S=0?(k>0&&(k===2?k=0:p.lastNeed=k-3),k):0))}(this,f,d);if(!this.lastNeed)return f.toString("utf8",d);this.lastTotal=y;var g=f.length-(y-this.lastNeed);return f.copy(this.lastChar,0,g),f.toString("utf8",d,g)},n.prototype.fillLast=function(f){if(this.lastNeed<=f.length)return f.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);f.copy(this.lastChar,this.lastTotal-this.lastNeed,0,f.length),this.lastNeed-=f.length}}),Qv=we((t,e)=>{me(),be(),ye();var r=di(),{PromisePrototypeThen:n,SymbolAsyncIterator:i,SymbolIterator:o}=at(),{Buffer:s}=(vt(),Xe(bt)),{ERR_INVALID_ARG_TYPE:a,ERR_STREAM_NULL_VALUES:l}=Lt().codes;e.exports=function(c,u,h){let f,d;if(typeof u=="string"||u instanceof s)return new c({objectMode:!0,...h,read(){this.push(u),this.push(null)}});if(u&&u[i])d=!0,f=u[i]();else{if(!u||!u[o])throw new a("iterable",["Iterable"],u);d=!1,f=u[o]()}let y=new c({objectMode:!0,highWaterMark:1,...h}),g=!1;return y._read=function(){g||(g=!0,async function(){for(;;){try{let{value:p,done:b}=d?await f.next():f.next();if(b)y.push(null);else{let v=p&&typeof p.then=="function"?await p:p;if(v===null)throw g=!1,new l;if(y.push(v))continue;g=!1}}catch(p){y.destroy(p)}break}}())},y._destroy=function(p,b){n(async function(v){let S=v!=null,k=typeof f.throw=="function";if(S&&k){let{value:M,done:A}=await f.throw(v);if(await M,A)return}if(typeof f.return=="function"){let{value:M}=await f.return();await M}}(p),()=>r.nextTick(b,p),v=>r.nextTick(b,v||p))},y}}),$l=we((t,e)=>{me(),be(),ye();var r=di(),{ArrayPrototypeIndexOf:n,NumberIsInteger:i,NumberIsNaN:o,NumberParseInt:s,ObjectDefineProperties:a,ObjectKeys:l,ObjectSetPrototypeOf:c,Promise:u,SafeSet:h,SymbolAsyncDispose:f,SymbolAsyncIterator:d,Symbol:y}=at();e.exports=oe,oe.ReadableState=ie;var{EventEmitter:g}=(fi(),Xe(Nn)),{Stream:p,prependListener:b}=Od(),{Buffer:v}=(vt(),Xe(bt)),{addAbortSignal:S}=Ul(),k=Ln(),M=Vt().debuglog("stream",C=>{M=C}),A=Ik(),I=io(),{getHighWaterMark:T,getDefaultHighWaterMark:_}=Vl(),{aggregateTwoErrors:x,codes:{ERR_INVALID_ARG_TYPE:O,ERR_METHOD_NOT_IMPLEMENTED:N,ERR_OUT_OF_RANGE:G,ERR_STREAM_PUSH_AFTER_EOF:U,ERR_STREAM_UNSHIFT_AFTER_END_EVENT:z},AbortError:q}=Lt(),{validateObject:B}=Ts(),ne=y("kPaused"),{StringDecoder:H}=Ck(),Q=Qv();c(oe.prototype,p.prototype),c(oe,p);var F=()=>{},{errorOrDestroy:V}=I,Z=1,$=16,se=32,le=2048,ue=4096;function Y(C){return{enumerable:!1,get(){return(this.state&C)!==0},set(L){L?this.state|=C:this.state&=~C}}}function ie(C,L,ae){typeof ae!="boolean"&&(ae=L instanceof Hr()),this.state=le|ue|$|se,C&&C.objectMode&&(this.state|=Z),ae&&C&&C.readableObjectMode&&(this.state|=Z),this.highWaterMark=C?T(this,C,"readableHighWaterMark",ae):_(!1),this.buffer=new A,this.length=0,this.pipes=[],this.flowing=null,this[ne]=null,C&&C.emitClose===!1&&(this.state&=~le),C&&C.autoDestroy===!1&&(this.state&=~ue),this.errored=null,this.defaultEncoding=C&&C.defaultEncoding||"utf8",this.awaitDrainWriters=null,this.decoder=null,this.encoding=null,C&&C.encoding&&(this.decoder=new H(C.encoding),this.encoding=C.encoding)}function oe(C){if(!(this instanceof oe))return new oe(C);let L=this instanceof Hr();this._readableState=new ie(C,this,L),C&&(typeof C.read=="function"&&(this._read=C.read),typeof C.destroy=="function"&&(this._destroy=C.destroy),typeof C.construct=="function"&&(this._construct=C.construct),C.signal&&!L&&S(C.signal,this)),p.call(this,C),I.construct(this,()=>{this._readableState.needReadable&&E(this,this._readableState)})}function j(C,L,ae,ge){M("readableAddChunk",L);let xe,ve=C._readableState;if(!(ve.state&Z)&&(typeof L=="string"?(ae=ae||ve.defaultEncoding,ve.encoding!==ae&&(ge&&ve.encoding?L=v.from(L,ae).toString(ve.encoding):(L=v.from(L,ae),ae=""))):L instanceof v?ae="":p._isUint8Array(L)?(L=p._uint8ArrayToBuffer(L),ae=""):L!=null&&(xe=new O("chunk",["string","Buffer","Uint8Array"],L))),xe)V(C,xe);else if(L===null)ve.state&=-9,function(Ce,ke){if(M("onEofChunk"),!ke.ended){if(ke.decoder){let Be=ke.decoder.end();Be&&Be.length&&(ke.buffer.push(Be),ke.length+=ke.objectMode?1:Be.length)}ke.ended=!0,ke.sync?m(Ce):(ke.needReadable=!1,ke.emittedReadable=!0,w(Ce))}}(C,ve);else if(ve.state&Z||L&&L.length>0)if(ge)if(4&ve.state)V(C,new z);else{if(ve.destroyed||ve.errored)return!1;X(C,ve,L,!0)}else if(ve.ended)V(C,new U);else{if(ve.destroyed||ve.errored)return!1;ve.state&=-9,ve.decoder&&!ae?(L=ve.decoder.write(L),ve.objectMode||L.length!==0?X(C,ve,L,!1):E(C,ve)):X(C,ve,L,!1)}else ge||(ve.state&=-9,E(C,ve));return!ve.ended&&(ve.length0?(65536&L.state?L.awaitDrainWriters.clear():L.awaitDrainWriters=null,L.dataEmitted=!0,C.emit("data",ae)):(L.length+=L.objectMode?1:ae.length,ge?L.buffer.unshift(ae):L.buffer.push(ae),64&L.state&&m(C)),E(C,L)}a(ie.prototype,{objectMode:Y(Z),ended:Y(2),endEmitted:Y(4),reading:Y(8),constructed:Y($),sync:Y(se),needReadable:Y(64),emittedReadable:Y(128),readableListening:Y(256),resumeScheduled:Y(512),errorEmitted:Y(1024),emitClose:Y(le),autoDestroy:Y(ue),destroyed:Y(8192),closed:Y(16384),closeEmitted:Y(32768),multiAwaitDrain:Y(65536),readingMore:Y(1<<17),dataEmitted:Y(1<<18)}),oe.prototype.destroy=I.destroy,oe.prototype._undestroy=I.undestroy,oe.prototype._destroy=function(C,L){L(C)},oe.prototype[g.captureRejectionSymbol]=function(C){this.destroy(C)},oe.prototype[f]=function(){let C;return this.destroyed||(C=this.readableEnded?null:new q,this.destroy(C)),new u((L,ae)=>k(this,ge=>ge&&ge!==C?ae(ge):L(null)))},oe.prototype.push=function(C,L){return j(this,C,L,!1)},oe.prototype.unshift=function(C,L){return j(this,C,L,!0)},oe.prototype.isPaused=function(){let C=this._readableState;return C[ne]===!0||C.flowing===!1},oe.prototype.setEncoding=function(C){let L=new H(C);this._readableState.decoder=L,this._readableState.encoding=this._readableState.decoder.encoding;let ae=this._readableState.buffer,ge="";for(let xe of ae)ge+=L.write(xe);return ae.clear(),ge!==""&&ae.push(ge),this._readableState.length=ge.length,this};var R;function te(C,L){return C<=0||L.length===0&&L.ended?0:L.state&Z?1:o(C)?L.flowing&&L.length?L.buffer.first().length:L.length:C<=L.length?C:L.ended?L.length:0}function m(C){let L=C._readableState;M("emitReadable",L.needReadable,L.emittedReadable),L.needReadable=!1,L.emittedReadable||(M("emitReadable",L.flowing),L.emittedReadable=!0,r.nextTick(w,C))}function w(C){let L=C._readableState;M("emitReadable_",L.destroyed,L.length,L.ended),!L.destroyed&&!L.errored&&(L.length||L.ended)&&(C.emit("readable"),L.emittedReadable=!1),L.needReadable=!L.flowing&&!L.ended&&L.length<=L.highWaterMark,he(C)}function E(C,L){!L.readingMore&&L.constructed&&(L.readingMore=!0,r.nextTick(D,C,L))}function D(C,L){for(;!L.reading&&!L.ended&&(L.length0,L.resumeScheduled&&L[ne]===!1?L.flowing=!0:C.listenerCount("data")>0?C.resume():L.readableListening||(L.flowing=null)}function P(C){M("readable nexttick read 0"),C.read(0)}function J(C,L){M("resume",L.reading),L.reading||C.read(0),L.resumeScheduled=!1,C.emit("resume"),he(C),L.flowing&&!L.reading&&C.read(0)}function he(C){let L=C._readableState;for(M("flow",L.flowing);L.flowing&&C.read()!==null;);}function fe(C,L){typeof C.read!="function"&&(C=oe.wrap(C,{objectMode:!0}));let ae=async function*(ge,xe){let ve=F;function Ce(De){this===ge?(ve(),ve=F):ve=De}ge.on("readable",Ce);let ke,Be=k(ge,{writable:!1},De=>{ke=De?x(ke,De):null,ve(),ve=F});try{for(;;){let De=ge.destroyed?null:ge.read();if(De!==null)yield De;else{if(ke)throw ke;if(ke===null)return;await new u(Ce)}}}catch(De){throw ke=x(ke,De),ke}finally{!ke&&(xe==null?void 0:xe.destroyOnReturn)===!1||ke!==void 0&&!ge._readableState.autoDestroy?(ge.off("readable",Ce),Be()):I.destroyer(ge,null)}}(C,L);return ae.stream=C,ae}function de(C,L){if(L.length===0)return null;let ae;return L.objectMode?ae=L.buffer.shift():!C||C>=L.length?(ae=L.decoder?L.buffer.join(""):L.buffer.length===1?L.buffer.first():L.buffer.concat(L.length),L.buffer.clear()):ae=L.buffer.consume(C,L.decoder),ae}function pe(C){let L=C._readableState;M("endReadable",L.endEmitted),L.endEmitted||(L.ended=!0,r.nextTick(K,L,C))}function K(C,L){if(M("endReadableNT",C.endEmitted,C.length),!C.errored&&!C.closeEmitted&&!C.endEmitted&&C.length===0){if(C.endEmitted=!0,L.emit("end"),L.writable&&L.allowHalfOpen===!1)r.nextTick(ee,L);else if(C.autoDestroy){let ae=L._writableState;(!ae||ae.autoDestroy&&(ae.finished||ae.writable===!1))&&L.destroy()}}}function ee(C){C.writable&&!C.writableEnded&&!C.destroyed&&C.end()}function ce(){return R===void 0&&(R={}),R}oe.prototype.read=function(C){M("read",C),C===void 0?C=NaN:i(C)||(C=s(C,10));let L=this._readableState,ae=C;if(C>L.highWaterMark&&(L.highWaterMark=function(ve){if(ve>1073741824)throw new G("size","<= 1GiB",ve);return ve--,ve|=ve>>>1,ve|=ve>>>2,ve|=ve>>>4,ve|=ve>>>8,ve|=ve>>>16,++ve}(C)),C!==0&&(L.state&=-129),C===0&&L.needReadable&&((L.highWaterMark!==0?L.length>=L.highWaterMark:L.length>0)||L.ended))return M("read: emitReadable",L.length,L.ended),L.length===0&&L.ended?pe(this):m(this),null;if((C=te(C,L))===0&&L.ended)return L.length===0&&pe(this),null;let ge,xe=!!(64&L.state);if(M("need readable",xe),(L.length===0||L.length-C0?de(C,L):null,ge===null?(L.needReadable=L.length<=L.highWaterMark,C=0):(L.length-=C,L.multiAwaitDrain?L.awaitDrainWriters.clear():L.awaitDrainWriters=null),L.length===0&&(L.ended||(L.needReadable=!0),ae!==C&&L.ended&&pe(this)),ge!==null&&!L.errorEmitted&&!L.closeEmitted&&(L.dataEmitted=!0,this.emit("data",ge)),ge},oe.prototype._read=function(C){throw new N("_read()")},oe.prototype.pipe=function(C,L){let ae=this,ge=this._readableState;ge.pipes.length===1&&(ge.multiAwaitDrain||(ge.multiAwaitDrain=!0,ge.awaitDrainWriters=new h(ge.awaitDrainWriters?[ge.awaitDrainWriters]:[]))),ge.pipes.push(C),M("pipe count=%d opts=%j",ge.pipes.length,L);let xe=L&&L.end===!1||C===r.stdout||C===r.stderr?Se:Ce;function ve(He,qe){M("onunpipe"),He===ae&&qe&&qe.hasUnpiped===!1&&(qe.hasUnpiped=!0,M("cleanup"),C.removeListener("close",ze),C.removeListener("finish",Me),ke&&C.removeListener("drain",ke),C.removeListener("error",Fe),C.removeListener("unpipe",ve),ae.removeListener("end",Ce),ae.removeListener("end",Se),ae.removeListener("data",Ve),Be=!0,ke&&ge.awaitDrainWriters&&(!C._writableState||C._writableState.needDrain)&&ke())}function Ce(){M("onend"),C.end()}ge.endEmitted?r.nextTick(xe):ae.once("end",xe),C.on("unpipe",ve);let ke,Be=!1;function De(){Be||(ge.pipes.length===1&&ge.pipes[0]===C?(M("false write response, pause",0),ge.awaitDrainWriters=C,ge.multiAwaitDrain=!1):ge.pipes.length>1&&ge.pipes.includes(C)&&(M("false write response, pause",ge.awaitDrainWriters.size),ge.awaitDrainWriters.add(C)),ae.pause()),ke||(ke=function(He,qe){return function(){let lt=He._readableState;lt.awaitDrainWriters===qe?(M("pipeOnDrain",1),lt.awaitDrainWriters=null):lt.multiAwaitDrain&&(M("pipeOnDrain",lt.awaitDrainWriters.size),lt.awaitDrainWriters.delete(qe)),(!lt.awaitDrainWriters||lt.awaitDrainWriters.size===0)&&He.listenerCount("data")&&He.resume()}}(ae,C),C.on("drain",ke))}function Ve(He){M("ondata");let qe=C.write(He);M("dest.write",qe),qe===!1&&De()}function Fe(He){if(M("onerror",He),Se(),C.removeListener("error",Fe),C.listenerCount("error")===0){let qe=C._writableState||C._readableState;qe&&!qe.errorEmitted?V(C,He):C.emit("error",He)}}function ze(){C.removeListener("finish",Me),Se()}function Me(){M("onfinish"),C.removeListener("close",ze),Se()}function Se(){M("unpipe"),ae.unpipe(C)}return ae.on("data",Ve),b(C,"error",Fe),C.once("close",ze),C.once("finish",Me),C.emit("pipe",ae),C.writableNeedDrain===!0?De():ge.flowing||(M("pipe resume"),ae.resume()),C},oe.prototype.unpipe=function(C){let L=this._readableState;if(L.pipes.length===0)return this;if(!C){let ge=L.pipes;L.pipes=[],this.pause();for(let xe=0;xe0,ge.flowing!==!1&&this.resume()):C==="readable"&&!ge.endEmitted&&!ge.readableListening&&(ge.readableListening=ge.needReadable=!0,ge.flowing=!1,ge.emittedReadable=!1,M("on readable",ge.length,ge.reading),ge.length?m(this):ge.reading||r.nextTick(P,this)),ae},oe.prototype.addListener=oe.prototype.on,oe.prototype.removeListener=function(C,L){let ae=p.prototype.removeListener.call(this,C,L);return C==="readable"&&r.nextTick(W,this),ae},oe.prototype.off=oe.prototype.removeListener,oe.prototype.removeAllListeners=function(C){let L=p.prototype.removeAllListeners.apply(this,arguments);return(C==="readable"||C===void 0)&&r.nextTick(W,this),L},oe.prototype.resume=function(){let C=this._readableState;return C.flowing||(M("resume"),C.flowing=!C.readableListening,function(L,ae){ae.resumeScheduled||(ae.resumeScheduled=!0,r.nextTick(J,L,ae))}(this,C)),C[ne]=!1,this},oe.prototype.pause=function(){return M("call pause flowing=%j",this._readableState.flowing),this._readableState.flowing!==!1&&(M("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState[ne]=!0,this},oe.prototype.wrap=function(C){let L=!1;C.on("data",ge=>{!this.push(ge)&&C.pause&&(L=!0,C.pause())}),C.on("end",()=>{this.push(null)}),C.on("error",ge=>{V(this,ge)}),C.on("close",()=>{this.destroy()}),C.on("destroy",()=>{this.destroy()}),this._read=()=>{L&&C.resume&&(L=!1,C.resume())};let ae=l(C);for(let ge=1;ge{me(),be(),ye();var r=di(),{ArrayPrototypeSlice:n,Error:i,FunctionPrototypeSymbolHasInstance:o,ObjectDefineProperty:s,ObjectDefineProperties:a,ObjectSetPrototypeOf:l,StringPrototypeToLowerCase:c,Symbol:u,SymbolHasInstance:h}=at();e.exports=B,B.WritableState=z;var{EventEmitter:f}=(fi(),Xe(Nn)),d=Od().Stream,{Buffer:y}=(vt(),Xe(bt)),g=io(),{addAbortSignal:p}=Ul(),{getHighWaterMark:b,getDefaultHighWaterMark:v}=Vl(),{ERR_INVALID_ARG_TYPE:S,ERR_METHOD_NOT_IMPLEMENTED:k,ERR_MULTIPLE_CALLBACK:M,ERR_STREAM_CANNOT_PIPE:A,ERR_STREAM_DESTROYED:I,ERR_STREAM_ALREADY_FINISHED:T,ERR_STREAM_NULL_VALUES:_,ERR_STREAM_WRITE_AFTER_END:x,ERR_UNKNOWN_ENCODING:O}=Lt().codes,{errorOrDestroy:N}=g;function G(){}l(B.prototype,d.prototype),l(B,d);var U=u("kOnFinished");function z(R,te,m){typeof m!="boolean"&&(m=te instanceof Hr()),this.objectMode=!(!R||!R.objectMode),m&&(this.objectMode=this.objectMode||!(!R||!R.writableObjectMode)),this.highWaterMark=R?b(this,R,"writableHighWaterMark",m):v(!1),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;let w=!(!R||R.decodeStrings!==!1);this.decodeStrings=!w,this.defaultEncoding=R&&R.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=F.bind(void 0,te),this.writecb=null,this.writelen=0,this.afterWriteTickInfo=null,q(this),this.pendingcb=0,this.constructed=!0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!R||R.emitClose!==!1,this.autoDestroy=!R||R.autoDestroy!==!1,this.errored=null,this.closed=!1,this.closeEmitted=!1,this[U]=[]}function q(R){R.buffered=[],R.bufferedIndex=0,R.allBuffers=!0,R.allNoop=!0}function B(R){let te=this instanceof Hr();if(!te&&!o(B,this))return new B(R);this._writableState=new z(R,this,te),R&&(typeof R.write=="function"&&(this._write=R.write),typeof R.writev=="function"&&(this._writev=R.writev),typeof R.destroy=="function"&&(this._destroy=R.destroy),typeof R.final=="function"&&(this._final=R.final),typeof R.construct=="function"&&(this._construct=R.construct),R.signal&&p(R.signal,this)),d.call(this,R),g.construct(this,()=>{let m=this._writableState;m.writing||se(this,m),Y(this,m)})}function ne(R,te,m,w){let E,D=R._writableState;if(typeof m=="function")w=m,m=D.defaultEncoding;else{if(m){if(m!=="buffer"&&!y.isEncoding(m))throw new O(m)}else m=D.defaultEncoding;typeof w!="function"&&(w=G)}if(te===null)throw new _;if(!D.objectMode)if(typeof te=="string")D.decodeStrings!==!1&&(te=y.from(te,m),m="buffer");else if(te instanceof y)m="buffer";else{if(!d._isUint8Array(te))throw new S("chunk",["string","Buffer","Uint8Array"],te);te=d._uint8ArrayToBuffer(te),m="buffer"}return D.ending?E=new x:D.destroyed&&(E=new I("write")),E?(r.nextTick(w,E),N(R,E,!0),E):(D.pendingcb++,function(W,P,J,he,fe){let de=P.objectMode?1:J.length;P.length+=de;let pe=P.lengthm.bufferedIndex&&se(R,m),w?m.afterWriteTickInfo!==null&&m.afterWriteTickInfo.cb===E?m.afterWriteTickInfo.count++:(m.afterWriteTickInfo={count:1,cb:E,stream:R,state:m},r.nextTick(V,m.afterWriteTickInfo)):Z(R,m,1,E))):N(R,new M)}function V({stream:R,state:te,count:m,cb:w}){return te.afterWriteTickInfo=null,Z(R,te,m,w)}function Z(R,te,m,w){for(!te.ending&&!R.destroyed&&te.length===0&&te.needDrain&&(te.needDrain=!1,R.emit("drain"));m-- >0;)te.pendingcb--,w();te.destroyed&&$(te),Y(R,te)}function $(R){if(R.writing)return;for(let E=R.bufferedIndex;E1&&R._writev){te.pendingcb-=D-1;let P=te.allNoop?G:he=>{for(let fe=W;fe256?(m.splice(0,W),te.bufferedIndex=0):te.bufferedIndex=W}te.bufferProcessing=!1}function le(R){return R.ending&&!R.destroyed&&R.constructed&&R.length===0&&!R.errored&&R.buffered.length===0&&!R.finished&&!R.writing&&!R.errorEmitted&&!R.closeEmitted}function ue(R,te){!te.prefinished&&!te.finalCalled&&(typeof R._final!="function"||te.destroyed?(te.prefinished=!0,R.emit("prefinish")):(te.finalCalled=!0,function(m,w){let E=!1;function D(W){if(E)N(m,W??M());else if(E=!0,w.pendingcb--,W){let P=w[U].splice(0);for(let J=0;J{le(E)?ie(w,E):E.pendingcb--},R,te)):le(te)&&(te.pendingcb++,ie(R,te))))}function ie(R,te){te.pendingcb--,te.finished=!0;let m=te[U].splice(0);for(let w=0;w{me(),be(),ye();var r=di(),n=(vt(),Xe(bt)),{isReadable:i,isWritable:o,isIterable:s,isNodeStream:a,isReadableNodeStream:l,isWritableNodeStream:c,isDuplexNodeStream:u,isReadableStream:h,isWritableStream:f}=Zr(),d=Ln(),{AbortError:y,codes:{ERR_INVALID_ARG_TYPE:g,ERR_INVALID_RETURN_VALUE:p}}=Lt(),{destroyer:b}=io(),v=Hr(),S=$l(),k=Md(),{createDeferredPromise:M}=Vt(),A=Qv(),I=globalThis.Blob||n.Blob,T=typeof I<"u"?function(G){return G instanceof I}:function(G){return!1},_=globalThis.AbortController||gs().AbortController,{FunctionPrototypeCall:x}=at(),O=class extends v{constructor(G){super(G),(G==null?void 0:G.readable)===!1&&(this._readableState.readable=!1,this._readableState.ended=!0,this._readableState.endEmitted=!0),(G==null?void 0:G.writable)===!1&&(this._writableState.writable=!1,this._writableState.ending=!0,this._writableState.ended=!0,this._writableState.finished=!0)}};function N(G){let U,z,q,B,ne,H=G.readable&&typeof G.readable.read!="function"?S.wrap(G.readable):G.readable,Q=G.writable,F=!!i(H),V=!!o(Q);function Z($){let se=B;B=null,se?se($):$&&ne.destroy($)}return ne=new O({readableObjectMode:!(H==null||!H.readableObjectMode),writableObjectMode:!(Q==null||!Q.writableObjectMode),readable:F,writable:V}),V&&(d(Q,$=>{V=!1,$&&b(H,$),Z($)}),ne._write=function($,se,le){Q.write($,se)?le():U=le},ne._final=function($){Q.end(),z=$},Q.on("drain",function(){if(U){let $=U;U=null,$()}}),Q.on("finish",function(){if(z){let $=z;z=null,$()}})),F&&(d(H,$=>{F=!1,$&&b(H,$),Z($)}),H.on("readable",function(){if(q){let $=q;q=null,$()}}),H.on("end",function(){ne.push(null)}),ne._read=function(){for(;;){let $=H.read();if($===null)return void(q=ne._read);if(!ne.push($))return}}),ne._destroy=function($,se){!$&&B!==null&&($=new y),q=null,U=null,z=null,B===null?se($):(B=se,b(Q,$),b(H,$))},ne}e.exports=function G(U,z){if(u(U))return U;if(l(U))return N({readable:U});if(c(U))return N({writable:U});if(a(U))return N({writable:!1,readable:!1});if(h(U))return N({readable:S.fromWeb(U)});if(f(U))return N({writable:k.fromWeb(U)});if(typeof U=="function"){let{value:B,write:ne,final:H,destroy:Q}=function(V){let{promise:Z,resolve:$}=M(),se=new _,le=se.signal;return{value:V(async function*(){for(;;){let ue=Z;Z=null;let{chunk:Y,done:ie,cb:oe}=await ue;if(r.nextTick(oe),ie)return;if(le.aborted)throw new y(void 0,{cause:le.reason});({promise:Z,resolve:$}=M()),yield Y}}(),{signal:le}),write(ue,Y,ie){let oe=$;$=null,oe({chunk:ue,done:!1,cb:ie})},final(ue){let Y=$;$=null,Y({done:!0,cb:ue})},destroy(ue,Y){se.abort(),Y(ue)}}}(U);if(s(B))return A(O,B,{objectMode:!0,write:ne,final:H,destroy:Q});let F=B==null?void 0:B.then;if(typeof F=="function"){let V,Z=x(F,B,$=>{if($!=null)throw new p("nully","body",$)},$=>{b(V,$)});return V=new O({objectMode:!0,readable:!1,write:ne,final($){H(async()=>{try{await Z,r.nextTick($,null)}catch(se){r.nextTick($,se)}})},destroy:Q})}throw new p("Iterable, AsyncIterable or AsyncFunction",z,B)}if(T(U))return G(U.arrayBuffer());if(s(U))return A(O,U,{objectMode:!0,writable:!1});if(h(U==null?void 0:U.readable)&&f(U==null?void 0:U.writable))return O.fromWeb(U);if(typeof(U==null?void 0:U.writable)=="object"||typeof(U==null?void 0:U.readable)=="object")return N({readable:U!=null&&U.readable?l(U==null?void 0:U.readable)?U==null?void 0:U.readable:G(U.readable):void 0,writable:U!=null&&U.writable?c(U==null?void 0:U.writable)?U==null?void 0:U.writable:G(U.writable):void 0});let q=U==null?void 0:U.then;if(typeof q=="function"){let B;return x(q,U,ne=>{ne!=null&&B.push(ne),B.push(null)},ne=>{b(B,ne)}),B=new O({objectMode:!0,writable:!1,read(){}})}throw new g(z,["Blob","ReadableStream","WritableStream","Stream","Iterable","AsyncIterable","Function","{ readable, writable } pair","Promise"],U)}}),Hr=we((t,e)=>{me(),be(),ye();var{ObjectDefineProperties:r,ObjectGetOwnPropertyDescriptor:n,ObjectKeys:i,ObjectSetPrototypeOf:o}=at();e.exports=u;var s,a,l=$l(),c=Md();o(u.prototype,l.prototype),o(u,l);{let f=i(c.prototype);for(let d=0;d{me(),be(),ye();var{ObjectSetPrototypeOf:r,Symbol:n}=at();e.exports=l;var{ERR_METHOD_NOT_IMPLEMENTED:i}=Lt().codes,o=Hr(),{getHighWaterMark:s}=Vl();r(l.prototype,o.prototype),r(l,o);var a=n("kCallback");function l(h){if(!(this instanceof l))return new l(h);let f=h?s(this,h,"readableHighWaterMark",!0):null;f===0&&(h={...h,highWaterMark:null,readableHighWaterMark:f,writableHighWaterMark:h.writableHighWaterMark||0}),o.call(this,h),this._readableState.sync=!1,this[a]=null,h&&(typeof h.transform=="function"&&(this._transform=h.transform),typeof h.flush=="function"&&(this._flush=h.flush)),this.on("prefinish",u)}function c(h){typeof this._flush!="function"||this.destroyed?(this.push(null),h&&h()):this._flush((f,d)=>{f?h?h(f):this.destroy(f):(d!=null&&this.push(d),this.push(null),h&&h())})}function u(){this._final!==c&&c.call(this)}l.prototype._final=c,l.prototype._transform=function(h,f,d){throw new i("_transform()")},l.prototype._write=function(h,f,d){let y=this._readableState,g=this._writableState,p=y.length;this._transform(h,f,(b,v)=>{b?d(b):(v!=null&&this.push(v),g.ended||p===y.length||y.length{me(),be(),ye();var{ObjectSetPrototypeOf:r}=at();e.exports=i;var n=Zv();function i(o){if(!(this instanceof i))return new i(o);n.call(this,o)}r(i.prototype,n.prototype),r(i,n),i.prototype._transform=function(o,s,a){a(null,o)}}),Ad=we((t,e)=>{me(),be(),ye();var r,n,i,o=di(),{ArrayIsArray:s,Promise:a,SymbolAsyncIterator:l,SymbolDispose:c}=at(),u=Ln(),{once:h}=Vt(),f=io(),d=Hr(),{aggregateTwoErrors:y,codes:{ERR_INVALID_ARG_TYPE:g,ERR_INVALID_RETURN_VALUE:p,ERR_MISSING_ARGS:b,ERR_STREAM_DESTROYED:v,ERR_STREAM_PREMATURE_CLOSE:S},AbortError:k}=Lt(),{validateFunction:M,validateAbortSignal:A}=Ts(),{isIterable:I,isReadable:T,isReadableNodeStream:_,isNodeStream:x,isTransformStream:O,isWebStream:N,isReadableStream:G,isReadableFinished:U}=Zr(),z=globalThis.AbortController||gs().AbortController;function q(V,Z,$){let se=!1;V.on("close",()=>{se=!0});let le=u(V,{readable:Z,writable:$},ue=>{se=!ue});return{destroy:ue=>{se||(se=!0,f.destroyer(V,ue||new v("pipe")))},cleanup:le}}function B(V){if(I(V))return V;if(_(V))return async function*(Z){n||(n=$l()),yield*n.prototype[l].call(Z)}(V);throw new g("val",["Readable","Iterable","AsyncIterable"],V)}async function ne(V,Z,$,{end:se}){let le,ue=null,Y=j=>{if(j&&(le=j),ue){let X=ue;ue=null,X()}},ie=()=>new a((j,X)=>{le?X(le):ue=()=>{le?X(le):j()}});Z.on("drain",Y);let oe=u(Z,{readable:!1},Y);try{Z.writableNeedDrain&&await ie();for await(let j of V)Z.write(j)||await ie();se&&(Z.end(),await ie()),$()}catch(j){$(le!==j?y(le,j):j)}finally{oe(),Z.off("drain",Y)}}async function H(V,Z,$,{end:se}){O(Z)&&(Z=Z.writable);let le=Z.getWriter();try{for await(let ue of V)await le.ready,le.write(ue).catch(()=>{});await le.ready,se&&await le.close(),$()}catch(ue){try{await le.abort(ue),$(ue)}catch(Y){$(Y)}}}function Q(V,Z,$){if(V.length===1&&s(V[0])&&(V=V[0]),V.length<2)throw new b("streams");let se,le=new z,ue=le.signal,Y=$==null?void 0:$.signal,ie=[];function oe(){E(new k)}A(Y,"options.signal"),i=i||Vt().addAbortListener,Y&&(se=i(Y,oe));let j,X,R,te=[],m=0;function w(P){E(P,--m===0)}function E(P,J){var he;if(P&&(!j||j.code==="ERR_STREAM_PREMATURE_CLOSE")&&(j=P),j||J){for(;te.length;)te.shift()(j);(he=se)===null||he===void 0||he[c](),le.abort(),J&&(j||ie.forEach(fe=>fe()),o.nextTick(Z,j,X))}}for(let P=0;P0,de=he||($==null?void 0:$.end)!==!1,pe=P===V.length-1;if(x(J)){let K=function(ee){ee&&ee.name!=="AbortError"&&ee.code!=="ERR_STREAM_PREMATURE_CLOSE"&&w(ee)};if(de){let{destroy:ee,cleanup:ce}=q(J,he,fe);te.push(ee),T(J)&&pe&&ie.push(ce)}J.on("error",K),T(J)&&pe&&ie.push(()=>{J.removeListener("error",K)})}if(P===0)if(typeof J=="function"){if(R=J({signal:ue}),!I(R))throw new p("Iterable, AsyncIterable or Stream","source",R)}else R=I(J)||_(J)||O(J)?J:d.from(J);else if(typeof J=="function"){var D;if(O(R)?R=B((D=R)===null||D===void 0?void 0:D.readable):R=B(R),R=J(R,{signal:ue}),he){if(!I(R,!0))throw new p("AsyncIterable",`transform[${P-1}]`,R)}else{var W;r||(r=Xv());let K=new r({objectMode:!0}),ee=(W=R)===null||W===void 0?void 0:W.then;if(typeof ee=="function")m++,ee.call(R,L=>{X=L,L!=null&&K.write(L),de&&K.end(),o.nextTick(w)},L=>{K.destroy(L),o.nextTick(w,L)});else if(I(R,!0))m++,ne(R,K,w,{end:de});else{if(!G(R)&&!O(R))throw new p("AsyncIterable or Promise","destination",R);{let L=R.readable||R;m++,ne(L,K,w,{end:de})}}R=K;let{destroy:ce,cleanup:C}=q(R,!1,!0);te.push(ce),pe&&ie.push(C)}}else if(x(J)){if(_(R)){m+=2;let K=F(R,J,w,{end:de});T(J)&&pe&&ie.push(K)}else if(O(R)||G(R)){let K=R.readable||R;m++,ne(K,J,w,{end:de})}else{if(!I(R))throw new g("val",["Readable","Iterable","AsyncIterable","ReadableStream","TransformStream"],R);m++,ne(R,J,w,{end:de})}R=J}else if(N(J)){if(_(R))m++,H(B(R),J,w,{end:de});else if(G(R)||I(R))m++,H(R,J,w,{end:de});else{if(!O(R))throw new g("val",["Readable","Iterable","AsyncIterable","ReadableStream","TransformStream"],R);m++,H(R.readable,J,w,{end:de})}R=J}else R=d.from(J)}return(ue!=null&&ue.aborted||Y!=null&&Y.aborted)&&o.nextTick(oe),R}function F(V,Z,$,{end:se}){let le=!1;if(Z.on("close",()=>{le||$(new S)}),V.pipe(Z,{end:!1}),se){let ue=function(){le=!0,Z.end()};U(V)?o.nextTick(ue):V.once("end",ue)}else $();return u(V,{readable:!0,writable:!1},ue=>{let Y=V._readableState;ue&&ue.code==="ERR_STREAM_PREMATURE_CLOSE"&&Y&&Y.ended&&!Y.errored&&!Y.errorEmitted?V.once("end",$).once("error",$):$(ue)}),u(Z,{readable:!1,writable:!0},$)}e.exports={pipelineImpl:Q,pipeline:function(...V){return Q(V,h(function(Z){return M(Z[Z.length-1],"streams[stream.length - 1]"),Z.pop()}(V)))}}}),Jv=we((t,e)=>{me(),be(),ye();var{pipeline:r}=Ad(),n=Hr(),{destroyer:i}=io(),{isNodeStream:o,isReadable:s,isWritable:a,isWebStream:l,isTransformStream:c,isWritableStream:u,isReadableStream:h}=Zr(),{AbortError:f,codes:{ERR_INVALID_ARG_VALUE:d,ERR_MISSING_ARGS:y}}=Lt(),g=Ln();e.exports=function(...p){if(p.length===0)throw new y("streams");if(p.length===1)return n.from(p[0]);let b,v,S,k,M,A=[...p];if(typeof p[0]=="function"&&(p[0]=n.from(p[0])),typeof p[p.length-1]=="function"){let O=p.length-1;p[O]=n.from(p[O])}for(let O=0;O0&&!(a(p[O])||u(p[O])||c(p[O])))throw new d(`streams[${O}]`,A[O],"must be writable")}let I=p[0],T=r(p,function(O){let N=k;k=null,N?N(O):O?M.destroy(O):!x&&!_&&M.destroy()}),_=!!(a(I)||u(I)||c(I)),x=!!(s(T)||h(T)||c(T));if(M=new n({writableObjectMode:!(I==null||!I.writableObjectMode),readableObjectMode:!(T==null||!T.readableObjectMode),writable:_,readable:x}),_){if(o(I))M._write=function(N,G,U){I.write(N,G)?U():b=U},M._final=function(N){I.end(),v=N},I.on("drain",function(){if(b){let N=b;b=null,N()}});else if(l(I)){let N=(c(I)?I.writable:I).getWriter();M._write=async function(G,U,z){try{await N.ready,N.write(G).catch(()=>{}),z()}catch(q){z(q)}},M._final=async function(G){try{await N.ready,N.close().catch(()=>{}),v=G}catch(U){G(U)}}}let O=c(T)?T.readable:T;g(O,()=>{if(v){let N=v;v=null,N()}})}if(x){if(o(T))T.on("readable",function(){if(S){let O=S;S=null,O()}}),T.on("end",function(){M.push(null)}),M._read=function(){for(;;){let O=T.read();if(O===null)return void(S=M._read);if(!M.push(O))return}};else if(l(T)){let O=(c(T)?T.readable:T).getReader();M._read=async function(){for(;;)try{let{value:N,done:G}=await O.read();if(!M.push(N))return;if(G)return void M.push(null)}catch{return}}}}return M._destroy=function(O,N){!O&&k!==null&&(O=new f),S=null,b=null,v=null,k===null?N(O):(k=N,o(T)&&i(T,O))},M}}),Dk=we((t,e)=>{me(),be(),ye();var r=globalThis.AbortController||gs().AbortController,{codes:{ERR_INVALID_ARG_VALUE:n,ERR_INVALID_ARG_TYPE:i,ERR_MISSING_ARGS:o,ERR_OUT_OF_RANGE:s},AbortError:a}=Lt(),{validateAbortSignal:l,validateInteger:c,validateObject:u}=Ts(),h=at().Symbol("kWeak"),f=at().Symbol("kResistStopPropagation"),{finished:d}=Ln(),y=Jv(),{addAbortSignalNoValidate:g}=Ul(),{isWritable:p,isNodeStream:b}=Zr(),{deprecate:v}=Vt(),{ArrayPrototypePush:S,Boolean:k,MathFloor:M,Number:A,NumberIsNaN:I,Promise:T,PromiseReject:_,PromiseResolve:x,PromisePrototypeThen:O,Symbol:N}=at(),G=N("kEmpty"),U=N("kEof");function z(Q,F){if(typeof Q!="function")throw new i("fn",["Function","AsyncFunction"],Q);F!=null&&u(F,"options"),(F==null?void 0:F.signal)!=null&&l(F.signal,"options.signal");let V=1;(F==null?void 0:F.concurrency)!=null&&(V=M(F.concurrency));let Z=V-1;return(F==null?void 0:F.highWaterMark)!=null&&(Z=M(F.highWaterMark)),c(V,"options.concurrency",1),c(Z,"options.highWaterMark",0),Z+=V,(async function*(){let $,se,le=Vt().AbortSignalAny([F==null?void 0:F.signal].filter(k)),ue=this,Y=[],ie={signal:le},oe=!1,j=0;function X(){oe=!0,R()}function R(){j-=1,te()}function te(){se&&!oe&&j=Z||j>=V)&&await new T(w=>{se=w})}Y.push(U)}catch(m){let w=_(m);O(w,R,X),Y.push(w)}finally{oe=!0,$&&($(),$=null)}})();try{for(;;){for(;Y.length>0;){let m=await Y[0];if(m===U)return;if(le.aborted)throw new a;m!==G&&(yield m),Y.shift(),te()}await new T(m=>{$=m})}}finally{oe=!0,se&&(se(),se=null)}}).call(this)}async function q(Q,F=void 0){for await(let V of B.call(this,Q,F))return!0;return!1}function B(Q,F){if(typeof Q!="function")throw new i("fn",["Function","AsyncFunction"],Q);return z.call(this,async function(V,Z){return await Q(V,Z)?V:G},F)}var ne=class extends o{constructor(){super("reduce"),this.message="Reduce of an empty stream requires an initial value"}};function H(Q){if(Q=A(Q),I(Q))return 0;if(Q<0)throw new s("number",">= 0",Q);return Q}e.exports.streamReturningOperators={asIndexedPairs:v(function(Q=void 0){return Q!=null&&u(Q,"options"),(Q==null?void 0:Q.signal)!=null&&l(Q.signal,"options.signal"),(async function*(){let F=0;for await(let Z of this){var V;if(Q!=null&&(V=Q.signal)!==null&&V!==void 0&&V.aborted)throw new a({cause:Q.signal.reason});yield[F++,Z]}}).call(this)},"readable.asIndexedPairs will be removed in a future version."),drop:function(Q,F=void 0){return F!=null&&u(F,"options"),(F==null?void 0:F.signal)!=null&&l(F.signal,"options.signal"),Q=H(Q),(async function*(){var V;if(F!=null&&(V=F.signal)!==null&&V!==void 0&&V.aborted)throw new a;for await(let $ of this){var Z;if(F!=null&&(Z=F.signal)!==null&&Z!==void 0&&Z.aborted)throw new a;Q--<=0&&(yield $)}}).call(this)},filter:B,flatMap:function(Q,F){let V=z.call(this,Q,F);return(async function*(){for await(let Z of V)yield*Z}).call(this)},map:z,take:function(Q,F=void 0){return F!=null&&u(F,"options"),(F==null?void 0:F.signal)!=null&&l(F.signal,"options.signal"),Q=H(Q),(async function*(){var V;if(F!=null&&(V=F.signal)!==null&&V!==void 0&&V.aborted)throw new a;for await(let $ of this){var Z;if(F!=null&&(Z=F.signal)!==null&&Z!==void 0&&Z.aborted)throw new a;if(Q-- >0&&(yield $),Q<=0)return}}).call(this)},compose:function(Q,F){if(F!=null&&u(F,"options"),(F==null?void 0:F.signal)!=null&&l(F.signal,"options.signal"),b(Q)&&!p(Q))throw new n("stream",Q,"must be writable");let V=y(this,Q);return F!=null&&F.signal&&g(F.signal,V),V}},e.exports.promiseReturningOperators={every:async function(Q,F=void 0){if(typeof Q!="function")throw new i("fn",["Function","AsyncFunction"],Q);return!await q.call(this,async(...V)=>!await Q(...V),F)},forEach:async function(Q,F){if(typeof Q!="function")throw new i("fn",["Function","AsyncFunction"],Q);for await(let V of z.call(this,async function(Z,$){return await Q(Z,$),G},F));},reduce:async function(Q,F,V){var Z;if(typeof Q!="function")throw new i("reducer",["Function","AsyncFunction"],Q);V!=null&&u(V,"options"),(V==null?void 0:V.signal)!=null&&l(V.signal,"options.signal");let $=arguments.length>1;if(V!=null&&(Z=V.signal)!==null&&Z!==void 0&&Z.aborted){let ie=new a(void 0,{cause:V.signal.reason});throw this.once("error",()=>{}),await d(this.destroy(ie)),ie}let se=new r,le=se.signal;if(V!=null&&V.signal){let ie={once:!0,[h]:this,[f]:!0};V.signal.addEventListener("abort",()=>se.abort(),ie)}let ue=!1;try{for await(let ie of this){var Y;if(ue=!0,V!=null&&(Y=V.signal)!==null&&Y!==void 0&&Y.aborted)throw new a;$?F=await Q(F,ie,{signal:le}):(F=ie,$=!0)}if(!ue&&!$)throw new ne}finally{se.abort()}return F},toArray:async function(Q){Q!=null&&u(Q,"options"),(Q==null?void 0:Q.signal)!=null&&l(Q.signal,"options.signal");let F=[];for await(let Z of this){var V;if(Q!=null&&(V=Q.signal)!==null&&V!==void 0&&V.aborted)throw new a(void 0,{cause:Q.signal.reason});S(F,Z)}return F},some:q,find:async function(Q,F){for await(let V of B.call(this,Q,F))return V}}}),ew=we((t,e)=>{me(),be(),ye();var{ArrayPrototypePop:r,Promise:n}=at(),{isIterable:i,isNodeStream:o,isWebStream:s}=Zr(),{pipelineImpl:a}=Ad(),{finished:l}=Ln();tw(),e.exports={finished:l,pipeline:function(...c){return new n((u,h)=>{let f,d,y=c[c.length-1];if(y&&typeof y=="object"&&!o(y)&&!i(y)&&!s(y)){let g=r(c);f=g.signal,d=g.end}a(c,(g,p)=>{g?h(g):u(p)},{signal:f,end:d})})}}}),tw=we((t,e)=>{me(),be(),ye();var{Buffer:r}=(vt(),Xe(bt)),{ObjectDefineProperty:n,ObjectKeys:i,ReflectApply:o}=at(),{promisify:{custom:s}}=Vt(),{streamReturningOperators:a,promiseReturningOperators:l}=Dk(),{codes:{ERR_ILLEGAL_CONSTRUCTOR:c}}=Lt(),u=Jv(),{setDefaultHighWaterMark:h,getDefaultHighWaterMark:f}=Vl(),{pipeline:d}=Ad(),{destroyer:y}=io(),g=Ln(),p=ew(),b=Zr(),v=e.exports=Od().Stream;v.isDestroyed=b.isDestroyed,v.isDisturbed=b.isDisturbed,v.isErrored=b.isErrored,v.isReadable=b.isReadable,v.isWritable=b.isWritable,v.Readable=$l();for(let k of i(a)){let M=function(...I){if(new.target)throw c();return v.Readable.from(o(A,this,I))},A=a[k];n(M,"name",{__proto__:null,value:A.name}),n(M,"length",{__proto__:null,value:A.length}),n(v.Readable.prototype,k,{__proto__:null,value:M,enumerable:!1,configurable:!0,writable:!0})}for(let k of i(l)){let M=function(...I){if(new.target)throw c();return o(A,this,I)},A=l[k];n(M,"name",{__proto__:null,value:A.name}),n(M,"length",{__proto__:null,value:A.length}),n(v.Readable.prototype,k,{__proto__:null,value:M,enumerable:!1,configurable:!0,writable:!0})}v.Writable=Md(),v.Duplex=Hr(),v.Transform=Zv(),v.PassThrough=Xv(),v.pipeline=d;var{addAbortSignal:S}=Ul();v.addAbortSignal=S,v.finished=g,v.destroy=y,v.compose=u,v.setDefaultHighWaterMark=h,v.getDefaultHighWaterMark=f,n(v,"promises",{__proto__:null,configurable:!0,enumerable:!0,get:()=>p}),n(d,s,{__proto__:null,enumerable:!0,get:()=>p.pipeline}),n(g,s,{__proto__:null,enumerable:!0,get:()=>p.finished}),v.Stream=v,v._isUint8Array=function(k){return k instanceof Uint8Array},v._uint8ArrayToBuffer=function(k){return r.from(k.buffer,k.byteOffset,k.byteLength)}}),pi=we((t,e)=>{me(),be(),ye();var r=tw(),n=ew(),i=r.Readable.destroy;e.exports=r.Readable,e.exports._uint8ArrayToBuffer=r._uint8ArrayToBuffer,e.exports._isUint8Array=r._isUint8Array,e.exports.isDisturbed=r.isDisturbed,e.exports.isErrored=r.isErrored,e.exports.isReadable=r.isReadable,e.exports.Readable=r.Readable,e.exports.Writable=r.Writable,e.exports.Duplex=r.Duplex,e.exports.Transform=r.Transform,e.exports.PassThrough=r.PassThrough,e.exports.addAbortSignal=r.addAbortSignal,e.exports.finished=r.finished,e.exports.destroy=r.destroy,e.exports.destroy=i,e.exports.pipeline=r.pipeline,e.exports.compose=r.compose,Object.defineProperty(r,"promises",{configurable:!0,enumerable:!0,get:()=>n}),e.exports.Stream=r.Stream,e.exports.default=e.exports}),jk=we((t,e)=>{me(),be(),ye(),typeof Object.create=="function"?e.exports=function(r,n){n&&(r.super_=n,r.prototype=Object.create(n.prototype,{constructor:{value:r,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(r,n){if(n){r.super_=n;var i=function(){};i.prototype=n.prototype,r.prototype=new i,r.prototype.constructor=r}}}),Nk=we((t,e)=>{me(),be(),ye();var{Buffer:r}=(vt(),Xe(bt)),n=Symbol.for("BufferList");function i(o){if(!(this instanceof i))return new i(o);i._init.call(this,o)}i._init=function(o){Object.defineProperty(this,n,{value:!0}),this._bufs=[],this.length=0,o&&this.append(o)},i.prototype._new=function(o){return new i(o)},i.prototype._offset=function(o){if(o===0)return[0,0];let s=0;for(let a=0;athis.length||o<0)return;let s=this._offset(o);return this._bufs[s[0]][s[1]]},i.prototype.slice=function(o,s){return typeof o=="number"&&o<0&&(o+=this.length),typeof s=="number"&&s<0&&(s+=this.length),this.copy(null,0,o,s)},i.prototype.copy=function(o,s,a,l){if((typeof a!="number"||a<0)&&(a=0),(typeof l!="number"||l>this.length)&&(l=this.length),a>=this.length||l<=0)return o||r.alloc(0);let c=!!o,u=this._offset(a),h=l-a,f=h,d=c&&s||0,y=u[1];if(a===0&&l===this.length){if(!c)return this._bufs.length===1?this._bufs[0]:r.concat(this._bufs,this.length);for(let g=0;gp)){this._bufs[g].copy(o,d,y,y+f),d+=p;break}this._bufs[g].copy(o,d,y),d+=p,f-=p,y&&(y=0)}return o.length>d?o.slice(0,d):o},i.prototype.shallowSlice=function(o,s){if(o=o||0,s=typeof s!="number"?this.length:s,o<0&&(o+=this.length),s<0&&(s+=this.length),o===s)return this._new();let a=this._offset(o),l=this._offset(s),c=this._bufs.slice(a[0],l[0]+1);return l[1]===0?c.pop():c[c.length-1]=c[c.length-1].slice(0,l[1]),a[1]!==0&&(c[0]=c[0].slice(a[1])),this._new(c)},i.prototype.toString=function(o,s,a){return this.slice(s,a).toString(o)},i.prototype.consume=function(o){if(o=Math.trunc(o),Number.isNaN(o)||o<=0)return this;for(;this._bufs.length;){if(!(o>=this._bufs[0].length)){this._bufs[0]=this._bufs[0].slice(o),this.length-=o;break}o-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift()}return this},i.prototype.duplicate=function(){let o=this._new();for(let s=0;s=0&&u=0&&uthis.length?this.length:s;let l=this._offset(s),c=l[0],u=l[1];for(;c=o.length){let f=h.indexOf(o,u);if(f!==-1)return this._reverseOffset([c,f]);u=h.length-o.length+1}else{let f=this._reverseOffset([c,u]);if(this._match(f,o))return f;u++}u=0}return-1},i.prototype._match=function(o,s){if(this.length-o{me(),be(),ye();var r=pi().Duplex,n=jk(),i=Nk();function o(s){if(!(this instanceof o))return new o(s);if(typeof s=="function"){this._callback=s;let a=(function(l){this._callback&&(this._callback(l),this._callback=null)}).bind(this);this.on("pipe",function(l){l.on("error",a)}),this.on("unpipe",function(l){l.removeListener("error",a)}),s=null}i._init.call(this,s),r.call(this)}n(o,r),Object.assign(o.prototype,i.prototype),o.prototype._new=function(s){return new o(s)},o.prototype._write=function(s,a,l){this._appendBuffer(s),typeof l=="function"&&l()},o.prototype._read=function(s){if(!this.length)return this.push(null);s=Math.min(s,this.length),this.push(this.slice(0,s)),this.consume(s)},o.prototype.end=function(s){r.prototype.end.call(this,s),this._callback&&(this._callback(null,this.slice()),this._callback=null)},o.prototype._destroy=function(s,a){this._bufs.length=0,this.length=0,a(s)},o.prototype._isBufferList=function(s){return s instanceof o||s instanceof i||o.isBufferList(s)},o.isBufferList=i.isBufferList,e.exports=o,e.exports.BufferListStream=o,e.exports.BufferList=i}),Bk=we((t,e)=>{me(),be(),ye(),e.exports=class{constructor(){this.cmd=null,this.retain=!1,this.qos=0,this.dup=!1,this.length=-1,this.topic=null,this.payload=null}}}),rw=we((t,e)=>{me(),be(),ye();var r=e.exports,{Buffer:n}=(vt(),Xe(bt));r.types={0:"reserved",1:"connect",2:"connack",3:"publish",4:"puback",5:"pubrec",6:"pubrel",7:"pubcomp",8:"subscribe",9:"suback",10:"unsubscribe",11:"unsuback",12:"pingreq",13:"pingresp",14:"disconnect",15:"auth"},r.requiredHeaderFlags={1:0,2:0,4:0,5:0,6:2,7:0,8:2,9:0,10:2,11:0,12:0,13:0,14:0,15:0},r.requiredHeaderFlagsErrors={};for(let o in r.requiredHeaderFlags){let s=r.requiredHeaderFlags[o];r.requiredHeaderFlagsErrors[o]="Invalid header flag bits, must be 0x"+s.toString(16)+" for "+r.types[o]+" packet"}r.codes={};for(let o in r.types){let s=r.types[o];r.codes[s]=o}r.CMD_SHIFT=4,r.CMD_MASK=240,r.DUP_MASK=8,r.QOS_MASK=3,r.QOS_SHIFT=1,r.RETAIN_MASK=1,r.VARBYTEINT_MASK=127,r.VARBYTEINT_FIN_MASK=128,r.VARBYTEINT_MAX=268435455,r.SESSIONPRESENT_MASK=1,r.SESSIONPRESENT_HEADER=n.from([r.SESSIONPRESENT_MASK]),r.CONNACK_HEADER=n.from([r.codes.connack<[0,1].map(a=>[0,1].map(l=>{let c=n.alloc(1);return c.writeUInt8(r.codes[o]<n.from([o])),r.EMPTY={pingreq:n.from([r.codes.pingreq<<4,0]),pingresp:n.from([r.codes.pingresp<<4,0]),disconnect:n.from([r.codes.disconnect<<4,0])},r.MQTT5_PUBACK_PUBREC_CODES={0:"Success",16:"No matching subscribers",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",144:"Topic Name invalid",145:"Packet identifier in use",151:"Quota exceeded",153:"Payload format invalid"},r.MQTT5_PUBREL_PUBCOMP_CODES={0:"Success",146:"Packet Identifier not found"},r.MQTT5_SUBACK_CODES={0:"Granted QoS 0",1:"Granted QoS 1",2:"Granted QoS 2",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",143:"Topic Filter invalid",145:"Packet Identifier in use",151:"Quota exceeded",158:"Shared Subscriptions not supported",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},r.MQTT5_UNSUBACK_CODES={0:"Success",17:"No subscription existed",128:"Unspecified error",131:"Implementation specific error",135:"Not authorized",143:"Topic Filter invalid",145:"Packet Identifier in use"},r.MQTT5_DISCONNECT_CODES={0:"Normal disconnection",4:"Disconnect with Will Message",128:"Unspecified error",129:"Malformed Packet",130:"Protocol Error",131:"Implementation specific error",135:"Not authorized",137:"Server busy",139:"Server shutting down",141:"Keep Alive timeout",142:"Session taken over",143:"Topic Filter invalid",144:"Topic Name invalid",147:"Receive Maximum exceeded",148:"Topic Alias invalid",149:"Packet too large",150:"Message rate too high",151:"Quota exceeded",152:"Administrative action",153:"Payload format invalid",154:"Retain not supported",155:"QoS not supported",156:"Use another server",157:"Server moved",158:"Shared Subscriptions not supported",159:"Connection rate exceeded",160:"Maximum connect time",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},r.MQTT5_AUTH_CODES={0:"Success",24:"Continue authentication",25:"Re-authenticate"}}),Fk=we((t,e)=>{me(),be(),ye();var r=1e3,n=6e4,i=60*n,o=24*i,s=7*o,a=365.25*o;function l(c,u,h,f){var d=u>=1.5*h;return Math.round(c/h)+" "+f+(d?"s":"")}e.exports=function(c,u){u=u||{};var h=typeof c;if(h==="string"&&c.length>0)return function(f){if(f=String(f),!(f.length>100)){var d=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(f);if(d){var y=parseFloat(d[1]);switch((d[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return y*a;case"weeks":case"week":case"w":return y*s;case"days":case"day":case"d":return y*o;case"hours":case"hour":case"hrs":case"hr":case"h":return y*i;case"minutes":case"minute":case"mins":case"min":case"m":return y*n;case"seconds":case"second":case"secs":case"sec":case"s":return y*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return y;default:return}}}}(c);if(h==="number"&&isFinite(c))return u.long?function(f){var d=Math.abs(f);return d>=o?l(f,d,o,"day"):d>=i?l(f,d,i,"hour"):d>=n?l(f,d,n,"minute"):d>=r?l(f,d,r,"second"):f+" ms"}(c):function(f){var d=Math.abs(f);return d>=o?Math.round(f/o)+"d":d>=i?Math.round(f/i)+"h":d>=n?Math.round(f/n)+"m":d>=r?Math.round(f/r)+"s":f+"ms"}(c);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(c))}}),Wk=we((t,e)=>{me(),be(),ye(),e.exports=function(r){function n(s){let a,l,c,u=null;function h(...f){if(!h.enabled)return;let d=h,y=Number(new Date),g=y-(a||y);d.diff=g,d.prev=a,d.curr=y,a=y,f[0]=n.coerce(f[0]),typeof f[0]!="string"&&f.unshift("%O");let p=0;f[0]=f[0].replace(/%([a-zA-Z%])/g,(b,v)=>{if(b==="%%")return"%";p++;let S=n.formatters[v];if(typeof S=="function"){let k=f[p];b=S.call(d,k),f.splice(p,1),p--}return b}),n.formatArgs.call(d,f),(d.log||n.log).apply(d,f)}return h.namespace=s,h.useColors=n.useColors(),h.color=n.selectColor(s),h.extend=i,h.destroy=n.destroy,Object.defineProperty(h,"enabled",{enumerable:!0,configurable:!1,get:()=>u!==null?u:(l!==n.namespaces&&(l=n.namespaces,c=n.enabled(s)),c),set:f=>{u=f}}),typeof n.init=="function"&&n.init(h),h}function i(s,a){let l=n(this.namespace+(typeof a>"u"?":":a)+s);return l.log=this.log,l}function o(s,a){let l=0,c=0,u=-1,h=0;for(;l"-"+a)].join(",");return n.enable(""),s},n.enable=function(s){n.save(s),n.namespaces=s,n.names=[],n.skips=[];let a=(typeof s=="string"?s:"").trim().replace(/\s+/g,",").split(",").filter(Boolean);for(let l of a)l[0]==="-"?n.skips.push(l.slice(1)):n.names.push(l)},n.enabled=function(s){for(let a of n.skips)if(o(s,a))return!1;for(let a of n.names)if(o(s,a))return!0;return!1},n.humanize=Fk(),n.destroy=function(){},Object.keys(r).forEach(s=>{n[s]=r[s]}),n.names=[],n.skips=[],n.formatters={},n.selectColor=function(s){let a=0;for(let l=0;l{me(),be(),ye(),t.formatArgs=function(n){if(n[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+n[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;let i="color: "+this.color;n.splice(1,0,i,"color: inherit");let o=0,s=0;n[0].replace(/%[a-zA-Z%]/g,a=>{a!=="%%"&&(o++,a==="%c"&&(s=o))}),n.splice(s,0,i)},t.save=function(n){try{n?t.storage.setItem("debug",n):t.storage.removeItem("debug")}catch{}},t.load=function(){let n;try{n=t.storage.getItem("debug")||t.storage.getItem("DEBUG")}catch{}return!n&&typeof $e<"u"&&"env"in $e&&(n=$e.env.DEBUG),n},t.useColors=function(){if(typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs))return!0;if(typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;let n;return typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&(n=navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/))&&parseInt(n[1],10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch{}}(),t.destroy=(()=>{let n=!1;return()=>{n||(n=!0)}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=Wk()(t);var{formatters:r}=e.exports;r.j=function(n){try{return JSON.stringify(n)}catch(i){return"[UnexpectedJSONParseError]: "+i.message}}}),Uk=we((t,e)=>{me(),be(),ye();var r=Lk(),{EventEmitter:n}=(fi(),Xe(Nn)),i=Bk(),o=rw(),s=qr()("mqtt-packet:parser");e.exports=class kh extends n{constructor(){super(),this.parser=this.constructor.parser}static parser(l){return this instanceof kh?(this.settings=l||{},this._states=["_parseHeader","_parseLength","_parsePayload","_newPacket"],this._resetState(),this):new kh().parser(l)}_resetState(){s("_resetState: resetting packet, error, _list, and _stateCounter"),this.packet=new i,this.error=null,this._list=r(),this._stateCounter=0}parse(l){for(this.error&&this._resetState(),this._list.append(l),s("parse: current state: %s",this._states[this._stateCounter]);(this.packet.length!==-1||this._list.length>0)&&this[this._states[this._stateCounter]]()&&!this.error;)this._stateCounter++,s("parse: state complete. _stateCounter is now: %d",this._stateCounter),s("parse: packet.length: %d, buffer list length: %d",this.packet.length,this._list.length),this._stateCounter>=this._states.length&&(this._stateCounter=0);return s("parse: exited while loop. packet: %d, buffer list length: %d",this.packet.length,this._list.length),this._list.length}_parseHeader(){let l=this._list.readUInt8(0),c=l>>o.CMD_SHIFT;this.packet.cmd=o.types[c];let u=15&l,h=o.requiredHeaderFlags[c];return h!=null&&u!==h?this._emitError(new Error(o.requiredHeaderFlagsErrors[c])):(this.packet.retain=(l&o.RETAIN_MASK)!==0,this.packet.qos=l>>o.QOS_SHIFT&o.QOS_MASK,this.packet.qos>2?this._emitError(new Error("Packet must not have both QoS bits set to 1")):(this.packet.dup=(l&o.DUP_MASK)!==0,s("_parseHeader: packet: %o",this.packet),this._list.consume(1),!0))}_parseLength(){let l=this._parseVarByteNum(!0);return l&&(this.packet.length=l.value,this._list.consume(l.bytes)),s("_parseLength %d",l.value),!!l}_parsePayload(){s("_parsePayload: payload %O",this._list);let l=!1;if(this.packet.length===0||this._list.length>=this.packet.length){switch(this._pos=0,this.packet.cmd){case"connect":this._parseConnect();break;case"connack":this._parseConnack();break;case"publish":this._parsePublish();break;case"puback":case"pubrec":case"pubrel":case"pubcomp":this._parseConfirmation();break;case"subscribe":this._parseSubscribe();break;case"suback":this._parseSuback();break;case"unsubscribe":this._parseUnsubscribe();break;case"unsuback":this._parseUnsuback();break;case"pingreq":case"pingresp":break;case"disconnect":this._parseDisconnect();break;case"auth":this._parseAuth();break;default:this._emitError(new Error("Not supported"))}l=!0}return s("_parsePayload complete result: %s",l),l}_parseConnect(){s("_parseConnect");let l,c,u,h,f={},d=this.packet,y=this._parseString();if(y===null)return this._emitError(new Error("Cannot parse protocolId"));if(y!=="MQTT"&&y!=="MQIsdp")return this._emitError(new Error("Invalid protocolId"));if(d.protocolId=y,this._pos>=this._list.length)return this._emitError(new Error("Packet too short"));if(d.protocolVersion=this._list.readUInt8(this._pos),d.protocolVersion>=128&&(d.bridgeMode=!0,d.protocolVersion=d.protocolVersion-128),d.protocolVersion!==3&&d.protocolVersion!==4&&d.protocolVersion!==5)return this._emitError(new Error("Invalid protocol version"));if(this._pos++,this._pos>=this._list.length)return this._emitError(new Error("Packet too short"));if(1&this._list.readUInt8(this._pos))return this._emitError(new Error("Connect flag bit 0 must be 0, but got 1"));f.username=this._list.readUInt8(this._pos)&o.USERNAME_MASK,f.password=this._list.readUInt8(this._pos)&o.PASSWORD_MASK,f.will=this._list.readUInt8(this._pos)&o.WILL_FLAG_MASK;let g=!!(this._list.readUInt8(this._pos)&o.WILL_RETAIN_MASK),p=(this._list.readUInt8(this._pos)&o.WILL_QOS_MASK)>>o.WILL_QOS_SHIFT;if(f.will)d.will={},d.will.retain=g,d.will.qos=p;else{if(g)return this._emitError(new Error("Will Retain Flag must be set to zero when Will Flag is set to 0"));if(p)return this._emitError(new Error("Will QoS must be set to zero when Will Flag is set to 0"))}if(d.clean=(this._list.readUInt8(this._pos)&o.CLEAN_SESSION_MASK)!==0,this._pos++,d.keepalive=this._parseNum(),d.keepalive===-1)return this._emitError(new Error("Packet too short"));if(d.protocolVersion===5){let v=this._parseProperties();Object.getOwnPropertyNames(v).length&&(d.properties=v)}let b=this._parseString();if(b===null)return this._emitError(new Error("Packet too short"));if(d.clientId=b,s("_parseConnect: packet.clientId: %s",d.clientId),f.will){if(d.protocolVersion===5){let v=this._parseProperties();Object.getOwnPropertyNames(v).length&&(d.will.properties=v)}if(l=this._parseString(),l===null)return this._emitError(new Error("Cannot parse will topic"));if(d.will.topic=l,s("_parseConnect: packet.will.topic: %s",d.will.topic),c=this._parseBuffer(),c===null)return this._emitError(new Error("Cannot parse will payload"));d.will.payload=c,s("_parseConnect: packet.will.paylaod: %s",d.will.payload)}if(f.username){if(h=this._parseString(),h===null)return this._emitError(new Error("Cannot parse username"));d.username=h,s("_parseConnect: packet.username: %s",d.username)}if(f.password){if(u=this._parseBuffer(),u===null)return this._emitError(new Error("Cannot parse password"));d.password=u}return this.settings=d,s("_parseConnect: complete"),d}_parseConnack(){s("_parseConnack");let l=this.packet;if(this._list.length<1)return null;let c=this._list.readUInt8(this._pos++);if(c>1)return this._emitError(new Error("Invalid connack flags, bits 7-1 must be set to 0"));if(l.sessionPresent=!!(c&o.SESSIONPRESENT_MASK),this.settings.protocolVersion===5)this._list.length>=2?l.reasonCode=this._list.readUInt8(this._pos++):l.reasonCode=0;else{if(this._list.length<2)return null;l.returnCode=this._list.readUInt8(this._pos++)}if(l.returnCode===-1||l.reasonCode===-1)return this._emitError(new Error("Cannot parse return code"));if(this.settings.protocolVersion===5){let u=this._parseProperties();Object.getOwnPropertyNames(u).length&&(l.properties=u)}s("_parseConnack: complete")}_parsePublish(){s("_parsePublish");let l=this.packet;if(l.topic=this._parseString(),l.topic===null)return this._emitError(new Error("Cannot parse topic"));if(!(l.qos>0)||this._parseMessageId()){if(this.settings.protocolVersion===5){let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(l.properties=c)}l.payload=this._list.slice(this._pos,l.length),s("_parsePublish: payload from buffer list: %o",l.payload)}}_parseSubscribe(){s("_parseSubscribe");let l,c,u,h,f,d,y,g=this.packet;if(g.subscriptions=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let p=this._parseProperties();Object.getOwnPropertyNames(p).length&&(g.properties=p)}if(g.length<=0)return this._emitError(new Error("Malformed subscribe, no payload specified"));for(;this._pos=g.length)return this._emitError(new Error("Malformed Subscribe Payload"));if(c=this._parseByte(),this.settings.protocolVersion===5){if(192&c)return this._emitError(new Error("Invalid subscribe topic flag bits, bits 7-6 must be 0"))}else if(252&c)return this._emitError(new Error("Invalid subscribe topic flag bits, bits 7-2 must be 0"));if(u=c&o.SUBSCRIBE_OPTIONS_QOS_MASK,u>2)return this._emitError(new Error("Invalid subscribe QoS, must be <= 2"));if(d=(c>>o.SUBSCRIBE_OPTIONS_NL_SHIFT&o.SUBSCRIBE_OPTIONS_NL_MASK)!==0,f=(c>>o.SUBSCRIBE_OPTIONS_RAP_SHIFT&o.SUBSCRIBE_OPTIONS_RAP_MASK)!==0,h=c>>o.SUBSCRIBE_OPTIONS_RH_SHIFT&o.SUBSCRIBE_OPTIONS_RH_MASK,h>2)return this._emitError(new Error("Invalid retain handling, must be <= 2"));y={topic:l,qos:u},this.settings.protocolVersion===5?(y.nl=d,y.rap=f,y.rh=h):this.settings.bridgeMode&&(y.rh=0,y.rap=!0,y.nl=!0),s("_parseSubscribe: push subscription `%s` to subscription",y),g.subscriptions.push(y)}}}_parseSuback(){s("_parseSuback");let l=this.packet;if(this.packet.granted=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(l.properties=c)}if(l.length<=0)return this._emitError(new Error("Malformed suback, no payload specified"));for(;this._pos2&&c!==128)return this._emitError(new Error("Invalid suback QoS, must be 0, 1, 2 or 128"));this.packet.granted.push(c)}}}_parseUnsubscribe(){s("_parseUnsubscribe");let l=this.packet;if(l.unsubscriptions=[],this._parseMessageId()){if(this.settings.protocolVersion===5){let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(l.properties=c)}if(l.length<=0)return this._emitError(new Error("Malformed unsubscribe, no payload specified"));for(;this._pos2){switch(l.reasonCode=this._parseByte(),this.packet.cmd){case"puback":case"pubrec":if(!o.MQTT5_PUBACK_PUBREC_CODES[l.reasonCode])return this._emitError(new Error("Invalid "+this.packet.cmd+" reason code"));break;case"pubrel":case"pubcomp":if(!o.MQTT5_PUBREL_PUBCOMP_CODES[l.reasonCode])return this._emitError(new Error("Invalid "+this.packet.cmd+" reason code"))}s("_parseConfirmation: packet.reasonCode `%d`",l.reasonCode)}else l.reasonCode=0;if(l.length>3){let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(l.properties=c)}}return!0}_parseDisconnect(){let l=this.packet;if(s("_parseDisconnect"),this.settings.protocolVersion===5){this._list.length>0?(l.reasonCode=this._parseByte(),o.MQTT5_DISCONNECT_CODES[l.reasonCode]||this._emitError(new Error("Invalid disconnect reason code"))):l.reasonCode=0;let c=this._parseProperties();Object.getOwnPropertyNames(c).length&&(l.properties=c)}return s("_parseDisconnect result: true"),!0}_parseAuth(){s("_parseAuth");let l=this.packet;if(this.settings.protocolVersion!==5)return this._emitError(new Error("Not supported auth packet for this version MQTT"));if(l.reasonCode=this._parseByte(),!o.MQTT5_AUTH_CODES[l.reasonCode])return this._emitError(new Error("Invalid auth reason code"));let c=this._parseProperties();return Object.getOwnPropertyNames(c).length&&(l.properties=c),s("_parseAuth: result: true"),!0}_parseMessageId(){let l=this.packet;return l.messageId=this._parseNum(),l.messageId===null?(this._emitError(new Error("Cannot parse messageId")),!1):(s("_parseMessageId: packet.messageId %d",l.messageId),!0)}_parseString(l){let c=this._parseNum(),u=c+this._pos;if(c===-1||u>this._list.length||u>this.packet.length)return null;let h=this._list.toString("utf8",this._pos,u);return this._pos+=c,s("_parseString: result: %s",h),h}_parseStringPair(){return s("_parseStringPair"),{name:this._parseString(),value:this._parseString()}}_parseBuffer(){let l=this._parseNum(),c=l+this._pos;if(l===-1||c>this._list.length||c>this.packet.length)return null;let u=this._list.slice(this._pos,c);return this._pos+=l,s("_parseBuffer: result: %o",u),u}_parseNum(){if(this._list.length-this._pos<2)return-1;let l=this._list.readUInt16BE(this._pos);return this._pos+=2,s("_parseNum: result: %s",l),l}_parse4ByteNum(){if(this._list.length-this._pos<4)return-1;let l=this._list.readUInt32BE(this._pos);return this._pos+=4,s("_parse4ByteNum: result: %s",l),l}_parseVarByteNum(l){s("_parseVarByteNum");let c,u=0,h=1,f=0,d=!1,y=this._pos?this._pos:0;for(;u<4&&y+u=u&&this._emitError(new Error("Invalid variable byte integer")),y&&(this._pos+=u),d=!!d&&(l?{bytes:u,value:f}:f),s("_parseVarByteNum: result: %o",d),d}_parseByte(){let l;return this._pos{me(),be(),ye();var{Buffer:r}=(vt(),Xe(bt)),n={},i=r.isBuffer(r.from([1,2]).subarray(0,1));function o(s){let a=r.allocUnsafe(2);return a.writeUInt8(s>>8,0),a.writeUInt8(255&s,1),a}e.exports={cache:n,generateCache:function(){for(let s=0;s<65536;s++)n[s]=o(s)},generateNumber:o,genBufVariableByteInt:function(s){let a=0,l=0,c=r.allocUnsafe(4);do a=s%128|0,(s=s/128|0)>0&&(a|=128),c.writeUInt8(a,l++);while(s>0&&l<4);return s>0&&(l=0),i?c.subarray(0,l):c.slice(0,l)},generate4ByteBuffer:function(s){let a=r.allocUnsafe(4);return a.writeUInt32BE(s,0),a}}}),$k=we((t,e)=>{me(),be(),ye(),typeof $e>"u"||!$e.version||$e.version.indexOf("v0.")===0||$e.version.indexOf("v1.")===0&&$e.version.indexOf("v1.8.")!==0?e.exports={nextTick:function(r,n,i,o){if(typeof r!="function")throw new TypeError('"callback" argument must be a function');var s,a,l=arguments.length;switch(l){case 0:case 1:return $e.nextTick(r);case 2:return $e.nextTick(function(){r.call(null,n)});case 3:return $e.nextTick(function(){r.call(null,n,i)});case 4:return $e.nextTick(function(){r.call(null,n,i,o)});default:for(s=new Array(l-1),a=0;a{me(),be(),ye();var r=rw(),{Buffer:n}=(vt(),Xe(bt)),i=n.allocUnsafe(0),o=n.from([0]),s=Vk(),a=$k().nextTick,l=qr()("mqtt-packet:writeToStream"),c=s.cache,u=s.generateNumber,h=s.generateCache,f=s.genBufVariableByteInt,d=s.generate4ByteBuffer,y=A,g=!0;function p(z,q,B){switch(l("generate called"),q.cork&&(q.cork(),a(b,q)),g&&(g=!1,h()),l("generate: packet.cmd: %s",z.cmd),z.cmd){case"connect":return function(ne,H){let Q=ne||{},F=Q.protocolId||"MQTT",V=Q.protocolVersion||4,Z=Q.will,$=Q.clean,se=Q.keepalive||0,le=Q.clientId||"",ue=Q.username,Y=Q.password,ie=Q.properties;$===void 0&&($=!0);let oe,j,X=0;if(typeof F!="string"&&!n.isBuffer(F))return H.destroy(new Error("Invalid protocolId")),!1;if(X+=F.length+2,V!==3&&V!==4&&V!==5)return H.destroy(new Error("Invalid protocol version")),!1;if(X+=1,(typeof le=="string"||n.isBuffer(le))&&(le||V>=4)&&(le||$))X+=n.byteLength(le)+2;else{if(V<4)return H.destroy(new Error("clientId must be supplied before 3.1.1")),!1;if(1*$==0)return H.destroy(new Error("clientId must be given if cleanSession set to 0")),!1}if(typeof se!="number"||se<0||se>65535||se%1!=0)return H.destroy(new Error("Invalid keepalive")),!1;if(X+=2,X+=1,V===5){if(oe=_(H,ie),!oe)return!1;X+=oe.length}if(Z){if(typeof Z!="object")return H.destroy(new Error("Invalid will")),!1;if(!Z.topic||typeof Z.topic!="string")return H.destroy(new Error("Invalid will topic")),!1;if(X+=n.byteLength(Z.topic)+2,X+=2,Z.payload){if(!(Z.payload.length>=0))return H.destroy(new Error("Invalid will payload")),!1;typeof Z.payload=="string"?X+=n.byteLength(Z.payload):X+=Z.payload.length}if(j={},V===5){if(j=_(H,Z.properties),!j)return!1;X+=j.length}}let R=!1;if(ue!=null){if(!U(ue))return H.destroy(new Error("Invalid username")),!1;R=!0,X+=n.byteLength(ue)+2}if(Y!=null){if(!R)return H.destroy(new Error("Username is required to use password")),!1;if(!U(Y))return H.destroy(new Error("Invalid password")),!1;X+=G(Y)+2}H.write(r.CONNECT_HEADER),S(H,X),T(H,F),Q.bridgeMode&&(V+=128),H.write(V===131?r.VERSION131:V===132?r.VERSION132:V===4?r.VERSION4:V===5?r.VERSION5:r.VERSION3);let te=0;return te|=ue!=null?r.USERNAME_MASK:0,te|=Y!=null?r.PASSWORD_MASK:0,te|=Z&&Z.retain?r.WILL_RETAIN_MASK:0,te|=Z&&Z.qos?Z.qos<0&&y(H,ue),oe==null||oe.write(),l("publish: payload: %o",le),H.write(le)}(z,q,B);case"puback":case"pubrec":case"pubrel":case"pubcomp":return function(ne,H,Q){let F=Q?Q.protocolVersion:4,V=ne||{},Z=V.cmd||"puback",$=V.messageId,se=V.dup&&Z==="pubrel"?r.DUP_MASK:0,le=0,ue=V.reasonCode,Y=V.properties,ie=F===5?3:2;if(Z==="pubrel"&&(le=1),typeof $!="number")return H.destroy(new Error("Invalid messageId")),!1;let oe=null;if(F===5&&typeof Y=="object"){if(oe=x(H,Y,Q,ie),!oe)return!1;ie+=oe.length}return H.write(r.ACKS[Z][le][se][0]),ie===3&&(ie+=ue!==0?1:-1),S(H,ie),y(H,$),F===5&&ie!==2&&H.write(n.from([ue])),oe!==null?oe.write():ie===4&&H.write(n.from([0])),!0}(z,q,B);case"subscribe":return function(ne,H,Q){l("subscribe: packet: ");let F=Q?Q.protocolVersion:4,V=ne||{},Z=V.dup?r.DUP_MASK:0,$=V.messageId,se=V.subscriptions,le=V.properties,ue=0;if(typeof $!="number")return H.destroy(new Error("Invalid messageId")),!1;ue+=2;let Y=null;if(F===5){if(Y=_(H,le),!Y)return!1;ue+=Y.length}if(typeof se!="object"||!se.length)return H.destroy(new Error("Invalid subscriptions")),!1;for(let oe=0;oe2)return H.destroy(new Error("Invalid subscriptions - invalid Retain Handling")),!1}ue+=n.byteLength(j)+2+1}l("subscribe: writing to stream: %o",r.SUBSCRIBE_HEADER),H.write(r.SUBSCRIBE_HEADER[1][Z?1:0][0]),S(H,ue),y(H,$),Y!==null&&Y.write();let ie=!0;for(let oe of se){let j,X=oe.topic,R=oe.qos,te=+oe.nl,m=+oe.rap,w=oe.rh;k(H,X),j=r.SUBSCRIBE_OPTIONS_QOS[R],F===5&&(j|=te?r.SUBSCRIBE_OPTIONS_NL:0,j|=m?r.SUBSCRIBE_OPTIONS_RAP:0,j|=w?r.SUBSCRIBE_OPTIONS_RH[w]:0),ie=H.write(n.from([j]))}return ie}(z,q,B);case"suback":return function(ne,H,Q){let F=Q?Q.protocolVersion:4,V=ne||{},Z=V.messageId,$=V.granted,se=V.properties,le=0;if(typeof Z!="number")return H.destroy(new Error("Invalid messageId")),!1;if(le+=2,typeof $!="object"||!$.length)return H.destroy(new Error("Invalid qos vector")),!1;for(let Y=0;Y<$.length;Y+=1){if(typeof $[Y]!="number")return H.destroy(new Error("Invalid qos vector")),!1;le+=1}let ue=null;if(F===5){if(ue=x(H,se,Q,le),!ue)return!1;le+=ue.length}return H.write(r.SUBACK_HEADER),S(H,le),y(H,Z),ue!==null&&ue.write(),H.write(n.from($))}(z,q,B);case"unsubscribe":return function(ne,H,Q){let F=Q?Q.protocolVersion:4,V=ne||{},Z=V.messageId,$=V.dup?r.DUP_MASK:0,se=V.unsubscriptions,le=V.properties,ue=0;if(typeof Z!="number")return H.destroy(new Error("Invalid messageId")),!1;if(ue+=2,typeof se!="object"||!se.length)return H.destroy(new Error("Invalid unsubscriptions")),!1;for(let oe=0;oey===A,set(z){z?((!c||Object.keys(c).length===0)&&(g=!0),y=A):(g=!1,y=I)}});var v={};function S(z,q){if(q>r.VARBYTEINT_MAX)return z.destroy(new Error(`Invalid variable byte integer: ${q}`)),!1;let B=v[q];return B||(B=f(q),q<16384&&(v[q]=B)),l("writeVarByteInt: writing to stream: %o",B),z.write(B)}function k(z,q){let B=n.byteLength(q);return y(z,B),l("writeString: %s",q),z.write(q,"utf8")}function M(z,q,B){k(z,q),k(z,B)}function A(z,q){return l("writeNumberCached: number: %d",q),l("writeNumberCached: %o",c[q]),z.write(c[q])}function I(z,q){let B=u(q);return l("writeNumberGenerated: %o",B),z.write(B)}function T(z,q){typeof q=="string"?k(z,q):q?(y(z,q.length),z.write(q)):y(z,0)}function _(z,q){if(typeof q!="object"||q.length!=null)return{length:1,write(){N(z,{},0)}};let B=0;function ne(H,Q){let F=0;switch(r.propertiesTypes[H]){case"byte":if(typeof Q!="boolean")return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=2;break;case"int8":if(typeof Q!="number"||Q<0||Q>255)return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=2;break;case"binary":if(Q&&Q===null)return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=1+n.byteLength(Q)+2;break;case"int16":if(typeof Q!="number"||Q<0||Q>65535)return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=3;break;case"int32":if(typeof Q!="number"||Q<0||Q>4294967295)return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=5;break;case"var":if(typeof Q!="number"||Q<0||Q>268435455)return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=1+n.byteLength(f(Q));break;case"string":if(typeof Q!="string")return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=3+n.byteLength(Q.toString());break;case"pair":if(typeof Q!="object")return z.destroy(new Error(`Invalid ${H}: ${Q}`)),!1;F+=Object.getOwnPropertyNames(Q).reduce((V,Z)=>{let $=Q[Z];return Array.isArray($)?V+=$.reduce((se,le)=>se+=3+n.byteLength(Z.toString())+2+n.byteLength(le.toString()),0):V+=3+n.byteLength(Z.toString())+2+n.byteLength(Q[Z].toString()),V},0);break;default:return z.destroy(new Error(`Invalid property ${H}: ${Q}`)),!1}return F}if(q)for(let H in q){let Q=0,F=0,V=q[H];if(V!==void 0){if(Array.isArray(V))for(let Z=0;ZQ;){let V=H.shift();if(!V||!q[V])return!1;delete q[V],F=_(z,q)}return F}function O(z,q,B){switch(r.propertiesTypes[q]){case"byte":z.write(n.from([r.properties[q]])),z.write(n.from([+B]));break;case"int8":z.write(n.from([r.properties[q]])),z.write(n.from([B]));break;case"binary":z.write(n.from([r.properties[q]])),T(z,B);break;case"int16":z.write(n.from([r.properties[q]])),y(z,B);break;case"int32":z.write(n.from([r.properties[q]])),function(ne,H){let Q=d(H);l("write4ByteNumber: %o",Q),ne.write(Q)}(z,B);break;case"var":z.write(n.from([r.properties[q]])),S(z,B);break;case"string":z.write(n.from([r.properties[q]])),k(z,B);break;case"pair":Object.getOwnPropertyNames(B).forEach(ne=>{let H=B[ne];Array.isArray(H)?H.forEach(Q=>{z.write(n.from([r.properties[q]])),M(z,ne.toString(),Q.toString())}):(z.write(n.from([r.properties[q]])),M(z,ne.toString(),H.toString()))});break;default:return z.destroy(new Error(`Invalid property ${q} value: ${B}`)),!1}}function N(z,q,B){S(z,B);for(let ne in q)if(Object.prototype.hasOwnProperty.call(q,ne)&&q[ne]!=null){let H=q[ne];if(Array.isArray(H))for(let Q=0;Q{me(),be(),ye();var r=nw(),{EventEmitter:n}=(fi(),Xe(Nn)),{Buffer:i}=(vt(),Xe(bt)),o=class extends n{constructor(){super(),this._array=new Array(20),this._i=0}write(s){return this._array[this._i++]=s,!0}concat(){let s,a=0,l=new Array(this._array.length),c=this._array,u=0;for(s=0;s{me(),be(),ye(),t.parser=Uk().parser,t.generate=zk(),t.writeToStream=nw()}),qk=we((t,e)=>{function r(n){return n instanceof ol?ol.from(n):new n.constructor(n.buffer.slice(),n.byteOffset,n.length)}me(),be(),ye(),e.exports=function(n){if((n=n||{}).circles)return function(a){let l=[],c=[],u=new Map;if(u.set(Date,g=>new Date(g)),u.set(Map,(g,p)=>new Map(f(Array.from(g),p))),u.set(Set,(g,p)=>new Set(f(Array.from(g),p))),a.constructorHandlers)for(let g of a.constructorHandlers)u.set(g[0],g[1]);let h=null;return a.proto?y:d;function f(g,p){let b=Object.keys(g),v=new Array(b.length);for(let S=0;Snew Date(a)),i.set(Map,(a,l)=>new Map(s(Array.from(a),l))),i.set(Set,(a,l)=>new Set(s(Array.from(a),l))),n.constructorHandlers)for(let a of n.constructorHandlers)i.set(a[0],a[1]);let o=null;return n.proto?function a(l){if(typeof l!="object"||l===null)return l;if(Array.isArray(l))return s(l,a);if(l.constructor!==Object&&(o=i.get(l.constructor)))return o(l,a);let c={};for(let u in l){let h=l[u];typeof h!="object"||h===null?c[u]=h:h.constructor!==Object&&(o=i.get(h.constructor))?c[u]=o(h,a):ArrayBuffer.isView(h)?c[u]=r(h):c[u]=a(h)}return c}:function a(l){if(typeof l!="object"||l===null)return l;if(Array.isArray(l))return s(l,a);if(l.constructor!==Object&&(o=i.get(l.constructor)))return o(l,a);let c={};for(let u in l){if(Object.hasOwnProperty.call(l,u)===!1)continue;let h=l[u];typeof h!="object"||h===null?c[u]=h:h.constructor!==Object&&(o=i.get(h.constructor))?c[u]=o(h,a):ArrayBuffer.isView(h)?c[u]=r(h):c[u]=a(h)}return c};function s(a,l){let c=Object.keys(a),u=new Array(c.length);for(let h=0;h{me(),be(),ye(),e.exports=qk()()}),Kk=we(t=>{function e(r){let n=r.split("/");for(let i=0;i{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e=pi(),r={objectMode:!0},n={clean:!0};t.default=class{constructor(i){_e(this,"options");_e(this,"_inflights");this.options=i||{},this.options={...n,...i},this._inflights=new Map}put(i,o){return this._inflights.set(i.messageId,i),o&&o(),this}createStream(){let i=new e.Readable(r),o=[],s=!1,a=0;return this._inflights.forEach((l,c)=>{o.push(l)}),i._read=()=>{!s&&a{if(!s)return s=!0,setTimeout(()=>{i.emit("close")},0),i},i}del(i,o){let s=this._inflights.get(i.messageId);return s?(this._inflights.delete(i.messageId),o(null,s)):o&&o(new Error("missing packet")),this}get(i,o){let s=this._inflights.get(i.messageId);return s?o(null,s):o&&o(new Error("missing packet")),this}close(i){this.options.clean&&(this._inflights=null),i&&i()}}}),Gk=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e=[0,16,128,131,135,144,145,151,153];t.default=(r,n,i)=>{r.log("handlePublish: packet %o",n),i=typeof i<"u"?i:r.noop;let o=n.topic.toString(),s=n.payload,{qos:a}=n,{messageId:l}=n,{options:c}=r;if(r.options.protocolVersion===5){let u;if(n.properties&&(u=n.properties.topicAlias),typeof u<"u")if(o.length===0){if(!(u>0&&u<=65535))return r.log("handlePublish :: topic alias out of range. alias: %d",u),void r.emit("error",new Error("Received Topic Alias is out of range"));{let h=r.topicAliasRecv.getTopicByAlias(u);if(!h)return r.log("handlePublish :: unregistered topic alias. alias: %d",u),void r.emit("error",new Error("Received unregistered Topic Alias"));o=h,r.log("handlePublish :: topic complemented by alias. topic: %s - alias: %d",o,u)}}else{if(!r.topicAliasRecv.put(o,u))return r.log("handlePublish :: topic alias out of range. alias: %d",u),void r.emit("error",new Error("Received Topic Alias is out of range"));r.log("handlePublish :: registered topic: %s - alias: %d",o,u)}}switch(r.log("handlePublish: qos %d",a),a){case 2:c.customHandleAcks(o,s,n,(u,h)=>(typeof u=="number"&&(h=u,u=null),u?r.emit("error",u):e.indexOf(h)===-1?r.emit("error",new Error("Wrong reason code for pubrec")):void(h?r._sendPacket({cmd:"pubrec",messageId:l,reasonCode:h},i):r.incomingStore.put(n,()=>{r._sendPacket({cmd:"pubrec",messageId:l},i)}))));break;case 1:c.customHandleAcks(o,s,n,(u,h)=>(typeof u=="number"&&(h=u,u=null),u?r.emit("error",u):e.indexOf(h)===-1?r.emit("error",new Error("Wrong reason code for puback")):(h||r.emit("message",o,s,n),void r.handleMessage(n,f=>{if(f)return i&&i(f);r._sendPacket({cmd:"puback",messageId:l,reasonCode:h},i)}))));break;case 0:r.emit("message",o,s,n),r.handleMessage(n,i);break;default:r.log("handlePublish: unknown QoS. Doing nothing.")}}}),Qk=we((t,e)=>{e.exports={version:"5.14.0"}}),oo=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.MQTTJS_VERSION=t.nextTick=t.ErrorWithSubackPacket=t.ErrorWithReasonCode=void 0,t.applyMixin=function(n,i,o=!1){let s=[i];for(;;){let a=s[0],l=Object.getPrototypeOf(a);if(!(l!=null&&l.prototype))break;s.unshift(l)}for(let a of s)for(let l of Object.getOwnPropertyNames(a.prototype))(o||l!=="constructor")&&Object.defineProperty(n.prototype,l,Object.getOwnPropertyDescriptor(a.prototype,l)??Object.create(null))};var e=class ow extends Error{constructor(o,s){super(o);_e(this,"code");this.code=s,Object.setPrototypeOf(this,ow.prototype),Object.getPrototypeOf(this).name="ErrorWithReasonCode"}};t.ErrorWithReasonCode=e;var r=class sw extends Error{constructor(o,s){super(o);_e(this,"packet");this.packet=s,Object.setPrototypeOf(this,sw.prototype),Object.getPrototypeOf(this).name="ErrorWithSubackPacket"}};t.ErrorWithSubackPacket=r,t.nextTick=typeof($e==null?void 0:$e.nextTick)=="function"?$e.nextTick:n=>{setTimeout(n,0)},t.MQTTJS_VERSION=Qk().version}),zl=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.ReasonCodes=void 0;var e=oo();t.ReasonCodes={0:"",1:"Unacceptable protocol version",2:"Identifier rejected",3:"Server unavailable",4:"Bad username or password",5:"Not authorized",16:"No matching subscribers",17:"No subscription existed",128:"Unspecified error",129:"Malformed Packet",130:"Protocol Error",131:"Implementation specific error",132:"Unsupported Protocol Version",133:"Client Identifier not valid",134:"Bad User Name or Password",135:"Not authorized",136:"Server unavailable",137:"Server busy",138:"Banned",139:"Server shutting down",140:"Bad authentication method",141:"Keep Alive timeout",142:"Session taken over",143:"Topic Filter invalid",144:"Topic Name invalid",145:"Packet identifier in use",146:"Packet Identifier not found",147:"Receive Maximum exceeded",148:"Topic Alias invalid",149:"Packet too large",150:"Message rate too high",151:"Quota exceeded",152:"Administrative action",153:"Payload format invalid",154:"Retain not supported",155:"QoS not supported",156:"Use another server",157:"Server moved",158:"Shared Subscriptions not supported",159:"Connection rate exceeded",160:"Maximum connect time",161:"Subscription Identifiers not supported",162:"Wildcard Subscriptions not supported"},t.default=(r,n)=>{let{messageId:i}=n,o=n.cmd,s=null,a=r.outgoing[i]?r.outgoing[i].cb:null,l=null;if(a){switch(r.log("_handleAck :: packet type",o),o){case"pubcomp":case"puback":{let c=n.reasonCode;c&&c>0&&c!==16?(l=new e.ErrorWithReasonCode(`Publish error: ${t.ReasonCodes[c]}`,c),r._removeOutgoingAndStoreMessage(i,()=>{a(l,n)})):r._removeOutgoingAndStoreMessage(i,a);break}case"pubrec":{s={cmd:"pubrel",qos:2,messageId:i};let c=n.reasonCode;c&&c>0&&c!==16?(l=new e.ErrorWithReasonCode(`Publish error: ${t.ReasonCodes[c]}`,c),r._removeOutgoingAndStoreMessage(i,()=>{a(l,n)})):r._sendPacket(s);break}case"suback":{delete r.outgoing[i],r.messageIdProvider.deallocate(i);let c=n.granted;for(let u=0;u{delete r._resubscribeTopics[d]})}}delete r.messageIdToTopic[i],r._invokeStoreProcessingQueue(),a(l,n);break}case"unsuback":delete r.outgoing[i],r.messageIdProvider.deallocate(i),r._invokeStoreProcessingQueue(),a(null,n);break;default:r.emit("error",new Error("unrecognized packet type"))}r.disconnecting&&Object.keys(r.outgoing).length===0&&r.emit("outgoingEmpty")}else r.log("_handleAck :: Server sent an ack in error. Ignoring.")}}),Zk=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e=oo(),r=zl();t.default=(n,i)=>{let{options:o}=n,s=o.protocolVersion,a=s===5?i.reasonCode:i.returnCode;if(s!==5){let l=new e.ErrorWithReasonCode(`Protocol error: Auth packets are only supported in MQTT 5. Your version:${s}`,a);return void n.emit("error",l)}n.handleAuth(i,(l,c)=>{if(l)n.emit("error",l);else if(a===24)n.reconnecting=!1,n._sendPacket(c);else{let u=new e.ErrorWithReasonCode(`Connection refused: ${r.ReasonCodes[a]}`,a);n.emit("error",u)}})}}),Xk=we(t=>{var f,d,y,g,p,b,v,S,k,M,A,I,T,_,x,O,N,G,U,z,q,B,ne,H,Q,F,V,Z,$,Eh,le,ue,Y,ie,aw,j,X,R,un,hn,Oh,Pa,Ca,tt,Mh,Io,fn,Ah,de;me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.LRUCache=void 0;var e=typeof performance=="object"&&performance&&typeof performance.now=="function"?performance:Date,r=new Set,n=typeof $e=="object"&&$e?$e:{},i=(pe,K,ee,ce)=>{typeof n.emitWarning=="function"&&n.emitWarning(pe,K,ee,ce)},o=globalThis.AbortController,s=globalThis.AbortSignal;if(typeof o>"u"){s=class{constructor(){_e(this,"onabort");_e(this,"_onabort",[]);_e(this,"reason");_e(this,"aborted",!1)}addEventListener(ee,ce){this._onabort.push(ce)}},o=class{constructor(){_e(this,"signal",new s);K()}abort(ee){var ce,C;if(!this.signal.aborted){this.signal.reason=ee,this.signal.aborted=!0;for(let L of this.signal._onabort)L(ee);(C=(ce=this.signal).onabort)==null||C.call(ce,ee)}}};let pe=((f=n.env)==null?void 0:f.LRU_CACHE_IGNORE_AC_WARNING)!=="1",K=()=>{pe&&(pe=!1,i("AbortController is not defined. If using lru-cache in node 14, load an AbortController polyfill from the `node-abort-controller` package. A minimal polyfill is provided for use by LRUCache.fetch(), but it should not be relied upon in other contexts (eg, passing it to other APIs that use AbortController/AbortSignal might have undesirable effects). You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.","NO_ABORT_CONTROLLER","ENOTSUP",K))}}var a=pe=>pe&&pe===Math.floor(pe)&&pe>0&&isFinite(pe),l=pe=>a(pe)?pe<=Math.pow(2,8)?Uint8Array:pe<=Math.pow(2,16)?Uint16Array:pe<=Math.pow(2,32)?Uint32Array:pe<=Number.MAX_SAFE_INTEGER?c:null:null,c=class extends Array{constructor(pe){super(pe),this.fill(0)}},u=(d=class{constructor(K,ee){_e(this,"heap");_e(this,"length");if(!re(d,y))throw new TypeError("instantiate Stack using Stack.create(n)");this.heap=new ee(K),this.length=0}static create(K){let ee=l(K);if(!ee)return[];Te(d,y,!0);let ce=new d(K,ee);return Te(d,y,!1),ce}push(K){this.heap[this.length++]=K}pop(){return this.heap[--this.length]}},y=new WeakMap,We(d,y,!1),d),h=(de=class{constructor(K){We(this,$);We(this,b);We(this,v);We(this,S);We(this,k);We(this,M);We(this,A);_e(this,"ttl");_e(this,"ttlResolution");_e(this,"ttlAutopurge");_e(this,"updateAgeOnGet");_e(this,"updateAgeOnHas");_e(this,"allowStale");_e(this,"noDisposeOnSet");_e(this,"noUpdateTTL");_e(this,"maxEntrySize");_e(this,"sizeCalculation");_e(this,"noDeleteOnFetchRejection");_e(this,"noDeleteOnStaleGet");_e(this,"allowStaleOnFetchAbort");_e(this,"allowStaleOnFetchRejection");_e(this,"ignoreFetchAbort");We(this,I);We(this,T);We(this,_);We(this,x);We(this,O);We(this,N);We(this,G);We(this,U);We(this,z);We(this,q);We(this,B);We(this,ne);We(this,H);We(this,Q);We(this,F);We(this,V);We(this,Z);We(this,le,()=>{});We(this,ue,()=>{});We(this,Y,()=>{});We(this,ie,()=>!1);We(this,j,K=>{});We(this,X,(K,ee,ce)=>{});We(this,R,(K,ee,ce,C)=>{if(ce||C)throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");return 0});_e(this,g,"LRUCache");let{max:ee=0,ttl:ce,ttlResolution:C=1,ttlAutopurge:L,updateAgeOnGet:ae,updateAgeOnHas:ge,allowStale:xe,dispose:ve,disposeAfter:Ce,noDisposeOnSet:ke,noUpdateTTL:Be,maxSize:De=0,maxEntrySize:Ve=0,sizeCalculation:Fe,fetchMethod:ze,memoMethod:Me,noDeleteOnFetchRejection:Se,noDeleteOnStaleGet:He,allowStaleOnFetchRejection:qe,allowStaleOnFetchAbort:lt,ignoreFetchAbort:ho}=K;if(ee!==0&&!a(ee))throw new TypeError("max option must be a nonnegative integer");let en=ee?l(ee):Array;if(!en)throw new Error("invalid max value: "+ee);if(Te(this,b,ee),Te(this,v,De),this.maxEntrySize=Ve||re(this,v),this.sizeCalculation=Fe,this.sizeCalculation){if(!re(this,v)&&!this.maxEntrySize)throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");if(typeof this.sizeCalculation!="function")throw new TypeError("sizeCalculation set to non-function")}if(Me!==void 0&&typeof Me!="function")throw new TypeError("memoMethod must be a function if defined");if(Te(this,A,Me),ze!==void 0&&typeof ze!="function")throw new TypeError("fetchMethod must be a function if specified");if(Te(this,M,ze),Te(this,V,!!ze),Te(this,_,new Map),Te(this,x,new Array(ee).fill(void 0)),Te(this,O,new Array(ee).fill(void 0)),Te(this,N,new en(ee)),Te(this,G,new en(ee)),Te(this,U,0),Te(this,z,0),Te(this,q,u.create(ee)),Te(this,I,0),Te(this,T,0),typeof ve=="function"&&Te(this,S,ve),typeof Ce=="function"?(Te(this,k,Ce),Te(this,B,[])):(Te(this,k,void 0),Te(this,B,void 0)),Te(this,F,!!re(this,S)),Te(this,Z,!!re(this,k)),this.noDisposeOnSet=!!ke,this.noUpdateTTL=!!Be,this.noDeleteOnFetchRejection=!!Se,this.allowStaleOnFetchRejection=!!qe,this.allowStaleOnFetchAbort=!!lt,this.ignoreFetchAbort=!!ho,this.maxEntrySize!==0){if(re(this,v)!==0&&!a(re(this,v)))throw new TypeError("maxSize must be a positive integer if specified");if(!a(this.maxEntrySize))throw new TypeError("maxEntrySize must be a positive integer if specified");Oe(this,$,aw).call(this)}if(this.allowStale=!!xe,this.noDeleteOnStaleGet=!!He,this.updateAgeOnGet=!!ae,this.updateAgeOnHas=!!ge,this.ttlResolution=a(C)||C===0?C:1,this.ttlAutopurge=!!L,this.ttl=ce||0,this.ttl){if(!a(this.ttl))throw new TypeError("ttl must be a positive integer if specified");Oe(this,$,Eh).call(this)}if(re(this,b)===0&&this.ttl===0&&re(this,v)===0)throw new TypeError("At least one of max, maxSize, or ttl is required");if(!this.ttlAutopurge&&!re(this,b)&&!re(this,v)){let yi="LRU_CACHE_UNBOUNDED";(Ws=>!r.has(Ws))(yi)&&(r.add(yi),i("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.","UnboundedCacheWarning",yi,de))}}static unsafeExposeInternals(K){return{starts:re(K,H),ttls:re(K,Q),sizes:re(K,ne),keyMap:re(K,_),keyList:re(K,x),valList:re(K,O),next:re(K,N),prev:re(K,G),get head(){return re(K,U)},get tail(){return re(K,z)},free:re(K,q),isBackgroundFetch:ee=>{var ce;return Oe(ce=K,$,tt).call(ce,ee)},backgroundFetch:(ee,ce,C,L)=>{var ae;return Oe(ae=K,$,Ca).call(ae,ee,ce,C,L)},moveToTail:ee=>{var ce;return Oe(ce=K,$,Io).call(ce,ee)},indexes:ee=>{var ce;return Oe(ce=K,$,un).call(ce,ee)},rindexes:ee=>{var ce;return Oe(ce=K,$,hn).call(ce,ee)},isStale:ee=>{var ce;return re(ce=K,ie).call(ce,ee)}}}get max(){return re(this,b)}get maxSize(){return re(this,v)}get calculatedSize(){return re(this,T)}get size(){return re(this,I)}get fetchMethod(){return re(this,M)}get memoMethod(){return re(this,A)}get dispose(){return re(this,S)}get disposeAfter(){return re(this,k)}getRemainingTTL(K){return re(this,_).has(K)?1/0:0}*entries(){for(let K of Oe(this,$,un).call(this))re(this,O)[K]!==void 0&&re(this,x)[K]!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield[re(this,x)[K],re(this,O)[K]])}*rentries(){for(let K of Oe(this,$,hn).call(this))re(this,O)[K]!==void 0&&re(this,x)[K]!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield[re(this,x)[K],re(this,O)[K]])}*keys(){for(let K of Oe(this,$,un).call(this)){let ee=re(this,x)[K];ee!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield ee)}}*rkeys(){for(let K of Oe(this,$,hn).call(this)){let ee=re(this,x)[K];ee!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield ee)}}*values(){for(let K of Oe(this,$,un).call(this))re(this,O)[K]!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield re(this,O)[K])}*rvalues(){for(let K of Oe(this,$,hn).call(this))re(this,O)[K]!==void 0&&!Oe(this,$,tt).call(this,re(this,O)[K])&&(yield re(this,O)[K])}[(p=Symbol.iterator,g=Symbol.toStringTag,p)](){return this.entries()}find(K,ee={}){for(let ce of Oe(this,$,un).call(this)){let C=re(this,O)[ce],L=Oe(this,$,tt).call(this,C)?C.__staleWhileFetching:C;if(L!==void 0&&K(L,re(this,x)[ce],this))return this.get(re(this,x)[ce],ee)}}forEach(K,ee=this){for(let ce of Oe(this,$,un).call(this)){let C=re(this,O)[ce],L=Oe(this,$,tt).call(this,C)?C.__staleWhileFetching:C;L!==void 0&&K.call(ee,L,re(this,x)[ce],this)}}rforEach(K,ee=this){for(let ce of Oe(this,$,hn).call(this)){let C=re(this,O)[ce],L=Oe(this,$,tt).call(this,C)?C.__staleWhileFetching:C;L!==void 0&&K.call(ee,L,re(this,x)[ce],this)}}purgeStale(){let K=!1;for(let ee of Oe(this,$,hn).call(this,{allowStale:!0}))re(this,ie).call(this,ee)&&(Oe(this,$,fn).call(this,re(this,x)[ee],"expire"),K=!0);return K}info(K){let ee=re(this,_).get(K);if(ee===void 0)return;let ce=re(this,O)[ee],C=Oe(this,$,tt).call(this,ce)?ce.__staleWhileFetching:ce;if(C===void 0)return;let L={value:C};if(re(this,Q)&&re(this,H)){let ae=re(this,Q)[ee],ge=re(this,H)[ee];if(ae&&ge){let xe=ae-(e.now()-ge);L.ttl=xe,L.start=Date.now()}}return re(this,ne)&&(L.size=re(this,ne)[ee]),L}dump(){let K=[];for(let ee of Oe(this,$,un).call(this,{allowStale:!0})){let ce=re(this,x)[ee],C=re(this,O)[ee],L=Oe(this,$,tt).call(this,C)?C.__staleWhileFetching:C;if(L===void 0||ce===void 0)continue;let ae={value:L};if(re(this,Q)&&re(this,H)){ae.ttl=re(this,Q)[ee];let ge=e.now()-re(this,H)[ee];ae.start=Math.floor(Date.now()-ge)}re(this,ne)&&(ae.size=re(this,ne)[ee]),K.unshift([ce,ae])}return K}load(K){this.clear();for(let[ee,ce]of K){if(ce.start){let C=Date.now()-ce.start;ce.start=e.now()-C}this.set(ee,ce.value,ce)}}set(K,ee,ce={}){var Be,De,Ve,Fe,ze;if(ee===void 0)return this.delete(K),this;let{ttl:C=this.ttl,start:L,noDisposeOnSet:ae=this.noDisposeOnSet,sizeCalculation:ge=this.sizeCalculation,status:xe}=ce,{noUpdateTTL:ve=this.noUpdateTTL}=ce,Ce=re(this,R).call(this,K,ee,ce.size||0,ge);if(this.maxEntrySize&&Ce>this.maxEntrySize)return xe&&(xe.set="miss",xe.maxEntrySizeExceeded=!0),Oe(this,$,fn).call(this,K,"set"),this;let ke=re(this,I)===0?void 0:re(this,_).get(K);if(ke===void 0)ke=re(this,I)===0?re(this,z):re(this,q).length!==0?re(this,q).pop():re(this,I)===re(this,b)?Oe(this,$,Pa).call(this,!1):re(this,I),re(this,x)[ke]=K,re(this,O)[ke]=ee,re(this,_).set(K,ke),re(this,N)[re(this,z)]=ke,re(this,G)[ke]=re(this,z),Te(this,z,ke),Us(this,I)._++,re(this,X).call(this,ke,Ce,xe),xe&&(xe.set="add"),ve=!1;else{Oe(this,$,Io).call(this,ke);let Me=re(this,O)[ke];if(ee!==Me){if(re(this,V)&&Oe(this,$,tt).call(this,Me)){Me.__abortController.abort(new Error("replaced"));let{__staleWhileFetching:Se}=Me;Se!==void 0&&!ae&&(re(this,F)&&((Be=re(this,S))==null||Be.call(this,Se,K,"set")),re(this,Z)&&((De=re(this,B))==null||De.push([Se,K,"set"])))}else ae||(re(this,F)&&((Ve=re(this,S))==null||Ve.call(this,Me,K,"set")),re(this,Z)&&((Fe=re(this,B))==null||Fe.push([Me,K,"set"])));if(re(this,j).call(this,ke),re(this,X).call(this,ke,Ce,xe),re(this,O)[ke]=ee,xe){xe.set="replace";let Se=Me&&Oe(this,$,tt).call(this,Me)?Me.__staleWhileFetching:Me;Se!==void 0&&(xe.oldValue=Se)}}else xe&&(xe.set="update")}if(C!==0&&!re(this,Q)&&Oe(this,$,Eh).call(this),re(this,Q)&&(ve||re(this,Y).call(this,ke,C,L),xe&&re(this,ue).call(this,xe,ke)),!ae&&re(this,Z)&&re(this,B)){let Me,Se=re(this,B);for(;Me=Se==null?void 0:Se.shift();)(ze=re(this,k))==null||ze.call(this,...Me)}return this}pop(){var K;try{for(;re(this,I);){let ee=re(this,O)[re(this,U)];if(Oe(this,$,Pa).call(this,!0),Oe(this,$,tt).call(this,ee)){if(ee.__staleWhileFetching)return ee.__staleWhileFetching}else if(ee!==void 0)return ee}}finally{if(re(this,Z)&&re(this,B)){let ee,ce=re(this,B);for(;ee=ce==null?void 0:ce.shift();)(K=re(this,k))==null||K.call(this,...ee)}}}has(K,ee={}){let{updateAgeOnHas:ce=this.updateAgeOnHas,status:C}=ee,L=re(this,_).get(K);if(L!==void 0){let ae=re(this,O)[L];if(Oe(this,$,tt).call(this,ae)&&ae.__staleWhileFetching===void 0)return!1;if(!re(this,ie).call(this,L))return ce&&re(this,le).call(this,L),C&&(C.has="hit",re(this,ue).call(this,C,L)),!0;C&&(C.has="stale",re(this,ue).call(this,C,L))}else C&&(C.has="miss");return!1}peek(K,ee={}){let{allowStale:ce=this.allowStale}=ee,C=re(this,_).get(K);if(C===void 0||!ce&&re(this,ie).call(this,C))return;let L=re(this,O)[C];return Oe(this,$,tt).call(this,L)?L.__staleWhileFetching:L}async fetch(K,ee={}){let{allowStale:ce=this.allowStale,updateAgeOnGet:C=this.updateAgeOnGet,noDeleteOnStaleGet:L=this.noDeleteOnStaleGet,ttl:ae=this.ttl,noDisposeOnSet:ge=this.noDisposeOnSet,size:xe=0,sizeCalculation:ve=this.sizeCalculation,noUpdateTTL:Ce=this.noUpdateTTL,noDeleteOnFetchRejection:ke=this.noDeleteOnFetchRejection,allowStaleOnFetchRejection:Be=this.allowStaleOnFetchRejection,ignoreFetchAbort:De=this.ignoreFetchAbort,allowStaleOnFetchAbort:Ve=this.allowStaleOnFetchAbort,context:Fe,forceRefresh:ze=!1,status:Me,signal:Se}=ee;if(!re(this,V))return Me&&(Me.fetch="get"),this.get(K,{allowStale:ce,updateAgeOnGet:C,noDeleteOnStaleGet:L,status:Me});let He={allowStale:ce,updateAgeOnGet:C,noDeleteOnStaleGet:L,ttl:ae,noDisposeOnSet:ge,size:xe,sizeCalculation:ve,noUpdateTTL:Ce,noDeleteOnFetchRejection:ke,allowStaleOnFetchRejection:Be,allowStaleOnFetchAbort:Ve,ignoreFetchAbort:De,status:Me,signal:Se},qe=re(this,_).get(K);if(qe===void 0){Me&&(Me.fetch="miss");let lt=Oe(this,$,Ca).call(this,K,qe,He,Fe);return lt.__returned=lt}{let lt=re(this,O)[qe];if(Oe(this,$,tt).call(this,lt)){let Ws=ce&<.__staleWhileFetching!==void 0;return Me&&(Me.fetch="inflight",Ws&&(Me.returnedStale=!0)),Ws?lt.__staleWhileFetching:lt.__returned=lt}let ho=re(this,ie).call(this,qe);if(!ze&&!ho)return Me&&(Me.fetch="hit"),Oe(this,$,Io).call(this,qe),C&&re(this,le).call(this,qe),Me&&re(this,ue).call(this,Me,qe),lt;let en=Oe(this,$,Ca).call(this,K,qe,He,Fe),yi=en.__staleWhileFetching!==void 0&&ce;return Me&&(Me.fetch=ho?"stale":"refresh",yi&&ho&&(Me.returnedStale=!0)),yi?en.__staleWhileFetching:en.__returned=en}}async forceFetch(K,ee={}){let ce=await this.fetch(K,ee);if(ce===void 0)throw new Error("fetch() returned undefined");return ce}memo(K,ee={}){let ce=re(this,A);if(!ce)throw new Error("no memoMethod provided to constructor");let{context:C,forceRefresh:L,...ae}=ee,ge=this.get(K,ae);if(!L&&ge!==void 0)return ge;let xe=ce(K,ge,{options:ae,context:C});return this.set(K,xe,ae),xe}get(K,ee={}){let{allowStale:ce=this.allowStale,updateAgeOnGet:C=this.updateAgeOnGet,noDeleteOnStaleGet:L=this.noDeleteOnStaleGet,status:ae}=ee,ge=re(this,_).get(K);if(ge!==void 0){let xe=re(this,O)[ge],ve=Oe(this,$,tt).call(this,xe);return ae&&re(this,ue).call(this,ae,ge),re(this,ie).call(this,ge)?(ae&&(ae.get="stale"),ve?(ae&&ce&&xe.__staleWhileFetching!==void 0&&(ae.returnedStale=!0),ce?xe.__staleWhileFetching:void 0):(L||Oe(this,$,fn).call(this,K,"expire"),ae&&ce&&(ae.returnedStale=!0),ce?xe:void 0)):(ae&&(ae.get="hit"),ve?xe.__staleWhileFetching:(Oe(this,$,Io).call(this,ge),C&&re(this,le).call(this,ge),xe))}ae&&(ae.get="miss")}delete(K){return Oe(this,$,fn).call(this,K,"delete")}clear(){return Oe(this,$,Ah).call(this,"delete")}},b=new WeakMap,v=new WeakMap,S=new WeakMap,k=new WeakMap,M=new WeakMap,A=new WeakMap,I=new WeakMap,T=new WeakMap,_=new WeakMap,x=new WeakMap,O=new WeakMap,N=new WeakMap,G=new WeakMap,U=new WeakMap,z=new WeakMap,q=new WeakMap,B=new WeakMap,ne=new WeakMap,H=new WeakMap,Q=new WeakMap,F=new WeakMap,V=new WeakMap,Z=new WeakMap,$=new WeakSet,Eh=function(){let K=new c(re(this,b)),ee=new c(re(this,b));Te(this,Q,K),Te(this,H,ee),Te(this,Y,(L,ae,ge=e.now())=>{if(ee[L]=ae!==0?ge:0,K[L]=ae,ae!==0&&this.ttlAutopurge){let xe=setTimeout(()=>{re(this,ie).call(this,L)&&Oe(this,$,fn).call(this,re(this,x)[L],"expire")},ae+1);xe.unref&&xe.unref()}}),Te(this,le,L=>{ee[L]=K[L]!==0?e.now():0}),Te(this,ue,(L,ae)=>{if(K[ae]){let ge=K[ae],xe=ee[ae];if(!ge||!xe)return;L.ttl=ge,L.start=xe,L.now=ce||C();let ve=L.now-xe;L.remainingTTL=ge-ve}});let ce=0,C=()=>{let L=e.now();if(this.ttlResolution>0){ce=L;let ae=setTimeout(()=>ce=0,this.ttlResolution);ae.unref&&ae.unref()}return L};this.getRemainingTTL=L=>{let ae=re(this,_).get(L);if(ae===void 0)return 0;let ge=K[ae],xe=ee[ae];return ge&&xe?ge-((ce||C())-xe):1/0},Te(this,ie,L=>{let ae=ee[L],ge=K[L];return!!ge&&!!ae&&(ce||C())-ae>ge})},le=new WeakMap,ue=new WeakMap,Y=new WeakMap,ie=new WeakMap,aw=function(){let K=new c(re(this,b));Te(this,T,0),Te(this,ne,K),Te(this,j,ee=>{Te(this,T,re(this,T)-K[ee]),K[ee]=0}),Te(this,R,(ee,ce,C,L)=>{if(Oe(this,$,tt).call(this,ce))return 0;if(!a(C)){if(!L)throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");if(typeof L!="function")throw new TypeError("sizeCalculation must be a function");if(C=L(ce,ee),!a(C))throw new TypeError("sizeCalculation return invalid (expect positive integer)")}return C}),Te(this,X,(ee,ce,C)=>{if(K[ee]=ce,re(this,v)){let L=re(this,v)-K[ee];for(;re(this,T)>L;)Oe(this,$,Pa).call(this,!0)}Te(this,T,re(this,T)+K[ee]),C&&(C.entrySize=ce,C.totalCalculatedSize=re(this,T))})},j=new WeakMap,X=new WeakMap,R=new WeakMap,un=function*({allowStale:K=this.allowStale}={}){if(re(this,I))for(let ee=re(this,z);Oe(this,$,Oh).call(this,ee)&&((K||!re(this,ie).call(this,ee))&&(yield ee),ee!==re(this,U));)ee=re(this,G)[ee]},hn=function*({allowStale:K=this.allowStale}={}){if(re(this,I))for(let ee=re(this,U);Oe(this,$,Oh).call(this,ee)&&((K||!re(this,ie).call(this,ee))&&(yield ee),ee!==re(this,z));)ee=re(this,N)[ee]},Oh=function(K){return K!==void 0&&re(this,_).get(re(this,x)[K])===K},Pa=function(K){var L,ae;let ee=re(this,U),ce=re(this,x)[ee],C=re(this,O)[ee];return re(this,V)&&Oe(this,$,tt).call(this,C)?C.__abortController.abort(new Error("evicted")):(re(this,F)||re(this,Z))&&(re(this,F)&&((L=re(this,S))==null||L.call(this,C,ce,"evict")),re(this,Z)&&((ae=re(this,B))==null||ae.push([C,ce,"evict"]))),re(this,j).call(this,ee),K&&(re(this,x)[ee]=void 0,re(this,O)[ee]=void 0,re(this,q).push(ee)),re(this,I)===1?(Te(this,U,Te(this,z,0)),re(this,q).length=0):Te(this,U,re(this,N)[ee]),re(this,_).delete(ce),Us(this,I)._--,ee},Ca=function(K,ee,ce,C){let L=ee===void 0?void 0:re(this,O)[ee];if(Oe(this,$,tt).call(this,L))return L;let ae=new o,{signal:ge}=ce;ge==null||ge.addEventListener("abort",()=>ae.abort(ge.reason),{signal:ae.signal});let xe={signal:ae.signal,options:ce,context:C},ve=(De,Ve=!1)=>{let{aborted:Fe}=ae.signal,ze=ce.ignoreFetchAbort&&De!==void 0;if(ce.status&&(Fe&&!Ve?(ce.status.fetchAborted=!0,ce.status.fetchError=ae.signal.reason,ze&&(ce.status.fetchAbortIgnored=!0)):ce.status.fetchResolved=!0),Fe&&!ze&&!Ve)return Ce(ae.signal.reason);let Me=ke;return re(this,O)[ee]===ke&&(De===void 0?Me.__staleWhileFetching?re(this,O)[ee]=Me.__staleWhileFetching:Oe(this,$,fn).call(this,K,"fetch"):(ce.status&&(ce.status.fetchUpdated=!0),this.set(K,De,xe.options))),De},Ce=De=>{let{aborted:Ve}=ae.signal,Fe=Ve&&ce.allowStaleOnFetchAbort,ze=Fe||ce.allowStaleOnFetchRejection,Me=ze||ce.noDeleteOnFetchRejection,Se=ke;if(re(this,O)[ee]===ke&&(Me&&Se.__staleWhileFetching!==void 0?Fe||(re(this,O)[ee]=Se.__staleWhileFetching):Oe(this,$,fn).call(this,K,"fetch")),ze)return ce.status&&Se.__staleWhileFetching!==void 0&&(ce.status.returnedStale=!0),Se.__staleWhileFetching;if(Se.__returned===Se)throw De};ce.status&&(ce.status.fetchDispatched=!0);let ke=new Promise((De,Ve)=>{var ze;let Fe=(ze=re(this,M))==null?void 0:ze.call(this,K,L,xe);Fe&&Fe instanceof Promise&&Fe.then(Me=>De(Me===void 0?void 0:Me),Ve),ae.signal.addEventListener("abort",()=>{(!ce.ignoreFetchAbort||ce.allowStaleOnFetchAbort)&&(De(void 0),ce.allowStaleOnFetchAbort&&(De=Me=>ve(Me,!0)))})}).then(ve,De=>(ce.status&&(ce.status.fetchRejected=!0,ce.status.fetchError=De),Ce(De))),Be=Object.assign(ke,{__abortController:ae,__staleWhileFetching:L,__returned:void 0});return ee===void 0?(this.set(K,Be,{...xe.options,status:void 0}),ee=re(this,_).get(K)):re(this,O)[ee]=Be,Be},tt=function(K){if(!re(this,V))return!1;let ee=K;return!!ee&&ee instanceof Promise&&ee.hasOwnProperty("__staleWhileFetching")&&ee.__abortController instanceof o},Mh=function(K,ee){re(this,G)[ee]=K,re(this,N)[K]=ee},Io=function(K){K!==re(this,z)&&(K===re(this,U)?Te(this,U,re(this,N)[K]):Oe(this,$,Mh).call(this,re(this,G)[K],re(this,N)[K]),Oe(this,$,Mh).call(this,re(this,z),K),Te(this,z,K))},fn=function(K,ee){var C,L,ae,ge;let ce=!1;if(re(this,I)!==0){let xe=re(this,_).get(K);if(xe!==void 0)if(ce=!0,re(this,I)===1)Oe(this,$,Ah).call(this,ee);else{re(this,j).call(this,xe);let ve=re(this,O)[xe];if(Oe(this,$,tt).call(this,ve)?ve.__abortController.abort(new Error("deleted")):(re(this,F)||re(this,Z))&&(re(this,F)&&((C=re(this,S))==null||C.call(this,ve,K,ee)),re(this,Z)&&((L=re(this,B))==null||L.push([ve,K,ee]))),re(this,_).delete(K),re(this,x)[xe]=void 0,re(this,O)[xe]=void 0,xe===re(this,z))Te(this,z,re(this,G)[xe]);else if(xe===re(this,U))Te(this,U,re(this,N)[xe]);else{let Ce=re(this,G)[xe];re(this,N)[Ce]=re(this,N)[xe];let ke=re(this,N)[xe];re(this,G)[ke]=re(this,G)[xe]}Us(this,I)._--,re(this,q).push(xe)}}if(re(this,Z)&&((ae=re(this,B))!=null&&ae.length)){let xe,ve=re(this,B);for(;xe=ve==null?void 0:ve.shift();)(ge=re(this,k))==null||ge.call(this,...xe)}return ce},Ah=function(K){var ee,ce,C;for(let L of Oe(this,$,hn).call(this,{allowStale:!0})){let ae=re(this,O)[L];if(Oe(this,$,tt).call(this,ae))ae.__abortController.abort(new Error("deleted"));else{let ge=re(this,x)[L];re(this,F)&&((ee=re(this,S))==null||ee.call(this,ae,ge,K)),re(this,Z)&&((ce=re(this,B))==null||ce.push([ae,ge,K]))}}if(re(this,_).clear(),re(this,O).fill(void 0),re(this,x).fill(void 0),re(this,Q)&&re(this,H)&&(re(this,Q).fill(0),re(this,H).fill(0)),re(this,ne)&&re(this,ne).fill(0),Te(this,U,0),Te(this,z,0),re(this,q).length=0,Te(this,T,0),Te(this,I,0),re(this,Z)&&re(this,B)){let L,ae=re(this,B);for(;L=ae==null?void 0:ae.shift();)(C=re(this,k))==null||C.call(this,...L)}},de);t.LRUCache=h}),Xr=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.ContainerIterator=t.Container=t.Base=void 0,t.ContainerIterator=class{constructor(r=0){this.iteratorType=r}equals(r){return this.o===r.o}};var e=class{constructor(){this.i=0}get length(){return this.i}size(){return this.i}empty(){return this.i===0}};t.Base=e,t.Container=class extends e{}}),Jk=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=Xr(),r=class extends e.Base{constructor(i=[]){super(),this.S=[];let o=this;i.forEach(function(s){o.push(s)})}clear(){this.i=0,this.S=[]}push(i){return this.S.push(i),this.i+=1,this.i}pop(){if(this.i!==0)return this.i-=1,this.S.pop()}top(){return this.S[this.i-1]}},n=r;t.default=n}),eE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=Xr(),r=class extends e.Base{constructor(i=[]){super(),this.j=0,this.q=[];let o=this;i.forEach(function(s){o.push(s)})}clear(){this.q=[],this.i=this.j=0}push(i){let o=this.q.length;if(this.j/o>.5&&this.j+this.i>=o&&o>4096){let s=this.i;for(let a=0;a{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=Xr(),r=class extends e.Base{constructor(i=[],o=function(a,l){return a>l?-1:a>1;for(let l=this.i-1>>1;l>=0;--l)this.k(l,a)}m(i){let o=this.C[i];for(;i>0;){let s=i-1>>1,a=this.C[s];if(this.v(a,o)<=0)break;this.C[i]=a,i=s}this.C[i]=o}k(i,o){let s=this.C[i];for(;i0&&(a=l,c=this.C[l]),this.v(c,s)>=0)break;this.C[i]=c,i=a}this.C[i]=s}clear(){this.i=0,this.C.length=0}push(i){this.C.push(i),this.m(this.i),this.i+=1}pop(){if(this.i===0)return;let i=this.C[0],o=this.C.pop();return this.i-=1,this.i&&(this.C[0]=o,this.k(0,this.i>>1)),i}top(){return this.C[0]}find(i){return this.C.indexOf(i)>=0}remove(i){let o=this.C.indexOf(i);return!(o<0)&&(o===0?this.pop():o===this.i-1?(this.C.pop(),this.i-=1):(this.C.splice(o,1,this.C.pop()),this.i-=1,this.m(o),this.k(o,this.i>>1)),!0)}updateItem(i){let o=this.C.indexOf(i);return!(o<0)&&(this.m(o),this.k(o,this.i>>1),!0)}toArray(){return[...this.C]}},n=r;t.default=n}),Td=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=Xr(),r=class extends e.Container{};t.default=r}),Jr=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.throwIteratorAccessError=function(){throw new RangeError("Iterator access denied!")}}),lw=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.RandomIterator=void 0;var e=Xr(),r=Jr(),n=class extends e.ContainerIterator{constructor(i,o){super(o),this.o=i,this.iteratorType===0?(this.pre=function(){return this.o===0&&(0,r.throwIteratorAccessError)(),this.o-=1,this},this.next=function(){return this.o===this.container.size()&&(0,r.throwIteratorAccessError)(),this.o+=1,this}):(this.pre=function(){return this.o===this.container.size()-1&&(0,r.throwIteratorAccessError)(),this.o+=1,this},this.next=function(){return this.o===-1&&(0,r.throwIteratorAccessError)(),this.o-=1,this})}get pointer(){return this.container.getElementByPos(this.o)}set pointer(i){this.container.setElementByPos(this.o,i)}};t.RandomIterator=n}),rE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=Td())&&e.t?e:{default:e},n=lw(),i=class cw extends n.RandomIterator{constructor(l,c,u){super(l,u),this.container=c}copy(){return new cw(this.o,this.container,this.iteratorType)}},o=class extends r.default{constructor(a=[],l=!0){if(super(),Array.isArray(a))this.J=l?[...a]:a,this.i=a.length;else{this.J=[];let c=this;a.forEach(function(u){c.pushBack(u)})}}clear(){this.i=0,this.J.length=0}begin(){return new i(0,this)}end(){return new i(this.i,this)}rBegin(){return new i(this.i-1,this,1)}rEnd(){return new i(-1,this,1)}front(){return this.J[0]}back(){return this.J[this.i-1]}getElementByPos(a){if(a<0||a>this.i-1)throw new RangeError;return this.J[a]}eraseElementByPos(a){if(a<0||a>this.i-1)throw new RangeError;return this.J.splice(a,1),this.i-=1,this.i}eraseElementByValue(a){let l=0;for(let c=0;cthis.i-1)throw new RangeError;this.J[a]=l}insert(a,l,c=1){if(a<0||a>this.i)throw new RangeError;return this.J.splice(a,0,...new Array(c).fill(l)),this.i+=c,this.i}find(a){for(let l=0;l{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=Td())&&e.t?e:{default:e},n=Xr(),i=Jr(),o=class uw extends n.ContainerIterator{constructor(c,u,h,f){super(f),this.o=c,this.h=u,this.container=h,this.iteratorType===0?(this.pre=function(){return this.o.L===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.L,this},this.next=function(){return this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.B,this}):(this.pre=function(){return this.o.B===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.B,this},this.next=function(){return this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.L,this})}get pointer(){return this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o.l}set pointer(c){this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o.l=c}copy(){return new uw(this.o,this.h,this.container,this.iteratorType)}},s=class extends r.default{constructor(l=[]){super(),this.h={},this.p=this._=this.h.L=this.h.B=this.h;let c=this;l.forEach(function(u){c.pushBack(u)})}V(l){let{L:c,B:u}=l;c.B=u,u.L=c,l===this.p&&(this.p=u),l===this._&&(this._=c),this.i-=1}G(l,c){let u=c.B,h={l,L:c,B:u};c.B=h,u.L=h,c===this.h&&(this.p=h),u===this.h&&(this._=h),this.i+=1}clear(){this.i=0,this.p=this._=this.h.L=this.h.B=this.h}begin(){return new o(this.p,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this._,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){return this.p.l}back(){return this._.l}getElementByPos(l){if(l<0||l>this.i-1)throw new RangeError;let c=this.p;for(;l--;)c=c.B;return c.l}eraseElementByPos(l){if(l<0||l>this.i-1)throw new RangeError;let c=this.p;for(;l--;)c=c.B;return this.V(c),this.i}eraseElementByValue(l){let c=this.p;for(;c!==this.h;)c.l===l&&this.V(c),c=c.B;return this.i}eraseElementByIterator(l){let c=l.o;return c===this.h&&(0,i.throwIteratorAccessError)(),l=l.next(),this.V(c),l}pushBack(l){return this.G(l,this._),this.i}popBack(){if(this.i===0)return;let l=this._.l;return this.V(this._),l}pushFront(l){return this.G(l,this.h),this.i}popFront(){if(this.i===0)return;let l=this.p.l;return this.V(this.p),l}setElementByPos(l,c){if(l<0||l>this.i-1)throw new RangeError;let u=this.p;for(;l--;)u=u.B;u.l=c}insert(l,c,u=1){if(l<0||l>this.i)throw new RangeError;if(u<=0)return this.i;if(l===0)for(;u--;)this.pushFront(c);else if(l===this.i)for(;u--;)this.pushBack(c);else{let h=this.p;for(let d=1;d{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=(e=Td())&&e.t?e:{default:e},n=lw(),i=class hw extends n.RandomIterator{constructor(l,c,u){super(l,u),this.container=c}copy(){return new hw(this.o,this.container,this.iteratorType)}},o=class extends r.default{constructor(a=[],l=4096){super(),this.j=0,this.D=0,this.R=0,this.N=0,this.P=0,this.A=[];let c=(()=>{if(typeof a.length=="number")return a.length;if(typeof a.size=="number")return a.size;if(typeof a.size=="function")return a.size();throw new TypeError("Cannot get the length or size of the container")})();this.F=l,this.P=Math.max(Math.ceil(c/this.F),1);for(let f=0;f>1)-(u>>1),this.D=this.N=this.F-c%this.F>>1;let h=this;a.forEach(function(f){h.pushBack(f)})}T(){let a=[],l=Math.max(this.P>>1,1);for(let c=0;c>1}begin(){return new i(0,this)}end(){return new i(this.i,this)}rBegin(){return new i(this.i-1,this,1)}rEnd(){return new i(-1,this,1)}front(){if(this.i!==0)return this.A[this.j][this.D]}back(){if(this.i!==0)return this.A[this.R][this.N]}pushBack(a){return this.i&&(this.N0?this.N-=1:this.R>0?(this.R-=1,this.N=this.F-1):(this.R=this.P-1,this.N=this.F-1)),this.i-=1,a}pushFront(a){return this.i&&(this.D>0?this.D-=1:this.j>0?(this.j-=1,this.D=this.F-1):(this.j=this.P-1,this.D=this.F-1),this.j===this.R&&this.D===this.N&&this.T()),this.i+=1,this.A[this.j][this.D]=a,this.i}popFront(){if(this.i===0)return;let a=this.A[this.j][this.D];return this.i!==1&&(this.Dthis.i-1)throw new RangeError;let{curNodeBucketIndex:l,curNodePointerIndex:c}=this.O(a);return this.A[l][c]}setElementByPos(a,l){if(a<0||a>this.i-1)throw new RangeError;let{curNodeBucketIndex:c,curNodePointerIndex:u}=this.O(a);this.A[c][u]=l}insert(a,l,c=1){if(a<0||a>this.i)throw new RangeError;if(a===0)for(;c--;)this.pushFront(l);else if(a===this.i)for(;c--;)this.pushBack(l);else{let u=[];for(let h=a;hthis.i-1)throw new RangeError;if(a===0)this.popFront();else if(a===this.i-1)this.popBack();else{let l=[];for(let u=a+1;ua;)this.popBack();return this.i}sort(a){let l=[];for(let c=0;c{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.TreeNodeEnableIndex=t.TreeNode=void 0;var e=class{constructor(r,n){this.ee=1,this.u=void 0,this.l=void 0,this.U=void 0,this.W=void 0,this.tt=void 0,this.u=r,this.l=n}L(){let r=this;if(r.ee===1&&r.tt.tt===r)r=r.W;else if(r.U)for(r=r.U;r.W;)r=r.W;else{let n=r.tt;for(;n.U===r;)r=n,n=r.tt;r=n}return r}B(){let r=this;if(r.W){for(r=r.W;r.U;)r=r.U;return r}{let n=r.tt;for(;n.W===r;)r=n,n=r.tt;return r.W!==n?n:r}}te(){let r=this.tt,n=this.W,i=n.U;return r.tt===this?r.tt=n:r.U===this?r.U=n:r.W=n,n.tt=r,n.U=this,this.tt=n,this.W=i,i&&(i.tt=this),n}se(){let r=this.tt,n=this.U,i=n.W;return r.tt===this?r.tt=n:r.U===this?r.U=n:r.W=n,n.tt=r,n.W=this,this.tt=n,this.U=i,i&&(i.tt=this),n}};t.TreeNode=e,t.TreeNodeEnableIndex=class extends e{constructor(){super(...arguments),this.rt=1}te(){let r=super.te();return this.ie(),r.ie(),r}se(){let r=super.se();return this.ie(),r.ie(),r}ie(){this.rt=1,this.U&&(this.rt+=this.U.rt),this.W&&(this.rt+=this.W.rt)}}}),fw=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=oE(),r=Xr(),n=Jr(),i=class extends r.Container{constructor(s=function(l,c){return lc?1:0},a=!1){super(),this.Y=void 0,this.v=s,a?(this.re=e.TreeNodeEnableIndex,this.M=function(l,c,u){let h=this.ne(l,c,u);if(h){let f=h.tt;for(;f!==this.h;)f.rt+=1,f=f.tt;let d=this.he(h);if(d){let{parentNode:y,grandParent:g,curNode:p}=d;y.ie(),g.ie(),p.ie()}}return this.i},this.V=function(l){let c=this.fe(l);for(;c!==this.h;)c.rt-=1,c=c.tt}):(this.re=e.TreeNode,this.M=function(l,c,u){let h=this.ne(l,c,u);return h&&this.he(h),this.i},this.V=this.fe),this.h=new this.re}X(s,a){let l=this.h;for(;s;){let c=this.v(s.u,a);if(c<0)s=s.W;else{if(!(c>0))return s;l=s,s=s.U}}return l}Z(s,a){let l=this.h;for(;s;)this.v(s.u,a)<=0?s=s.W:(l=s,s=s.U);return l}$(s,a){let l=this.h;for(;s;){let c=this.v(s.u,a);if(c<0)l=s,s=s.W;else{if(!(c>0))return s;s=s.U}}return l}rr(s,a){let l=this.h;for(;s;)this.v(s.u,a)<0?(l=s,s=s.W):s=s.U;return l}ue(s){for(;;){let a=s.tt;if(a===this.h)return;if(s.ee===1)return void(s.ee=0);if(s===a.U){let l=a.W;if(l.ee===1)l.ee=0,a.ee=1,a===this.Y?this.Y=a.te():a.te();else{if(l.W&&l.W.ee===1)return l.ee=a.ee,a.ee=0,l.W.ee=0,void(a===this.Y?this.Y=a.te():a.te());l.U&&l.U.ee===1?(l.ee=1,l.U.ee=0,l.se()):(l.ee=1,s=a)}}else{let l=a.U;if(l.ee===1)l.ee=0,a.ee=1,a===this.Y?this.Y=a.se():a.se();else{if(l.U&&l.U.ee===1)return l.ee=a.ee,a.ee=0,l.U.ee=0,void(a===this.Y?this.Y=a.se():a.se());l.W&&l.W.ee===1?(l.ee=1,l.W.ee=0,l.te()):(l.ee=1,s=a)}}}}fe(s){if(this.i===1)return this.clear(),this.h;let a=s;for(;a.U||a.W;){if(a.W)for(a=a.W;a.U;)a=a.U;else a=a.U;[s.u,a.u]=[a.u,s.u],[s.l,a.l]=[a.l,s.l],s=a}this.h.U===a?this.h.U=a.tt:this.h.W===a&&(this.h.W=a.tt),this.ue(a);let l=a.tt;return a===l.U?l.U=void 0:l.W=void 0,this.i-=1,this.Y.ee=0,l}oe(s,a){return s!==void 0&&(!(!this.oe(s.U,a)&&!a(s))||this.oe(s.W,a))}he(s){for(;;){let a=s.tt;if(a.ee===0)return;let l=a.tt;if(a===l.U){let c=l.W;if(c&&c.ee===1){if(c.ee=a.ee=0,l===this.Y)return;l.ee=1,s=l;continue}if(s===a.W){if(s.ee=0,s.U&&(s.U.tt=a),s.W&&(s.W.tt=l),a.W=s.U,l.U=s.W,s.U=a,s.W=l,l===this.Y)this.Y=s,this.h.tt=s;else{let u=l.tt;u.U===l?u.U=s:u.W=s}return s.tt=l.tt,a.tt=s,l.tt=s,l.ee=1,{parentNode:a,grandParent:l,curNode:s}}a.ee=0,l===this.Y?this.Y=l.se():l.se(),l.ee=1}else{let c=l.U;if(c&&c.ee===1){if(c.ee=a.ee=0,l===this.Y)return;l.ee=1,s=l;continue}if(s===a.U){if(s.ee=0,s.U&&(s.U.tt=l),s.W&&(s.W.tt=a),l.W=s.U,a.U=s.W,s.U=l,s.W=a,l===this.Y)this.Y=s,this.h.tt=s;else{let u=l.tt;u.U===l?u.U=s:u.W=s}return s.tt=l.tt,a.tt=s,l.tt=s,l.ee=1,{parentNode:a,grandParent:l,curNode:s}}a.ee=0,l===this.Y?this.Y=l.te():l.te(),l.ee=1}return}}ne(s,a,l){if(this.Y===void 0)return this.i+=1,this.Y=new this.re(s,a),this.Y.ee=0,this.Y.tt=this.h,this.h.tt=this.Y,this.h.U=this.Y,void(this.h.W=this.Y);let c,u=this.h.U,h=this.v(u.u,s);if(h!==0){if(h>0)u.U=new this.re(s,a),u.U.tt=u,c=u.U,this.h.U=c;else{let f=this.h.W,d=this.v(f.u,s);if(d===0)return void(f.l=a);if(d<0)f.W=new this.re(s,a),f.W.tt=f,c=f.W,this.h.W=c;else{if(l!==void 0){let y=l.o;if(y!==this.h){let g=this.v(y.u,s);if(g===0)return void(y.l=a);if(g>0){let p=y.L(),b=this.v(p.u,s);if(b===0)return void(p.l=a);b<0&&(c=new this.re(s,a),p.W===void 0?(p.W=c,c.tt=p):(y.U=c,c.tt=y))}}}if(c===void 0)for(c=this.Y;;){let y=this.v(c.u,s);if(y>0){if(c.U===void 0){c.U=new this.re(s,a),c.U.tt=c,c=c.U;break}c=c.U}else{if(!(y<0))return void(c.l=a);if(c.W===void 0){c.W=new this.re(s,a),c.W.tt=c,c=c.W;break}c=c.W}}}}return this.i+=1,c}u.l=a}I(s,a){for(;s;){let l=this.v(s.u,a);if(l<0)s=s.W;else{if(!(l>0))return s;s=s.U}}return s||this.h}clear(){this.i=0,this.Y=void 0,this.h.tt=void 0,this.h.U=this.h.W=void 0}updateKeyByIterator(s,a){let l=s.o;if(l===this.h&&(0,n.throwIteratorAccessError)(),this.i===1)return l.u=a,!0;if(l===this.h.U)return this.v(l.B().u,a)>0&&(l.u=a,!0);if(l===this.h.W)return this.v(l.L().u,a)<0&&(l.u=a,!0);let c=l.L().u;if(this.v(c,a)>=0)return!1;let u=l.B().u;return!(this.v(u,a)<=0)&&(l.u=a,!0)}eraseElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;let a=0,l=this;return this.oe(this.Y,function(c){return s===a?(l.V(c),!0):(a+=1,!1)}),this.i}eraseElementByKey(s){if(this.i===0)return!1;let a=this.I(this.Y,s);return a!==this.h&&(this.V(a),!0)}eraseElementByIterator(s){let a=s.o;a===this.h&&(0,n.throwIteratorAccessError)();let l=a.W===void 0;return s.iteratorType===0?l&&s.next():(!l||a.U===void 0)&&s.next(),this.V(a),s}forEach(s){let a=0;for(let l of this)s(l,a++,this)}getElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;let a,l=0;for(let c of this){if(l===s){a=c;break}l+=1}return a}getHeight(){if(this.i===0)return 0;let s=function(a){return a?Math.max(s(a.U),s(a.W))+1:0};return s(this.Y)}},o=i;t.default=o}),dw=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=Xr(),r=Jr(),n=class extends e.ContainerIterator{constructor(o,s,a){super(a),this.o=o,this.h=s,this.iteratorType===0?(this.pre=function(){return this.o===this.h.U&&(0,r.throwIteratorAccessError)(),this.o=this.o.L(),this},this.next=function(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o=this.o.B(),this}):(this.pre=function(){return this.o===this.h.W&&(0,r.throwIteratorAccessError)(),this.o=this.o.B(),this},this.next=function(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o=this.o.L(),this})}get index(){let o=this.o,s=this.h.tt;if(o===this.h)return s?s.rt-1:0;let a=0;for(o.U&&(a+=o.U.rt);o!==s;){let l=o.tt;o===l.W&&(a+=1,l.U&&(a+=l.U.rt)),o=l}return a}},i=n;t.default=i}),sE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=i(fw()),r=i(dw()),n=Jr();function i(l){return l&&l.t?l:{default:l}}var o=class pw extends r.default{constructor(c,u,h,f){super(c,u,f),this.container=h}get pointer(){return this.o===this.h&&(0,n.throwIteratorAccessError)(),this.o.u}copy(){return new pw(this.o,this.h,this.container,this.iteratorType)}},s=class extends e.default{constructor(l=[],c,u){super(c,u);let h=this;l.forEach(function(f){h.insert(f)})}*K(l){l!==void 0&&(yield*this.K(l.U),yield l.u,yield*this.K(l.W))}begin(){return new o(this.h.U||this.h,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this.h.W||this.h,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){return this.h.U?this.h.U.u:void 0}back(){return this.h.W?this.h.W.u:void 0}insert(l,c){return this.M(l,void 0,c)}find(l){let c=this.I(this.Y,l);return new o(c,this.h,this)}lowerBound(l){let c=this.X(this.Y,l);return new o(c,this.h,this)}upperBound(l){let c=this.Z(this.Y,l);return new o(c,this.h,this)}reverseLowerBound(l){let c=this.$(this.Y,l);return new o(c,this.h,this)}reverseUpperBound(l){let c=this.rr(this.Y,l);return new o(c,this.h,this)}union(l){let c=this;return l.forEach(function(u){c.insert(u)}),this.i}[Symbol.iterator](){return this.K(this.Y)}},a=s;t.default=a}),aE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=i(fw()),r=i(dw()),n=Jr();function i(l){return l&&l.t?l:{default:l}}var o=class gw extends r.default{constructor(c,u,h,f){super(c,u,f),this.container=h}get pointer(){this.o===this.h&&(0,n.throwIteratorAccessError)();let c=this;return new Proxy([],{get:(u,h)=>h==="0"?c.o.u:h==="1"?c.o.l:void 0,set(u,h,f){if(h!=="1")throw new TypeError("props must be 1");return c.o.l=f,!0}})}copy(){return new gw(this.o,this.h,this.container,this.iteratorType)}},s=class extends e.default{constructor(l=[],c,u){super(c,u);let h=this;l.forEach(function(f){h.setElement(f[0],f[1])})}*K(l){l!==void 0&&(yield*this.K(l.U),yield[l.u,l.l],yield*this.K(l.W))}begin(){return new o(this.h.U||this.h,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this.h.W||this.h,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){if(this.i===0)return;let l=this.h.U;return[l.u,l.l]}back(){if(this.i===0)return;let l=this.h.W;return[l.u,l.l]}lowerBound(l){let c=this.X(this.Y,l);return new o(c,this.h,this)}upperBound(l){let c=this.Z(this.Y,l);return new o(c,this.h,this)}reverseLowerBound(l){let c=this.$(this.Y,l);return new o(c,this.h,this)}reverseUpperBound(l){let c=this.rr(this.Y,l);return new o(c,this.h,this)}setElement(l,c,u){return this.M(l,c,u)}find(l){let c=this.I(this.Y,l);return new o(c,this.h,this)}getElementByKey(l){return this.I(this.Y,l).l}union(l){let c=this;return l.forEach(function(u){c.setElement(u[0],u[1])}),this.i}[Symbol.iterator](){return this.K(this.Y)}},a=s;t.default=a}),mw=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=function(e){let r=typeof e;return r==="object"&&e!==null||r==="function"}}),yw=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.HashContainerIterator=t.HashContainer=void 0;var e,r=Xr(),n=(e=mw())&&e.t?e:{default:e},i=Jr(),o=class extends r.ContainerIterator{constructor(a,l,c){super(c),this.o=a,this.h=l,this.iteratorType===0?(this.pre=function(){return this.o.L===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.L,this},this.next=function(){return this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.B,this}):(this.pre=function(){return this.o.B===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.B,this},this.next=function(){return this.o===this.h&&(0,i.throwIteratorAccessError)(),this.o=this.o.L,this})}};t.HashContainerIterator=o;var s=class extends r.Container{constructor(){super(),this.H=[],this.g={},this.HASH_TAG=Symbol("@@HASH_TAG"),Object.setPrototypeOf(this.g,null),this.h={},this.h.L=this.h.B=this.p=this._=this.h}V(a){let{L:l,B:c}=a;l.B=c,c.L=l,a===this.p&&(this.p=c),a===this._&&(this._=l),this.i-=1}M(a,l,c){let u;if(c===void 0&&(c=(0,n.default)(a)),c){let h=a[this.HASH_TAG];if(h!==void 0)return this.H[h].l=l,this.i;Object.defineProperty(a,this.HASH_TAG,{value:this.H.length,configurable:!0}),u={u:a,l,L:this._,B:this.h},this.H.push(u)}else{let h=this.g[a];if(h)return h.l=l,this.i;u={u:a,l,L:this._,B:this.h},this.g[a]=u}return this.i===0?(this.p=u,this.h.B=u):this._.B=u,this._=u,this.h.L=u,++this.i}I(a,l){if(l===void 0&&(l=(0,n.default)(a)),l){let c=a[this.HASH_TAG];return c===void 0?this.h:this.H[c]}return this.g[a]||this.h}clear(){let a=this.HASH_TAG;this.H.forEach(function(l){delete l.u[a]}),this.H=[],this.g={},Object.setPrototypeOf(this.g,null),this.i=0,this.p=this._=this.h.L=this.h.B=this.h}eraseElementByKey(a,l){let c;if(l===void 0&&(l=(0,n.default)(a)),l){let u=a[this.HASH_TAG];if(u===void 0)return!1;delete a[this.HASH_TAG],c=this.H[u],delete this.H[u]}else{if(c=this.g[a],c===void 0)return!1;delete this.g[a]}return this.V(c),!0}eraseElementByIterator(a){let l=a.o;return l===this.h&&(0,i.throwIteratorAccessError)(),this.V(l),a.next()}eraseElementByPos(a){if(a<0||a>this.i-1)throw new RangeError;let l=this.p;for(;a--;)l=l.B;return this.V(l),this.i}};t.HashContainer=s}),lE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e=yw(),r=Jr(),n=class bw extends e.HashContainerIterator{constructor(a,l,c,u){super(a,l,u),this.container=c}get pointer(){return this.o===this.h&&(0,r.throwIteratorAccessError)(),this.o.u}copy(){return new bw(this.o,this.h,this.container,this.iteratorType)}},i=class extends e.HashContainer{constructor(s=[]){super();let a=this;s.forEach(function(l){a.insert(l)})}begin(){return new n(this.p,this.h,this)}end(){return new n(this.h,this.h,this)}rBegin(){return new n(this._,this.h,this,1)}rEnd(){return new n(this.h,this.h,this,1)}front(){return this.p.u}back(){return this._.u}insert(s,a){return this.M(s,void 0,a)}getElementByPos(s){if(s<0||s>this.i-1)throw new RangeError;let a=this.p;for(;s--;)a=a.B;return a.u}find(s,a){let l=this.I(s,a);return new n(l,this.h,this)}forEach(s){let a=0,l=this.p;for(;l!==this.h;)s(l.u,a++,this),l=l.B}[Symbol.iterator](){return(function*(){let s=this.p;for(;s!==this.h;)yield s.u,s=s.B}).bind(this)()}},o=i;t.default=o}),cE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),t.default=void 0;var e,r=yw(),n=(e=mw())&&e.t?e:{default:e},i=Jr(),o=class vw extends r.HashContainerIterator{constructor(c,u,h,f){super(c,u,f),this.container=h}get pointer(){this.o===this.h&&(0,i.throwIteratorAccessError)();let c=this;return new Proxy([],{get:(u,h)=>h==="0"?c.o.u:h==="1"?c.o.l:void 0,set(u,h,f){if(h!=="1")throw new TypeError("props must be 1");return c.o.l=f,!0}})}copy(){return new vw(this.o,this.h,this.container,this.iteratorType)}},s=class extends r.HashContainer{constructor(l=[]){super();let c=this;l.forEach(function(u){c.setElement(u[0],u[1])})}begin(){return new o(this.p,this.h,this)}end(){return new o(this.h,this.h,this)}rBegin(){return new o(this._,this.h,this,1)}rEnd(){return new o(this.h,this.h,this,1)}front(){if(this.i!==0)return[this.p.u,this.p.l]}back(){if(this.i!==0)return[this._.u,this._.l]}setElement(l,c,u){return this.M(l,c,u)}getElementByKey(l,c){if(c===void 0&&(c=(0,n.default)(l)),c){let h=l[this.HASH_TAG];return h!==void 0?this.H[h].l:void 0}let u=this.g[l];return u?u.l:void 0}getElementByPos(l){if(l<0||l>this.i-1)throw new RangeError;let c=this.p;for(;l--;)c=c.B;return[c.u,c.l]}find(l,c){let u=this.I(l,c);return new o(u,this.h,this)}forEach(l){let c=0,u=this.p;for(;u!==this.h;)l([u.u,u.l],c++,this),u=u.B}[Symbol.iterator](){return(function*(){let l=this.p;for(;l!==this.h;)yield[l.u,l.l],l=l.B}).bind(this)()}},a=s;t.default=a}),uE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"t",{value:!0}),Object.defineProperty(t,"Deque",{enumerable:!0,get:function(){return s.default}}),Object.defineProperty(t,"HashMap",{enumerable:!0,get:function(){return u.default}}),Object.defineProperty(t,"HashSet",{enumerable:!0,get:function(){return c.default}}),Object.defineProperty(t,"LinkList",{enumerable:!0,get:function(){return o.default}}),Object.defineProperty(t,"OrderedMap",{enumerable:!0,get:function(){return l.default}}),Object.defineProperty(t,"OrderedSet",{enumerable:!0,get:function(){return a.default}}),Object.defineProperty(t,"PriorityQueue",{enumerable:!0,get:function(){return n.default}}),Object.defineProperty(t,"Queue",{enumerable:!0,get:function(){return r.default}}),Object.defineProperty(t,"Stack",{enumerable:!0,get:function(){return e.default}}),Object.defineProperty(t,"Vector",{enumerable:!0,get:function(){return i.default}});var e=h(Jk()),r=h(eE()),n=h(tE()),i=h(rE()),o=h(nE()),s=h(iE()),a=h(sE()),l=h(aE()),c=h(lE()),u=h(cE());function h(f){return f&&f.t?f:{default:f}}}),hE=we((t,e)=>{me(),be(),ye();var r=uE().OrderedSet,n=qr()("number-allocator:trace"),i=qr()("number-allocator:error");function o(a,l){this.low=a,this.high=l}function s(a,l){if(!(this instanceof s))return new s(a,l);this.min=a,this.max=l,this.ss=new r([],(c,u)=>c.compare(u)),n("Create"),this.clear()}o.prototype.equals=function(a){return this.low===a.low&&this.high===a.high},o.prototype.compare=function(a){return this.lowa)&&(u===a?(this.ss.updateKeyByIterator(c,new o(u+1,h)),n("use():"+a),!0):h===a?(this.ss.updateKeyByIterator(c,new o(u,h-1)),n("use():"+a),!0):(this.ss.updateKeyByIterator(c,new o(a+1,h)),this.ss.insert(new o(u,a-1)),n("use():"+a),!0))}return n("use():failed"),!1},s.prototype.free=function(a){if(athis.max)return void i("free():"+a+" is out of range");let l=new o(a,a),c=this.ss.upperBound(l);if(c.equals(this.ss.end())){if(c.equals(this.ss.begin()))return void this.ss.insert(l);c.pre();let u=c.pointer.high;c.pointer.high+1===a?this.ss.updateKeyByIterator(c,new o(u,a)):this.ss.insert(l)}else if(c.equals(this.ss.begin()))if(a+1===c.pointer.low){let u=c.pointer.high;this.ss.updateKeyByIterator(c,new o(a,u))}else this.ss.insert(l);else{let u=c.pointer.low,h=c.pointer.high;c.pre();let f=c.pointer.low;c.pointer.high+1===a?a+1===u?(this.ss.eraseElementByIterator(c),this.ss.updateKeyByIterator(c,new o(f,h))):this.ss.updateKeyByIterator(c,new o(f,a)):a+1===u?(this.ss.eraseElementByIterator(c.next()),this.ss.insert(new o(a,h))):this.ss.insert(l)}n("free():"+a)},s.prototype.clear=function(){n("clear()"),this.ss.clear(),this.ss.insert(new o(this.min,this.max))},s.prototype.intervalCount=function(){return this.ss.size()},s.prototype.dump=function(){for(let a of this.ss);},e.exports=s}),ww=we((t,e)=>{me(),be(),ye();var r=hE();e.exports.NumberAllocator=r}),fE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e=Xk(),r=ww();t.default=class{constructor(n){_e(this,"aliasToTopic");_e(this,"topicToAlias");_e(this,"max");_e(this,"numberAllocator");_e(this,"length");n>0&&(this.aliasToTopic=new e.LRUCache({max:n}),this.topicToAlias={},this.numberAllocator=new r.NumberAllocator(1,n),this.max=n,this.length=0)}put(n,i){if(i===0||i>this.max)return!1;let o=this.aliasToTopic.get(i);return o&&delete this.topicToAlias[o],this.aliasToTopic.set(i,n),this.topicToAlias[n]=i,this.numberAllocator.use(i),this.length=this.aliasToTopic.size,!0}getTopicByAlias(n){return this.aliasToTopic.get(n)}getAliasByTopic(n){let i=this.topicToAlias[n];return typeof i<"u"&&this.aliasToTopic.get(i),i}clear(){this.aliasToTopic.clear(),this.topicToAlias={},this.numberAllocator.clear(),this.length=0}getLruAlias(){return this.numberAllocator.firstVacant()||[...this.aliasToTopic.keys()][this.aliasToTopic.size-1]}}}),dE=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t,"__esModule",{value:!0});var r=zl(),n=e(fE()),i=oo();t.default=(o,s)=>{o.log("_handleConnack");let{options:a}=o,l=a.protocolVersion===5?s.reasonCode:s.returnCode;if(clearTimeout(o.connackTimer),delete o.topicAliasSend,s.properties){if(s.properties.topicAliasMaximum){if(s.properties.topicAliasMaximum>65535)return void o.emit("error",new Error("topicAliasMaximum from broker is out of range"));s.properties.topicAliasMaximum>0&&(o.topicAliasSend=new n.default(s.properties.topicAliasMaximum))}s.properties.serverKeepAlive&&a.keepalive&&(a.keepalive=s.properties.serverKeepAlive),s.properties.maximumPacketSize&&(a.properties||(a.properties={}),a.properties.maximumPacketSize=s.properties.maximumPacketSize)}if(l===0)o.reconnecting=!1,o._onConnect(s);else if(l>0){let c=new i.ErrorWithReasonCode(`Connection refused: ${r.ReasonCodes[l]}`,l);o.emit("error",c),o.options.reconnectOnConnackError&&o._cleanUp(!0)}}}),pE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=(e,r,n)=>{e.log("handling pubrel packet");let i=typeof n<"u"?n:e.noop,{messageId:o}=r,s={cmd:"pubcomp",messageId:o};e.incomingStore.get(r,(a,l)=>{a?e._sendPacket(s,i):(e.emit("message",l.topic,l.payload,l),e.handleMessage(l,c=>{if(c)return i(c);e.incomingStore.del(l,e.noop),e._sendPacket(s,i)}))})}}),gE=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(a){return a&&a.__esModule?a:{default:a}};Object.defineProperty(t,"__esModule",{value:!0});var r=e(Gk()),n=e(Zk()),i=e(dE()),o=e(zl()),s=e(pE());t.default=(a,l,c)=>{let{options:u}=a;if(u.protocolVersion===5&&u.properties&&u.properties.maximumPacketSize&&u.properties.maximumPacketSize{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(){_e(this,"nextId");this.nextId=Math.max(1,Math.floor(65535*Math.random()))}allocate(){let e=this.nextId++;return this.nextId===65536&&(this.nextId=1),e}getLastAllocated(){return this.nextId===1?65535:this.nextId-1}register(e){return!0}deallocate(e){}clear(){}}}),mE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.default=class{constructor(e){_e(this,"aliasToTopic");_e(this,"max");_e(this,"length");this.aliasToTopic={},this.max=e}put(e,r){return!(r===0||r>this.max)&&(this.aliasToTopic[r]=e,this.length=Object.keys(this.aliasToTopic).length,!0)}getTopicByAlias(e){return this.aliasToTopic[e]}clear(){this.aliasToTopic={}}}}),yE=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(o){return o&&o.__esModule?o:{default:o}};Object.defineProperty(t,"__esModule",{value:!0}),t.TypedEventEmitter=void 0;var r=e((fi(),Xe(Nn))),n=oo(),i=class{};t.TypedEventEmitter=i,(0,n.applyMixin)(i,r.default)}),Hl=we((t,e)=>{function r(n){return e.exports=r=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(i){return typeof i}:function(i){return i&&typeof Symbol=="function"&&i.constructor===Symbol&&i!==Symbol.prototype?"symbol":typeof i},e.exports.__esModule=!0,e.exports.default=e.exports,r(n)}me(),be(),ye(),e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports}),bE=we((t,e)=>{me(),be(),ye();var r=Hl().default;e.exports=function(n,i){if(r(n)!="object"||!n)return n;var o=n[Symbol.toPrimitive];if(o!==void 0){var s=o.call(n,i||"default");if(r(s)!="object")return s;throw new TypeError("@@toPrimitive must return a primitive value.")}return(i==="string"?String:Number)(n)},e.exports.__esModule=!0,e.exports.default=e.exports}),vE=we((t,e)=>{me(),be(),ye();var r=Hl().default,n=bE();e.exports=function(i){var o=n(i,"string");return r(o)=="symbol"?o:o+""},e.exports.__esModule=!0,e.exports.default=e.exports}),wE=we((t,e)=>{me(),be(),ye();var r=vE();e.exports=function(n,i,o){return(i=r(i))in n?Object.defineProperty(n,i,{value:o,enumerable:!0,configurable:!0,writable:!0}):n[i]=o,n},e.exports.__esModule=!0,e.exports.default=e.exports}),_E=we((t,e)=>{me(),be(),ye(),e.exports=function(r){if(Array.isArray(r))return r},e.exports.__esModule=!0,e.exports.default=e.exports}),xE=we((t,e)=>{me(),be(),ye(),e.exports=function(r,n){var i=r==null?null:typeof Symbol<"u"&&r[Symbol.iterator]||r["@@iterator"];if(i!=null){var o,s,a,l,c=[],u=!0,h=!1;try{if(a=(i=i.call(r)).next,n===0){if(Object(i)!==i)return;u=!1}else for(;!(u=(o=a.call(i)).done)&&(c.push(o.value),c.length!==n);u=!0);}catch(f){h=!0,s=f}finally{try{if(!u&&i.return!=null&&(l=i.return(),Object(l)!==l))return}finally{if(h)throw s}}return c}},e.exports.__esModule=!0,e.exports.default=e.exports}),SE=we((t,e)=>{me(),be(),ye(),e.exports=function(r,n){(n==null||n>r.length)&&(n=r.length);for(var i=0,o=Array(n);i{me(),be(),ye();var r=SE();e.exports=function(n,i){if(n){if(typeof n=="string")return r(n,i);var o={}.toString.call(n).slice(8,-1);return o==="Object"&&n.constructor&&(o=n.constructor.name),o==="Map"||o==="Set"?Array.from(n):o==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?r(n,i):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports}),EE=we((t,e)=>{me(),be(),ye(),e.exports=function(){throw new TypeError(`Invalid attempt to destructure non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)},e.exports.__esModule=!0,e.exports.default=e.exports}),OE=we((t,e)=>{me(),be(),ye();var r=_E(),n=xE(),i=kE(),o=EE();e.exports=function(s,a){return r(s)||n(s,a)||i(s,a)||o()},e.exports.__esModule=!0,e.exports.default=e.exports}),xw=we((t,e)=>{var r,n;me(),be(),ye(),r=t,n=function(i){var o,s=Number.MAX_SAFE_INTEGER===void 0?9007199254740991:Number.MAX_SAFE_INTEGER,a=536870912,l=2*a,c=new WeakMap,u=function(f,d){return function(y){var g=d.get(y),p=g===void 0?y.size:gs)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;y.has(p);)p=Math.floor(Math.random()*s);return f(y,p)}}((o=c,function(f,d){return o.set(f,d),d}),c),h=function(f){return function(d){var y=f(d);return d.add(y),y}}(u);i.addUniqueNumber=h,i.generateUniqueNumber=u},typeof t=="object"&&typeof e<"u"?n(t):typeof define=="function"&&define.amd?define(["exports"],n):n((r=typeof globalThis<"u"?globalThis:r||self).fastUniqueNumbers={})}),ME=we((t,e)=>{function r(n,i,o,s,a,l,c){try{var u=n[l](c),h=u.value}catch(f){return void o(f)}u.done?i(h):Promise.resolve(h).then(s,a)}me(),be(),ye(),e.exports=function(n){return function(){var i=this,o=arguments;return new Promise(function(s,a){var l=n.apply(i,o);function c(h){r(l,s,a,c,u,"next",h)}function u(h){r(l,s,a,c,u,"throw",h)}c(void 0)})}},e.exports.__esModule=!0,e.exports.default=e.exports}),Sw=we((t,e)=>{me(),be(),ye(),e.exports=function(r,n){this.v=r,this.k=n},e.exports.__esModule=!0,e.exports.default=e.exports}),kw=we((t,e)=>{function r(n,i,o,s){var a=Object.defineProperty;try{a({},"",{})}catch{a=0}e.exports=r=function(l,c,u,h){function f(d,y){r(l,d,function(g){return this._invoke(d,y,g)})}c?a?a(l,c,{value:u,enumerable:!h,configurable:!h,writable:!h}):l[c]=u:(f("next",0),f("throw",1),f("return",2))},e.exports.__esModule=!0,e.exports.default=e.exports,r(n,i,o,s)}me(),be(),ye(),e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports}),Ew=we((t,e)=>{me(),be(),ye();var r=kw();function n(){var i,o,s=typeof Symbol=="function"?Symbol:{},a=s.iterator||"@@iterator",l=s.toStringTag||"@@toStringTag";function c(b,v,S,k){var M=v&&v.prototype instanceof h?v:h,A=Object.create(M.prototype);return r(A,"_invoke",function(I,T,_){var x,O,N,G=0,U=_||[],z=!1,q={p:0,n:0,v:i,a:B,f:B.bind(i,4),d:function(ne,H){return x=ne,O=0,N=i,q.n=H,u}};function B(ne,H){for(O=ne,N=H,o=0;!z&&G&&!Q&&o3?(Q=Z===H)&&(N=F[(O=F[4])?5:(O=3,3)],F[4]=F[5]=i):F[0]<=V&&((Q=ne<2&&VH||H>Z)&&(F[4]=ne,F[5]=H,q.n=Z,O=0))}if(Q||ne>1)return u;throw z=!0,H}return function(ne,H,Q){if(G>1)throw TypeError("Generator is already running");for(z&&H===1&&B(H,Q),O=H,N=Q;(o=O<2?i:N)||!z;){x||(O?O<3?(O>1&&(q.n=-1),B(O,N)):q.n=N:q.v=N);try{if(G=2,x){if(O||(ne="next"),o=x[ne]){if(!(o=o.call(x,N)))throw TypeError("iterator result is not an object");if(!o.done)return o;N=o.value,O<2&&(O=0)}else O===1&&(o=x.return)&&o.call(x),O<2&&(N=TypeError("The iterator does not provide a '"+ne+"' method"),O=1);x=i}else if((o=(z=q.n<0)?N:I.call(T,q))!==u)break}catch(F){x=i,O=1,N=F}finally{G=1}}return{value:o,done:z}}}(b,S,k),!0),A}var u={};function h(){}function f(){}function d(){}o=Object.getPrototypeOf;var y=[][a]?o(o([][a]())):(r(o={},a,function(){return this}),o),g=d.prototype=h.prototype=Object.create(y);function p(b){return Object.setPrototypeOf?Object.setPrototypeOf(b,d):(b.__proto__=d,r(b,l,"GeneratorFunction")),b.prototype=Object.create(g),b}return f.prototype=d,r(g,"constructor",d),r(d,"constructor",f),f.displayName="GeneratorFunction",r(d,l,"GeneratorFunction"),r(g),r(g,l,"Generator"),r(g,a,function(){return this}),r(g,"toString",function(){return"[object Generator]"}),(e.exports=n=function(){return{w:c,m:p}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=n,e.exports.__esModule=!0,e.exports.default=e.exports}),Ow=we((t,e)=>{me(),be(),ye();var r=Sw(),n=kw();e.exports=function i(o,s){function a(c,u,h,f){try{var d=o[c](u),y=d.value;return y instanceof r?s.resolve(y.v).then(function(g){a("next",g,h,f)},function(g){a("throw",g,h,f)}):s.resolve(y).then(function(g){d.value=g,h(d)},function(g){return a("throw",g,h,f)})}catch(g){f(g)}}var l;this.next||(n(i.prototype),n(i.prototype,typeof Symbol=="function"&&Symbol.asyncIterator||"@asyncIterator",function(){return this})),n(this,"_invoke",function(c,u,h){function f(){return new s(function(d,y){a(c,h,d,y)})}return l=l?l.then(f,f):f()},!0)},e.exports.__esModule=!0,e.exports.default=e.exports}),Mw=we((t,e)=>{me(),be(),ye();var r=Ew(),n=Ow();e.exports=function(i,o,s,a,l){return new n(r().w(i,o,s,a),l||Promise)},e.exports.__esModule=!0,e.exports.default=e.exports}),AE=we((t,e)=>{me(),be(),ye();var r=Mw();e.exports=function(n,i,o,s,a){var l=r(n,i,o,s,a);return l.next().then(function(c){return c.done?c.value:l.next()})},e.exports.__esModule=!0,e.exports.default=e.exports}),TE=we((t,e)=>{me(),be(),ye(),e.exports=function(r){var n=Object(r),i=[];for(var o in n)i.unshift(o);return function s(){for(;i.length;)if((o=i.pop())in n)return s.value=o,s.done=!1,s;return s.done=!0,s}},e.exports.__esModule=!0,e.exports.default=e.exports}),IE=we((t,e)=>{me(),be(),ye();var r=Hl().default;e.exports=function(n){if(n!=null){var i=n[typeof Symbol=="function"&&Symbol.iterator||"@@iterator"],o=0;if(i)return i.call(n);if(typeof n.next=="function")return n;if(!isNaN(n.length))return{next:function(){return n&&o>=n.length&&(n=void 0),{value:n&&n[o++],done:!n}}}}throw new TypeError(r(n)+" is not iterable")},e.exports.__esModule=!0,e.exports.default=e.exports}),PE=we((t,e)=>{me(),be(),ye();var r=Sw(),n=Ew(),i=AE(),o=Mw(),s=Ow(),a=TE(),l=IE();function c(){var u=n(),h=u.m(c),f=(Object.getPrototypeOf?Object.getPrototypeOf(h):h.__proto__).constructor;function d(p){var b=typeof p=="function"&&p.constructor;return!!b&&(b===f||(b.displayName||b.name)==="GeneratorFunction")}var y={throw:1,return:2,break:3,continue:3};function g(p){var b,v;return function(S){b||(b={stop:function(){return v(S.a,2)},catch:function(){return S.v},abrupt:function(k,M){return v(S.a,y[k],M)},delegateYield:function(k,M,A){return b.resultName=M,v(S.d,l(k),A)},finish:function(k){return v(S.f,k)}},v=function(k,M,A){S.p=b.prev,S.n=b.next;try{return k(M,A)}finally{b.next=S.n}}),b.resultName&&(b[b.resultName]=S.v,b.resultName=void 0),b.sent=S.v,b.next=S.n;try{return p.call(this,b)}finally{S.p=b.prev,S.n=b.next}}}return(e.exports=c=function(){return{wrap:function(p,b,v,S){return u.w(g(p),b,v,S&&S.reverse())},isGeneratorFunction:d,mark:u.m,awrap:function(p,b){return new r(p,b)},AsyncIterator:s,async:function(p,b,v,S,k){return(d(b)?o:i)(g(p),b,v,S,k)},keys:a,values:l}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=c,e.exports.__esModule=!0,e.exports.default=e.exports}),CE=we((t,e)=>{me(),be(),ye();var r=PE()();e.exports=r;try{regeneratorRuntime=r}catch{typeof globalThis=="object"?globalThis.regeneratorRuntime=r:Function("r","regeneratorRuntime = r")(r)}}),RE=we((t,e)=>{var r,n;me(),be(),ye(),r=t,n=function(i,o,s,a,l,c){var u=new WeakMap;function h(p,b){var v=Object.keys(p);if(Object.getOwnPropertySymbols){var S=Object.getOwnPropertySymbols(p);b&&(S=S.filter(function(k){return Object.getOwnPropertyDescriptor(p,k).enumerable})),v.push.apply(v,S)}return v}function f(p){for(var b=1;b1&&arguments[1]!==void 0?arguments[1]:null,U=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];return new Promise(function(z,q){var B=a.generateUniqueNumber(S);S.set(B,{reject:q,resolve:z}),G===null?v.postMessage({id:B,method:N},U):v.postMessage({id:B,method:N,params:G},U)})},M=function(N,G){var U=arguments.length>2&&arguments[2]!==void 0?arguments[2]:[];v.postMessage({id:null,method:N,params:G},U)},A={},I=0,T=Object.entries(b);I{var r,n;me(),be(),ye(),r=t,n=function(i,o,s,a){var l=new Map([[0,null]]),c=new Map([[0,null]]),u=s.createBroker({clearInterval:function(h){var f=h.call;return function(d){o(l.get(d))==="symbol"&&(l.set(d,null),f("clear",{timerId:d,timerType:"interval"}).then(function(){l.delete(d)}))}},clearTimeout:function(h){var f=h.call;return function(d){o(c.get(d))==="symbol"&&(c.set(d,null),f("clear",{timerId:d,timerType:"timeout"}).then(function(){c.delete(d)}))}},setInterval:function(h){var f=h.call;return function(d){for(var y=arguments.length>1&&arguments[1]!==void 0?arguments[1]:0,g=arguments.length,p=new Array(g>2?g-2:0),b=2;b1&&arguments[1]!==void 0?arguments[1]:0,g=arguments.length,p=new Array(g>2?g-2:0),b=2;b{var r,n;me(),be(),ye(),r=t,n=function(i,o){var s=function(a,l){var c=null;return function(){if(c!==null)return c;var u=new Blob([l],{type:"application/javascript; charset=utf-8"}),h=URL.createObjectURL(u);return c=a(h),setTimeout(function(){return URL.revokeObjectURL(h)}),c}}(o.load,`(()=>{var e={45:(e,t,r)=>{var n=r(738).default;e.exports=function(e,t){if("object"!=n(e)||!e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var o=r.call(e,t||"default");if("object"!=n(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)},e.exports.__esModule=!0,e.exports.default=e.exports},79:e=>{e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=Array(t);r{var n=r(79);e.exports=function(e,t){if(e){if("string"==typeof e)return n(e,t);var r={}.toString.call(e).slice(8,-1);return"Object"===r&&e.constructor&&(r=e.constructor.name),"Map"===r||"Set"===r?Array.from(e):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(e,t):void 0}},e.exports.__esModule=!0,e.exports.default=e.exports},156:e=>{e.exports=function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,o,u,a,i=[],s=!0,c=!1;try{if(u=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;s=!1}else for(;!(s=(n=u.call(r)).done)&&(i.push(n.value),i.length!==t);s=!0);}catch(e){c=!0,o=e}finally{try{if(!s&&null!=r.return&&(a=r.return(),Object(a)!==a))return}finally{if(c)throw o}}return i}},e.exports.__esModule=!0,e.exports.default=e.exports},172:e=>{e.exports=function(e,t){this.v=e,this.k=t},e.exports.__esModule=!0,e.exports.default=e.exports},293:e=>{function t(e,t,r,n,o,u,a){try{var i=e[u](a),s=i.value}catch(e){return void r(e)}i.done?t(s):Promise.resolve(s).then(n,o)}e.exports=function(e){return function(){var r=this,n=arguments;return new Promise((function(o,u){var a=e.apply(r,n);function i(e){t(a,o,u,i,s,"next",e)}function s(e){t(a,o,u,i,s,"throw",e)}i(void 0)}))}},e.exports.__esModule=!0,e.exports.default=e.exports},373:e=>{e.exports=function(e){var t=Object(e),r=[];for(var n in t)r.unshift(n);return function e(){for(;r.length;)if((n=r.pop())in t)return e.value=n,e.done=!1,e;return e.done=!0,e}},e.exports.__esModule=!0,e.exports.default=e.exports},389:function(e,t){!function(e){"use strict";var t=function(e){return function(t){var r=e(t);return t.add(r),r}},r=function(e){return function(t,r){return e.set(t,r),r}},n=void 0===Number.MAX_SAFE_INTEGER?9007199254740991:Number.MAX_SAFE_INTEGER,o=536870912,u=2*o,a=function(e,t){return function(r){var a=t.get(r),i=void 0===a?r.size:an)throw new Error("Congratulations, you created a collection of unique numbers which uses all available integers!");for(;r.has(i);)i=Math.floor(Math.random()*n);return e(r,i)}},i=new WeakMap,s=r(i),c=a(s,i),f=t(c);e.addUniqueNumber=f,e.generateUniqueNumber=c}(t)},472:function(e,t,r){!function(e,t,r,n){"use strict";var o=function(e,t){return function(r){var o=t.get(r);if(void 0===o)return Promise.resolve(!1);var u=n(o,2),a=u[0],i=u[1];return e(a),t.delete(r),i(!1),Promise.resolve(!0)}},u=function(e,t){var r=function(n,o,u,a){var i=n-e.now();i>0?o.set(a,[t(r,i,n,o,u,a),u]):(o.delete(a),u(!0))};return r},a=function(e,t,r,n){return function(o,u,a){var i=o+u-t.timeOrigin,s=i-t.now();return new Promise((function(t){e.set(a,[r(n,s,i,e,t,a),t])}))}},i=new Map,s=o(globalThis.clearTimeout,i),c=new Map,f=o(globalThis.clearTimeout,c),l=u(performance,globalThis.setTimeout),p=a(i,performance,globalThis.setTimeout,l),d=a(c,performance,globalThis.setTimeout,l);r.createWorker(self,{clear:function(){var r=e(t.mark((function e(r){var n,o,u;return t.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=r.timerId,o=r.timerType,e.next=1,"interval"===o?s(n):f(n);case 1:return u=e.sent,e.abrupt("return",{result:u});case 2:case"end":return e.stop()}}),e)})));function n(e){return r.apply(this,arguments)}return n}(),set:function(){var r=e(t.mark((function e(r){var n,o,u,a,i;return t.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=r.delay,o=r.now,u=r.timerId,a=r.timerType,e.next=1,("interval"===a?p:d)(n,o,u);case 1:return i=e.sent,e.abrupt("return",{result:i});case 2:case"end":return e.stop()}}),e)})));function n(e){return r.apply(this,arguments)}return n}()})}(r(293),r(756),r(623),r(715))},546:e=>{function t(r,n,o,u){var a=Object.defineProperty;try{a({},"",{})}catch(r){a=0}e.exports=t=function(e,r,n,o){if(r)a?a(e,r,{value:n,enumerable:!o,configurable:!o,writable:!o}):e[r]=n;else{var u=function(r,n){t(e,r,(function(e){return this._invoke(r,n,e)}))};u("next",0),u("throw",1),u("return",2)}},e.exports.__esModule=!0,e.exports.default=e.exports,t(r,n,o,u)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},579:(e,t,r)=>{var n=r(738).default;e.exports=function(e){if(null!=e){var t=e["function"==typeof Symbol&&Symbol.iterator||"@@iterator"],r=0;if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length))return{next:function(){return e&&r>=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}}}throw new TypeError(n(e)+" is not iterable")},e.exports.__esModule=!0,e.exports.default=e.exports},623:function(e,t,r){!function(e,t,r,n,o){"use strict";var u={INTERNAL_ERROR:-32603,INVALID_PARAMS:-32602,METHOD_NOT_FOUND:-32601},a=function(e,t){return Object.assign(new Error(e),{status:t})},i=function(e){return a('The requested method called "'.concat(e,'" is not supported.'),u.METHOD_NOT_FOUND)},s=function(e){return a('The handler of the method called "'.concat(e,'" returned no required result.'),u.INTERNAL_ERROR)},c=function(e){return a('The handler of the method called "'.concat(e,'" returned an unexpected result.'),u.INTERNAL_ERROR)},f=function(e){return a('The specified parameter called "portId" with the given value "'.concat(e,'" does not identify a port connected to this worker.'),u.INVALID_PARAMS)},l=function(e,n){return function(){var o=t(r.mark((function t(o){var u,a,f,l,p,d,v,x,y,b,h,m,_,g,w;return r.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(u=o.data,a=u.id,f=u.method,l=u.params,p=n[f],t.prev=1,void 0!==p){t.next=2;break}throw i(f);case 2:if(void 0!==(d=void 0===l?p():p(l))){t.next=3;break}throw s(f);case 3:if(!(d instanceof Promise)){t.next=5;break}return t.next=4,d;case 4:g=t.sent,t.next=6;break;case 5:g=d;case 6:if(v=g,null!==a){t.next=8;break}if(void 0===v.result){t.next=7;break}throw c(f);case 7:t.next=10;break;case 8:if(void 0!==v.result){t.next=9;break}throw c(f);case 9:x=v.result,y=v.transferables,b=void 0===y?[]:y,e.postMessage({id:a,result:x},b);case 10:t.next=12;break;case 11:t.prev=11,w=t.catch(1),h=w.message,m=w.status,_=void 0===m?-32603:m,e.postMessage({error:{code:_,message:h},id:a});case 12:case"end":return t.stop()}}),t,null,[[1,11]])})));return function(e){return o.apply(this,arguments)}}()},p=function(){return new Promise((function(e){var t=new ArrayBuffer(0),r=new MessageChannel,n=r.port1,o=r.port2;n.onmessage=function(t){var r=t.data;return e(null!==r)},o.postMessage(t,[t])}))};function d(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function v(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:function(){return!0}),n=l(e,r);return e.addEventListener("message",n),function(){return e.removeEventListener("message",n)}};e.createWorker=b,e.isSupported=p}(t,r(293),r(756),r(693),r(389))},633:(e,t,r)=>{var n=r(172),o=r(993),u=r(869),a=r(887),i=r(791),s=r(373),c=r(579);function f(){"use strict";var t=o(),r=t.m(f),l=(Object.getPrototypeOf?Object.getPrototypeOf(r):r.__proto__).constructor;function p(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===l||"GeneratorFunction"===(t.displayName||t.name))}var d={throw:1,return:2,break:3,continue:3};function v(e){var t,r;return function(n){t||(t={stop:function(){return r(n.a,2)},catch:function(){return n.v},abrupt:function(e,t){return r(n.a,d[e],t)},delegateYield:function(e,o,u){return t.resultName=o,r(n.d,c(e),u)},finish:function(e){return r(n.f,e)}},r=function(e,r,o){n.p=t.prev,n.n=t.next;try{return e(r,o)}finally{t.next=n.n}}),t.resultName&&(t[t.resultName]=n.v,t.resultName=void 0),t.sent=n.v,t.next=n.n;try{return e.call(this,t)}finally{n.p=t.prev,n.n=t.next}}}return(e.exports=f=function(){return{wrap:function(e,r,n,o){return t.w(v(e),r,n,o&&o.reverse())},isGeneratorFunction:p,mark:t.m,awrap:function(e,t){return new n(e,t)},AsyncIterator:i,async:function(e,t,r,n,o){return(p(t)?a:u)(v(e),t,r,n,o)},keys:s,values:c}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=f,e.exports.__esModule=!0,e.exports.default=e.exports},693:(e,t,r)=>{var n=r(736);e.exports=function(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e},e.exports.__esModule=!0,e.exports.default=e.exports},715:(e,t,r)=>{var n=r(987),o=r(156),u=r(122),a=r(752);e.exports=function(e,t){return n(e)||o(e,t)||u(e,t)||a()},e.exports.__esModule=!0,e.exports.default=e.exports},736:(e,t,r)=>{var n=r(738).default,o=r(45);e.exports=function(e){var t=o(e,"string");return"symbol"==n(t)?t:t+""},e.exports.__esModule=!0,e.exports.default=e.exports},738:e=>{function t(r){return e.exports=t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.__esModule=!0,e.exports.default=e.exports,t(r)}e.exports=t,e.exports.__esModule=!0,e.exports.default=e.exports},752:e=>{e.exports=function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")},e.exports.__esModule=!0,e.exports.default=e.exports},756:(e,t,r)=>{var n=r(633)();e.exports=n;try{regeneratorRuntime=n}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=n:Function("r","regeneratorRuntime = r")(n)}},791:(e,t,r)=>{var n=r(172),o=r(546);e.exports=function e(t,r){function u(e,o,a,i){try{var s=t[e](o),c=s.value;return c instanceof n?r.resolve(c.v).then((function(e){u("next",e,a,i)}),(function(e){u("throw",e,a,i)})):r.resolve(c).then((function(e){s.value=e,a(s)}),(function(e){return u("throw",e,a,i)}))}catch(e){i(e)}}var a;this.next||(o(e.prototype),o(e.prototype,"function"==typeof Symbol&&Symbol.asyncIterator||"@asyncIterator",(function(){return this}))),o(this,"_invoke",(function(e,t,n){function o(){return new r((function(t,r){u(e,n,t,r)}))}return a=a?a.then(o,o):o()}),!0)},e.exports.__esModule=!0,e.exports.default=e.exports},869:(e,t,r)=>{var n=r(887);e.exports=function(e,t,r,o,u){var a=n(e,t,r,o,u);return a.next().then((function(e){return e.done?e.value:a.next()}))},e.exports.__esModule=!0,e.exports.default=e.exports},887:(e,t,r)=>{var n=r(993),o=r(791);e.exports=function(e,t,r,u,a){return new o(n().w(e,t,r,u),a||Promise)},e.exports.__esModule=!0,e.exports.default=e.exports},987:e=>{e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports},993:(e,t,r)=>{var n=r(546);function o(){var t,r,u="function"==typeof Symbol?Symbol:{},a=u.iterator||"@@iterator",i=u.toStringTag||"@@toStringTag";function s(e,o,u,a){var i=o&&o.prototype instanceof f?o:f,s=Object.create(i.prototype);return n(s,"_invoke",function(e,n,o){var u,a,i,s=0,f=o||[],l=!1,p={p:0,n:0,v:t,a:d,f:d.bind(t,4),d:function(e,r){return u=e,a=0,i=t,p.n=r,c}};function d(e,n){for(a=e,i=n,r=0;!l&&s&&!o&&r3?(o=v===n)&&(i=u[(a=u[4])?5:(a=3,3)],u[4]=u[5]=t):u[0]<=d&&((o=e<2&&dn||n>v)&&(u[4]=e,u[5]=n,p.n=v,a=0))}if(o||e>1)return c;throw l=!0,n}return function(o,f,v){if(s>1)throw TypeError("Generator is already running");for(l&&1===f&&d(f,v),a=f,i=v;(r=a<2?t:i)||!l;){u||(a?a<3?(a>1&&(p.n=-1),d(a,i)):p.n=i:p.v=i);try{if(s=2,u){if(a||(o="next"),r=u[o]){if(!(r=r.call(u,i)))throw TypeError("iterator result is not an object");if(!r.done)return r;i=r.value,a<2&&(a=0)}else 1===a&&(r=u.return)&&r.call(u),a<2&&(i=TypeError("The iterator does not provide a '"+o+"' method"),a=1);u=t}else if((r=(l=p.n<0)?i:e.call(n,p))!==c)break}catch(e){u=t,a=1,i=e}finally{s=1}}return{value:r,done:l}}}(e,u,a),!0),s}var c={};function f(){}function l(){}function p(){}r=Object.getPrototypeOf;var d=[][a]?r(r([][a]())):(n(r={},a,(function(){return this})),r),v=p.prototype=f.prototype=Object.create(d);function x(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,p):(e.__proto__=p,n(e,i,"GeneratorFunction")),e.prototype=Object.create(v),e}return l.prototype=p,n(v,"constructor",p),n(p,"constructor",l),l.displayName="GeneratorFunction",n(p,i,"GeneratorFunction"),n(v),n(v,i,"Generator"),n(v,a,(function(){return this})),n(v,"toString",(function(){return"[object Generator]"})),(e.exports=o=function(){return{w:s,m:x}},e.exports.__esModule=!0,e.exports.default=e.exports)()}e.exports=o,e.exports.__esModule=!0,e.exports.default=e.exports}},t={};function r(n){var o=t[n];if(void 0!==o)return o.exports;var u=t[n]={exports:{}};return e[n].call(u.exports,u,u.exports,r),u.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{"use strict";r(472)})()})();`);i.clearInterval=function(a){return s().clearInterval(a)},i.clearTimeout=function(a){return s().clearTimeout(a)},i.setInterval=function(){var a;return(a=s()).setInterval.apply(a,arguments)},i.setTimeout=function(){var a;return(a=s()).setTimeout.apply(a,arguments)}},typeof t=="object"&&typeof e<"u"?n(t,DE()):typeof define=="function"&&define.amd?define(["exports","worker-timers-broker"],n):n((r=typeof globalThis<"u"?globalThis:r||self).workerTimers={},r.workerTimersBroker)}),ql=we(t=>{var i;me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.isReactNativeBrowser=t.isWebWorker=void 0;var e=()=>{var o,s;return!(typeof self!="object"||!((s=(o=self==null?void 0:self.constructor)==null?void 0:o.name)!=null&&s.includes("WorkerGlobalScope")))},r=()=>typeof navigator<"u"&&navigator.product==="ReactNative",n=typeof window<"u"&&(typeof navigator<"u"&&((i=navigator.userAgent)==null?void 0:i.toLowerCase().indexOf(" electron/"))>-1&&($e!=null&&$e.versions)?!Object.prototype.hasOwnProperty.call($e.versions,"electron"):typeof window.document<"u")||e()||r();t.isWebWorker=e(),t.isReactNativeBrowser=r(),t.default=n}),NE=we(t=>{me(),be(),ye();var e,r=t&&t.__createBinding||(Object.create?function(c,u,h,f){f===void 0&&(f=h);var d=Object.getOwnPropertyDescriptor(u,h);(!d||("get"in d?!u.__esModule:d.writable||d.configurable))&&(d={enumerable:!0,get:function(){return u[h]}}),Object.defineProperty(c,f,d)}:function(c,u,h,f){f===void 0&&(f=h),c[f]=u[h]}),n=t&&t.__setModuleDefault||(Object.create?function(c,u){Object.defineProperty(c,"default",{enumerable:!0,value:u})}:function(c,u){c.default=u}),i=t&&t.__importStar||(e=function(c){return e=Object.getOwnPropertyNames||function(u){var h=[];for(var f in u)Object.prototype.hasOwnProperty.call(u,f)&&(h[h.length]=f);return h},e(c)},function(c){if(c&&c.__esModule)return c;var u={};if(c!=null)for(var h=e(c),f=0;fsetInterval(c,u),clear:c=>clearInterval(c)};t.default=c=>{switch(c){case"native":return l;case"worker":return a;default:return!s.default||s.isWebWorker||s.isReactNativeBrowser?l:a}}}),Aw=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var r=e(NE());t.default=class{constructor(n,i){_e(this,"_keepalive");_e(this,"timerId");_e(this,"timer");_e(this,"destroyed",!1);_e(this,"counter");_e(this,"client");_e(this,"_keepaliveTimeoutTimestamp");_e(this,"_intervalEvery");this.client=n,this.timer=typeof i=="object"&&"set"in i&&"clear"in i?i:(0,r.default)(i),this.setKeepalive(n.options.keepalive)}get keepaliveTimeoutTimestamp(){return this._keepaliveTimeoutTimestamp}get intervalEvery(){return this._intervalEvery}get keepalive(){return this._keepalive}clear(){this.timerId&&(this.timer.clear(this.timerId),this.timerId=null)}setKeepalive(n){if(n*=1e3,isNaN(n)||n<=0||n>2147483647)throw new Error(`Keepalive value must be an integer between 0 and 2147483647. Provided value is ${n}`);this._keepalive=n,this.reschedule(),this.client.log(`KeepaliveManager: set keepalive to ${n}ms`)}destroy(){this.clear(),this.destroyed=!0}reschedule(){if(this.destroyed)return;this.clear(),this.counter=0;let n=Math.ceil(1.5*this._keepalive);this._keepaliveTimeoutTimestamp=Date.now()+n,this._intervalEvery=Math.ceil(this._keepalive/2),this.timerId=this.timer.set(()=>{this.destroyed||(this.counter+=1,this.counter===2?this.client.sendPing():this.counter>2&&this.client.onKeepaliveTimeout())},this._intervalEvery)}}}),Th=we(t=>{var A;me(),be(),ye();var e,r=t&&t.__createBinding||(Object.create?function(I,T,_,x){x===void 0&&(x=_);var O=Object.getOwnPropertyDescriptor(T,_);(!O||("get"in O?!T.__esModule:O.writable||O.configurable))&&(O={enumerable:!0,get:function(){return T[_]}}),Object.defineProperty(I,x,O)}:function(I,T,_,x){x===void 0&&(x=_),I[x]=T[_]}),n=t&&t.__setModuleDefault||(Object.create?function(I,T){Object.defineProperty(I,"default",{enumerable:!0,value:T})}:function(I,T){I.default=T}),i=t&&t.__importStar||(e=function(I){return e=Object.getOwnPropertyNames||function(T){var _=[];for(var x in T)Object.prototype.hasOwnProperty.call(T,x)&&(_[_.length]=x);return _},e(I)},function(I){if(I&&I.__esModule)return I;var T={};if(I!=null)for(var _=e(I),x=0;x<_.length;x++)_[x]!=="default"&&r(T,I,_[x]);return n(T,I),T}),o=t&&t.__importDefault||function(I){return I&&I.__esModule?I:{default:I}};Object.defineProperty(t,"__esModule",{value:!0});var s=o(Hk()),a=pi(),l=o(Yk()),c=o(qr()),u=i(Kk()),h=o(iw()),f=o(gE()),d=o(_w()),y=o(mE()),g=oo(),p=yE(),b=o(Aw()),v=i(ql()),S=globalThis.setImmediate||((...I)=>{let T=I.shift();(0,g.nextTick)(()=>{T(...I)})}),k={keepalive:60,reschedulePings:!0,protocolId:"MQTT",protocolVersion:4,reconnectPeriod:1e3,connectTimeout:3e4,clean:!0,resubscribe:!0,subscribeBatchSize:null,writeCache:!0,timerVariant:"auto"},M=(A=class extends p.TypedEventEmitter{constructor(_,x){super();_e(this,"connected");_e(this,"disconnecting");_e(this,"disconnected");_e(this,"reconnecting");_e(this,"incomingStore");_e(this,"outgoingStore");_e(this,"options");_e(this,"queueQoSZero");_e(this,"_reconnectCount");_e(this,"log");_e(this,"messageIdProvider");_e(this,"outgoing");_e(this,"messageIdToTopic");_e(this,"noop");_e(this,"keepaliveManager");_e(this,"stream");_e(this,"queue");_e(this,"streamBuilder");_e(this,"_resubscribeTopics");_e(this,"connackTimer");_e(this,"reconnectTimer");_e(this,"_storeProcessing");_e(this,"_packetIdsDuringStoreProcessing");_e(this,"_storeProcessingQueue");_e(this,"_firstConnection");_e(this,"topicAliasRecv");_e(this,"topicAliasSend");_e(this,"_deferredReconnect");_e(this,"connackPacket");this.options=x||{};for(let O in k)typeof this.options[O]>"u"?this.options[O]=k[O]:this.options[O]=x[O];this.log=this.options.log||(0,c.default)("mqttjs:client"),this.noop=this._noop.bind(this),this.log("MqttClient :: version:",A.VERSION),v.isWebWorker?this.log("MqttClient :: environment","webworker"):this.log("MqttClient :: environment",v.default?"browser":"node"),this.log("MqttClient :: options.protocol",x.protocol),this.log("MqttClient :: options.protocolVersion",x.protocolVersion),this.log("MqttClient :: options.username",x.username),this.log("MqttClient :: options.keepalive",x.keepalive),this.log("MqttClient :: options.reconnectPeriod",x.reconnectPeriod),this.log("MqttClient :: options.rejectUnauthorized",x.rejectUnauthorized),this.log("MqttClient :: options.properties.topicAliasMaximum",x.properties?x.properties.topicAliasMaximum:void 0),this.options.clientId=typeof x.clientId=="string"?x.clientId:A.defaultId(),this.log("MqttClient :: clientId",this.options.clientId),this.options.customHandleAcks=x.protocolVersion===5&&x.customHandleAcks?x.customHandleAcks:(...O)=>{O[3](null,0)},this.options.writeCache||(s.default.writeToStream.cacheNumbers=!1),this.streamBuilder=_,this.messageIdProvider=typeof this.options.messageIdProvider>"u"?new d.default:this.options.messageIdProvider,this.outgoingStore=x.outgoingStore||new h.default,this.incomingStore=x.incomingStore||new h.default,this.queueQoSZero=x.queueQoSZero===void 0||x.queueQoSZero,this._resubscribeTopics={},this.messageIdToTopic={},this.keepaliveManager=null,this.connected=!1,this.disconnecting=!1,this.reconnecting=!1,this.queue=[],this.connackTimer=null,this.reconnectTimer=null,this._storeProcessing=!1,this._packetIdsDuringStoreProcessing={},this._storeProcessingQueue=[],this.outgoing={},this._firstConnection=!0,x.properties&&x.properties.topicAliasMaximum>0&&(x.properties.topicAliasMaximum>65535?this.log("MqttClient :: options.properties.topicAliasMaximum is out of range"):this.topicAliasRecv=new y.default(x.properties.topicAliasMaximum)),this.on("connect",()=>{let{queue:O}=this,N=()=>{let G=O.shift();this.log("deliver :: entry %o",G);let U=null;if(!G)return void this._resubscribe();U=G.packet,this.log("deliver :: call _sendPacket for %o",U);let z=!0;U.messageId&&U.messageId!==0&&(this.messageIdProvider.register(U.messageId)||(z=!1)),z?this._sendPacket(U,q=>{G.cb&&G.cb(q),N()}):(this.log("messageId: %d has already used. The message is skipped and removed.",U.messageId),N())};this.log("connect :: sending queued packets"),N()}),this.on("close",()=>{this.log("close :: connected set to `false`"),this.connected=!1,this.log("close :: clearing connackTimer"),clearTimeout(this.connackTimer),this._destroyKeepaliveManager(),this.topicAliasRecv&&this.topicAliasRecv.clear(),this.log("close :: calling _setupReconnect"),this._setupReconnect()}),this.options.manualConnect||(this.log("MqttClient :: setting up stream"),this.connect())}static defaultId(){return`mqttjs_${Math.random().toString(16).substr(2,8)}`}handleAuth(_,x){x()}handleMessage(_,x){x()}_nextId(){return this.messageIdProvider.allocate()}getLastMessageId(){return this.messageIdProvider.getLastAllocated()}connect(){var q;let _=new a.Writable,x=s.default.parser(this.options),O=null,N=[];this.log("connect :: calling method to clear reconnect"),this._clearReconnect(),this.disconnected&&!this.reconnecting&&(this.incomingStore=this.options.incomingStore||new h.default,this.outgoingStore=this.options.outgoingStore||new h.default,this.disconnecting=!1,this.disconnected=!1),this.log("connect :: using streamBuilder provided to client to create stream"),this.stream=this.streamBuilder(this),x.on("packet",B=>{this.log("parser :: on packet push to packets array."),N.push(B)});let G=()=>{this.log("work :: getting next packet in queue");let B=N.shift();if(B)this.log("work :: packet pulled from queue"),(0,f.default)(this,B,U);else{this.log("work :: no packets in queue");let ne=O;O=null,this.log("work :: done flag is %s",!!ne),ne&&ne()}},U=()=>{if(N.length)(0,g.nextTick)(G);else{let B=O;O=null,B()}};_._write=(B,ne,H)=>{O=H,this.log("writable stream :: parsing buffer"),x.parse(B),G()},this.log("connect :: pipe stream to writable stream"),this.stream.pipe(_),this.stream.on("error",B=>{this.log("streamErrorHandler :: error",B.message),B.code?(this.log("streamErrorHandler :: emitting error"),this.emit("error",B)):this.noop(B)}),this.stream.on("close",()=>{this.log("(%s)stream :: on close",this.options.clientId),this._flushVolatile(),this.log("stream: emit close to MqttClient"),this.emit("close")}),this.log("connect: sending packet `connect`");let z={cmd:"connect",protocolId:this.options.protocolId,protocolVersion:this.options.protocolVersion,clean:this.options.clean,clientId:this.options.clientId,keepalive:this.options.keepalive,username:this.options.username,password:this.options.password,properties:this.options.properties};if(this.options.will&&(z.will={...this.options.will,payload:(q=this.options.will)==null?void 0:q.payload}),this.topicAliasRecv&&(z.properties||(z.properties={}),this.topicAliasRecv&&(z.properties.topicAliasMaximum=this.topicAliasRecv.max)),this._writePacket(z),x.on("error",this.emit.bind(this,"error")),this.options.properties){if(!this.options.properties.authenticationMethod&&this.options.properties.authenticationData)return this.end(()=>this.emit("error",new Error("Packet has no Authentication Method"))),this;if(this.options.properties.authenticationMethod&&this.options.authPacket&&typeof this.options.authPacket=="object"){let B={cmd:"auth",reasonCode:0,...this.options.authPacket};this._writePacket(B)}}return this.stream.setMaxListeners(1e3),clearTimeout(this.connackTimer),this.connackTimer=setTimeout(()=>{this.log("!!connectTimeout hit!! Calling _cleanUp with force `true`"),this.emit("error",new Error("connack timeout")),this._cleanUp(!0)},this.options.connectTimeout),this}publish(_,x,O,N){this.log("publish :: message `%s` to topic `%s`",x,_);let{options:G}=this;typeof O=="function"&&(N=O,O=null),O=O||{},O={qos:0,retain:!1,dup:!1,...O};let{qos:U,retain:z,dup:q,properties:B,cbStorePut:ne}=O;if(this._checkDisconnecting(N))return this;let H=()=>{let Q=0;if((U===1||U===2)&&(Q=this._nextId(),Q===null))return this.log("No messageId left"),!1;let F={cmd:"publish",topic:_,payload:x,qos:U,retain:z,messageId:Q,dup:q};switch(G.protocolVersion===5&&(F.properties=B),this.log("publish :: qos",U),U){case 1:case 2:this.outgoing[F.messageId]={volatile:!1,cb:N||this.noop},this.log("MqttClient:publish: packet cmd: %s",F.cmd),this._sendPacket(F,void 0,ne);break;default:this.log("MqttClient:publish: packet cmd: %s",F.cmd),this._sendPacket(F,N,ne)}return!0};return(this._storeProcessing||this._storeProcessingQueue.length>0||!H())&&this._storeProcessingQueue.push({invoke:H,cbStorePut:O.cbStorePut,callback:N}),this}publishAsync(_,x,O){return new Promise((N,G)=>{this.publish(_,x,O,(U,z)=>{U?G(U):N(z)})})}subscribe(_,x,O){let N=this.options.protocolVersion;typeof x=="function"&&(O=x),O=O||this.noop;let G=!1,U=[];typeof _=="string"?U=_=[_]:Array.isArray(_)?U=_:typeof _=="object"&&(G=_.resubscribe,delete _.resubscribe,U=Object.keys(_));let z=u.validateTopics(U);if(z!==null)return S(O,new Error(`Invalid topic ${z}`)),this;if(this._checkDisconnecting(O))return this.log("subscribe: discconecting true"),this;let q={qos:0};N===5&&(q.nl=!1,q.rap=!1,q.rh=0),x={...q,...x};let{properties:B}=x,ne=[],H=(V,Z)=>{if(Z=Z||x,!Object.prototype.hasOwnProperty.call(this._resubscribeTopics,V)||this._resubscribeTopics[V].qos{this.log("subscribe: array topic %s",V),H(V)}):Object.keys(_).forEach(V=>{this.log("subscribe: object topic %s, %o",V,_[V]),H(V,_[V])}),!ne.length)return O(null,[]),this;let Q=(V,Z)=>{let $={cmd:"subscribe",subscriptions:V,messageId:Z};if(B&&($.properties=B),this.options.resubscribe){this.log("subscribe :: resubscribe true");let le=[];V.forEach(ue=>{if(this.options.reconnectPeriod>0){let Y={qos:ue.qos};N===5&&(Y.nl=ue.nl||!1,Y.rap=ue.rap||!1,Y.rh=ue.rh||0,Y.properties=ue.properties),this._resubscribeTopics[ue.topic]=Y,le.push(ue.topic)}}),this.messageIdToTopic[$.messageId]=le}let se=new Promise((le,ue)=>{this.outgoing[$.messageId]={volatile:!0,cb(Y,ie){if(!Y){let{granted:oe}=ie;for(let j=0;j{let V=this.options.subscribeBatchSize??ne.length,Z=[];for(let $=0;${O(null,ne,$.at(-1))}).catch($=>{O($,ne,$.packet)}),!0};return(this._storeProcessing||this._storeProcessingQueue.length>0||!F())&&this._storeProcessingQueue.push({invoke:F,callback:O}),this}subscribeAsync(_,x){return new Promise((O,N)=>{this.subscribe(_,x,(G,U)=>{G?N(G):O(U)})})}unsubscribe(_,x,O){typeof _=="string"&&(_=[_]),typeof x=="function"&&(O=x),O=O||this.noop;let N=u.validateTopics(_);if(N!==null)return S(O,new Error(`Invalid topic ${N}`)),this;if(this._checkDisconnecting(O))return this;let G=()=>{let U=this._nextId();if(U===null)return this.log("No messageId left"),!1;let z={cmd:"unsubscribe",messageId:U,unsubscriptions:[]};return typeof _=="string"?z.unsubscriptions=[_]:Array.isArray(_)&&(z.unsubscriptions=_),this.options.resubscribe&&z.unsubscriptions.forEach(q=>{delete this._resubscribeTopics[q]}),typeof x=="object"&&x.properties&&(z.properties=x.properties),this.outgoing[z.messageId]={volatile:!0,cb:O},this.log("unsubscribe: call _sendPacket"),this._sendPacket(z),!0};return(this._storeProcessing||this._storeProcessingQueue.length>0||!G())&&this._storeProcessingQueue.push({invoke:G,callback:O}),this}unsubscribeAsync(_,x){return new Promise((O,N)=>{this.unsubscribe(_,x,(G,U)=>{G?N(G):O(U)})})}end(_,x,O){this.log("end :: (%s)",this.options.clientId),(_==null||typeof _!="boolean")&&(O=O||x,x=_,_=!1),typeof x!="object"&&(O=O||x,x=null),this.log("end :: cb? %s",!!O),(!O||typeof O!="function")&&(O=this.noop);let N=()=>{this.log("end :: closeStores: closing incoming and outgoing stores"),this.disconnected=!0,this.incomingStore.close(U=>{this.outgoingStore.close(z=>{if(this.log("end :: closeStores: emitting end"),this.emit("end"),O){let q=U||z;this.log("end :: closeStores: invoking callback with args"),O(q)}})}),this._deferredReconnect?this._deferredReconnect():(this.options.reconnectPeriod===0||this.options.manualConnect)&&(this.disconnecting=!1)},G=()=>{this.log("end :: (%s) :: finish :: calling _cleanUp with force %s",this.options.clientId,_),this._cleanUp(_,()=>{this.log("end :: finish :: calling process.nextTick on closeStores"),(0,g.nextTick)(N)},x)};return this.disconnecting?(O(),this):(this._clearReconnect(),this.disconnecting=!0,!_&&Object.keys(this.outgoing).length>0?(this.log("end :: (%s) :: calling finish in 10ms once outgoing is empty",this.options.clientId),this.once("outgoingEmpty",setTimeout.bind(null,G,10))):(this.log("end :: (%s) :: immediately calling finish",this.options.clientId),G()),this)}endAsync(_,x){return new Promise((O,N)=>{this.end(_,x,G=>{G?N(G):O()})})}removeOutgoingMessage(_){if(this.outgoing[_]){let{cb:x}=this.outgoing[_];this._removeOutgoingAndStoreMessage(_,()=>{x(new Error("Message removed"))})}return this}reconnect(_){this.log("client reconnect");let x=()=>{_?(this.options.incomingStore=_.incomingStore,this.options.outgoingStore=_.outgoingStore):(this.options.incomingStore=null,this.options.outgoingStore=null),this.incomingStore=this.options.incomingStore||new h.default,this.outgoingStore=this.options.outgoingStore||new h.default,this.disconnecting=!1,this.disconnected=!1,this._deferredReconnect=null,this._reconnect()};return this.disconnecting&&!this.disconnected?this._deferredReconnect=x:x(),this}_flushVolatile(){this.outgoing&&(this.log("_flushVolatile :: deleting volatile messages from the queue and setting their callbacks as error function"),Object.keys(this.outgoing).forEach(_=>{this.outgoing[_].volatile&&typeof this.outgoing[_].cb=="function"&&(this.outgoing[_].cb(new Error("Connection closed")),delete this.outgoing[_])}))}_flush(){this.outgoing&&(this.log("_flush: queue exists? %b",!!this.outgoing),Object.keys(this.outgoing).forEach(_=>{typeof this.outgoing[_].cb=="function"&&(this.outgoing[_].cb(new Error("Connection closed")),delete this.outgoing[_])}))}_removeTopicAliasAndRecoverTopicName(_){let x;_.properties&&(x=_.properties.topicAlias);let O=_.topic.toString();if(this.log("_removeTopicAliasAndRecoverTopicName :: alias %d, topic %o",x,O),O.length===0){if(typeof x>"u")return new Error("Unregistered Topic Alias");if(O=this.topicAliasSend.getTopicByAlias(x),typeof O>"u")return new Error("Unregistered Topic Alias");_.topic=O}x&&delete _.properties.topicAlias}_checkDisconnecting(_){return this.disconnecting&&(_&&_!==this.noop?_(new Error("client disconnecting")):this.emit("error",new Error("client disconnecting"))),this.disconnecting}_reconnect(){this.log("_reconnect: emitting reconnect to client"),this.emit("reconnect"),this.connected?(this.end(()=>{this.connect()}),this.log("client already connected. disconnecting first.")):(this.log("_reconnect: calling connect"),this.connect())}_setupReconnect(){!this.disconnecting&&!this.reconnectTimer&&this.options.reconnectPeriod>0?(this.reconnecting||(this.log("_setupReconnect :: emit `offline` state"),this.emit("offline"),this.log("_setupReconnect :: set `reconnecting` to `true`"),this.reconnecting=!0),this.log("_setupReconnect :: setting reconnectTimer for %d ms",this.options.reconnectPeriod),this.reconnectTimer=setInterval(()=>{this.log("reconnectTimer :: reconnect triggered!"),this._reconnect()},this.options.reconnectPeriod)):this.log("_setupReconnect :: doing nothing...")}_clearReconnect(){this.log("_clearReconnect : clearing reconnect timer"),this.reconnectTimer&&(clearInterval(this.reconnectTimer),this.reconnectTimer=null)}_cleanUp(_,x,O={}){if(x&&(this.log("_cleanUp :: done callback provided for on stream close"),this.stream.on("close",x)),this.log("_cleanUp :: forced? %s",_),_)this.options.reconnectPeriod===0&&this.options.clean&&this._flush(),this.log("_cleanUp :: (%s) :: destroying stream",this.options.clientId),this.stream.destroy();else{let N={cmd:"disconnect",...O};this.log("_cleanUp :: (%s) :: call _sendPacket with disconnect packet",this.options.clientId),this._sendPacket(N,()=>{this.log("_cleanUp :: (%s) :: destroying stream",this.options.clientId),S(()=>{this.stream.end(()=>{this.log("_cleanUp :: (%s) :: stream destroyed",this.options.clientId)})})})}!this.disconnecting&&!this.reconnecting&&(this.log("_cleanUp :: client not disconnecting/reconnecting. Clearing and resetting reconnect."),this._clearReconnect(),this._setupReconnect()),this._destroyKeepaliveManager(),x&&!this.connected&&(this.log("_cleanUp :: (%s) :: removing stream `done` callback `close` listener",this.options.clientId),this.stream.removeListener("close",x),x())}_storeAndSend(_,x,O){this.log("storeAndSend :: store packet with cmd %s to outgoingStore",_.cmd);let N,G=_;if(G.cmd==="publish"&&(G=(0,l.default)(_),N=this._removeTopicAliasAndRecoverTopicName(G),N))return x&&x(N);this.outgoingStore.put(G,U=>{if(U)return x&&x(U);O(),this._writePacket(_,x)})}_applyTopicAlias(_){if(this.options.protocolVersion===5&&_.cmd==="publish"){let x;_.properties&&(x=_.properties.topicAlias);let O=_.topic.toString();if(this.topicAliasSend)if(x){if(O.length!==0&&(this.log("applyTopicAlias :: register topic: %s - alias: %d",O,x),!this.topicAliasSend.put(O,x)))return this.log("applyTopicAlias :: error out of range. topic: %s - alias: %d",O,x),new Error("Sending Topic Alias out of range")}else O.length!==0&&(this.options.autoAssignTopicAlias?(x=this.topicAliasSend.getAliasByTopic(O),x?(_.topic="",_.properties={..._.properties,topicAlias:x},this.log("applyTopicAlias :: auto assign(use) topic: %s - alias: %d",O,x)):(x=this.topicAliasSend.getLruAlias(),this.topicAliasSend.put(O,x),_.properties={..._.properties,topicAlias:x},this.log("applyTopicAlias :: auto assign topic: %s - alias: %d",O,x))):this.options.autoUseTopicAlias&&(x=this.topicAliasSend.getAliasByTopic(O),x&&(_.topic="",_.properties={..._.properties,topicAlias:x},this.log("applyTopicAlias :: auto use topic: %s - alias: %d",O,x))));else if(x)return this.log("applyTopicAlias :: error out of range. topic: %s - alias: %d",O,x),new Error("Sending Topic Alias out of range")}}_noop(_){this.log("noop ::",_)}_writePacket(_,x){this.log("_writePacket :: packet: %O",_),this.log("_writePacket :: emitting `packetsend`"),this.emit("packetsend",_),this.log("_writePacket :: writing to stream");let O=s.default.writeToStream(_,this.stream,this.options);this.log("_writePacket :: writeToStream result %s",O),!O&&x&&x!==this.noop?(this.log("_writePacket :: handle events on `drain` once through callback."),this.stream.once("drain",x)):x&&(this.log("_writePacket :: invoking cb"),x())}_sendPacket(_,x,O,N){this.log("_sendPacket :: (%s) :: start",this.options.clientId),O=O||this.noop,x=x||this.noop;let G=this._applyTopicAlias(_);if(G)x(G);else{if(!this.connected)return _.cmd==="auth"?void this._writePacket(_,x):(this.log("_sendPacket :: client not connected. Storing packet offline."),void this._storePacket(_,x,O));if(N)this._writePacket(_,x);else{switch(_.cmd){case"publish":break;case"pubrel":return void this._storeAndSend(_,x,O);default:return void this._writePacket(_,x)}switch(_.qos){case 2:case 1:this._storeAndSend(_,x,O);break;default:this._writePacket(_,x)}this.log("_sendPacket :: (%s) :: end",this.options.clientId)}}}_storePacket(_,x,O){this.log("_storePacket :: packet: %o",_),this.log("_storePacket :: cb? %s",!!x),O=O||this.noop;let N=_;if(N.cmd==="publish"){N=(0,l.default)(_);let U=this._removeTopicAliasAndRecoverTopicName(N);if(U)return x&&x(U)}let G=N.qos||0;G===0&&this.queueQoSZero||N.cmd!=="publish"?this.queue.push({packet:N,cb:x}):G>0?(x=this.outgoing[N.messageId]?this.outgoing[N.messageId].cb:null,this.outgoingStore.put(N,U=>{if(U)return x&&x(U);O()})):x&&x(new Error("No connection to broker"))}_setupKeepaliveManager(){this.log("_setupKeepaliveManager :: keepalive %d (seconds)",this.options.keepalive),!this.keepaliveManager&&this.options.keepalive&&(this.keepaliveManager=new b.default(this,this.options.timerVariant))}_destroyKeepaliveManager(){this.keepaliveManager&&(this.log("_destroyKeepaliveManager :: destroying keepalive manager"),this.keepaliveManager.destroy(),this.keepaliveManager=null)}reschedulePing(_=!1){this.keepaliveManager&&this.options.keepalive&&(_||this.options.reschedulePings)&&this._reschedulePing()}_reschedulePing(){this.log("_reschedulePing :: rescheduling ping"),this.keepaliveManager.reschedule()}sendPing(){this.log("_sendPing :: sending pingreq"),this._sendPacket({cmd:"pingreq"})}onKeepaliveTimeout(){this.emit("error",new Error("Keepalive timeout")),this.log("onKeepaliveTimeout :: calling _cleanUp with force true"),this._cleanUp(!0)}_resubscribe(){this.log("_resubscribe");let _=Object.keys(this._resubscribeTopics);if(!this._firstConnection&&(this.options.clean||this.options.protocolVersion>=4&&!this.connackPacket.sessionPresent)&&_.length>0)if(this.options.resubscribe)if(this.options.protocolVersion===5){this.log("_resubscribe: protocolVersion 5");for(let x=0;x<_.length;x++){let O={};O[_[x]]=this._resubscribeTopics[_[x]],O.resubscribe=!0,this.subscribe(O,{properties:O[_[x]].properties})}}else this._resubscribeTopics.resubscribe=!0,this.subscribe(this._resubscribeTopics);else this._resubscribeTopics={};this._firstConnection=!1}_onConnect(_){if(this.disconnected)return void this.emit("connect",_);this.connackPacket=_,this.messageIdProvider.clear(),this._setupKeepaliveManager(),this.connected=!0;let x=()=>{let O=this.outgoingStore.createStream(),N=()=>{O.destroy(),O=null,this._flushStoreProcessingQueue(),G()},G=()=>{this._storeProcessing=!1,this._packetIdsDuringStoreProcessing={}};this.once("close",N),O.on("error",z=>{G(),this._flushStoreProcessingQueue(),this.removeListener("close",N),this.emit("error",z)});let U=()=>{if(!O)return;let z,q=O.read(1);q?(this._storeProcessing=!0,this._packetIdsDuringStoreProcessing[q.messageId]?U():this.disconnecting||this.reconnectTimer?O.destroy&&O.destroy():(z=this.outgoing[q.messageId]?this.outgoing[q.messageId].cb:null,this.outgoing[q.messageId]={volatile:!1,cb(B,ne){z&&z(B,ne),U()}},this._packetIdsDuringStoreProcessing[q.messageId]=!0,this.messageIdProvider.register(q.messageId)?this._sendPacket(q,void 0,void 0,!0):this.log("messageId: %d has already used.",q.messageId))):O.once("readable",U)};O.on("end",()=>{let z=!0;for(let q in this._packetIdsDuringStoreProcessing)if(!this._packetIdsDuringStoreProcessing[q]){z=!1;break}this.removeListener("close",N),z?(G(),this._invokeAllStoreProcessingQueue(),this.emit("connect",_)):x()}),U()};x()}_invokeStoreProcessingQueue(){if(!this._storeProcessing&&this._storeProcessingQueue.length>0){let _=this._storeProcessingQueue[0];if(_&&_.invoke())return this._storeProcessingQueue.shift(),!0}return!1}_invokeAllStoreProcessingQueue(){for(;this._invokeStoreProcessingQueue(););}_flushStoreProcessingQueue(){for(let _ of this._storeProcessingQueue)_.cbStorePut&&_.cbStorePut(new Error("Connection closed")),_.callback&&_.callback(new Error("Connection closed"));this._storeProcessingQueue.splice(0)}_removeOutgoingAndStoreMessage(_,x){delete this.outgoing[_],this.outgoingStore.del({messageId:_},(O,N)=>{x(O,N),this.messageIdProvider.deallocate(_),this._invokeStoreProcessingQueue()})}},_e(A,"VERSION",g.MQTTJS_VERSION),A);t.default=M}),LE=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e=ww();t.default=class{constructor(){_e(this,"numberAllocator");_e(this,"lastId");this.numberAllocator=new e.NumberAllocator(1,65535)}allocate(){return this.lastId=this.numberAllocator.alloc(),this.lastId}getLastAllocated(){return this.lastId}register(r){return this.numberAllocator.use(r)}deallocate(r){this.numberAllocator.free(r)}clear(){this.numberAllocator.clear()}}}),Mc,Ac,dn,BE=Ht(()=>{me(),be(),ye(),Mc={},Ac=!1,dn=function(){if(Ac)return Mc;Ac=!0;let t=2147483647,e=36,r=/^xn--/,n=/[^\0-\x7F]/,i=/[\x2E\u3002\uFF0E\uFF61]/g,o={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)","invalid-input":"Invalid input"},s=Math.floor,a=String.fromCharCode;function l(p){throw new RangeError(o[p])}function c(p,b){let v=p.split("@"),S="";v.length>1&&(S=v[0]+"@",p=v[1]);let k=function(M,A){let I=[],T=M.length;for(;T--;)I[T]=A(M[T]);return I}((p=p.replace(i,".")).split("."),b).join(".");return S+k}function u(p){let b=[],v=0,S=p.length;for(;v=55296&&k<=56319&&v=48&&p<58?p-48+26:p>=65&&p<91?p-65:p>=97&&p<123?p-97:e},f=function(p,b){return p+22+75*(p<26)-((b!=0)<<5)},d=function(p,b,v){let S=0;for(p=v?s(p/700):p>>1,p+=s(p/b);p>455;S+=e)p=s(p/35);return s(S+36*p/(p+38))},y=function(p){let b=[],v=p.length,S=0,k=128,M=72,A=p.lastIndexOf("-");A<0&&(A=0);for(let I=0;I=128&&l("not-basic"),b.push(p.charCodeAt(I));for(let I=A>0?A+1:0;I=v&&l("invalid-input");let N=h(p.charCodeAt(I++));N>=e&&l("invalid-input"),N>s((t-S)/x)&&l("overflow"),S+=N*x;let G=O<=M?1:O>=M+26?26:O-M;if(Ns(t/U)&&l("overflow"),x*=U}let _=b.length+1;M=d(S-T,_,T==0),s(S/_)>t-k&&l("overflow"),k+=s(S/_),S%=_,b.splice(S++,0,k)}return String.fromCodePoint(...b)},g=function(p){let b=[],v=(p=u(p)).length,S=128,k=0,M=72;for(let T of p)T<128&&b.push(a(T));let A=b.length,I=A;for(A&&b.push("-");I=S&&xs((t-k)/_)&&l("overflow"),k+=(T-S)*_,S=T;for(let x of p)if(xt&&l("overflow"),x===S){let O=k;for(let N=e;;N+=e){let G=N<=M?1:N>=M+26?26:N-M;if(OString.fromCodePoint(...p)},decode:y,encode:g,toASCII:function(p){return c(p,function(b){return n.test(b)?"xn--"+g(b):b})},toUnicode:function(p){return c(p,function(b){return r.test(b)?y(b.slice(4).toLowerCase()):b})}}}(),dn.decode,dn.encode,dn.toASCII,dn.toUnicode,dn.ucs2,dn.version});function Tw(){return $h||($h=!0,Vh=SyntaxError),Vh}function ms(){return Hh||(Hh=!0,zh=TypeError),zh}function FE(){if(Gh)return Kh;Gh=!0;var t=typeof Symbol<"u"&&Symbol,e=(Rh||(Rh=!0,Ch=function(){if(typeof Symbol!="function"||typeof Object.getOwnPropertySymbols!="function")return!1;if(typeof Symbol.iterator=="symbol")return!0;var r={},n=Symbol("test"),i=Object(n);if(typeof n=="string"||Object.prototype.toString.call(n)!=="[object Symbol]"||Object.prototype.toString.call(i)!=="[object Symbol]")return!1;for(n in r[n]=42,r)return!1;if(typeof Object.keys=="function"&&Object.keys(r).length!==0||typeof Object.getOwnPropertyNames=="function"&&Object.getOwnPropertyNames(r).length!==0)return!1;var o=Object.getOwnPropertySymbols(r);if(o.length!==1||o[0]!==n||!Object.prototype.propertyIsEnumerable.call(r,n))return!1;if(typeof Object.getOwnPropertyDescriptor=="function"){var s=Object.getOwnPropertyDescriptor(r,n);if(s.value!==42||s.enumerable!==!0)return!1}return!0}),Ch);return Kh=function(){return typeof t=="function"&&typeof Symbol=="function"&&typeof t("foo")=="symbol"&&typeof Symbol("bar")=="symbol"&&e()}}function Ih(){if(ef)return Jh;ef=!0;var t=function(){if(Xh)return Ra;Xh=!0;var e=Object.prototype.toString,r=Math.max,n=function(i,o){for(var s=[],a=0;a"u"||!g?t:g(Uint8Array),v={__proto__:null,"%AggregateError%":typeof AggregateError>"u"?t:AggregateError,"%Array%":Array,"%ArrayBuffer%":typeof ArrayBuffer>"u"?t:ArrayBuffer,"%ArrayIteratorPrototype%":d&&g?g([][Symbol.iterator]()):t,"%AsyncFromSyncIteratorPrototype%":t,"%AsyncFunction%":p,"%AsyncGenerator%":p,"%AsyncGeneratorFunction%":p,"%AsyncIteratorPrototype%":p,"%Atomics%":typeof Atomics>"u"?t:Atomics,"%BigInt%":typeof BigInt>"u"?t:BigInt,"%BigInt64Array%":typeof BigInt64Array>"u"?t:BigInt64Array,"%BigUint64Array%":typeof BigUint64Array>"u"?t:BigUint64Array,"%Boolean%":Boolean,"%DataView%":typeof DataView>"u"?t:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":e,"%eval%":eval,"%EvalError%":r,"%Float32Array%":typeof Float32Array>"u"?t:Float32Array,"%Float64Array%":typeof Float64Array>"u"?t:Float64Array,"%FinalizationRegistry%":typeof FinalizationRegistry>"u"?t:FinalizationRegistry,"%Function%":l,"%GeneratorFunction%":p,"%Int8Array%":typeof Int8Array>"u"?t:Int8Array,"%Int16Array%":typeof Int16Array>"u"?t:Int16Array,"%Int32Array%":typeof Int32Array>"u"?t:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":d&&g?g(g([][Symbol.iterator]())):t,"%JSON%":typeof JSON=="object"?JSON:t,"%Map%":typeof Map>"u"?t:Map,"%MapIteratorPrototype%":typeof Map>"u"||!d||!g?t:g(new Map()[Symbol.iterator]()),"%Math%":Math,"%Number%":Number,"%Object%":Object,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":typeof Promise>"u"?t:Promise,"%Proxy%":typeof Proxy>"u"?t:Proxy,"%RangeError%":n,"%ReferenceError%":i,"%Reflect%":typeof Reflect>"u"?t:Reflect,"%RegExp%":RegExp,"%Set%":typeof Set>"u"?t:Set,"%SetIteratorPrototype%":typeof Set>"u"||!d||!g?t:g(new Set()[Symbol.iterator]()),"%SharedArrayBuffer%":typeof SharedArrayBuffer>"u"?t:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":d&&g?g(""[Symbol.iterator]()):t,"%Symbol%":d?Symbol:t,"%SyntaxError%":o,"%ThrowTypeError%":f,"%TypedArray%":b,"%TypeError%":s,"%Uint8Array%":typeof Uint8Array>"u"?t:Uint8Array,"%Uint8ClampedArray%":typeof Uint8ClampedArray>"u"?t:Uint8ClampedArray,"%Uint16Array%":typeof Uint16Array>"u"?t:Uint16Array,"%Uint32Array%":typeof Uint32Array>"u"?t:Uint32Array,"%URIError%":a,"%WeakMap%":typeof WeakMap>"u"?t:WeakMap,"%WeakRef%":typeof WeakRef>"u"?t:WeakRef,"%WeakSet%":typeof WeakSet>"u"?t:WeakSet};if(g)try{null.error}catch(q){var S=g(g(q));v["%Error.prototype%"]=S}var k=function q(B){var ne;if(B==="%AsyncFunction%")ne=c("async function () {}");else if(B==="%GeneratorFunction%")ne=c("function* () {}");else if(B==="%AsyncGeneratorFunction%")ne=c("async function* () {}");else if(B==="%AsyncGenerator%"){var H=q("%AsyncGeneratorFunction%");H&&(ne=H.prototype)}else if(B==="%AsyncIteratorPrototype%"){var Q=q("%AsyncGenerator%");Q&&g&&(ne=g(Q.prototype))}return v[B]=ne,ne},M={__proto__:null,"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},A=Ih(),I=function(){if(rf)return tf;rf=!0;var q=Function.prototype.call,B=Object.prototype.hasOwnProperty,ne=Ih();return tf=ne.call(q,B)}(),T=A.call(Function.call,Array.prototype.concat),_=A.call(Function.apply,Array.prototype.splice),x=A.call(Function.call,String.prototype.replace),O=A.call(Function.call,String.prototype.slice),N=A.call(Function.call,RegExp.prototype.exec),G=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,U=/\\(\\)?/g,z=function(q,B){var ne,H=q;if(I(M,H)&&(H="%"+(ne=M[H])[0]+"%"),I(v,H)){var Q=v[H];if(Q===p&&(Q=k(H)),typeof Q>"u"&&!B)throw new s("intrinsic "+q+" exists, but is not available. Please file an issue!");return{alias:ne,name:H,value:Q}}throw new o("intrinsic "+q+" does not exist!")};return Da=function(q,B){if(typeof q!="string"||q.length===0)throw new s("intrinsic name must be a non-empty string");if(arguments.length>1&&typeof B!="boolean")throw new s('"allowMissing" argument must be a boolean');if(N(/^%?[^%]*%?$/,q)===null)throw new o("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var ne=function(j){var X=O(j,0,1),R=O(j,-1);if(X==="%"&&R!=="%")throw new o("invalid intrinsic syntax, expected closing `%`");if(R==="%"&&X!=="%")throw new o("invalid intrinsic syntax, expected opening `%`");var te=[];return x(j,G,function(m,w,E,D){te[te.length]=E?x(D,U,"$1"):w||m}),te}(q),H=ne.length>0?ne[0]:"",Q=z("%"+H+"%",B),F=Q.name,V=Q.value,Z=!1,$=Q.alias;$&&(H=$[0],_(ne,T([0,1],$)));for(var se=1,le=!0;se=ne.length){var oe=u(V,ue);V=(le=!!oe)&&"get"in oe&&!("originalValue"in oe.get)?oe.get:V[ue]}else le=I(V,ue),V=V[ue];le&&!Z&&(v[F]=V)}}return V},Da}function Ph(){if(sf)return of;sf=!0;var t=Gi()("%Object.defineProperty%",!0)||!1;if(t)try{t({},"a",{value:1})}catch{t=!1}return of=t}function Sg(){if(lf)return af;lf=!0;var t=Gi()("%Object.getOwnPropertyDescriptor%",!0);if(t)try{t([],"length")}catch{t=null}return af=t}function WE(){if(ff)return Na;ff=!0;var t=Gi(),e=function(){if(cf)return ja;cf=!0;var s=Ph(),a=Tw(),l=ms(),c=Sg();return ja=function(u,h,f){if(!u||typeof u!="object"&&typeof u!="function")throw new l("`obj` must be an object or a function`");if(typeof h!="string"&&typeof h!="symbol")throw new l("`property` must be a string or a symbol`");if(arguments.length>3&&typeof arguments[3]!="boolean"&&arguments[3]!==null)throw new l("`nonEnumerable`, if provided, must be a boolean or null");if(arguments.length>4&&typeof arguments[4]!="boolean"&&arguments[4]!==null)throw new l("`nonWritable`, if provided, must be a boolean or null");if(arguments.length>5&&typeof arguments[5]!="boolean"&&arguments[5]!==null)throw new l("`nonConfigurable`, if provided, must be a boolean or null");if(arguments.length>6&&typeof arguments[6]!="boolean")throw new l("`loose`, if provided, must be a boolean");var d=arguments.length>3?arguments[3]:null,y=arguments.length>4?arguments[4]:null,g=arguments.length>5?arguments[5]:null,p=arguments.length>6&&arguments[6],b=!!c&&c(u,h);if(s)s(u,h,{configurable:g===null&&b?b.configurable:!g,enumerable:d===null&&b?b.enumerable:!d,value:f,writable:y===null&&b?b.writable:!y});else{if(!p&&(d||y||g))throw new a("This environment does not support defining a property as non-configurable, non-writable, or non-enumerable.");u[h]=f}},ja}(),r=function(){if(hf)return uf;hf=!0;var s=Ph(),a=function(){return!!s};return a.hasArrayLengthDefineBug=function(){if(!s)return null;try{return s([],"length",{value:1}).length!==1}catch{return!0}},uf=a}()(),n=Sg(),i=ms(),o=t("%Math.floor%");return Na=function(s,a){if(typeof s!="function")throw new i("`fn` is not a function");if(typeof a!="number"||a<0||a>4294967295||o(a)!==a)throw new i("`length` must be a positive 32-bit integer");var l=arguments.length>2&&!!arguments[2],c=!0,u=!0;if("length"in s&&n){var h=n(s,"length");h&&!h.configurable&&(c=!1),h&&!h.writable&&(u=!1)}return(c||u||!l)&&(r?e(s,"length",a,!0,!0):e(s,"length",a)),s},Na}function UE(){if(pf)return La;pf=!0;var t=Gi(),e=function(){if(df)return Ti;df=!0;var n=Ih(),i=Gi(),o=WE(),s=ms(),a=i("%Function.prototype.apply%"),l=i("%Function.prototype.call%"),c=i("%Reflect.apply%",!0)||n.call(l,a),u=Ph(),h=i("%Math.max%");Ti=function(d){if(typeof d!="function")throw new s("a function is required");var y=c(n,l,arguments);return o(y,1+h(0,d.length-(arguments.length-1)),!0)};var f=function(){return c(n,a,arguments)};return u?u(Ti,"apply",{value:f}):Ti.apply=f,Ti}(),r=e(t("String.prototype.indexOf"));return La=function(n,i){var o=t(n,!!i);return typeof o=="function"&&r(n,".prototype.")>-1?e(o):o},La}var Ch,Rh,Dh,jh,Nh,Lh,Bh,Fh,Wh,Uh,Vh,$h,zh,Hh,qh,Yh,Kh,Gh,Qh,Zh,Ra,Xh,Jh,ef,tf,rf,Da,nf,of,sf,af,lf,ja,cf,uf,hf,Na,ff,Ti,df,La,pf,VE=Ht(()=>{me(),be(),ye(),Ch={},Rh=!1,Dh={},jh=!1,Nh={},Lh=!1,Bh={},Fh=!1,Wh={},Uh=!1,Vh={},$h=!1,zh={},Hh=!1,qh={},Yh=!1,Kh={},Gh=!1,Qh={},Zh=!1,Ra={},Xh=!1,Jh={},ef=!1,tf={},rf=!1,Da={},nf=!1,of={},sf=!1,af={},lf=!1,ja={},cf=!1,uf={},hf=!1,Na={},ff=!1,Ti={},df=!1,La={},pf=!1});function Id(t){throw new Error("Node.js process "+t+" is not supported by JSPM core outside of Node.js")}function $E(){!oi||!Xn||(oi=!1,Xn.length?Er=Xn.concat(Er):Qo=-1,Er.length&&Iw())}function Iw(){if(!oi){var t=setTimeout($E,0);oi=!0;for(var e=Er.length;e;){for(Xn=Er,Er=[];++Qo1)for(var r=1;r{me(),be(),ye(),Er=[],oi=!1,Qo=-1,Pw.prototype.run=function(){this.fun.apply(null,this.array)},kg={PATH:"/usr/bin",LANG:navigator.language+".UTF-8",PWD:"/",HOME:"/home",TMP:"/tmp"},Eg=["/usr/bin/node"],Og=[],Mg={},Ag=function(t,e){},Tg=function(t){Id("binding")},Ig=function(t){return 0},Pg=function(){return"/"},Cg=function(t){},Rg={name:"node",sourceUrl:"",headersUrl:"",libUrl:""},Dg=kt,jg=[],Ng={},Lg={},Bg=kt,Fg=kt,Wg=Ic=function(){return{}},Ug=Ic,Vg=kt,$g=kt,zg=kt,Hg={},qg={inspector:!1,debug:!1,uv:!1,ipv6:!1,tls_alpn:!1,tls_sni:!1,tls_ocsp:!1,tls:!1,cached_builtins:!0},Yg=kt,Kg=kt,Gg=kt,Qg=kt,Zg=kt,Xg=kt,Jg=kt,em=kt,tm=[],rm=kt,(xn={now:typeof performance<"u"?performance.now.bind(performance):void 0,timing:typeof performance<"u"?performance.timing:void 0}).now===void 0&&(Pc=Date.now(),xn.timing&&xn.timing.navigationStart&&(Pc=xn.timing.navigationStart),xn.now=()=>Date.now()-Pc),Ba=1e9,Tc.bigint=function(t){var e=Tc(t);return typeof BigInt>"u"?e[0]*Ba+e[1]:BigInt(e[0]*Ba)+BigInt(e[1])},Pd={version:"v16.8.0",versions:Mg,arch:"x64",platform:"browser",release:Rg,_rawDebug:Dg,moduleLoadList:jg,binding:Tg,_linkedBinding:HE,_events:{},_eventsCount:0,_maxListeners:10,on:nn,addListener:nn,once:nn,off:nn,removeListener:nn,removeAllListeners:nn,emit:kt,prependListener:nn,prependOnceListener:nn,listeners:XE,domain:Ng,_exiting:!1,config:Lg,dlopen:qE,uptime:ZE,_getActiveRequests:YE,_getActiveHandles:KE,reallyExit:Bg,_kill:Fg,cpuUsage:Ic,resourceUsage:Wg,memoryUsage:Ug,kill:Vg,exit:$g,openStdin:zg,allowedNodeEnvironmentFlags:Hg,assert:GE,features:qg,_fatalExceptions:Yg,setUncaughtExceptionCaptureCallback:Kg,hasUncaughtExceptionCaptureCallback:QE,emitWarning:Ag,nextTick:zE,_tickCallback:Gg,_debugProcess:Qg,_debugEnd:Zg,_startProfilerIdleNotifier:Xg,_stopProfilerIdleNotifier:Jg,stdout:void 0,stdin:void 0,stderr:void 0,abort:em,umask:Ig,chdir:Cg,cwd:Pg,env:kg,title:"browser",argv:Eg,execArgv:Og,pid:2,ppid:1,execPath:"/bin/usr/node",debugPort:9229,hrtime:Tc,argv0:"node",_preload_modules:tm,setSourceMapsEnabled:rm}}),Cc,Rc,gf,JE=Ht(()=>{me(),be(),ye(),Cw(),Cc={},Rc=!1,gf=function(){if(Rc)return Cc;Rc=!0;var t=Pd;function e(i){if(typeof i!="string")throw new TypeError("Path must be a string. Received "+JSON.stringify(i))}function r(i,o){for(var s,a="",l=0,c=-1,u=0,h=0;h<=i.length;++h){if(h2){var f=a.lastIndexOf("/");if(f!==a.length-1){f===-1?(a="",l=0):l=(a=a.slice(0,f)).length-1-a.lastIndexOf("/"),c=h,u=0;continue}}else if(a.length===2||a.length===1){a="",l=0,c=h,u=0;continue}}o&&(a.length>0?a+="/..":a="..",l=2)}else a.length>0?a+="/"+i.slice(c+1,h):a=i.slice(c+1,h),l=h-c-1;c=h,u=0}else s===46&&u!==-1?++u:u=-1}return a}var n={resolve:function(){for(var i,o="",s=!1,a=arguments.length-1;a>=-1&&!s;a--){var l;a>=0?l=arguments[a]:(i===void 0&&(i=t.cwd()),l=i),e(l),l.length!==0&&(o=l+"/"+o,s=l.charCodeAt(0)===47)}return o=r(o,!s),s?o.length>0?"/"+o:"/":o.length>0?o:"."},normalize:function(i){if(e(i),i.length===0)return".";var o=i.charCodeAt(0)===47,s=i.charCodeAt(i.length-1)===47;return(i=r(i,!o)).length===0&&!o&&(i="."),i.length>0&&s&&(i+="/"),o?"/"+i:i},isAbsolute:function(i){return e(i),i.length>0&&i.charCodeAt(0)===47},join:function(){if(arguments.length===0)return".";for(var i,o=0;o0&&(i===void 0?i=s:i+="/"+s)}return i===void 0?".":n.normalize(i)},relative:function(i,o){if(e(i),e(o),i===o||(i=n.resolve(i))===(o=n.resolve(o)))return"";for(var s=1;sh){if(o.charCodeAt(c+d)===47)return o.slice(c+d+1);if(d===0)return o.slice(c+d)}else l>h&&(i.charCodeAt(s+d)===47?f=d:d===0&&(f=0));break}var y=i.charCodeAt(s+d);if(y!==o.charCodeAt(c+d))break;y===47&&(f=d)}var g="";for(d=s+f+1;d<=a;++d)(d===a||i.charCodeAt(d)===47)&&(g.length===0?g+="..":g+="/..");return g.length>0?g+o.slice(c+f):(c+=f,o.charCodeAt(c)===47&&++c,o.slice(c))},_makeLong:function(i){return i},dirname:function(i){if(e(i),i.length===0)return".";for(var o=i.charCodeAt(0),s=o===47,a=-1,l=!0,c=i.length-1;c>=1;--c)if((o=i.charCodeAt(c))===47){if(!l){a=c;break}}else l=!1;return a===-1?s?"/":".":s&&a===1?"//":i.slice(0,a)},basename:function(i,o){if(o!==void 0&&typeof o!="string")throw new TypeError('"ext" argument must be a string');e(i);var s,a=0,l=-1,c=!0;if(o!==void 0&&o.length>0&&o.length<=i.length){if(o.length===i.length&&o===i)return"";var u=o.length-1,h=-1;for(s=i.length-1;s>=0;--s){var f=i.charCodeAt(s);if(f===47){if(!c){a=s+1;break}}else h===-1&&(c=!1,h=s+1),u>=0&&(f===o.charCodeAt(u)?--u===-1&&(l=s):(u=-1,l=h))}return a===l?l=h:l===-1&&(l=i.length),i.slice(a,l)}for(s=i.length-1;s>=0;--s)if(i.charCodeAt(s)===47){if(!c){a=s+1;break}}else l===-1&&(c=!1,l=s+1);return l===-1?"":i.slice(a,l)},extname:function(i){e(i);for(var o=-1,s=0,a=-1,l=!0,c=0,u=i.length-1;u>=0;--u){var h=i.charCodeAt(u);if(h!==47)a===-1&&(l=!1,a=u+1),h===46?o===-1?o=u:c!==1&&(c=1):o!==-1&&(c=-1);else if(!l){s=u+1;break}}return o===-1||a===-1||c===0||c===1&&o===a-1&&o===s+1?"":i.slice(o,a)},format:function(i){if(i===null||typeof i!="object")throw new TypeError('The "pathObject" argument must be of type Object. Received type '+typeof i);return function(o,s){var a=s.dir||s.root,l=s.base||(s.name||"")+(s.ext||"");return a?a===s.root?a+l:a+o+l:l}("/",i)},parse:function(i){e(i);var o={root:"",dir:"",base:"",ext:"",name:""};if(i.length===0)return o;var s,a=i.charCodeAt(0),l=a===47;l?(o.root="/",s=1):s=0;for(var c=-1,u=0,h=-1,f=!0,d=i.length-1,y=0;d>=s;--d)if((a=i.charCodeAt(d))!==47)h===-1&&(f=!1,h=d+1),a===46?c===-1?c=d:y!==1&&(y=1):c!==-1&&(y=-1);else if(!f){u=d+1;break}return c===-1||h===-1||y===0||y===1&&c===h-1&&c===u+1?h!==-1&&(o.base=o.name=u===0&&l?i.slice(1,h):i.slice(u,h)):(u===0&&l?(o.name=i.slice(1,c),o.base=i.slice(1,h)):(o.name=i.slice(u,c),o.base=i.slice(u,h)),o.ext=i.slice(c,h)),u>0?o.dir=i.slice(0,u-1):l&&(o.dir="/"),o},sep:"/",delimiter:":",win32:null,posix:null};return n.posix=n,Cc=n}()}),Rw={};function eO(){if(mf)return Fa;mf=!0;var t=typeof Map=="function"&&Map.prototype,e=Object.getOwnPropertyDescriptor&&t?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,r=t&&e&&typeof e.get=="function"?e.get:null,n=t&&Map.prototype.forEach,i=typeof Set=="function"&&Set.prototype,o=Object.getOwnPropertyDescriptor&&i?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,s=i&&o&&typeof o.get=="function"?o.get:null,a=i&&Set.prototype.forEach,l=typeof WeakMap=="function"&&WeakMap.prototype?WeakMap.prototype.has:null,c=typeof WeakSet=="function"&&WeakSet.prototype?WeakSet.prototype.has:null,u=typeof WeakRef=="function"&&WeakRef.prototype?WeakRef.prototype.deref:null,h=Boolean.prototype.valueOf,f=Object.prototype.toString,d=Function.prototype.toString,y=String.prototype.match,g=String.prototype.slice,p=String.prototype.replace,b=String.prototype.toUpperCase,v=String.prototype.toLowerCase,S=RegExp.prototype.test,k=Array.prototype.concat,M=Array.prototype.join,A=Array.prototype.slice,I=Math.floor,T=typeof BigInt=="function"?BigInt.prototype.valueOf:null,_=Object.getOwnPropertySymbols,x=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Symbol.prototype.toString:null,O=typeof Symbol=="function"&&typeof Symbol.iterator=="object",N=typeof Symbol=="function"&&Symbol.toStringTag?Symbol.toStringTag:null,G=Object.prototype.propertyIsEnumerable,U=(typeof Reflect=="function"?Reflect.getPrototypeOf:Object.getPrototypeOf)||([].__proto__===Array.prototype?function(m){return m.__proto__}:null);function z(m,w){if(m===1/0||m===-1/0||m!=m||m&&m>-1e3&&m<1e3||S.call(/e/,w))return w;var E=/[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;if(typeof m=="number"){var D=m<0?-I(-m):I(m);if(D!==m){var W=String(D),P=g.call(w,W.length+1);return p.call(W,E,"$&_")+"."+p.call(p.call(P,/([0-9]{3})/g,"$&_"),/_$/,"")}}return p.call(w,E,"$&_")}var q=Lw,B=q.custom,ne=Z(B)?B:null;function H(m,w,E){var D=(E.quoteStyle||w)==="double"?'"':"'";return D+m+D}function Q(m){return p.call(String(m),/"/g,""")}function F(m){return!(le(m)!=="[object Array]"||N&&typeof m=="object"&&N in m)}function V(m){return!(le(m)!=="[object RegExp]"||N&&typeof m=="object"&&N in m)}function Z(m){if(O)return m&&typeof m=="object"&&m instanceof Symbol;if(typeof m=="symbol")return!0;if(!m||typeof m!="object"||!x)return!1;try{return x.call(m),!0}catch{}return!1}Fa=function m(w,E,D,W){var P=E||{};if(se(P,"quoteStyle")&&P.quoteStyle!=="single"&&P.quoteStyle!=="double")throw new TypeError('option "quoteStyle" must be "single" or "double"');if(se(P,"maxStringLength")&&(typeof P.maxStringLength=="number"?P.maxStringLength<0&&P.maxStringLength!==1/0:P.maxStringLength!==null))throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');var J=!se(P,"customInspect")||P.customInspect;if(typeof J!="boolean"&&J!=="symbol")throw new TypeError("option \"customInspect\", if provided, must be `true`, `false`, or `'symbol'`");if(se(P,"indent")&&P.indent!==null&&P.indent!==" "&&!(parseInt(P.indent,10)===P.indent&&P.indent>0))throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');if(se(P,"numericSeparator")&&typeof P.numericSeparator!="boolean")throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var he=P.numericSeparator;if(typeof w>"u")return"undefined";if(w===null)return"null";if(typeof w=="boolean")return w?"true":"false";if(typeof w=="string")return Y(w,P);if(typeof w=="number"){if(w===0)return 1/0/w>0?"0":"-0";var fe=String(w);return he?z(w,fe):fe}if(typeof w=="bigint"){var de=String(w)+"n";return he?z(w,de):de}var pe=typeof P.depth>"u"?5:P.depth;if(typeof D>"u"&&(D=0),D>=pe&&pe>0&&typeof w=="object")return F(w)?"[Array]":"[Object]";var K=function(Se,He){var qe;if(Se.indent===" ")qe=" ";else{if(!(typeof Se.indent=="number"&&Se.indent>0))return null;qe=M.call(Array(Se.indent+1)," ")}return{base:qe,prev:M.call(Array(He+1),qe)}}(P,D);if(typeof W>"u")W=[];else if(ue(W,w)>=0)return"[Circular]";function ee(Se,He,qe){if(He&&(W=A.call(W)).push(He),qe){var lt={depth:P.depth};return se(P,"quoteStyle")&&(lt.quoteStyle=P.quoteStyle),m(Se,lt,D+1,W)}return m(Se,P,D+1,W)}if(typeof w=="function"&&!V(w)){var ce=function(Se){if(Se.name)return Se.name;var He=y.call(d.call(Se),/^function\s*([\w$]+)/);return He?He[1]:null}(w),C=te(w,ee);return"[Function"+(ce?": "+ce:" (anonymous)")+"]"+(C.length>0?" { "+M.call(C,", ")+" }":"")}if(Z(w)){var L=O?p.call(String(w),/^(Symbol\(.*\))_[^)]*$/,"$1"):x.call(w);return typeof w!="object"||O?L:oe(L)}if(function(Se){return!(!Se||typeof Se!="object")&&(typeof HTMLElement<"u"&&Se instanceof HTMLElement||typeof Se.nodeName=="string"&&typeof Se.getAttribute=="function")}(w)){for(var ae="<"+v.call(String(w.nodeName)),ge=w.attributes||[],xe=0;xe"}if(F(w)){if(w.length===0)return"[]";var ve=te(w,ee);return K&&!function(Se){for(var He=0;He=0)return!1;return!0}(ve)?"["+R(ve,K)+"]":"[ "+M.call(ve,", ")+" ]"}if(function(Se){return!(le(Se)!=="[object Error]"||N&&typeof Se=="object"&&N in Se)}(w)){var Ce=te(w,ee);return"cause"in Error.prototype||!("cause"in w)||G.call(w,"cause")?Ce.length===0?"["+String(w)+"]":"{ ["+String(w)+"] "+M.call(Ce,", ")+" }":"{ ["+String(w)+"] "+M.call(k.call("[cause]: "+ee(w.cause),Ce),", ")+" }"}if(typeof w=="object"&&J){if(ne&&typeof w[ne]=="function"&&q)return q(w,{depth:pe-D});if(J!=="symbol"&&typeof w.inspect=="function")return w.inspect()}if(function(Se){if(!r||!Se||typeof Se!="object")return!1;try{r.call(Se);try{s.call(Se)}catch{return!0}return Se instanceof Map}catch{}return!1}(w)){var ke=[];return n&&n.call(w,function(Se,He){ke.push(ee(He,w,!0)+" => "+ee(Se,w))}),X("Map",r.call(w),ke,K)}if(function(Se){if(!s||!Se||typeof Se!="object")return!1;try{s.call(Se);try{r.call(Se)}catch{return!0}return Se instanceof Set}catch{}return!1}(w)){var Be=[];return a&&a.call(w,function(Se){Be.push(ee(Se,w))}),X("Set",s.call(w),Be,K)}if(function(Se){if(!l||!Se||typeof Se!="object")return!1;try{l.call(Se,l);try{c.call(Se,c)}catch{return!0}return Se instanceof WeakMap}catch{}return!1}(w))return j("WeakMap");if(function(Se){if(!c||!Se||typeof Se!="object")return!1;try{c.call(Se,c);try{l.call(Se,l)}catch{return!0}return Se instanceof WeakSet}catch{}return!1}(w))return j("WeakSet");if(function(Se){if(!u||!Se||typeof Se!="object")return!1;try{return u.call(Se),!0}catch{}return!1}(w))return j("WeakRef");if(function(Se){return!(le(Se)!=="[object Number]"||N&&typeof Se=="object"&&N in Se)}(w))return oe(ee(Number(w)));if(function(Se){if(!Se||typeof Se!="object"||!T)return!1;try{return T.call(Se),!0}catch{}return!1}(w))return oe(ee(T.call(w)));if(function(Se){return!(le(Se)!=="[object Boolean]"||N&&typeof Se=="object"&&N in Se)}(w))return oe(h.call(w));if(function(Se){return!(le(Se)!=="[object String]"||N&&typeof Se=="object"&&N in Se)}(w))return oe(ee(String(w)));if(typeof window<"u"&&w===window)return"{ [object Window] }";if(typeof globalThis<"u"&&w===globalThis||typeof Wa<"u"&&w===Wa)return"{ [object globalThis] }";if(!function(Se){return!(le(Se)!=="[object Date]"||N&&typeof Se=="object"&&N in Se)}(w)&&!V(w)){var De=te(w,ee),Ve=U?U(w)===Object.prototype:w instanceof Object||w.constructor===Object,Fe=w instanceof Object?"":"null prototype",ze=!Ve&&N&&Object(w)===w&&N in w?g.call(le(w),8,-1):Fe?"Object":"",Me=(Ve||typeof w.constructor!="function"?"":w.constructor.name?w.constructor.name+" ":"")+(ze||Fe?"["+M.call(k.call([],ze||[],Fe||[]),": ")+"] ":"");return De.length===0?Me+"{}":K?Me+"{"+R(De,K)+"}":Me+"{ "+M.call(De,", ")+" }"}return String(w)};var $=Object.prototype.hasOwnProperty||function(m){return m in(this||Wa)};function se(m,w){return $.call(m,w)}function le(m){return f.call(m)}function ue(m,w){if(m.indexOf)return m.indexOf(w);for(var E=0,D=m.length;Ew.maxStringLength){var E=m.length-w.maxStringLength,D="... "+E+" more character"+(E>1?"s":"");return Y(g.call(m,0,w.maxStringLength),w)+D}return H(p.call(p.call(m,/(['\\])/g,"\\$1"),/[\x00-\x1f]/g,ie),"single",w)}function ie(m){var w=m.charCodeAt(0),E={8:"b",9:"t",10:"n",12:"f",13:"r"}[w];return E?"\\"+E:"\\x"+(w<16?"0":"")+b.call(w.toString(16))}function oe(m){return"Object("+m+")"}function j(m){return m+" { ? }"}function X(m,w,E,D){return m+" ("+w+") {"+(D?R(E,D):M.call(E,", "))+"}"}function R(m,w){if(m.length===0)return"";var E=` +`+w.prev+w.base;return E+M.call(m,","+E)+` +`+w.prev}function te(m,w){var E=F(m),D=[];if(E){D.length=m.length;for(var W=0;W1;){var b=p.pop(),v=b.obj[b.prop];if(r(v)){for(var S=[],k=0;k=o?h.slice(d,d+o):h,g=[],p=0;p=48&&b<=57||b>=65&&b<=90||b>=97&&b<=122||u===t.RFC1738&&(b===40||b===41)?g[g.length]=y.charAt(p):b<128?g[g.length]=n[b]:b<2048?g[g.length]=n[192|b>>6]+n[128|63&b]:b<55296||b>=57344?g[g.length]=n[224|b>>12]+n[128|b>>6&63]+n[128|63&b]:(p+=1,b=65536+((1023&b)<<10|1023&y.charCodeAt(p)),g[g.length]=n[240|b>>18]+n[128|b>>12&63]+n[128|b>>6&63]+n[128|63&b])}f+=g.join("")}return f},isBuffer:function(s){return!(!s||typeof s!="object")&&!!(s.constructor&&s.constructor.isBuffer&&s.constructor.isBuffer(s))},isRegExp:function(s){return Object.prototype.toString.call(s)==="[object RegExp]"},maybeMap:function(s,a){if(r(s)){for(var l=[],c=0;c"u"&&(ne=0)}if(typeof I=="function"?q=I(g,q):q instanceof Date?q=x(q):p==="comma"&&o(q)&&(q=e.maybeMap(q,function(X){return X instanceof Date?x(X):X})),q===null){if(S)return A&&!G?A(g,u.encoder,U,"key",O):g;q=""}if(function(X){return typeof X=="string"||typeof X=="number"||typeof X=="boolean"||typeof X=="symbol"||typeof X=="bigint"}(q)||e.isBuffer(q))return A?[N(G?g:A(g,u.encoder,U,"key",O))+"="+N(A(q,u.encoder,U,"value",O))]:[N(g)+"="+N(String(q))];var F,V=[];if(typeof q>"u")return V;if(p==="comma"&&o(q))G&&A&&(q=e.maybeMap(q,A)),F=[{value:q.length>0?q.join(",")||null:void 0}];else if(o(I))F=I;else{var Z=Object.keys(q);F=T?Z.sort(T):Z}var $=M?g.replace(/\./g,"%2E"):g,se=b&&o(q)&&q.length===1?$+"[]":$;if(v&&o(q)&&q.length===0)return se+"[]";for(var le=0;le"u"?x.encodeDotInKeys===!0||u.allowDots:!!x.allowDots;return{addQueryPrefix:typeof x.addQueryPrefix=="boolean"?x.addQueryPrefix:u.addQueryPrefix,allowDots:q,allowEmptyArrays:typeof x.allowEmptyArrays=="boolean"?!!x.allowEmptyArrays:u.allowEmptyArrays,arrayFormat:G,charset:O,charsetSentinel:typeof x.charsetSentinel=="boolean"?x.charsetSentinel:u.charsetSentinel,commaRoundTrip:x.commaRoundTrip,delimiter:typeof x.delimiter>"u"?u.delimiter:x.delimiter,encode:typeof x.encode=="boolean"?x.encode:u.encode,encodeDotInKeys:typeof x.encodeDotInKeys=="boolean"?x.encodeDotInKeys:u.encodeDotInKeys,encoder:typeof x.encoder=="function"?x.encoder:u.encoder,encodeValuesOnly:typeof x.encodeValuesOnly=="boolean"?x.encodeValuesOnly:u.encodeValuesOnly,filter:z,format:N,formatter:U,serializeDate:typeof x.serializeDate=="function"?x.serializeDate:u.serializeDate,skipNulls:typeof x.skipNulls=="boolean"?x.skipNulls:u.skipNulls,sort:typeof x.sort=="function"?x.sort:null,strictNullHandling:typeof x.strictNullHandling=="boolean"?x.strictNullHandling:u.strictNullHandling}}(y);typeof b.filter=="function"?p=(0,b.filter)("",p):o(b.filter)&&(g=b.filter);var v=[];if(typeof p!="object"||p===null)return"";var S=i[b.arrayFormat],k=S==="comma"&&b.commaRoundTrip;g||(g=Object.keys(p)),b.sort&&g.sort(b.sort);for(var M=t(),A=0;A0?_+T:""}}function nO(){if(Ef)return kf;Ef=!0;var t=Dw(),e=Object.prototype.hasOwnProperty,r=Array.isArray,n={allowDots:!1,allowEmptyArrays:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decodeDotInKeys:!1,decoder:t.decode,delimiter:"&",depth:5,duplicates:"combine",ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictDepth:!1,strictNullHandling:!1},i=function(a){return a.replace(/&#(\d+);/g,function(l,c){return String.fromCharCode(parseInt(c,10))})},o=function(a,l){return a&&typeof a=="string"&&l.comma&&a.indexOf(",")>-1?a.split(","):a},s=function(a,l,c,u){if(a){var h=c.allowDots?a.replace(/\.([^.[]+)/g,"[$1]"):a,f=/(\[[^[\]]*])/g,d=c.depth>0&&/(\[[^[\]]*])/.exec(h),y=d?h.slice(0,d.index):h,g=[];if(y){if(!c.plainObjects&&e.call(Object.prototype,y)&&!c.allowPrototypes)return;g.push(y)}for(var p=0;c.depth>0&&(d=f.exec(h))!==null&&p=0;--A){var I,T=b[A];if(T==="[]"&&S.parseArrays)I=S.allowEmptyArrays&&(M===""||S.strictNullHandling&&M===null)?[]:[].concat(M);else{I=S.plainObjects?Object.create(null):{};var _=T.charAt(0)==="["&&T.charAt(T.length-1)==="]"?T.slice(1,-1):T,x=S.decodeDotInKeys?_.replace(/%2E/g,"."):_,O=parseInt(x,10);S.parseArrays||x!==""?!isNaN(O)&&T!==x&&String(O)===x&&O>=0&&S.parseArrays&&O<=S.arrayLimit?(I=[])[O]=M:x!=="__proto__"&&(I[x]=M):I={0:M}}M=I}return M}(g,l,c,u)}};return kf=function(a,l){var c=function(p){if(!p)return n;if(typeof p.allowEmptyArrays<"u"&&typeof p.allowEmptyArrays!="boolean")throw new TypeError("`allowEmptyArrays` option can only be `true` or `false`, when provided");if(typeof p.decodeDotInKeys<"u"&&typeof p.decodeDotInKeys!="boolean")throw new TypeError("`decodeDotInKeys` option can only be `true` or `false`, when provided");if(p.decoder!==null&&typeof p.decoder<"u"&&typeof p.decoder!="function")throw new TypeError("Decoder has to be a function.");if(typeof p.charset<"u"&&p.charset!=="utf-8"&&p.charset!=="iso-8859-1")throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var b=typeof p.charset>"u"?n.charset:p.charset,v=typeof p.duplicates>"u"?n.duplicates:p.duplicates;if(v!=="combine"&&v!=="first"&&v!=="last")throw new TypeError("The duplicates option must be either combine, first, or last");return{allowDots:typeof p.allowDots>"u"?p.decodeDotInKeys===!0||n.allowDots:!!p.allowDots,allowEmptyArrays:typeof p.allowEmptyArrays=="boolean"?!!p.allowEmptyArrays:n.allowEmptyArrays,allowPrototypes:typeof p.allowPrototypes=="boolean"?p.allowPrototypes:n.allowPrototypes,allowSparse:typeof p.allowSparse=="boolean"?p.allowSparse:n.allowSparse,arrayLimit:typeof p.arrayLimit=="number"?p.arrayLimit:n.arrayLimit,charset:b,charsetSentinel:typeof p.charsetSentinel=="boolean"?p.charsetSentinel:n.charsetSentinel,comma:typeof p.comma=="boolean"?p.comma:n.comma,decodeDotInKeys:typeof p.decodeDotInKeys=="boolean"?p.decodeDotInKeys:n.decodeDotInKeys,decoder:typeof p.decoder=="function"?p.decoder:n.decoder,delimiter:typeof p.delimiter=="string"||t.isRegExp(p.delimiter)?p.delimiter:n.delimiter,depth:typeof p.depth=="number"||p.depth===!1?+p.depth:n.depth,duplicates:v,ignoreQueryPrefix:p.ignoreQueryPrefix===!0,interpretNumericEntities:typeof p.interpretNumericEntities=="boolean"?p.interpretNumericEntities:n.interpretNumericEntities,parameterLimit:typeof p.parameterLimit=="number"?p.parameterLimit:n.parameterLimit,parseArrays:p.parseArrays!==!1,plainObjects:typeof p.plainObjects=="boolean"?p.plainObjects:n.plainObjects,strictDepth:typeof p.strictDepth=="boolean"?!!p.strictDepth:n.strictDepth,strictNullHandling:typeof p.strictNullHandling=="boolean"?p.strictNullHandling:n.strictNullHandling}}(l);if(a===""||a===null||typeof a>"u")return c.plainObjects?Object.create(null):{};for(var u=typeof a=="string"?function(p,b){var v={__proto__:null},S=b.ignoreQueryPrefix?p.replace(/^\?/,""):p;S=S.replace(/%5B/gi,"[").replace(/%5D/gi,"]");var k,M=b.parameterLimit===1/0?void 0:b.parameterLimit,A=S.split(b.delimiter,M),I=-1,T=b.charset;if(b.charsetSentinel)for(k=0;k-1&&(x=r(x)?[x]:x);var U=e.call(v,_);U&&b.duplicates==="combine"?v[_]=t.combine(v[_],x):(!U||b.duplicates==="last")&&(v[_]=x)}return v}(a,c):a,h=c.plainObjects?Object.create(null):{},f=Object.keys(u),d=0;d",'"',"`"," ","\r",` +`," "]),s=["'"].concat(o),a=["%","/","?",";","#"].concat(s),l=["/","?","#"],c=/^[+a-z0-9A-Z_-]{0,63}$/,u=/^([+a-z0-9A-Z_-]{0,63})(.*)$/,h={javascript:!0,"javascript:":!0},f={javascript:!0,"javascript:":!0},d={http:!0,https:!0,ftp:!0,gopher:!0,file:!0,"http:":!0,"https:":!0,"ftp:":!0,"gopher:":!0,"file:":!0},y=function(){if(Mf)return Of;Mf=!0;var p=rO(),b=nO(),v=Cd();return Of={formats:v,parse:b,stringify:p}}();function g(p,b,v){if(p&&typeof p=="object"&&p instanceof e)return p;var S=new e;return S.parse(p,b,v),S}return e.prototype.parse=function(p,b,v){if(typeof p!="string")throw new TypeError("Parameter 'url' must be a string, not "+typeof p);var S=p.indexOf("?"),k=S!==-1&&S127?Q+="x":Q+=H[F];if(!Q.match(c)){var Z=B.slice(0,N),$=B.slice(N+1),se=H.match(u);se&&(Z.push(se[1]),$.unshift(se[2])),$.length&&(A="/"+$.join(".")+A),this.hostname=Z.join(".");break}}}this.hostname.length>255?this.hostname="":this.hostname=this.hostname.toLowerCase(),q||(this.hostname=t.toASCII(this.hostname));var le=this.port?":"+this.port:"",ue=this.hostname||"";this.host=ue+le,this.href+=this.host,q&&(this.hostname=this.hostname.substr(1,this.hostname.length-2),A[0]!=="/"&&(A="/"+A))}if(!h[_])for(N=0,ne=s.length;N0)&&v.host.split("@"))&&(v.auth=se.shift(),v.hostname=se.shift(),v.host=v.hostname)),v.search=p.search,v.query=p.query,(v.pathname!==null||v.search!==null)&&(v.path=(v.pathname?v.pathname:"")+(v.search?v.search:"")),v.href=v.format(),v;if(!H.length)return v.pathname=null,v.search?v.path="/"+v.search:v.path=null,v.href=v.format(),v;for(var F=H.slice(-1)[0],V=(v.host||p.host||H.length>1)&&(F==="."||F==="..")||F==="",Z=0,$=H.length;$>=0;$--)(F=H[$])==="."?H.splice($,1):F===".."?(H.splice($,1),Z++):Z&&(H.splice($,1),Z--);if(!B&&!ne)for(;Z--;Z)H.unshift("..");B&&H[0]!==""&&(!H[0]||H[0].charAt(0)!=="/")&&H.unshift(""),V&&H.join("/").substr(-1)!=="/"&&H.push("");var se,le=H[0]===""||H[0]&&H[0].charAt(0)==="/";return Q&&(v.hostname=le?"":H.length?H.shift():"",v.host=v.hostname,(se=!!(v.host&&v.host.indexOf("@")>0)&&v.host.split("@"))&&(v.auth=se.shift(),v.hostname=se.shift(),v.host=v.hostname)),(B=B||v.host&&H.length)&&!le&&H.unshift(""),H.length>0?v.pathname=H.join("/"):(v.pathname=null,v.path=null),(v.pathname!==null||v.search!==null)&&(v.path=(v.pathname?v.pathname:"")+(v.search?v.search:"")),v.auth=p.auth||v.auth,v.slashes=v.slashes||p.slashes,v.href=v.format(),v},e.prototype.parseHost=function(){var p=this.host,b=n.exec(p);b&&((b=b[0])!==":"&&(this.port=b.substr(1)),p=p.substr(0,p.length-b.length)),p&&(this.hostname=p)},pn.parse=g,pn.resolve=function(p,b){return g(p,!1,!0).resolve(b)},pn.resolveObject=function(p,b){return p?g(p,!1,!0).resolveObject(b):b},pn.format=function(p){return typeof p=="string"&&(p=g(p)),p instanceof e?p.format():e.prototype.format.call(p)},pn.Url=e,pn}function jw(t){if(typeof t=="string")t=new URL(t);else if(!(t instanceof URL))throw new Deno.errors.InvalidData("invalid argument path , must be a string or URL");if(t.protocol!=="file:")throw new Deno.errors.InvalidData("invalid url scheme");return sl?function(e){let r=e.hostname,n=e.pathname;for(let i=0;iYw||o!==":")throw new Deno.errors.InvalidData("file url path must be absolute");return n.slice(1)}}(t):function(e){if(e.hostname!=="")throw new Deno.errors.InvalidData("invalid file url hostname");let r=e.pathname;for(let n=0;n$w,Url:()=>Bw,default:()=>Rt,fileURLToPath:()=>jw,format:()=>Fw,parse:()=>Vw,pathToFileURL:()=>Nw,resolve:()=>Ww,resolveObject:()=>Uw});var Lw,Fa,mf,Wa,yf,bf,Ua,vf,wf,_f,xf,Sf,kf,Ef,Of,Mf,pn,Af,Rt,nm,Bw,Fw,Ww,Uw,Vw,$w,zw,Hw,qw,Yw,sl,Kw,Gw,Qw,Zw,Xw,Jw,oO=Ht(()=>{me(),be(),ye(),BE(),VE(),JE(),Cw(),Lw=Object.freeze(Object.create(null)),Fa={},mf=!1,Wa=typeof globalThis<"u"?globalThis:typeof self<"u"?self:lu,yf={},bf=!1,Ua={},vf=!1,wf={},_f=!1,xf={},Sf=!1,kf={},Ef=!1,Of={},Mf=!1,pn={},Af=!1,(Rt=iO()).parse,Rt.resolve,Rt.resolveObject,Rt.format,Rt.Url,nm=typeof Deno<"u"?Deno.build.os==="windows"?"win32":Deno.build.os:void 0,Rt.URL=typeof URL<"u"?URL:null,Rt.pathToFileURL=Nw,Rt.fileURLToPath=jw,Bw=Rt.Url,Fw=Rt.format,Ww=Rt.resolve,Uw=Rt.resolveObject,Vw=Rt.parse,$w=Rt.URL,zw=92,Hw=47,qw=97,Yw=122,sl=nm==="win32",Kw=/\//g,Gw=/%/g,Qw=/\\/g,Zw=/\n/g,Xw=/\r/g,Jw=/\t/g}),sO=we((t,e)=>{me(),be(),ye(),e.exports=function(){throw new Error("ws does not work in the browser. Browser clients must use the native WebSocket object")}}),Rd=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0}),t.BufferedDuplex=void 0,t.writev=n;var e=pi(),r=(vt(),Xe(bt));function n(o,s){let a=new Array(o.length);for(let l=0;l{!this.destroyed&&this.readable&&this.push(c)})}_read(s){this.proxy.read(s)}_write(s,a,l){this.isSocketOpen?this.writeToProxy(s,a,l):this.writeQueue.push({chunk:s,encoding:a,cb:l})}_final(s){this.writeQueue=[],this.proxy.end(s)}_destroy(s,a){this.writeQueue=[],this.proxy.destroy(),a(s)}socketReady(){this.emit("connect"),this.isSocketOpen=!0,this.processWriteQueue()}writeToProxy(s,a,l){this.proxy.write(s,a)===!1?this.proxy.once("drain",l):l()}processWriteQueue(){for(;this.writeQueue.length>0;){let{chunk:s,encoding:a,cb:l}=this.writeQueue.shift();this.writeToProxy(s,a,l)}}};t.BufferedDuplex=i}),Gs=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(f){return f&&f.__esModule?f:{default:f}};Object.defineProperty(t,"__esModule",{value:!0}),t.streamBuilder=t.browserStreamBuilder=void 0;var r=(vt(),Xe(bt)),n=e(sO()),i=e(qr()),o=pi(),s=e(ql()),a=Rd(),l=(0,i.default)("mqttjs:ws"),c=["rejectUnauthorized","ca","cert","key","pfx","passphrase"];function u(f,d){let y=`${f.protocol}://${f.hostname}:${f.port}${f.path}`;return typeof f.transformWsUrl=="function"&&(y=f.transformWsUrl(y,f,d)),y}function h(f){let d=f;return f.port||(f.protocol==="wss"?d.port=443:d.port=80),f.path||(d.path="/"),f.wsOptions||(d.wsOptions={}),!s.default&&!f.forceNativeWebSocket&&f.protocol==="wss"&&c.forEach(y=>{Object.prototype.hasOwnProperty.call(f,y)&&!Object.prototype.hasOwnProperty.call(f.wsOptions,y)&&(d.wsOptions[y]=f[y])}),d}t.streamBuilder=(f,d)=>{l("streamBuilder");let y=h(d);y.hostname=y.hostname||y.host||"localhost";let g=u(y,f),p=function(v,S,k){l("createWebSocket"),l(`protocol: ${k.protocolId} ${k.protocolVersion}`);let M,A=k.protocolId==="MQIsdp"&&k.protocolVersion===3?"mqttv3.1":"mqtt";return l(`creating new Websocket for url: ${S} and protocol: ${A}`),M=k.createWebsocket?k.createWebsocket(S,[A],k):new n.default(S,[A],k.wsOptions),M}(0,g,y),b=n.default.createWebSocketStream(p,y.wsOptions);return b.url=g,p.on("close",()=>{b.destroy()}),b},t.browserStreamBuilder=(f,d)=>{l("browserStreamBuilder");let y,g=function(_){let x=h(_);if(x.hostname||(x.hostname=x.host),!x.hostname){if(typeof document>"u")throw new Error("Could not determine host. Specify host manually.");let O=new URL(document.URL);x.hostname=O.hostname,x.port||(x.port=Number(O.port))}return x.objectMode===void 0&&(x.objectMode=!(x.binary===!0||x.binary===void 0)),x}(d).browserBufferSize||524288,p=d.browserBufferTimeout||1e3,b=!d.objectMode,v=function(_,x){let O,N=x.protocolId==="MQIsdp"&&x.protocolVersion===3?"mqttv3.1":"mqtt",G=u(x,_);return O=x.createWebsocket?x.createWebsocket(G,[N],x):new WebSocket(G,[N]),O.binaryType="arraybuffer",O}(f,d),S=function(_,x,O){let N=new o.Transform({objectMode:_.objectMode});return N._write=x,N._flush=O,N}(d,function _(x,O,N){if(v.bufferedAmount>g)return void setTimeout(_,p,x,O,N);b&&typeof x=="string"&&(x=r.Buffer.from(x,"utf8"));try{v.send(x)}catch(G){return N(G)}N()},function(_){v.close(),_()});d.objectMode||(S._writev=a.writev.bind(S)),S.on("close",()=>{v.close()});let k=typeof v.addEventListener<"u";function M(){l("WebSocket onOpen"),y instanceof a.BufferedDuplex&&y.socketReady()}function A(_){l("WebSocket onClose",_),y.end(),y.destroy()}function I(_){l("WebSocket onError",_);let x=new Error("WebSocket error");x.event=_,y.destroy(x)}async function T(_){if(!S||S.destroyed||!S.readable)return;let{data:x}=_;x=x instanceof ArrayBuffer?r.Buffer.from(x):x instanceof Blob?r.Buffer.from(await new Response(x).arrayBuffer()):r.Buffer.from(x,"utf8"),S.push(x)}return v.readyState===v.OPEN?(y=S,y.socket=v):(y=new a.BufferedDuplex(d,S,v),k?v.addEventListener("open",M):v.onopen=M),k?(v.addEventListener("close",A),v.addEventListener("error",I),v.addEventListener("message",T)):(v.onclose=A,v.onerror=I,v.onmessage=T),y}}),Dd={};function nt(){throw new Error("Node.js net module is not supported by JSPM core outside of Node.js")}no(Dd,{Server:()=>nt,Socket:()=>nt,Stream:()=>nt,_createServerHandle:()=>nt,_normalizeArgs:()=>nt,_setSimultaneousAccepts:()=>nt,connect:()=>nt,createConnection:()=>nt,createServer:()=>nt,default:()=>e_,isIP:()=>nt,isIPv4:()=>nt,isIPv6:()=>nt});var e_,t_=Ht(()=>{me(),be(),ye(),e_={_createServerHandle:nt,_normalizeArgs:nt,_setSimultaneousAccepts:nt,connect:nt,createConnection:nt,createServer:nt,isIP:nt,isIPv4:nt,isIPv6:nt,Server:nt,Socket:nt,Stream:nt}}),r_=we((t,e)=>{me(),be(),ye(),e.exports={}}),im=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(s){return s&&s.__esModule?s:{default:s}};Object.defineProperty(t,"__esModule",{value:!0});var r=e((t_(),Xe(Dd))),n=e(qr()),i=e(r_()),o=(0,n.default)("mqttjs:tcp");t.default=(s,a)=>{if(a.port=a.port||1883,a.hostname=a.hostname||a.host||"localhost",a.socksProxy)return(0,i.default)(a.hostname,a.port,a.socksProxy,{timeout:a.socksTimeout});let{port:l,path:c}=a,u=a.hostname;return o("port %d and host %s",l,u),r.default.createConnection({port:l,host:u,path:c})}}),n_={};no(n_,{default:()=>i_});var i_,aO=Ht(()=>{me(),be(),ye(),i_={}}),om=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(a){return a&&a.__esModule?a:{default:a}};Object.defineProperty(t,"__esModule",{value:!0});var r=(aO(),Xe(n_)),n=e((t_(),Xe(Dd))),i=e(qr()),o=e(r_()),s=(0,i.default)("mqttjs:tls");t.default=(a,l)=>{l.port=l.port||8883,l.host=l.hostname||l.host||"localhost",n.default.isIP(l.host)===0&&(l.servername=l.host),l.rejectUnauthorized=l.rejectUnauthorized!==!1,delete l.path,s("port %d host %s rejectUnauthorized %b",l.port,l.host,l.rejectUnauthorized);let c=function(h){let{host:f,port:d,socksProxy:y,...g}=h;if(y!==void 0){let p=(0,o.default)(f,d,y,{timeout:h.socksTimeout});return(0,r.connect)({...g,socket:p})}return(0,r.connect)(h)}(l);function u(h){l.rejectUnauthorized&&a.emit("error",h),c.end()}return c.on("secureConnect",()=>{l.rejectUnauthorized&&!c.authorized?c.emit("error",new Error("TLS not authorized")):c.removeListener("error",u)}),c.on("error",u),c}}),sm=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e,r,n,i=(vt(),Xe(bt)),o=pi(),s=Rd();t.default=(a,l)=>{if(l.hostname=l.hostname||l.host,!l.hostname)throw new Error("Could not determine host. Specify host manually.");let c=l.protocolId==="MQIsdp"&&l.protocolVersion===3?"mqttv3.1":"mqtt";(function(f){f.hostname||(f.hostname="localhost"),f.path||(f.path="/"),f.wsOptions||(f.wsOptions={})})(l);let u=function(f,d){let y=f.protocol==="wxs"?"wss":"ws",g=`${y}://${f.hostname}${f.path}`;return f.port&&f.port!==80&&f.port!==443&&(g=`${y}://${f.hostname}:${f.port}${f.path}`),typeof f.transformWsUrl=="function"&&(g=f.transformWsUrl(g,f,d)),g}(l,a);e=wx.connectSocket({url:u,protocols:[c]}),r=function(){let f=new o.Transform;return f._write=(d,y,g)=>{e.send({data:d.buffer,success(){g()},fail(p){g(new Error(p))}})},f._flush=d=>{e.close({success(){d()}})},f}(),(n=new s.BufferedDuplex(l,r,e))._destroy=(f,d)=>{e.close({success(){d&&d(f)}})};let h=n.destroy;return n.destroy=(f,d)=>(n.destroy=h,setTimeout(()=>{e.close({fail(){n._destroy(f,d)}})},0),n),e.onOpen(()=>{n.socketReady()}),e.onMessage(f=>{let{data:d}=f;d=d instanceof ArrayBuffer?i.Buffer.from(d):i.Buffer.from(d,"utf8"),r.push(d)}),e.onClose(()=>{n.emit("close"),n.end(),n.destroy()}),e.onError(f=>{let d=new Error(f.errMsg);n.destroy(d)}),n}}),am=we(t=>{me(),be(),ye(),Object.defineProperty(t,"__esModule",{value:!0});var e,r,n,i=(vt(),Xe(bt)),o=pi(),s=Rd(),a=!1;t.default=(l,c)=>{if(c.hostname=c.hostname||c.host,!c.hostname)throw new Error("Could not determine host. Specify host manually.");let u=c.protocolId==="MQIsdp"&&c.protocolVersion===3?"mqttv3.1":"mqtt";(function(f){f.hostname||(f.hostname="localhost"),f.path||(f.path="/"),f.wsOptions||(f.wsOptions={})})(c);let h=function(f,d){let y=f.protocol==="alis"?"wss":"ws",g=`${y}://${f.hostname}${f.path}`;return f.port&&f.port!==80&&f.port!==443&&(g=`${y}://${f.hostname}:${f.port}${f.path}`),typeof f.transformWsUrl=="function"&&(g=f.transformWsUrl(g,f,d)),g}(c,l);return(e=c.my).connectSocket({url:h,protocols:u}),r=function(){let f=new o.Transform;return f._write=(d,y,g)=>{e.sendSocketMessage({data:d.buffer,success(){g()},fail(){g(new Error)}})},f._flush=d=>{e.closeSocket({success(){d()}})},f}(),n=new s.BufferedDuplex(c,r,e),a||(a=!0,e.onSocketOpen(()=>{n.socketReady()}),e.onSocketMessage(f=>{if(typeof f.data=="string"){let d=i.Buffer.from(f.data,"base64");r.push(d)}else{let d=new FileReader;d.addEventListener("load",()=>{d.result instanceof ArrayBuffer?r.push(i.Buffer.from(d.result)):r.push(i.Buffer.from(d.result,"utf-8"))}),d.readAsArrayBuffer(f.data)}}),e.onSocketClose(()=>{n.end(),n.destroy()}),e.onSocketError(f=>{n.destroy(f)})),n}}),lO=we(t=>{me(),be(),ye();var e=t&&t.__importDefault||function(c){return c&&c.__esModule?c:{default:c}};Object.defineProperty(t,"__esModule",{value:!0}),t.connectAsync=function(c,u,h=!0){return new Promise((f,d)=>{let y=l(c,u),g={connect:b=>{p(),f(y)},end:()=>{p(),f(y)},error:b=>{p(),y.end(),d(b)}};function p(){Object.keys(g).forEach(b=>{y.off(b,g[b])})}h===!1&&(g.close=()=>{g.error(new Error("Couldn't connect to server"))}),Object.keys(g).forEach(b=>{y.on(b,g[b])})})};var r=e(qr()),n=e((oO(),Xe(Rw))),i=e(Th()),o=e(ql());typeof($e==null?void 0:$e.nextTick)!="function"&&($e.nextTick=setImmediate);var s=(0,r.default)("mqttjs"),a=null;function l(c,u){var f,d,y;if(s("connecting to an MQTT broker..."),typeof c=="object"&&!u&&(u=c,c=""),u=u||{},c&&typeof c=="string"){let g=n.default.parse(c,!0),p={};if(g.port!=null&&(p.port=Number(g.port)),p.host=g.hostname,p.query=g.query,p.auth=g.auth,p.protocol=g.protocol,p.path=g.path,!(u={...p,...u}).protocol)throw new Error("Missing protocol");u.protocol=u.protocol.replace(/:$/,"")}if(u.unixSocket=u.unixSocket||((f=u.protocol)==null?void 0:f.includes("+unix")),u.unixSocket?u.protocol=u.protocol.replace("+unix",""):!((d=u.protocol)!=null&&d.startsWith("ws"))&&!((y=u.protocol)!=null&&y.startsWith("wx"))&&delete u.path,function(g){let p;if(g.auth)if(p=g.auth.match(/^(.+):(.+)$/),p){let[,b,v]=p;g.username=b,g.password=v}else g.username=g.auth}(u),u.query&&typeof u.query.clientId=="string"&&(u.clientId=u.query.clientId),o.default||u.unixSocket?u.socksProxy=void 0:u.socksProxy===void 0&&typeof $e<"u"&&(u.socksProxy=$e.env.MQTTJS_SOCKS_PROXY),u.cert&&u.key){if(!u.protocol)throw new Error("Missing secure protocol key");if(["mqtts","wss","wxs","alis"].indexOf(u.protocol)===-1)switch(u.protocol){case"mqtt":u.protocol="mqtts";break;case"ws":u.protocol="wss";break;case"wx":u.protocol="wxs";break;case"ali":u.protocol="alis";break;default:throw new Error(`Unknown protocol for secure connection: "${u.protocol}"!`)}}if(a||(a={},o.default||u.forceNativeWebSocket?(a.ws=Gs().browserStreamBuilder,a.wss=Gs().browserStreamBuilder,a.wx=sm().default,a.wxs=sm().default,a.ali=am().default,a.alis=am().default):(a.ws=Gs().streamBuilder,a.wss=Gs().streamBuilder,a.mqtt=im().default,a.tcp=im().default,a.ssl=om().default,a.tls=a.ssl,a.mqtts=om().default)),!a[u.protocol]){let g=["mqtts","wss"].indexOf(u.protocol)!==-1;u.protocol=["mqtt","mqtts","ws","wss","wx","wxs","ali","alis"].filter((p,b)=>(!g||b%2!=0)&&typeof a[p]=="function")[0]}if(u.clean===!1&&!u.clientId)throw new Error("Missing clientId for unclean clients");u.protocol&&(u.defaultProtocol=u.protocol);let h=new i.default(function(g){return u.servers&&((!g._reconnectCount||g._reconnectCount===u.servers.length)&&(g._reconnectCount=0),u.host=u.servers[g._reconnectCount].host,u.port=u.servers[g._reconnectCount].port,u.protocol=u.servers[g._reconnectCount].protocol?u.servers[g._reconnectCount].protocol:u.defaultProtocol,u.hostname=u.host,g._reconnectCount++),s("calling streambuilder for",u.protocol),a[u.protocol](g,u)},u);return h.on("error",()=>{}),h}t.default=l}),lm=we(t=>{me(),be(),ye();var e,r=t&&t.__createBinding||(Object.create?function(y,g,p,b){b===void 0&&(b=p);var v=Object.getOwnPropertyDescriptor(g,p);(!v||("get"in v?!g.__esModule:v.writable||v.configurable))&&(v={enumerable:!0,get:function(){return g[p]}}),Object.defineProperty(y,b,v)}:function(y,g,p,b){b===void 0&&(b=p),y[b]=g[p]}),n=t&&t.__setModuleDefault||(Object.create?function(y,g){Object.defineProperty(y,"default",{enumerable:!0,value:g})}:function(y,g){y.default=g}),i=t&&t.__importStar||(e=function(y){return e=Object.getOwnPropertyNames||function(g){var p=[];for(var b in g)Object.prototype.hasOwnProperty.call(g,b)&&(p[p.length]=b);return p},e(y)},function(y){if(y&&y.__esModule)return y;var g={};if(y!=null)for(var p=e(y),b=0;b{me(),be(),ye();var e,r=t&&t.__createBinding||(Object.create?function(a,l,c,u){u===void 0&&(u=c);var h=Object.getOwnPropertyDescriptor(l,c);(!h||("get"in h?!l.__esModule:h.writable||h.configurable))&&(h={enumerable:!0,get:function(){return l[c]}}),Object.defineProperty(a,u,h)}:function(a,l,c,u){u===void 0&&(u=c),a[u]=l[c]}),n=t&&t.__setModuleDefault||(Object.create?function(a,l){Object.defineProperty(a,"default",{enumerable:!0,value:l})}:function(a,l){a.default=l}),i=t&&t.__importStar||(e=function(a){return e=Object.getOwnPropertyNames||function(l){var c=[];for(var u in l)Object.prototype.hasOwnProperty.call(l,u)&&(c[c.length]=u);return c},e(a)},function(a){if(a&&a.__esModule)return a;var l={};if(a!=null)for(var c=e(a),u=0;u *) + +safe-buffer/index.js: + (*! safe-buffer. MIT License. Feross Aboukhadijeh *) + +@babel/runtime/helpers/regenerator.js: + (*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE *) +*/var $t="top",or="bottom",sr="right",zt="left",Tf="auto",Is=[$t,or,sr,zt],Qi="start",ys="end",o_="viewport",bo="popper",cm=Is.reduce(function(t,e){return t.concat([e+"-"+Qi,e+"-"+ys])},[]),s_=[].concat(Is,[Tf]).reduce(function(t,e){return t.concat([e,e+"-"+Qi,e+"-"+ys])},[]),uO=["beforeRead","read","afterRead","beforeMain","main","afterMain","beforeWrite","write","afterWrite"];function Ir(t){return t?(t.nodeName||"").toLowerCase():null}function lr(t){if(t==null)return window;if(t.toString()!=="[object Window]"){var e=t.ownerDocument;return e&&e.defaultView||window}return t}function ai(t){return t instanceof lr(t).Element||t instanceof Element}function tr(t){return t instanceof lr(t).HTMLElement||t instanceof HTMLElement}function jd(t){return typeof ShadowRoot<"u"&&(t instanceof lr(t).ShadowRoot||t instanceof ShadowRoot)}const hO={name:"applyStyles",enabled:!0,phase:"write",fn:function(t){var e=t.state;Object.keys(e.elements).forEach(function(r){var n=e.styles[r]||{},i=e.attributes[r]||{},o=e.elements[r];tr(o)&&Ir(o)&&(Object.assign(o.style,n),Object.keys(i).forEach(function(s){var a=i[s];a===!1?o.removeAttribute(s):o.setAttribute(s,a===!0?"":a)}))})},effect:function(t){var e=t.state,r={popper:{position:e.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(e.elements.popper.style,r.popper),e.styles=r,e.elements.arrow&&Object.assign(e.elements.arrow.style,r.arrow),function(){Object.keys(e.elements).forEach(function(n){var i=e.elements[n],o=e.attributes[n]||{},s=Object.keys(e.styles.hasOwnProperty(n)?e.styles[n]:r[n]).reduce(function(a,l){return a[l]="",a},{});tr(i)&&Ir(i)&&(Object.assign(i.style,s),Object.keys(o).forEach(function(a){i.removeAttribute(a)}))})}},requires:["computeStyles"]};function Or(t){return t.split("-")[0]}var si=Math.max,al=Math.min,Zi=Math.round;function If(){var t=navigator.userAgentData;return t!=null&&t.brands?t.brands.map(function(e){return e.brand+"/"+e.version}).join(" "):navigator.userAgent}function a_(){return!/^((?!chrome|android).)*safari/i.test(If())}function Xi(t,e,r){e===void 0&&(e=!1),r===void 0&&(r=!1);var n=t.getBoundingClientRect(),i=1,o=1;e&&tr(t)&&(i=t.offsetWidth>0&&Zi(n.width)/t.offsetWidth||1,o=t.offsetHeight>0&&Zi(n.height)/t.offsetHeight||1);var s=(ai(t)?lr(t):window).visualViewport,a=!a_()&&r,l=(n.left+(a&&s?s.offsetLeft:0))/i,c=(n.top+(a&&s?s.offsetTop:0))/o,u=n.width/i,h=n.height/o;return{width:u,height:h,top:c,right:l+u,bottom:c+h,left:l,x:l,y:c}}function Nd(t){var e=Xi(t),r=t.offsetWidth,n=t.offsetHeight;return Math.abs(e.width-r)<=1&&(r=e.width),Math.abs(e.height-n)<=1&&(n=e.height),{x:t.offsetLeft,y:t.offsetTop,width:r,height:n}}function l_(t,e){var r=e.getRootNode&&e.getRootNode();if(t.contains(e))return!0;if(r&&jd(r)){var n=e;do{if(n&&t.isSameNode(n))return!0;n=n.parentNode||n.host}while(n)}return!1}function Vr(t){return lr(t).getComputedStyle(t)}function fO(t){return["table","td","th"].indexOf(Ir(t))>=0}function In(t){return((ai(t)?t.ownerDocument:t.document)||window.document).documentElement}function Yl(t){return Ir(t)==="html"?t:t.assignedSlot||t.parentNode||(jd(t)?t.host:null)||In(t)}function um(t){return tr(t)&&Vr(t).position!=="fixed"?t.offsetParent:null}function Ps(t){for(var e=lr(t),r=um(t);r&&fO(r)&&Vr(r).position==="static";)r=um(r);return r&&(Ir(r)==="html"||Ir(r)==="body"&&Vr(r).position==="static")?e:r||function(n){var i=/firefox/i.test(If());if(/Trident/i.test(If())&&tr(n)&&Vr(n).position==="fixed")return null;var o=Yl(n);for(jd(o)&&(o=o.host);tr(o)&&["html","body"].indexOf(Ir(o))<0;){var s=Vr(o);if(s.transform!=="none"||s.perspective!=="none"||s.contain==="paint"||["transform","perspective"].indexOf(s.willChange)!==-1||i&&s.willChange==="filter"||i&&s.filter&&s.filter!=="none")return o;o=o.parentNode}return null}(t)||e}function Ld(t){return["top","bottom"].indexOf(t)>=0?"x":"y"}function Po(t,e,r){return si(t,al(e,r))}function c_(t){return Object.assign({},{top:0,right:0,bottom:0,left:0},t)}function u_(t,e){return e.reduce(function(r,n){return r[n]=t,r},{})}const dO={name:"arrow",enabled:!0,phase:"main",fn:function(t){var e,r=t.state,n=t.name,i=t.options,o=r.elements.arrow,s=r.modifiersData.popperOffsets,a=Or(r.placement),l=Ld(a),c=[zt,sr].indexOf(a)>=0?"height":"width";if(o&&s){var u=function(T,_){return c_(typeof(T=typeof T=="function"?T(Object.assign({},_.rects,{placement:_.placement})):T)!="number"?T:u_(T,Is))}(i.padding,r),h=Nd(o),f=l==="y"?$t:zt,d=l==="y"?or:sr,y=r.rects.reference[c]+r.rects.reference[l]-s[l]-r.rects.popper[c],g=s[l]-r.rects.reference[l],p=Ps(o),b=p?l==="y"?p.clientHeight||0:p.clientWidth||0:0,v=y/2-g/2,S=u[f],k=b-h[c]-u[d],M=b/2-h[c]/2+v,A=Po(S,M,k),I=l;r.modifiersData[n]=((e={})[I]=A,e.centerOffset=A-M,e)}},effect:function(t){var e=t.state,r=t.options.element,n=r===void 0?"[data-popper-arrow]":r;n!=null&&(typeof n!="string"||(n=e.elements.popper.querySelector(n)))&&l_(e.elements.popper,n)&&(e.elements.arrow=n)},requires:["popperOffsets"],requiresIfExists:["preventOverflow"]};function Ji(t){return t.split("-")[1]}var pO={top:"auto",right:"auto",bottom:"auto",left:"auto"};function hm(t){var e,r=t.popper,n=t.popperRect,i=t.placement,o=t.variation,s=t.offsets,a=t.position,l=t.gpuAcceleration,c=t.adaptive,u=t.roundOffsets,h=t.isFixed,f=s.x,d=f===void 0?0:f,y=s.y,g=y===void 0?0:y,p=typeof u=="function"?u({x:d,y:g}):{x:d,y:g};d=p.x,g=p.y;var b=s.hasOwnProperty("x"),v=s.hasOwnProperty("y"),S=zt,k=$t,M=window;if(c){var A=Ps(r),I="clientHeight",T="clientWidth";A===lr(r)&&Vr(A=In(r)).position!=="static"&&a==="absolute"&&(I="scrollHeight",T="scrollWidth"),(i===$t||(i===zt||i===sr)&&o===ys)&&(k=or,g-=(h&&A===M&&M.visualViewport?M.visualViewport.height:A[I])-n.height,g*=l?1:-1),(i===zt||(i===$t||i===or)&&o===ys)&&(S=sr,d-=(h&&A===M&&M.visualViewport?M.visualViewport.width:A[T])-n.width,d*=l?1:-1)}var _,x=Object.assign({position:a},c&&pO),O=u===!0?function(N){var G=N.x,U=N.y,z=window.devicePixelRatio||1;return{x:Zi(G*z)/z||0,y:Zi(U*z)/z||0}}({x:d,y:g}):{x:d,y:g};return d=O.x,g=O.y,l?Object.assign({},x,((_={})[k]=v?"0":"",_[S]=b?"0":"",_.transform=(M.devicePixelRatio||1)<=1?"translate("+d+"px, "+g+"px)":"translate3d("+d+"px, "+g+"px, 0)",_)):Object.assign({},x,((e={})[k]=v?g+"px":"",e[S]=b?d+"px":"",e.transform="",e))}var Qs={passive:!0},gO={left:"right",right:"left",bottom:"top",top:"bottom"};function Zs(t){return t.replace(/left|right|bottom|top/g,function(e){return gO[e]})}var mO={start:"end",end:"start"};function fm(t){return t.replace(/start|end/g,function(e){return mO[e]})}function Bd(t){var e=lr(t);return{scrollLeft:e.pageXOffset,scrollTop:e.pageYOffset}}function Pf(t){return Xi(In(t)).left+Bd(t).scrollLeft}function Fd(t){var e=Vr(t),r=e.overflow,n=e.overflowX,i=e.overflowY;return/auto|scroll|overlay|hidden/.test(r+i+n)}function h_(t){return["html","body","#document"].indexOf(Ir(t))>=0?t.ownerDocument.body:tr(t)&&Fd(t)?t:h_(Yl(t))}function Zo(t,e){var r;e===void 0&&(e=[]);var n=h_(t),i=n===((r=t.ownerDocument)==null?void 0:r.body),o=lr(n),s=i?[o].concat(o.visualViewport||[],Fd(n)?n:[]):n,a=e.concat(s);return i?a:a.concat(Zo(Yl(s)))}function Cf(t){return Object.assign({},t,{left:t.x,top:t.y,right:t.x+t.width,bottom:t.y+t.height})}function dm(t,e,r){return e===o_?Cf(function(n,i){var o=lr(n),s=In(n),a=o.visualViewport,l=s.clientWidth,c=s.clientHeight,u=0,h=0;if(a){l=a.width,c=a.height;var f=a_();(f||!f&&i==="fixed")&&(u=a.offsetLeft,h=a.offsetTop)}return{width:l,height:c,x:u+Pf(n),y:h}}(t,r)):ai(e)?function(n,i){var o=Xi(n,!1,i==="fixed");return o.top=o.top+n.clientTop,o.left=o.left+n.clientLeft,o.bottom=o.top+n.clientHeight,o.right=o.left+n.clientWidth,o.width=n.clientWidth,o.height=n.clientHeight,o.x=o.left,o.y=o.top,o}(e,r):Cf(function(n){var i,o=In(n),s=Bd(n),a=(i=n.ownerDocument)==null?void 0:i.body,l=si(o.scrollWidth,o.clientWidth,a?a.scrollWidth:0,a?a.clientWidth:0),c=si(o.scrollHeight,o.clientHeight,a?a.scrollHeight:0,a?a.clientHeight:0),u=-s.scrollLeft+Pf(n),h=-s.scrollTop;return Vr(a||o).direction==="rtl"&&(u+=si(o.clientWidth,a?a.clientWidth:0)-l),{width:l,height:c,x:u,y:h}}(In(t)))}function yO(t,e,r,n){var i=e==="clippingParents"?function(l){var c=Zo(Yl(l)),u=["absolute","fixed"].indexOf(Vr(l).position)>=0&&tr(l)?Ps(l):l;return ai(u)?c.filter(function(h){return ai(h)&&l_(h,u)&&Ir(h)!=="body"}):[]}(t):[].concat(e),o=[].concat(i,[r]),s=o[0],a=o.reduce(function(l,c){var u=dm(t,c,n);return l.top=si(u.top,l.top),l.right=al(u.right,l.right),l.bottom=al(u.bottom,l.bottom),l.left=si(u.left,l.left),l},dm(t,s,n));return a.width=a.right-a.left,a.height=a.bottom-a.top,a.x=a.left,a.y=a.top,a}function f_(t){var e,r=t.reference,n=t.element,i=t.placement,o=i?Or(i):null,s=i?Ji(i):null,a=r.x+r.width/2-n.width/2,l=r.y+r.height/2-n.height/2;switch(o){case $t:e={x:a,y:r.y-n.height};break;case or:e={x:a,y:r.y+r.height};break;case sr:e={x:r.x+r.width,y:l};break;case zt:e={x:r.x-n.width,y:l};break;default:e={x:r.x,y:r.y}}var c=o?Ld(o):null;if(c!=null){var u=c==="y"?"height":"width";switch(s){case Qi:e[c]=e[c]-(r[u]/2-n[u]/2);break;case ys:e[c]=e[c]+(r[u]/2-n[u]/2)}}return e}function bs(t,e){e===void 0&&(e={});var r=e,n=r.placement,i=n===void 0?t.placement:n,o=r.strategy,s=o===void 0?t.strategy:o,a=r.boundary,l=a===void 0?"clippingParents":a,c=r.rootBoundary,u=c===void 0?o_:c,h=r.elementContext,f=h===void 0?bo:h,d=r.altBoundary,y=d!==void 0&&d,g=r.padding,p=g===void 0?0:g,b=c_(typeof p!="number"?p:u_(p,Is)),v=f===bo?"reference":bo,S=t.rects.popper,k=t.elements[y?v:f],M=yO(ai(k)?k:k.contextElement||In(t.elements.popper),l,u,s),A=Xi(t.elements.reference),I=f_({reference:A,element:S,placement:i}),T=Cf(Object.assign({},S,I)),_=f===bo?T:A,x={top:M.top-_.top+b.top,bottom:_.bottom-M.bottom+b.bottom,left:M.left-_.left+b.left,right:_.right-M.right+b.right},O=t.modifiersData.offset;if(f===bo&&O){var N=O[i];Object.keys(x).forEach(function(G){var U=[sr,or].indexOf(G)>=0?1:-1,z=[$t,or].indexOf(G)>=0?"y":"x";x[G]+=N[z]*U})}return x}function bO(t,e){e===void 0&&(e={});var r=e,n=r.placement,i=r.boundary,o=r.rootBoundary,s=r.padding,a=r.flipVariations,l=r.allowedAutoPlacements,c=l===void 0?s_:l,u=Ji(n),h=u?a?cm:cm.filter(function(y){return Ji(y)===u}):Is,f=h.filter(function(y){return c.indexOf(y)>=0});f.length===0&&(f=h);var d=f.reduce(function(y,g){return y[g]=bs(t,{placement:g,boundary:i,rootBoundary:o,padding:s})[Or(g)],y},{});return Object.keys(d).sort(function(y,g){return d[y]-d[g]})}const vO={name:"flip",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,n=t.name;if(!e.modifiersData[n]._skip){for(var i=r.mainAxis,o=i===void 0||i,s=r.altAxis,a=s===void 0||s,l=r.fallbackPlacements,c=r.padding,u=r.boundary,h=r.rootBoundary,f=r.altBoundary,d=r.flipVariations,y=d===void 0||d,g=r.allowedAutoPlacements,p=e.options.placement,b=Or(p),v=l||(b===p||!y?[Zs(p)]:function(F){if(Or(F)===Tf)return[];var V=Zs(F);return[fm(F),V,fm(V)]}(p)),S=[p].concat(v).reduce(function(F,V){return F.concat(Or(V)===Tf?bO(e,{placement:V,boundary:u,rootBoundary:h,padding:c,flipVariations:y,allowedAutoPlacements:g}):V)},[]),k=e.rects.reference,M=e.rects.popper,A=new Map,I=!0,T=S[0],_=0;_=0,U=G?"width":"height",z=bs(e,{placement:x,boundary:u,rootBoundary:h,altBoundary:f,padding:c}),q=G?N?sr:zt:N?or:$t;k[U]>M[U]&&(q=Zs(q));var B=Zs(q),ne=[];if(o&&ne.push(z[O]<=0),a&&ne.push(z[q]<=0,z[B]<=0),ne.every(function(F){return F})){T=x,I=!1;break}A.set(x,ne)}if(I)for(var H=function(F){var V=S.find(function(Z){var $=A.get(Z);if($)return $.slice(0,F).every(function(se){return se})});if(V)return T=V,"break"},Q=y?3:1;Q>0&&H(Q)!=="break";Q--);e.placement!==T&&(e.modifiersData[n]._skip=!0,e.placement=T,e.reset=!0)}},requiresIfExists:["offset"],data:{_skip:!1}};function pm(t,e,r){return r===void 0&&(r={x:0,y:0}),{top:t.top-e.height-r.y,right:t.right-e.width+r.x,bottom:t.bottom-e.height+r.y,left:t.left-e.width-r.x}}function gm(t){return[$t,sr,or,zt].some(function(e){return t[e]>=0})}const wO={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(t){var e=t.state,r=t.options,n=t.name,i=r.offset,o=i===void 0?[0,0]:i,s=s_.reduce(function(u,h){return u[h]=function(f,d,y){var g=Or(f),p=[zt,$t].indexOf(g)>=0?-1:1,b=typeof y=="function"?y(Object.assign({},d,{placement:f})):y,v=b[0],S=b[1];return v=v||0,S=(S||0)*p,[zt,sr].indexOf(g)>=0?{x:S,y:v}:{x:v,y:S}}(h,e.rects,o),u},{}),a=s[e.placement],l=a.x,c=a.y;e.modifiersData.popperOffsets!=null&&(e.modifiersData.popperOffsets.x+=l,e.modifiersData.popperOffsets.y+=c),e.modifiersData[n]=s}},_O={name:"preventOverflow",enabled:!0,phase:"main",fn:function(t){var e=t.state,r=t.options,n=t.name,i=r.mainAxis,o=i===void 0||i,s=r.altAxis,a=s!==void 0&&s,l=r.boundary,c=r.rootBoundary,u=r.altBoundary,h=r.padding,f=r.tether,d=f===void 0||f,y=r.tetherOffset,g=y===void 0?0:y,p=bs(e,{boundary:l,rootBoundary:c,padding:h,altBoundary:u}),b=Or(e.placement),v=Ji(e.placement),S=!v,k=Ld(b),M=k==="x"?"y":"x",A=e.modifiersData.popperOffsets,I=e.rects.reference,T=e.rects.popper,_=typeof g=="function"?g(Object.assign({},e.rects,{placement:e.placement})):g,x=typeof _=="number"?{mainAxis:_,altAxis:_}:Object.assign({mainAxis:0,altAxis:0},_),O=e.modifiersData.offset?e.modifiersData.offset[e.placement]:null,N={x:0,y:0};if(A){if(o){var G,U=k==="y"?$t:zt,z=k==="y"?or:sr,q=k==="y"?"height":"width",B=A[k],ne=B+p[U],H=B-p[z],Q=d?-T[q]/2:0,F=v===Qi?I[q]:T[q],V=v===Qi?-T[q]:-I[q],Z=e.elements.arrow,$=d&&Z?Nd(Z):{width:0,height:0},se=e.modifiersData["arrow#persistent"]?e.modifiersData["arrow#persistent"].padding:{top:0,right:0,bottom:0,left:0},le=se[U],ue=se[z],Y=Po(0,I[q],$[q]),ie=S?I[q]/2-Q-Y-le-x.mainAxis:F-Y-le-x.mainAxis,oe=S?-I[q]/2+Q+Y+ue+x.mainAxis:V+Y+ue+x.mainAxis,j=e.elements.arrow&&Ps(e.elements.arrow),X=j?k==="y"?j.clientTop||0:j.clientLeft||0:0,R=(G=O==null?void 0:O[k])!=null?G:0,te=B+oe-R,m=Po(d?al(ne,B+ie-R-X):ne,B,d?si(H,te):H);A[k]=m,N[k]=m-B}if(a){var w,E=k==="x"?$t:zt,D=k==="x"?or:sr,W=A[M],P=M==="y"?"height":"width",J=W+p[E],he=W-p[D],fe=[$t,zt].indexOf(b)!==-1,de=(w=O==null?void 0:O[M])!=null?w:0,pe=fe?J:W-I[P]-T[P]-de+x.altAxis,K=fe?W+I[P]+T[P]-de-x.altAxis:he,ee=d&&fe?function(ce,C,L){var ae=Po(ce,C,L);return ae>L?L:ae}(pe,W,K):Po(d?pe:J,W,d?K:he);A[M]=ee,N[M]=ee-W}e.modifiersData[n]=N}},requiresIfExists:["offset"]};function xO(t,e,r){r===void 0&&(r=!1);var n,i,o=tr(e),s=tr(e)&&function(h){var f=h.getBoundingClientRect(),d=Zi(f.width)/h.offsetWidth||1,y=Zi(f.height)/h.offsetHeight||1;return d!==1||y!==1}(e),a=In(e),l=Xi(t,s,r),c={scrollLeft:0,scrollTop:0},u={x:0,y:0};return(o||!o&&!r)&&((Ir(e)!=="body"||Fd(a))&&(c=(n=e)!==lr(n)&&tr(n)?{scrollLeft:(i=n).scrollLeft,scrollTop:i.scrollTop}:Bd(n)),tr(e)?((u=Xi(e,!0)).x+=e.clientLeft,u.y+=e.clientTop):a&&(u.x=Pf(a))),{x:l.left+c.scrollLeft-u.x,y:l.top+c.scrollTop-u.y,width:l.width,height:l.height}}function SO(t){var e=new Map,r=new Set,n=[];function i(o){r.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach(function(s){if(!r.has(s)){var a=e.get(s);a&&i(a)}}),n.push(o)}return t.forEach(function(o){e.set(o.name,o)}),t.forEach(function(o){r.has(o.name)||i(o)}),n}var mm={placement:"bottom",modifiers:[],strategy:"absolute"};function ym(){for(var t=arguments.length,e=new Array(t),r=0;rMath.max(Math.min(t,r),e);function Co(t){return kn(Cs(2.55*t),0,255)}function En(t){return kn(Cs(255*t),0,255)}function Dr(t){return kn(Cs(t/2.55)/100,0,1)}function bm(t){return kn(Cs(100*t),0,100)}const Zt={0:0,1:1,2:2,3:3,4:4,5:5,6:6,7:7,8:8,9:9,A:10,B:11,C:12,D:13,E:14,F:15,a:10,b:11,c:12,d:13,e:14,f:15},Rf=[..."0123456789ABCDEF"],EO=t=>Rf[15&t],OO=t=>Rf[(240&t)>>4]+Rf[15&t],Xs=t=>(240&t)>>4==(15&t);function MO(t){var e=(r=>Xs(r.r)&&Xs(r.g)&&Xs(r.b)&&Xs(r.a))(t)?EO:OO;return t?"#"+e(t.r)+e(t.g)+e(t.b)+((r,n)=>r<255?n(r):"")(t.a,e):void 0}const AO=/^(hsla?|hwb|hsv)\(\s*([-+.e\d]+)(?:deg)?[\s,]+([-+.e\d]+)%[\s,]+([-+.e\d]+)%(?:[\s,]+([-+.e\d]+)(%)?)?\s*\)$/;function d_(t,e,r){const n=e*Math.min(r,1-r),i=(o,s=(o+t/30)%12)=>r-n*Math.max(Math.min(s-3,9-s,1),-1);return[i(0),i(8),i(4)]}function TO(t,e,r){const n=(i,o=(i+t/60)%6)=>r-r*e*Math.max(Math.min(o,4-o,1),0);return[n(5),n(3),n(1)]}function IO(t,e,r){const n=d_(t,1,.5);let i;for(e+r>1&&(i=1/(e+r),e*=i,r*=i),i=0;i<3;i++)n[i]*=1-e-r,n[i]+=e;return n}function Df(t){const e=t.r/255,r=t.g/255,n=t.b/255,i=Math.max(e,r,n),o=Math.min(e,r,n),s=(i+o)/2;let a,l,c;return i!==o&&(c=i-o,l=s>.5?c/(2-i-o):c/(i+o),a=function(u,h,f,d,y){return u===y?(h-f)/d+(h>16&255,a>>8&255,255&a]}return r}(),Js.transparent=[0,0,0,0]);const e=Js[t.toLowerCase()];return e&&{r:e[0],g:e[1],b:e[2],a:e.length===4?e[3]:255}}const RO=/^rgba?\(\s*([-+.\d]+)(%)?[\s,]+([-+.e\d]+)(%)?[\s,]+([-+.e\d]+)(%)?(?:[\s,/]+([-+.e\d]+)(%)?)?\s*\)$/,Dc=t=>t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055,_i=t=>t<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4);function ea(t,e,r){if(t){let n=Df(t);n[e]=Math.max(0,Math.min(n[e]+n[e]*r,e===0?360:1)),n=Wd(n),t.r=n[0],t.g=n[1],t.b=n[2]}}function g_(t,e){return t&&Object.assign(e||{},t)}function _m(t){var e={r:0,g:0,b:0,a:255};return Array.isArray(t)?t.length>=3&&(e={r:t[0],g:t[1],b:t[2],a:255},t.length>3&&(e.a=En(t[3]))):(e=g_(t,{r:0,g:0,b:0,a:1})).a=En(e.a),e}function DO(t){return t.charAt(0)==="r"?function(e){const r=RO.exec(e);let n,i,o,s=255;if(r){if(r[7]!==n){const a=+r[7];s=r[8]?Co(a):kn(255*a,0,255)}return n=+r[1],i=+r[3],o=+r[5],n=255&(r[2]?Co(n):kn(n,0,255)),i=255&(r[4]?Co(i):kn(i,0,255)),o=255&(r[6]?Co(o):kn(o,0,255)),{r:n,g:i,b:o,a:s}}}(t):PO(t)}class vs{constructor(e){if(e instanceof vs)return e;const r=typeof e;let n;var i,o,s;r==="object"?n=_m(e):r==="string"&&(s=(i=e).length,i[0]==="#"&&(s===4||s===5?o={r:255&17*Zt[i[1]],g:255&17*Zt[i[2]],b:255&17*Zt[i[3]],a:s===5?17*Zt[i[4]]:255}:s!==7&&s!==9||(o={r:Zt[i[1]]<<4|Zt[i[2]],g:Zt[i[3]]<<4|Zt[i[4]],b:Zt[i[5]]<<4|Zt[i[6]],a:s===9?Zt[i[7]]<<4|Zt[i[8]]:255})),n=o||CO(e)||DO(e)),this._rgb=n,this._valid=!!n}get valid(){return this._valid}get rgb(){var e=g_(this._rgb);return e&&(e.a=Dr(e.a)),e}set rgb(e){this._rgb=_m(e)}rgbString(){return this._valid?(e=this._rgb)&&(e.a<255?`rgba(${e.r}, ${e.g}, ${e.b}, ${Dr(e.a)})`:`rgb(${e.r}, ${e.g}, ${e.b})`):void 0;var e}hexString(){return this._valid?MO(this._rgb):void 0}hslString(){return this._valid?function(e){if(!e)return;const r=Df(e),n=r[0],i=bm(r[1]),o=bm(r[2]);return e.a<255?`hsla(${n}, ${i}%, ${o}%, ${Dr(e.a)})`:`hsl(${n}, ${i}%, ${o}%)`}(this._rgb):void 0}mix(e,r){if(e){const n=this.rgb,i=e.rgb;let o;const s=r===o?.5:r,a=2*s-1,l=n.a-i.a,c=((a*l===-1?a:(a+l)/(1+a*l))+1)/2;o=1-c,n.r=255&c*n.r+o*i.r+.5,n.g=255&c*n.g+o*i.g+.5,n.b=255&c*n.b+o*i.b+.5,n.a=s*n.a+(1-s)*i.a,this.rgb=n}return this}interpolate(e,r){return e&&(this._rgb=function(n,i,o){const s=_i(Dr(n.r)),a=_i(Dr(n.g)),l=_i(Dr(n.b));return{r:En(Dc(s+o*(_i(Dr(i.r))-s))),g:En(Dc(a+o*(_i(Dr(i.g))-a))),b:En(Dc(l+o*(_i(Dr(i.b))-l))),a:n.a+o*(i.a-n.a)}}(this._rgb,e._rgb,r)),this}clone(){return new vs(this.rgb)}alpha(e){return this._rgb.a=En(e),this}clearer(e){return this._rgb.a*=1-e,this}greyscale(){const e=this._rgb,r=Cs(.3*e.r+.59*e.g+.11*e.b);return e.r=e.g=e.b=r,this}opaquer(e){return this._rgb.a*=1+e,this}negate(){const e=this._rgb;return e.r=255-e.r,e.g=255-e.g,e.b=255-e.b,this}lighten(e){return ea(this._rgb,2,e),this}darken(e){return ea(this._rgb,2,-e),this}saturate(e){return ea(this._rgb,1,e),this}desaturate(e){return ea(this._rgb,1,-e),this}rotate(e){return function(r,n){var i=Df(r);i[0]=p_(i[0]+n),i=Wd(i),r.r=i[0],r.g=i[1],r.b=i[2]}(this._rgb,e),this}}/*! + * Chart.js v4.5.0 + * https://www.chartjs.org + * (c) 2025 Chart.js Contributors + * Released under the MIT License + */const jO=(()=>{let t=0;return()=>t++})();function it(t){return t==null}function st(t){if(Array.isArray&&Array.isArray(t))return!0;const e=Object.prototype.toString.call(t);return e.slice(0,7)==="[object"&&e.slice(-6)==="Array]"}function je(t){return t!==null&&Object.prototype.toString.call(t)==="[object Object]"}function yt(t){return(typeof t=="number"||t instanceof Number)&&isFinite(+t)}function br(t,e){return yt(t)?t:e}function Ne(t,e){return t===void 0?e:t}const m_=(t,e)=>typeof t=="string"&&t.endsWith("%")?parseFloat(t)/100*e:+t;function Ze(t,e,r){if(t&&typeof t.call=="function")return t.apply(r,e)}function Et(t,e,r,n){let i,o,s;if(st(t))for(o=t.length,i=0;it,x:t=>t.x,y:t=>t.y};function _s(t,e){return(Sm[e]||(Sm[e]=function(n){const i=function(o){const s=o.split("."),a=[];let l="";for(const c of s)l+=c,l.endsWith("\\")?l=l.slice(0,-1)+".":(a.push(l),l="");return a}(n);return o=>{for(const s of i){if(s==="")break;o=o&&o[s]}return o}}(e)))(t)}function Ud(t){return t.charAt(0).toUpperCase()+t.slice(1)}const Gt=t=>t!==void 0,Ut=t=>typeof t=="function",km=(t,e)=>{if(t.size!==e.size)return!1;for(const r of t)if(!e.has(r))return!1;return!0},Qe=Math.PI,pt=2*Qe,BO=pt+Qe,ta=Number.POSITIVE_INFINITY,Vd=Qe/180,xt=Qe/2,Xt=Qe/4,cl=2*Qe/3,b_=Math.log10,eo=Math.sign;function Jo(t,e,r){return Math.abs(t-e)l&&c=Math.min(e,r)-n&&t<=Math.max(e,r)+n}function $d(t,e,r){r=r||(s=>t[s]1;)n=o+i>>1,r(n)?o=n:i=n;return{lo:o,hi:i}}const Ro=(t,e,r,n)=>$d(t,r,n?i=>{const o=t[i][e];return ot[i][e]$d(t,r,n=>t[n][e]>=r),w_=["push","pop","shift","splice","unshift"];function Mm(t,e){const r=t._chartjs;if(!r)return;const n=r.listeners,i=n.indexOf(e);i!==-1&&n.splice(i,1),n.length>0||(w_.forEach(o=>{delete t[o]}),delete t._chartjs)}const __=typeof window>"u"?function(t){return t()}:window.requestAnimationFrame;function x_(t,e){let r=[],n=!1;return function(...i){r=i,n||(n=!0,__.call(window,()=>{n=!1,t.apply(e,r)}))}}const S_=t=>t==="start"?"left":t==="end"?"right":"center",qt=(t,e,r)=>t==="start"?e:t==="end"?r:(e+r)/2,ra=t=>t===0||t===1,Am=(t,e,r)=>-Math.pow(2,10*(t-=1))*Math.sin((t-e)*pt/r),Tm=(t,e,r)=>Math.pow(2,-10*t)*Math.sin((t-e)*pt/r)+1,es={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>-t*(t-2),easeInOutQuad:t=>(t/=.5)<1?.5*t*t:-.5*(--t*(t-2)-1),easeInCubic:t=>t*t*t,easeOutCubic:t=>(t-=1)*t*t+1,easeInOutCubic:t=>(t/=.5)<1?.5*t*t*t:.5*((t-=2)*t*t+2),easeInQuart:t=>t*t*t*t,easeOutQuart:t=>-((t-=1)*t*t*t-1),easeInOutQuart:t=>(t/=.5)<1?.5*t*t*t*t:-.5*((t-=2)*t*t*t-2),easeInQuint:t=>t*t*t*t*t,easeOutQuint:t=>(t-=1)*t*t*t*t+1,easeInOutQuint:t=>(t/=.5)<1?.5*t*t*t*t*t:.5*((t-=2)*t*t*t*t+2),easeInSine:t=>1-Math.cos(t*xt),easeOutSine:t=>Math.sin(t*xt),easeInOutSine:t=>-.5*(Math.cos(Qe*t)-1),easeInExpo:t=>t===0?0:Math.pow(2,10*(t-1)),easeOutExpo:t=>t===1?1:1-Math.pow(2,-10*t),easeInOutExpo:t=>ra(t)?t:t<.5?.5*Math.pow(2,10*(2*t-1)):.5*(2-Math.pow(2,-10*(2*t-1))),easeInCirc:t=>t>=1?t:-(Math.sqrt(1-t*t)-1),easeOutCirc:t=>Math.sqrt(1-(t-=1)*t),easeInOutCirc:t=>(t/=.5)<1?-.5*(Math.sqrt(1-t*t)-1):.5*(Math.sqrt(1-(t-=2)*t)+1),easeInElastic:t=>ra(t)?t:Am(t,.075,.3),easeOutElastic:t=>ra(t)?t:Tm(t,.075,.3),easeInOutElastic(t){return ra(t)?t:t<.5?.5*Am(2*t,.1125,.45):.5+.5*Tm(2*t-1,.1125,.45)},easeInBack(t){return t*t*((1.70158+1)*t-1.70158)},easeOutBack(t){return(t-=1)*t*((1.70158+1)*t+1.70158)+1},easeInOutBack(t){let e=1.70158;return(t/=.5)<1?t*t*((1+(e*=1.525))*t-e)*.5:.5*((t-=2)*t*((1+(e*=1.525))*t+e)+2)},easeInBounce:t=>1-es.easeOutBounce(1-t),easeOutBounce(t){return t<1/2.75?7.5625*t*t:t<2/2.75?7.5625*(t-=1.5/2.75)*t+.75:t<2.5/2.75?7.5625*(t-=2.25/2.75)*t+.9375:7.5625*(t-=2.625/2.75)*t+.984375},easeInOutBounce:t=>t<.5?.5*es.easeInBounce(2*t):.5*es.easeOutBounce(2*t-1)+.5};function zd(t){if(t&&typeof t=="object"){const e=t.toString();return e==="[object CanvasPattern]"||e==="[object CanvasGradient]"}return!1}function Im(t){return zd(t)?t:new vs(t)}function jc(t){return zd(t)?t:new vs(t).saturate(.5).darken(.1).hexString()}const UO=["x","y","borderWidth","radius","tension"],VO=["color","borderColor","backgroundColor"],Pm=new Map;function Hd(t,e,r){return function(n,i){i=i||{};const o=n+JSON.stringify(i);let s=Pm.get(o);return s||(s=new Intl.NumberFormat(n,i),Pm.set(o,s)),s}(e,r).format(t)}var k_={formatters:{values:t=>st(t)?t:""+t,numeric(t,e,r){if(t===0)return"0";const n=this.chart.options.locale;let i,o=t;if(r.length>1){const c=Math.max(Math.abs(r[0].value),Math.abs(r[r.length-1].value));(c<1e-4||c>1e15)&&(i="scientific"),o=function(u,h){let f=h.length>3?h[2].value-h[1].value:h[1].value-h[0].value;return Math.abs(f)>=1&&u!==Math.floor(u)&&(f=u-Math.floor(u)),f}(t,r)}const s=b_(Math.abs(o)),a=isNaN(s)?1:Math.max(Math.min(-1*Math.floor(s),20),0),l={notation:i,minimumFractionDigits:a,maximumFractionDigits:a};return Object.assign(l,this.options.ticks.format),Hd(t,n,l)}}};const ci=Object.create(null),Lf=Object.create(null);function ts(t,e){if(!e)return t;const r=e.split(".");for(let n=0,i=r.length;nn.chart.platform.getDevicePixelRatio(),this.elements={},this.events=["mousemove","mouseout","click","touchstart","touchmove"],this.font={family:"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",size:12,style:"normal",lineHeight:1.2,weight:null},this.hover={},this.hoverBackgroundColor=(n,i)=>jc(i.backgroundColor),this.hoverBorderColor=(n,i)=>jc(i.borderColor),this.hoverColor=(n,i)=>jc(i.color),this.indexAxis="x",this.interaction={mode:"nearest",intersect:!0,includeInvisible:!1},this.maintainAspectRatio=!0,this.onHover=null,this.onClick=null,this.parsing=!0,this.plugins={},this.responsive=!0,this.scale=void 0,this.scales={},this.showLine=!0,this.drawActiveElementsOnTop=!0,this.describe(e),this.apply(r)}set(e,r){return Nc(this,e,r)}get(e){return ts(this,e)}describe(e,r){return Nc(Lf,e,r)}override(e,r){return Nc(ci,e,r)}route(e,r,n,i){const o=ts(this,e),s=ts(this,n),a="_"+r;Object.defineProperties(o,{[a]:{value:o[r],writable:!0},[r]:{enumerable:!0,get(){const l=this[a],c=s[i];return je(l)?Object.assign({},c,l):Ne(l,c)},set(l){this[a]=l}}})}apply(e){e.forEach(r=>r(this))}}var ht=new $O({_scriptable:t=>!t.startsWith("on"),_indexable:t=>t!=="events",hover:{_fallback:"interaction"},interaction:{_scriptable:!1,_indexable:!1}},[function(t){t.set("animation",{delay:void 0,duration:1e3,easing:"easeOutQuart",fn:void 0,from:void 0,loop:void 0,to:void 0,type:void 0}),t.describe("animation",{_fallback:!1,_indexable:!1,_scriptable:e=>e!=="onProgress"&&e!=="onComplete"&&e!=="fn"}),t.set("animations",{colors:{type:"color",properties:VO},numbers:{type:"number",properties:UO}}),t.describe("animations",{_fallback:"animation"}),t.set("transitions",{active:{animation:{duration:400}},resize:{animation:{duration:0}},show:{animations:{colors:{from:"transparent"},visible:{type:"boolean",duration:0}}},hide:{animations:{colors:{to:"transparent"},visible:{type:"boolean",easing:"linear",fn:e=>0|e}}}})},function(t){t.set("layout",{autoPadding:!0,padding:{top:0,right:0,bottom:0,left:0}})},function(t){t.set("scale",{display:!0,offset:!1,reverse:!1,beginAtZero:!1,bounds:"ticks",clip:!0,grace:0,grid:{display:!0,lineWidth:1,drawOnChartArea:!0,drawTicks:!0,tickLength:8,tickWidth:(e,r)=>r.lineWidth,tickColor:(e,r)=>r.color,offset:!1},border:{display:!0,dash:[],dashOffset:0,width:1},title:{display:!1,text:"",padding:{top:4,bottom:4}},ticks:{minRotation:0,maxRotation:50,mirror:!1,textStrokeWidth:0,textStrokeColor:"",padding:3,display:!0,autoSkip:!0,autoSkipPadding:3,labelOffset:0,callback:k_.formatters.values,minor:{},major:{},align:"center",crossAlign:"near",showLabelBackdrop:!1,backdropColor:"rgba(255, 255, 255, 0.75)",backdropPadding:2}}),t.route("scale.ticks","color","","color"),t.route("scale.grid","color","","borderColor"),t.route("scale.border","color","","borderColor"),t.route("scale.title","color","","color"),t.describe("scale",{_fallback:!1,_scriptable:e=>!e.startsWith("before")&&!e.startsWith("after")&&e!=="callback"&&e!=="parser",_indexable:e=>e!=="borderDash"&&e!=="tickBorderDash"&&e!=="dash"}),t.describe("scales",{_fallback:"scale"}),t.describe("scale.ticks",{_scriptable:e=>e!=="backdropPadding"&&e!=="callback",_indexable:e=>e!=="backdropPadding"})}]);function Cm(t,e,r,n,i){let o=e[i];return o||(o=e[i]=t.measureText(i).width,r.push(i)),o>n&&(n=o),n}function $n(t,e,r){const n=t.currentDevicePixelRatio,i=r!==0?Math.max(r/2,.5):0;return Math.round((e-i)*n)/n+i}function Rm(t,e){(e||t)&&((e=e||t.getContext("2d")).save(),e.resetTransform(),e.clearRect(0,0,t.width,t.height),e.restore())}function E_(t,e,r,n,i){let o,s,a,l,c,u,h,f;const d=e.pointStyle,y=e.rotation,g=e.radius;let p=(y||0)*Vd;if(d&&typeof d=="object"&&(o=d.toString(),o==="[object HTMLImageElement]"||o==="[object HTMLCanvasElement]"))return t.save(),t.translate(r,n),t.rotate(p),t.drawImage(d,-d.width/2,-d.height/2,d.width,d.height),void t.restore();if(!(isNaN(g)||g<=0)){switch(t.beginPath(),d){default:i?t.ellipse(r,n,i/2,g,0,0,pt):t.arc(r,n,g,0,pt),t.closePath();break;case"triangle":u=i?i/2:g,t.moveTo(r+Math.sin(p)*u,n-Math.cos(p)*g),p+=cl,t.lineTo(r+Math.sin(p)*u,n-Math.cos(p)*g),p+=cl,t.lineTo(r+Math.sin(p)*u,n-Math.cos(p)*g),t.closePath();break;case"rectRounded":c=.516*g,l=g-c,s=Math.cos(p+Xt)*l,h=Math.cos(p+Xt)*(i?i/2-c:l),a=Math.sin(p+Xt)*l,f=Math.sin(p+Xt)*(i?i/2-c:l),t.arc(r-h,n-a,c,p-Qe,p-xt),t.arc(r+f,n-s,c,p-xt,p),t.arc(r+h,n+a,c,p,p+xt),t.arc(r-f,n+s,c,p+xt,p+Qe),t.closePath();break;case"rect":if(!y){l=Math.SQRT1_2*g,u=i?i/2:l,t.rect(r-u,n-l,2*u,2*l);break}p+=Xt;case"rectRot":h=Math.cos(p)*(i?i/2:g),s=Math.cos(p)*g,a=Math.sin(p)*g,f=Math.sin(p)*(i?i/2:g),t.moveTo(r-h,n-a),t.lineTo(r+f,n-s),t.lineTo(r+h,n+a),t.lineTo(r-f,n+s),t.closePath();break;case"crossRot":p+=Xt;case"cross":h=Math.cos(p)*(i?i/2:g),s=Math.cos(p)*g,a=Math.sin(p)*g,f=Math.sin(p)*(i?i/2:g),t.moveTo(r-h,n-a),t.lineTo(r+h,n+a),t.moveTo(r+f,n-s),t.lineTo(r-f,n+s);break;case"star":h=Math.cos(p)*(i?i/2:g),s=Math.cos(p)*g,a=Math.sin(p)*g,f=Math.sin(p)*(i?i/2:g),t.moveTo(r-h,n-a),t.lineTo(r+h,n+a),t.moveTo(r+f,n-s),t.lineTo(r-f,n+s),p+=Xt,h=Math.cos(p)*(i?i/2:g),s=Math.cos(p)*g,a=Math.sin(p)*g,f=Math.sin(p)*(i?i/2:g),t.moveTo(r-h,n-a),t.lineTo(r+h,n+a),t.moveTo(r+f,n-s),t.lineTo(r-f,n+s);break;case"line":s=i?i/2:Math.cos(p)*g,a=Math.sin(p)*g,t.moveTo(r-s,n-a),t.lineTo(r+s,n+a);break;case"dash":t.moveTo(r,n),t.lineTo(r+Math.cos(p)*(i?i/2:g),n+Math.sin(p)*g);break;case!1:t.closePath()}t.fill(),e.borderWidth>0&&t.stroke()}}function Ss(t,e,r){return r=r||.5,!e||t&&t.x>e.left-r&&t.xe.top-r&&t.y0&&o.strokeColor!=="";let l,c;for(t.save(),t.font=i.string,function(u,h){h.translation&&u.translate(h.translation[0],h.translation[1]),it(h.rotation)||u.rotate(h.rotation),h.color&&(u.fillStyle=h.color),h.textAlign&&(u.textAlign=h.textAlign),h.textBaseline&&(u.textBaseline=h.textBaseline)}(t,o),l=0;l+t||0;function M_(t,e){const r={},n=je(e),i=n?Object.keys(e):e,o=je(t)?n?s=>Ne(t[s],t[e[s]]):s=>t[s]:()=>t;for(const s of i)r[s]=ZO(o(s));return r}function A_(t){return M_(t,["topLeft","topRight","bottomLeft","bottomRight"])}function ar(t){const e=function(r){return M_(r,{top:"y",right:"x",bottom:"y",left:"x"})}(t);return e.width=e.left+e.right,e.height=e.top+e.bottom,e}function Mr(t,e){t=t||{},e=e||ht.font;let r=Ne(t.size,e.size);typeof r=="string"&&(r=parseInt(r,10));let n=Ne(t.style,e.style);n&&!(""+n).match(GO)&&(n=void 0);const i={family:Ne(t.family,e.family),lineHeight:QO(Ne(t.lineHeight,e.lineHeight),r),size:r,style:n,weight:Ne(t.weight,e.weight),string:""};return i.string=function(o){return!o||it(o.size)||it(o.family)?null:(o.style?o.style+" ":"")+(o.weight?o.weight+" ":"")+o.size+"px "+o.family}(i),i}function na(t,e,r,n){let i,o,s;for(i=0,o=t.length;it[0]){const o=r||t;n===void 0&&(n=jm("_fallback",t));const s={[Symbol.toStringTag]:"Object",_cacheable:!0,_scopes:t,_rootScopes:o,_fallback:n,_getTarget:i,override:a=>qd([a,...t],e,o,n)};return new Proxy(s,{deleteProperty:(a,l)=>(delete a[l],delete a._keys,delete t[0][l],!0),get:(a,l)=>I_(a,l,()=>function(c,u,h,f){let d;for(const y of u)if(d=jm(XO(y,c),h),d!==void 0)return Bf(c,d)?Ff(h,f,c,d):d}(l,e,t,a)),getOwnPropertyDescriptor:(a,l)=>Reflect.getOwnPropertyDescriptor(a._scopes[0],l),getPrototypeOf:()=>Reflect.getPrototypeOf(t[0]),has:(a,l)=>Nm(a).includes(l),ownKeys:a=>Nm(a),set(a,l,c){const u=a._storage||(a._storage=i());return a[l]=u[l]=c,delete a._keys,!0}})}function Ci(t,e,r,n){const i={_cacheable:!1,_proxy:t,_context:e,_subProxy:r,_stack:new Set,_descriptors:T_(t,n),setContext:o=>Ci(t,o,r,n),override:o=>Ci(t.override(o),e,r,n)};return new Proxy(i,{deleteProperty:(o,s)=>(delete o[s],delete t[s],!0),get:(o,s,a)=>I_(o,s,()=>function(l,c,u){const{_proxy:h,_context:f,_subProxy:d,_descriptors:y}=l;let g=h[c];return Ut(g)&&y.isScriptable(c)&&(g=function(p,b,v,S){const{_proxy:k,_context:M,_subProxy:A,_stack:I}=v;if(I.has(p))throw new Error("Recursion detected: "+Array.from(I).join("->")+"->"+p);I.add(p);let T=b(M,A||S);return I.delete(p),Bf(p,T)&&(T=Ff(k._scopes,k,p,T)),T}(c,g,l,u)),st(g)&&g.length&&(g=function(p,b,v,S){const{_proxy:k,_context:M,_subProxy:A,_descriptors:I}=v;if(M.index!==void 0&&S(p))return b[M.index%b.length];if(je(b[0])){const T=b,_=k._scopes.filter(x=>x!==T);b=[];for(const x of T){const O=Ff(_,k,p,x);b.push(Ci(O,M,A&&A[p],I))}}return b}(c,g,l,y.isIndexable)),Bf(c,g)&&(g=Ci(g,f,d&&d[c],y)),g}(o,s,a)),getOwnPropertyDescriptor:(o,s)=>o._descriptors.allKeys?Reflect.has(t,s)?{enumerable:!0,configurable:!0}:void 0:Reflect.getOwnPropertyDescriptor(t,s),getPrototypeOf:()=>Reflect.getPrototypeOf(t),has:(o,s)=>Reflect.has(t,s),ownKeys:()=>Reflect.ownKeys(t),set:(o,s,a)=>(t[s]=a,delete o[s],!0)})}function T_(t,e={scriptable:!0,indexable:!0}){const{_scriptable:r=e.scriptable,_indexable:n=e.indexable,_allKeys:i=e.allKeys}=t;return{allKeys:i,scriptable:r,indexable:n,isScriptable:Ut(r)?r:()=>r,isIndexable:Ut(n)?n:()=>n}}const XO=(t,e)=>t?t+Ud(e):e,Bf=(t,e)=>je(e)&&t!=="adapters"&&(Object.getPrototypeOf(e)===null||e.constructor===Object);function I_(t,e,r){if(Object.prototype.hasOwnProperty.call(t,e)||e==="constructor")return t[e];const n=r();return t[e]=n,n}function P_(t,e,r){return Ut(t)?t(e,r):t}const JO=(t,e)=>t===!0?e:typeof t=="string"?_s(e,t):void 0;function eM(t,e,r,n,i){for(const o of e){const s=JO(r,o);if(s){t.add(s);const a=P_(s._fallback,r,i);if(a!==void 0&&a!==r&&a!==n)return a}else if(s===!1&&n!==void 0&&r!==n)return null}return!1}function Ff(t,e,r,n){const i=e._rootScopes,o=P_(e._fallback,r,n),s=[...t,...i],a=new Set;a.add(n);let l=Dm(a,s,r,o||r,n);return l!==null&&(o===void 0||o===r||(l=Dm(a,s,o,l,n),l!==null))&&qd(Array.from(a),[""],i,o,()=>function(c,u,h){const f=c._getTarget();u in f||(f[u]={});const d=f[u];return st(d)&&je(h)?h:d||{}}(e,r,n))}function Dm(t,e,r,n,i){for(;r;)r=eM(t,e,r,n,i);return r}function jm(t,e){for(const r of e){if(!r)continue;const n=r[t];if(n!==void 0)return n}}function Nm(t){let e=t._keys;return e||(e=t._keys=function(r){const n=new Set;for(const i of r)for(const o of Object.keys(i).filter(s=>!s.startsWith("_")))n.add(o);return Array.from(n)}(t._scopes)),e}const tM=Number.EPSILON||1e-14,xi=(t,e)=>et==="x"?"y":"x";function rM(t,e,r,n){const i=t.skip?e:t,o=e,s=r.skip?e:r,a=xs(o,i),l=xs(s,o);let c=a/(a+l),u=l/(a+l);c=isNaN(c)?0:c,u=isNaN(u)?0:u;const h=n*c,f=n*u;return{previous:{x:o.x-h*(s.x-i.x),y:o.y-h*(s.y-i.y)},next:{x:o.x+f*(s.x-i.x),y:o.y+f*(s.y-i.y)}}}function nM(t,e="x"){const r=Lm(e),n=t.length,i=Array(n).fill(0),o=Array(n);let s,a,l,c=xi(t,0);for(s=0;s!c.skip)),e.cubicInterpolationMode==="monotone")nM(t,i);else{let c=n?t[t.length-1]:t[0];for(o=0,s=t.length;ot.ownerDocument.defaultView.getComputedStyle(t,null),oM=["top","right","bottom","left"];function Qn(t,e,r){const n={};r=r?"-"+r:"";for(let i=0;i<4;i++){const o=oM[i];n[o]=parseFloat(t[e+"-"+o+r])||0}return n.width=n.left+n.right,n.height=n.top+n.bottom,n}function Yn(t,e){if("native"in t)return t;const{canvas:r,currentDevicePixelRatio:n}=e,i=fl(r),o=i.boxSizing==="border-box",s=Qn(i,"padding"),a=Qn(i,"border","width"),{x:l,y:c,box:u}=function(g,p){const b=g.touches,v=b&&b.length?b[0]:g,{offsetX:S,offsetY:k}=v;let M,A,I=!1;if(((T,_,x)=>(T>0||_>0)&&(!x||!x.shadowRoot))(S,k,g.target))M=S,A=k;else{const T=p.getBoundingClientRect();M=v.clientX-T.left,A=v.clientY-T.top,I=!0}return{x:M,y:A,box:I}}(t,r),h=s.left+(u&&a.left),f=s.top+(u&&a.top);let{width:d,height:y}=e;return o&&(d-=s.width+a.width,y-=s.height+a.height),{x:Math.round((l-h)/d*r.width/n),y:Math.round((c-f)/y*r.height/n)}}const sa=t=>Math.round(10*t)/10;function sM(t,e,r,n){const i=fl(t),o=Qn(i,"margin"),s=oa(i.maxWidth,t,"clientWidth")||ta,a=oa(i.maxHeight,t,"clientHeight")||ta,l=function(h,f,d){let y,g;if(f===void 0||d===void 0){const p=h&&Kd(h);if(p){const b=p.getBoundingClientRect(),v=fl(p),S=Qn(v,"border","width"),k=Qn(v,"padding");f=b.width-k.width-S.width,d=b.height-k.height-S.height,y=oa(v.maxWidth,p,"clientWidth"),g=oa(v.maxHeight,p,"clientHeight")}else f=h.clientWidth,d=h.clientHeight}return{width:f,height:d,maxWidth:y||ta,maxHeight:g||ta}}(t,e,r);let{width:c,height:u}=l;if(i.boxSizing==="content-box"){const h=Qn(i,"border","width"),f=Qn(i,"padding");c-=f.width+h.width,u-=f.height+h.height}return c=Math.max(0,c-o.width),u=Math.max(0,n?c/n:u-o.height),c=sa(Math.min(c,s,l.maxWidth)),u=sa(Math.min(u,a,l.maxHeight)),c&&!u&&(u=sa(c/2)),(e!==void 0||r!==void 0)&&n&&l.height&&u>l.height&&(u=l.height,c=sa(Math.floor(u*n))),{width:c,height:u}}function Bm(t,e,r){const n=e||1,i=Math.floor(t.height*n),o=Math.floor(t.width*n);t.height=Math.floor(t.height),t.width=Math.floor(t.width);const s=t.canvas;return s.style&&(r||!s.style.height&&!s.style.width)&&(s.style.height=`${t.height}px`,s.style.width=`${t.width}px`),(t.currentDevicePixelRatio!==n||s.height!==i||s.width!==o)&&(t.currentDevicePixelRatio=n,s.height=i,s.width=o,t.ctx.setTransform(n,0,0,n,0,0),!0)}const aM=function(){let t=!1;try{const e={get passive(){return t=!0,!1}};Yd()&&(window.addEventListener("test",null,e),window.removeEventListener("test",null,e))}catch{}return t}();function Fm(t,e){const r=function(i,o){return fl(i).getPropertyValue(o)}(t,e),n=r&&r.match(/^(\d+)(\.\d+)?px$/);return n?+n[1]:void 0}function Kn(t,e,r,n){return{x:t.x+r*(e.x-t.x),y:t.y+r*(e.y-t.y)}}function lM(t,e,r,n){return{x:t.x+r*(e.x-t.x),y:n==="middle"?r<.5?t.y:e.y:n==="after"?r<1?t.y:e.y:r>0?e.y:t.y}}function cM(t,e,r,n){const i={x:t.cp2x,y:t.cp2y},o={x:e.cp1x,y:e.cp1y},s=Kn(t,i,r),a=Kn(i,o,r),l=Kn(o,e,r),c=Kn(s,a,r),u=Kn(a,l,r);return Kn(c,u,r)}function Lc(t,e,r){return t?function(n,i){return{x:o=>n+n+i-o,setWidth(o){i=o},textAlign:o=>o==="center"?o:o==="right"?"left":"right",xPlus:(o,s)=>o-s,leftForLtr:(o,s)=>o-s}}(e,r):{x:n=>n,setWidth(n){},textAlign:n=>n,xPlus:(n,i)=>n+i,leftForLtr:(n,i)=>n}}function Wm(t){return t==="angle"?{between:ul,compare:FO,normalize:xr}:{between:Pi,compare:(e,r)=>e-r,normalize:e=>e}}function Um({start:t,end:e,count:r,loop:n,style:i}){return{start:t%r,end:e%r,loop:n&&(e-t+1)%r==0,style:i}}function C_(t,e,r){if(!r)return[t];const{property:n,start:i,end:o}=r,s=e.length,{compare:a,between:l,normalize:c}=Wm(n),{start:u,end:h,loop:f,style:d}=function(A,I,T){const{property:_,start:x,end:O}=T,{between:N,normalize:G}=Wm(_),U=I.length;let z,q,{start:B,end:ne,loop:H}=A;if(H){for(B+=U,ne+=U,z=0,q=U;zv||l(i,b,g)&&a(i,b)!==0,M=()=>!v||a(o,g)===0||l(o,b,g);for(let A=u,I=u;A<=h;++A)p=e[A%s],p.skip||(g=c(p[n]),g!==b&&(v=l(g,i,o),S===null&&k()&&(S=a(g,i)===0?A:I),S!==null&&M()&&(y.push(Um({start:S,end:A,loop:f,count:s,style:d})),S=null),I=A,b=g));return S!==null&&y.push(Um({start:S,end:h,loop:f,count:s,style:d})),y}function R_(t,e){const r=[],n=t.segments;for(let i=0;ia({chart:e,initial:r.initial,numSteps:s,currentStep:Math.min(n-r.start,s)}))}_refresh(){this._request||(this._running=!0,this._request=__.call(window,()=>{this._update(),this._request=null,this._running&&this._refresh()}))}_update(e=Date.now()){let r=0;this._charts.forEach((n,i)=>{if(!n.running||!n.items.length)return;const o=n.items;let s,a=o.length-1,l=!1;for(;a>=0;--a)s=o[a],s._active?(s._total>n.duration&&(n.duration=s._total),s.tick(e),l=!0):(o[a]=o[o.length-1],o.pop());l&&(i.draw(),this._notify(i,n,e,"progress")),o.length||(n.running=!1,this._notify(i,n,e,"complete"),n.initial=!1),r+=o.length}),this._lastDate=e,r===0&&(this._running=!1)}_getAnims(e){const r=this._charts;let n=r.get(e);return n||(n={running:!1,initial:!0,items:[],listeners:{complete:[],progress:[]}},r.set(e,n)),n}listen(e,r,n){this._getAnims(e).listeners[r].push(n)}add(e,r){r&&r.length&&this._getAnims(e).items.push(...r)}has(e){return this._getAnims(e).items.length>0}start(e){const r=this._charts.get(e);r&&(r.running=!0,r.start=Date.now(),r.duration=r.items.reduce((n,i)=>Math.max(n,i._duration),0),this._refresh())}running(e){if(!this._running)return!1;const r=this._charts.get(e);return!!(r&&r.running&&r.items.length)}stop(e){const r=this._charts.get(e);if(!r||!r.items.length)return;const n=r.items;let i=n.length-1;for(;i>=0;--i)n[i].cancel();r.items=[],this._notify(e,r,Date.now(),"complete")}remove(e){return this._charts.delete(e)}}var Lr=new fM;const $m="transparent",dM={boolean:(t,e,r)=>r>.5?e:t,color(t,e,r){const n=Im(t||$m),i=n.valid&&Im(e||$m);return i&&i.valid?i.mix(n,r).hexString():e},number:(t,e,r)=>t+(e-t)*r};class pM{constructor(e,r,n,i){const o=r[n];i=na([e.to,i,o,e.from]);const s=na([e.from,o,i]);this._active=!0,this._fn=e.fn||dM[e.type||typeof s],this._easing=es[e.easing]||es.linear,this._start=Math.floor(Date.now()+(e.delay||0)),this._duration=this._total=Math.floor(e.duration),this._loop=!!e.loop,this._target=r,this._prop=n,this._from=s,this._to=i,this._promises=void 0}active(){return this._active}update(e,r,n){if(this._active){this._notify(!1);const i=this._target[this._prop],o=n-this._start,s=this._duration-o;this._start=n,this._duration=Math.floor(Math.max(s,e.duration)),this._total+=o,this._loop=!!e.loop,this._to=na([e.to,r,i,e.from]),this._from=na([e.from,i,r])}}cancel(){this._active&&(this.tick(Date.now()),this._active=!1,this._notify(!1))}tick(e){const r=e-this._start,n=this._duration,i=this._prop,o=this._from,s=this._loop,a=this._to;let l;if(this._active=o!==a&&(s||r1?2-l:l,l=this._easing(Math.min(1,Math.max(0,l))),this._target[i]=this._fn(o,a,l))}wait(){const e=this._promises||(this._promises=[]);return new Promise((r,n)=>{e.push({res:r,rej:n})})}_notify(e){const r=e?"res":"rej",n=this._promises||[];for(let i=0;i{const o=e[i];if(!je(o))return;const s={};for(const a of r)s[a]=o[a];(st(o.properties)&&o.properties||[i]).forEach(a=>{a!==i&&n.has(a)||n.set(a,s)})})}_animateOptions(e,r){const n=r.options,i=function(s,a){if(!a)return;let l=s.options;return l?(l.$shared&&(s.options=l=Object.assign({},l,{$shared:!1,$animations:{}})),l):void(s.options=a)}(e,n);if(!i)return[];const o=this._createAnimations(i,n);return n.$shared&&function(s,a){const l=[],c=Object.keys(a);for(let u=0;u{e.options=n},()=>{}),o}_createAnimations(e,r){const n=this._properties,i=[],o=e.$animations||(e.$animations={}),s=Object.keys(r),a=Date.now();let l;for(l=s.length-1;l>=0;--l){const c=s[l];if(c.charAt(0)==="$")continue;if(c==="options"){i.push(...this._animateOptions(e,r));continue}const u=r[c];let h=o[c];const f=n.get(c);if(h){if(f&&h.active()){h.update(f,u,a);continue}h.cancel()}f&&f.duration?(o[c]=h=new pM(f,e,c,u),i.push(h)):e[c]=u}return i}update(e,r){if(this._properties.size===0)return void Object.assign(e,r);const n=this._createAnimations(e,r);return n.length?(Lr.add(this._chart,n),!0):void 0}}function zm(t,e){const r=t&&t.options||{},n=r.reverse,i=r.min===void 0?e:0,o=r.max===void 0?e:0;return{start:n?o:i,end:n?i:o}}function Hm(t,e){const r=[],n=t._getSortedDatasetMetas(e);let i,o;for(i=0,o=n.length;i0||!r&&o<0)return i.index}return null}function Km(t,e){const{chart:r,_cachedMeta:n}=t,i=r._stacks||(r._stacks={}),{iScale:o,vScale:s,index:a}=n,l=o.axis,c=s.axis,u=function(d,y,g){return`${d.id}.${y.id}.${g.stack||g.type}`}(o,s,n),h=e.length;let f;for(let d=0;dr[n].axis===e).shift()}function vo(t,e){const r=t.controller.index,n=t.vScale&&t.vScale.axis;if(n){e=e||t._parsed;for(const i of e){const o=i._stacks;if(!o||o[n]===void 0||o[n][r]===void 0)return;delete o[n][r],o[n]._visualValues!==void 0&&o[n]._visualValues[r]!==void 0&&delete o[n]._visualValues[r]}}}const Wc=t=>t==="reset"||t==="none",Gm=(t,e)=>e?t:Object.assign({},t);class Vi{constructor(e,r){this.chart=e,this._ctx=e.ctx,this.index=r,this._cachedDataOpts={},this._cachedMeta=this.getMeta(),this._type=this._cachedMeta.type,this.options=void 0,this._parsing=!1,this._data=void 0,this._objectData=void 0,this._sharedOptions=void 0,this._drawStart=void 0,this._drawCount=void 0,this.enableOptionSharing=!1,this.supportsDecimation=!1,this.$context=void 0,this._syncList=[],this.datasetElementType=new.target.datasetElementType,this.dataElementType=new.target.dataElementType,this.initialize()}initialize(){const e=this._cachedMeta;this.configure(),this.linkScales(),e._stacked=Bc(e.vScale,e),this.addElements(),this.options.fill&&this.chart.isPluginEnabled("filler")}updateIndex(e){this.index!==e&&vo(this._cachedMeta),this.index=e}linkScales(){const e=this.chart,r=this._cachedMeta,n=this.getDataset(),i=(h,f,d,y)=>h==="x"?f:h==="r"?y:d,o=r.xAxisID=Ne(n.xAxisID,Fc(e,"x")),s=r.yAxisID=Ne(n.yAxisID,Fc(e,"y")),a=r.rAxisID=Ne(n.rAxisID,Fc(e,"r")),l=r.indexAxis,c=r.iAxisID=i(l,o,s,a),u=r.vAxisID=i(l,s,o,a);r.xScale=this.getScaleForId(o),r.yScale=this.getScaleForId(s),r.rScale=this.getScaleForId(a),r.iScale=this.getScaleForId(c),r.vScale=this.getScaleForId(u)}getDataset(){return this.chart.data.datasets[this.index]}getMeta(){return this.chart.getDatasetMeta(this.index)}getScaleForId(e){return this.chart.scales[e]}_getOtherScale(e){const r=this._cachedMeta;return e===r.iScale?r.vScale:r.iScale}reset(){this._update("reset")}_destroy(){const e=this._cachedMeta;this._data&&Mm(this._data,this),e._stacked&&vo(e)}_dataCheck(){const e=this.getDataset(),r=e.data||(e.data=[]),n=this._data;if(je(r)){const s=this._cachedMeta;this._data=function(a,l){const{iScale:c,vScale:u}=l,h=c.axis==="x"?"x":"y",f=u.axis==="x"?"x":"y",d=Object.keys(a),y=new Array(d.length);let g,p,b;for(g=0,p=d.length;g{const a="_onData"+Ud(s),l=i[s];Object.defineProperty(i,s,{configurable:!0,enumerable:!1,value(...c){const u=l.apply(this,c);return i._chartjs.listeners.forEach(h=>{typeof h[a]=="function"&&h[a](...c)}),u}})}))),this._syncList=[],this._data=r}var i,o}addElements(){const e=this._cachedMeta;this._dataCheck(),this.datasetElementType&&(e.dataset=new this.datasetElementType)}buildOrUpdateElements(e){const r=this._cachedMeta,n=this.getDataset();let i=!1;this._dataCheck();const o=r._stacked;r._stacked=Bc(r.vScale,r),r.stack!==n.stack&&(i=!0,vo(r),r.stack=n.stack),this._resyncElements(e),(i||o!==r._stacked)&&(Km(this,r._parsed),r._stacked=Bc(r.vScale,r))}configure(){const e=this.chart.config,r=e.datasetScopeKeys(this._type),n=e.getOptionScopes(this.getDataset(),r,!0);this.options=e.createResolver(n,this.getContext()),this._parsing=this.options.parsing,this._cachedDataOpts={}}parse(e,r){const{_cachedMeta:n,_data:i}=this,{iScale:o,_stacked:s}=n,a=o.axis;let l,c,u,h=e===0&&r===i.length||n._sorted,f=e>0&&n._parsed[e-1];if(this._parsing===!1)n._parsed=i,n._sorted=!0,u=i;else{u=st(i[e])?this.parseArrayData(n,i,e,r):je(i[e])?this.parseObjectData(n,i,e,r):this.parsePrimitiveData(n,i,e,r);const d=()=>c[a]===null||f&&c[a]g&&!p.hidden&&p._stacked&&{keys:Hm(b,!0),values:null})(r,n,this.chart),c={min:Number.POSITIVE_INFINITY,max:Number.NEGATIVE_INFINITY},{min:u,max:h}=function(g){const{min:p,max:b,minDefined:v,maxDefined:S}=g.getUserBounds();return{min:v?p:Number.NEGATIVE_INFINITY,max:S?b:Number.POSITIVE_INFINITY}}(a);let f,d;function y(){d=i[f];const g=d[a.axis];return!yt(d[e.axis])||u>g||h=0;--f)if(!y()){this.updateRangeFromParsed(c,e,d,l);break}}return c}getAllParsedValues(e){const r=this._cachedMeta._parsed,n=[];let i,o,s;for(i=0,o=r.length;i=0&&ethis.getContext(n,i,r),h);return y.$shared&&(y.$shared=l,o[s]=Object.freeze(Gm(y,l))),y}_resolveAnimations(e,r,n){const i=this.chart,o=this._cachedDataOpts,s=`animation-${r}`,a=o[s];if(a)return a;let l;if(i.options.animation!==!1){const u=this.chart.config,h=u.datasetAnimationScopeKeys(this._type,r),f=u.getOptionScopes(this.getDataset(),h);l=u.createResolver(f,this.getContext(e,n,r))}const c=new j_(i,l&&l.animations);return l&&l._cacheable&&(o[s]=Object.freeze(c)),c}getSharedOptions(e){if(e.$shared)return this._sharedOptions||(this._sharedOptions=Object.assign({},e))}includeOptions(e,r){return!r||Wc(e)||this.chart._animationsDisabled}_getSharedOptions(e,r){const n=this.resolveDataElementOptions(e,r),i=this._sharedOptions,o=this.getSharedOptions(n),s=this.includeOptions(r,o)||o!==i;return this.updateSharedOptions(o,r,n),{sharedOptions:o,includeOptions:s}}updateElement(e,r,n,i){Wc(i)?Object.assign(e,n):this._resolveAnimations(r,i).update(e,n)}updateSharedOptions(e,r,n){e&&!Wc(r)&&this._resolveAnimations(void 0,r).update(e,n)}_setStyle(e,r,n,i){e.active=i;const o=this.getStyle(r,i);this._resolveAnimations(r,n,i).update(e,{options:!i&&this.getSharedOptions(o)||o})}removeHoverStyle(e,r,n){this._setStyle(e,n,"active",!1)}setHoverStyle(e,r,n){this._setStyle(e,n,"active",!0)}_removeDatasetHoverStyle(){const e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,"active",!1)}_setDatasetHoverStyle(){const e=this._cachedMeta.dataset;e&&this._setStyle(e,void 0,"active",!0)}_resyncElements(e){const r=this._data,n=this._cachedMeta.data;for(const[a,l,c]of this._syncList)this[a](l,c);this._syncList=[];const i=n.length,o=r.length,s=Math.min(o,i);s&&this.parse(0,s),o>i?this._insertElements(i,o-i,e):o{for(c.length+=r,a=c.length-1;a>=s;a--)c[a]=c[a-r]};for(l(o),a=e;a+n[l];if(je(n[e])){const{key:l="value"}=this._parsing;a=c=>+_s(n[c],l)}for(o=e,s=e+r;oul(le,U,z,!0)?1:Math.max(ue,ue*_,Y,Y*_),F=(le,ue,Y)=>ul(le,U,z,!0)?-1:Math.min(ue,ue*_,Y,Y*_),V=Q(0,q,ne),Z=Q(xt,B,H),$=F(Qe,q,ne),se=F(Qe+xt,B,H);x=(V-$)/2,O=(Z-se)/2,N=-(V+$)/2,G=-(Z+se)/2}return{ratioX:x,ratioY:O,offsetX:N,offsetY:G}}(d,f,l),v=(n.width-s)/y,S=(n.height-s)/g,k=Math.max(Math.min(v,S)/2,0),M=m_(this.options.radius,k),A=(M-Math.max(M*l,0))/this._getVisibleDatasetWeightTotal();this.offsetX=p*M,this.offsetY=b*M,i.total=this.calculateTotal(),this.outerRadius=M-A*this._getRingWeightOffset(this.index),this.innerRadius=Math.max(this.outerRadius-A*h,0),this.updateElements(o,0,o.length,e)}_circumference(e,r){const n=this.options,i=this._cachedMeta,o=this._getCircumference();return r&&n.animation.animateRotate||!this.chart.getDataVisibility(e)||i._parsed[e]===null||i.data[e].hidden?0:this.calculateCircumference(i._parsed[e]*o/pt)}updateElements(e,r,n,i){const o=i==="reset",s=this.chart,a=s.chartArea,l=s.options.animation,c=(a.left+a.right)/2,u=(a.top+a.bottom)/2,h=o&&l.animateScale,f=h?0:this.innerRadius,d=h?0:this.outerRadius,{sharedOptions:y,includeOptions:g}=this._getSharedOptions(r,i);let p,b=this._getRotation();for(p=0;p0&&!isNaN(e)?pt*(Math.abs(e)/r):0}getLabelAndValue(e){const r=this._cachedMeta,n=this.chart,i=n.data.labels||[],o=Hd(r._parsed[e],n.options.locale);return{label:i[e]||"",value:o}}getMaxBorderWidth(e){let r=0;const n=this.chart;let i,o,s,a,l;if(!e){for(i=0,o=n.data.datasets.length;ie!=="spacing",_indexable:e=>e!=="spacing"&&!e.startsWith("borderDash")&&!e.startsWith("hoverBorderDash")}),_e(Do,"overrides",{aspectRatio:1,plugins:{legend:{labels:{generateLabels(e){const r=e.data;if(r.labels.length&&r.datasets.length){const{labels:{pointStyle:n,color:i}}=e.legend.options;return r.labels.map((o,s)=>{const a=e.getDatasetMeta(0).controller.getStyle(s);return{text:o,fillStyle:a.backgroundColor,strokeStyle:a.borderColor,fontColor:i,lineWidth:a.borderWidth,pointStyle:n,hidden:!e.getDataVisibility(s),index:s}})}return[]}},onClick(e,r,n){n.chart.toggleDataVisibility(r.index),n.chart.update()}}}});class Va extends Vi{initialize(){this.enableOptionSharing=!0,this.supportsDecimation=!0,super.initialize()}update(e){const r=this._cachedMeta,{dataset:n,data:i=[],_dataset:o}=r,s=this.chart._animationsDisabled;let{start:a,count:l}=function(u,h,f){const d=h.length;let y=0,g=d;if(u._sorted){const{iScale:p,vScale:b,_parsed:v}=u,S=u.dataset&&u.dataset.options?u.dataset.options.spanGaps:null,k=p.axis,{min:M,max:A,minDefined:I,maxDefined:T}=p.getUserBounds();if(I){if(y=Math.min(Ro(v,k,M).lo,f?d:Ro(h,k,p.getPixelForValue(M)).lo),S){const _=v.slice(0,y+1).reverse().findIndex(x=>!it(x[b.axis]));y-=Math.max(0,_)}y=Ur(y,0,d-1)}if(T){let _=Math.max(Ro(v,p.axis,A,!0).hi+1,f?0:Ro(h,k,p.getPixelForValue(A),!0).hi+1);if(S){const x=v.slice(_-1).findIndex(O=>!it(O[b.axis]));_+=Math.max(0,x)}g=Ur(_,y,d)-y}else g=d-y}return{start:y,count:g}}(r,i,s);this._drawStart=a,this._drawCount=l,function(u){const{xScale:h,yScale:f,_scaleRanges:d}=u,y={xmin:h.min,xmax:h.max,ymin:f.min,ymax:f.max};if(!d)return u._scaleRanges=y,!0;const g=d.xmin!==h.min||d.xmax!==h.max||d.ymin!==f.min||d.ymax!==f.max;return Object.assign(d,y),g}(r)&&(a=0,l=i.length),n._chart=this.chart,n._datasetIndex=this.index,n._decimated=!!o._decimated,n.points=i;const c=this.resolveDatasetElementOptions(e);this.options.showLine||(c.borderWidth=0),c.segment=this.options.segment,this.updateElement(n,void 0,{animated:!s,options:c},e),this.updateElements(i,a,l,e)}updateElements(e,r,n,i){const o=i==="reset",{iScale:s,vScale:a,_stacked:l,_dataset:c}=this._cachedMeta,{sharedOptions:u,includeOptions:h}=this._getSharedOptions(r,i),f=s.axis,d=a.axis,{spanGaps:y,segment:g}=this.options,p=li(y)?y:Number.POSITIVE_INFINITY,b=this.chart._animationsDisabled||o||i==="none",v=r+n,S=e.length;let k=r>0&&this.getParsed(r-1);for(let M=0;M=v){I.skip=!0;continue}const T=this.getParsed(M),_=it(T[d]),x=I[f]=s.getPixelForValue(T[f],M),O=I[d]=o||_?a.getBasePixel():a.getPixelForValue(l?this.applyStack(a,T,l):T[d],M);I.skip=isNaN(x)||isNaN(O)||_,I.stop=M>0&&Math.abs(T[f]-k[f])>p,g&&(I.parsed=T,I.raw=c.data[M]),h&&(I.options=u||this.resolveDataElementOptions(M,A.active?"active":i)),b||this.updateElement(A,M,I,i),k=T}}getMaxOverflow(){const e=this._cachedMeta,r=e.dataset,n=r.options&&r.options.borderWidth||0,i=e.data||[];if(!i.length)return n;const o=i[0].size(this.resolveDataElementOptions(0)),s=i[i.length-1].size(this.resolveDataElementOptions(i.length-1));return Math.max(n,o,s)/2}draw(){const e=this._cachedMeta;e.dataset.updateControlPoints(this.chart.chartArea,e.iScale.axis),super.draw()}}_e(Va,"id","line"),_e(Va,"defaults",{datasetElementType:"line",dataElementType:"point",showLine:!0,spanGaps:!1}),_e(Va,"overrides",{scales:{_index_:{type:"category"},_value_:{type:"linear"}}});function zn(){throw new Error("This method is not implemented: Check that a complete date adapter is provided.")}class Gd{constructor(e){_e(this,"options");this.options=e||{}}static override(e){Object.assign(Gd.prototype,e)}init(){}formats(){return zn()}parse(){return zn()}format(){return zn()}add(){return zn()}diff(){return zn()}startOf(){return zn()}endOf(){return zn()}}var N_={_date:Gd};function mM(t,e,r,n){const{controller:i,data:o,_sorted:s}=t,a=i._cachedMeta.iScale,l=t.dataset&&t.dataset.options?t.dataset.options.spanGaps:null;if(a&&e===a.axis&&e!=="r"&&s&&o.length){const c=a._reversePixels?WO:Ro;if(!n){const u=c(o,e,r);if(l){const{vScale:h}=i._cachedMeta,{_parsed:f}=t,d=f.slice(0,u.lo+1).reverse().findIndex(g=>!it(g[h.axis]));u.lo-=Math.max(0,d);const y=f.slice(u.hi).findIndex(g=>!it(g[h.axis]));u.hi+=Math.max(0,y)}return u}if(i._sharedOptions){const u=o[0],h=typeof u.getRange=="function"&&u.getRange(e);if(h){const f=c(o,e,r-h),d=c(o,e,r+h);return{lo:f.lo,hi:d.hi}}}}return{lo:0,hi:o.length-1}}function Kl(t,e,r,n,i){const o=t.getSortedVisibleDatasetMetas(),s=r[e];for(let a=0,l=o.length;a{l[s]&&l[s](e[r],i)&&(o.push({element:l,datasetIndex:c,index:u}),a=a||l.inRange(e.x,e.y,i))}),n&&!a?[]:o}var bM={modes:{index(t,e,r,n){const i=Yn(e,t),o=r.axis||"x",s=r.includeInvisible||!1,a=r.intersect?Uc(t,i,o,n,s):Vc(t,i,o,!1,n,s),l=[];return a.length?(t.getSortedVisibleDatasetMetas().forEach(c=>{const u=a[0].index,h=c.data[u];h&&!h.skip&&l.push({element:h,datasetIndex:c.index,index:u})}),l):[]},dataset(t,e,r,n){const i=Yn(e,t),o=r.axis||"xy",s=r.includeInvisible||!1;let a=r.intersect?Uc(t,i,o,n,s):Vc(t,i,o,!1,n,s);if(a.length>0){const l=a[0].datasetIndex,c=t.getDatasetMeta(l).data;a=[];for(let u=0;uUc(t,Yn(e,t),r.axis||"xy",n,r.includeInvisible||!1),nearest(t,e,r,n){const i=Yn(e,t),o=r.axis||"xy",s=r.includeInvisible||!1;return Vc(t,i,o,r.intersect,n,s)},x:(t,e,r,n)=>Qm(t,Yn(e,t),"x",r.intersect,n),y:(t,e,r,n)=>Qm(t,Yn(e,t),"y",r.intersect,n)}};const L_=["left","top","right","bottom"];function wo(t,e){return t.filter(r=>r.pos===e)}function Zm(t,e){return t.filter(r=>L_.indexOf(r.pos)===-1&&r.box.axis===e)}function _o(t,e){return t.sort((r,n)=>{const i=e?n:r,o=e?r:n;return i.weight===o.weight?i.index-o.index:i.weight-o.weight})}function vM(t,e){const r=function(l){const c={};for(const u of l){const{stack:h,pos:f,stackWeight:d}=u;if(!h||!L_.includes(f))continue;const y=c[h]||(c[h]={count:0,placed:0,weight:0,size:0});y.count++,y.weight+=d}return c}(t),{vBoxMaxWidth:n,hBoxMaxHeight:i}=e;let o,s,a;for(o=0,s=t.length;o{o[s]=Math.max(e[s],r[s])}),o}return n(t?["left","right"]:["top","bottom"])}function jo(t,e,r,n){const i=[];let o,s,a,l,c,u;for(o=0,s=t.length,c=0;oT.box.fullSize),!0),v=_o(wo(p,"left"),!0),S=_o(wo(p,"right")),k=_o(wo(p,"top"),!0),M=_o(wo(p,"bottom")),A=Zm(p,"x"),I=Zm(p,"y");return{fullSize:b,leftAndTop:v.concat(k),rightAndBottom:S.concat(I).concat(M).concat(A),chartArea:wo(p,"chartArea"),vertical:v.concat(S).concat(I),horizontal:k.concat(M).concat(A)}}(t.boxes),l=a.vertical,c=a.horizontal;Et(t.boxes,g=>{typeof g.beforeLayout=="function"&&g.beforeLayout()});const u=l.reduce((g,p)=>p.box.options&&p.box.options.display===!1?g:g+1,0)||1,h=Object.freeze({outerWidth:e,outerHeight:r,padding:i,availableWidth:o,availableHeight:s,vBoxMaxWidth:o/2/u,hBoxMaxHeight:s/2}),f=Object.assign({},i);B_(f,ar(n));const d=Object.assign({maxPadding:f,w:o,h:s,x:i.left,y:i.top},i),y=vM(l.concat(c),h);jo(a.fullSize,d,h,y),jo(l,d,h,y),jo(c,d,h,y)&&jo(l,d,h,y),function(g){const p=g.maxPadding;function b(v){const S=Math.max(p[v]-g[v],0);return g[v]+=S,S}g.y+=b("top"),g.x+=b("left"),b("right"),b("bottom")}(d),Jm(a.leftAndTop,d,h,y),d.x+=d.w,d.y+=d.h,Jm(a.rightAndBottom,d,h,y),t.chartArea={left:d.left,top:d.top,right:d.left+d.w,bottom:d.top+d.h,height:d.h,width:d.w},Et(a.chartArea,g=>{const p=g.box;Object.assign(p,t.chartArea),p.update(d.w,d.h,{left:0,top:0,right:0,bottom:0})})}};class F_{acquireContext(e,r){}releaseContext(e){return!1}addEventListener(e,r,n){}removeEventListener(e,r,n){}getDevicePixelRatio(){return 1}getMaximumSize(e,r,n,i){return r=Math.max(0,r||e.width),n=n||e.height,{width:r,height:Math.max(0,i?Math.floor(r/i):n)}}isAttached(e){return!0}updateConfig(e){}}class xM extends F_{acquireContext(e){return e&&e.getContext&&e.getContext("2d")||null}updateConfig(e){e.options.animation=!1}}const ca="$chartjs",SM={touchstart:"mousedown",touchmove:"mousemove",touchend:"mouseup",pointerenter:"mouseenter",pointerdown:"mousedown",pointermove:"mousemove",pointerup:"mouseup",pointerleave:"mouseout",pointerout:"mouseout"},ey=t=>t===null||t==="",W_=!!aM&&{passive:!0};function kM(t,e,r){t&&t.canvas&&t.canvas.removeEventListener(e,r,W_)}function dl(t,e){for(const r of t)if(r===e||r.contains(e))return!0}function EM(t,e,r){const n=t.canvas,i=new MutationObserver(o=>{let s=!1;for(const a of o)s=s||dl(a.addedNodes,n),s=s&&!dl(a.removedNodes,n);s&&r()});return i.observe(document,{childList:!0,subtree:!0}),i}function OM(t,e,r){const n=t.canvas,i=new MutationObserver(o=>{let s=!1;for(const a of o)s=s||dl(a.removedNodes,n),s=s&&!dl(a.addedNodes,n);s&&r()});return i.observe(document,{childList:!0,subtree:!0}),i}const ks=new Map;let ty=0;function U_(){const t=window.devicePixelRatio;t!==ty&&(ty=t,ks.forEach((e,r)=>{r.currentDevicePixelRatio!==t&&e()}))}function MM(t,e,r){const n=t.canvas,i=n&&Kd(n);if(!i)return;const o=x_((a,l)=>{const c=i.clientWidth;r(a,l),c{const l=a[0],c=l.contentRect.width,u=l.contentRect.height;c===0&&u===0||o(c,u)});return s.observe(i),function(a,l){ks.size||window.addEventListener("resize",U_),ks.set(a,l)}(t,o),s}function $c(t,e,r){r&&r.disconnect(),e==="resize"&&function(n){ks.delete(n),ks.size||window.removeEventListener("resize",U_)}(t)}function AM(t,e,r){const n=t.canvas,i=x_(o=>{t.ctx!==null&&r(function(s,a){const l=SM[s.type]||s.type,{x:c,y:u}=Yn(s,a);return{type:l,chart:a,native:s,x:c!==void 0?c:null,y:u!==void 0?u:null}}(o,t))},t);return function(o,s,a){o&&o.addEventListener(s,a,W_)}(n,e,i),i}class TM extends F_{acquireContext(e,r){const n=e&&e.getContext&&e.getContext("2d");return n&&n.canvas===e?(function(i,o){const s=i.style,a=i.getAttribute("height"),l=i.getAttribute("width");if(i[ca]={initial:{height:a,width:l,style:{display:s.display,height:s.height,width:s.width}}},s.display=s.display||"block",s.boxSizing=s.boxSizing||"border-box",ey(l)){const c=Fm(i,"width");c!==void 0&&(i.width=c)}if(ey(a))if(i.style.height==="")i.height=i.width/(o||2);else{const c=Fm(i,"height");c!==void 0&&(i.height=c)}}(e,r),n):null}releaseContext(e){const r=e.canvas;if(!r[ca])return!1;const n=r[ca].initial;["height","width"].forEach(o=>{const s=n[o];it(s)?r.removeAttribute(o):r.setAttribute(o,s)});const i=n.style||{};return Object.keys(i).forEach(o=>{r.style[o]=i[o]}),r.width=r.width,delete r[ca],!0}addEventListener(e,r,n){this.removeEventListener(e,r);const i=e.$proxies||(e.$proxies={}),o={attach:EM,detach:OM,resize:MM}[r]||AM;i[r]=o(e,r,n)}removeEventListener(e,r){const n=e.$proxies||(e.$proxies={}),i=n[r];i&&(({attach:$c,detach:$c,resize:$c}[r]||kM)(e,r,i),n[r]=void 0)}getDevicePixelRatio(){return window.devicePixelRatio}getMaximumSize(e,r,n,i){return sM(e,r,n,i)}isAttached(e){const r=e&&Kd(e);return!(!r||!r.isConnected)}}var wa;let cr=(wa=class{constructor(){_e(this,"x");_e(this,"y");_e(this,"active",!1);_e(this,"options");_e(this,"$animations")}tooltipPosition(t){const{x:e,y:r}=this.getProps(["x","y"],t);return{x:e,y:r}}hasValue(){return li(this.x)&&li(this.y)}getProps(t,e){const r=this.$animations;if(!e||!r)return this;const n={};return t.forEach(i=>{n[i]=r[i]&&r[i].active()?r[i]._to:this[i]}),n}},_e(wa,"defaults",{}),_e(wa,"defaultRoutes"),wa);function IM(t,e){const r=t.options.ticks,n=function(h){const f=h.options.offset,d=h._tickSize(),y=h._length/d+(f?0:1),g=h._maxLength/d;return Math.floor(Math.min(y,g))}(t),i=Math.min(r.maxTicksLimit||n,n),o=r.major.enabled?function(h){const f=[];let d,y;for(d=0,y=h.length;di)return function(h,f,d,y){let g,p=0,b=d[0];for(y=Math.ceil(y),g=0;gM-A).pop(),v}(y);for(let b=0,v=p.length-1;bg)return S}return Math.max(g,1)}(o,e,i);if(s>0){let h,f;const d=s>1?Math.round((l-a)/(s-1)):null;for(ua(e,c,u,it(d)?0:a-d,a),h=0,f=s-1;he==="top"||e==="left"?t[e]+r:t[e]-r,ny=(t,e)=>Math.min(e||t,t);function iy(t,e){const r=[],n=t.length/e,i=t.length;let o=0;for(;os+a)))return c}function xo(t){return t.drawTicks?t.tickLength:0}function oy(t,e){if(!t.display)return 0;const r=Mr(t.font,e),n=ar(t.padding);return(st(t.text)?t.text.length:1)*r.lineHeight+n.height}function CM(t,e,r){let n=S_(t);return(r&&e!=="right"||!r&&e==="right")&&(n=(i=>i==="left"?"right":i==="right"?"left":i)(n)),n}class js extends cr{constructor(e){super(),this.id=e.id,this.type=e.type,this.options=void 0,this.ctx=e.ctx,this.chart=e.chart,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.width=void 0,this.height=void 0,this._margins={left:0,right:0,top:0,bottom:0},this.maxWidth=void 0,this.maxHeight=void 0,this.paddingTop=void 0,this.paddingBottom=void 0,this.paddingLeft=void 0,this.paddingRight=void 0,this.axis=void 0,this.labelRotation=void 0,this.min=void 0,this.max=void 0,this._range=void 0,this.ticks=[],this._gridLineItems=null,this._labelItems=null,this._labelSizes=null,this._length=0,this._maxLength=0,this._longestTextCache={},this._startPixel=void 0,this._endPixel=void 0,this._reversePixels=!1,this._userMax=void 0,this._userMin=void 0,this._suggestedMax=void 0,this._suggestedMin=void 0,this._ticksLength=0,this._borderValue=0,this._cache={},this._dataLimitsCached=!1,this.$context=void 0}init(e){this.options=e.setContext(this.getContext()),this.axis=e.axis,this._userMin=this.parse(e.min),this._userMax=this.parse(e.max),this._suggestedMin=this.parse(e.suggestedMin),this._suggestedMax=this.parse(e.suggestedMax)}parse(e,r){return e}getUserBounds(){let{_userMin:e,_userMax:r,_suggestedMin:n,_suggestedMax:i}=this;return e=br(e,Number.POSITIVE_INFINITY),r=br(r,Number.NEGATIVE_INFINITY),n=br(n,Number.POSITIVE_INFINITY),i=br(i,Number.NEGATIVE_INFINITY),{min:br(e,n),max:br(r,i),minDefined:yt(e),maxDefined:yt(r)}}getMinMax(e){let r,{min:n,max:i,minDefined:o,maxDefined:s}=this.getUserBounds();if(o&&s)return{min:n,max:i};const a=this.getMatchingVisibleMetas();for(let l=0,c=a.length;li?i:n,i=o&&n>i?n:i,{min:br(n,br(i,n)),max:br(i,br(n,i))}}getPadding(){return{left:this.paddingLeft||0,top:this.paddingTop||0,right:this.paddingRight||0,bottom:this.paddingBottom||0}}getTicks(){return this.ticks}getLabels(){const e=this.chart.data;return this.options.labels||(this.isHorizontal()?e.xLabels:e.yLabels)||e.labels||[]}getLabelItems(e=this.chart.chartArea){return this._labelItems||(this._labelItems=this._computeLabelItems(e))}beforeLayout(){this._cache={},this._dataLimitsCached=!1}beforeUpdate(){Ze(this.options.beforeUpdate,[this])}update(e,r,n){const{beginAtZero:i,grace:o,ticks:s}=this.options,a=s.sampleSize;this.beforeUpdate(),this.maxWidth=e,this.maxHeight=r,this._margins=n=Object.assign({left:0,right:0,top:0,bottom:0},n),this.ticks=null,this._labelSizes=null,this._gridLineItems=null,this._labelItems=null,this.beforeSetDimensions(),this.setDimensions(),this.afterSetDimensions(),this._maxLength=this.isHorizontal()?this.width+n.left+n.right:this.height+n.top+n.bottom,this._dataLimitsCached||(this.beforeDataLimits(),this.determineDataLimits(),this.afterDataLimits(),this._range=function(c,u,h){const{min:f,max:d}=c,y=m_(u,(d-f)/2),g=(p,b)=>h&&p===0?0:p+b;return{min:g(f,-Math.abs(y)),max:g(d,y)}}(this,o,i),this._dataLimitsCached=!0),this.beforeBuildTicks(),this.ticks=this.buildTicks()||[],this.afterBuildTicks();const l=a=o||n<=1||!this.isHorizontal())return void(this.labelRotation=i);const u=this._getLabelSizes(),h=u.widest.width,f=u.highest.height,d=Ur(this.chart.width-h,0,this.maxWidth);s=e.offset?this.maxWidth/n:d/(n-1),h+6>s&&(s=d/(n-(e.offset?.5:1)),a=this.maxHeight-xo(e.grid)-r.padding-oy(e.title,this.chart.options.font),l=Math.sqrt(h*h+f*f),c=v_(Math.min(Math.asin(Ur((u.highest.height+6)/s,-1,1)),Math.asin(Ur(a/l,-1,1))-Math.asin(Ur(f/l,-1,1)))),c=Math.max(i,Math.min(o,c))),this.labelRotation=c}afterCalculateLabelRotation(){Ze(this.options.afterCalculateLabelRotation,[this])}afterAutoSkip(){}beforeFit(){Ze(this.options.beforeFit,[this])}fit(){const e={width:0,height:0},{chart:r,options:{ticks:n,title:i,grid:o}}=this,s=this._isVisible(),a=this.isHorizontal();if(s){const l=oy(i,r.options.font);if(a?(e.width=this.maxWidth,e.height=xo(o)+l):(e.height=this.maxHeight,e.width=xo(o)+l),n.display&&this.ticks.length){const{first:c,last:u,widest:h,highest:f}=this._getLabelSizes(),d=2*n.padding,y=gt(this.labelRotation),g=Math.cos(y),p=Math.sin(y);if(a){const b=n.mirror?0:p*h.width+g*f.height;e.height=Math.min(this.maxHeight,e.height+b+d)}else{const b=n.mirror?0:g*h.width+p*f.height;e.width=Math.min(this.maxWidth,e.width+b+d)}this._calculatePadding(c,u,p,g)}}this._handleMargins(),a?(this.width=this._length=r.width-this._margins.left-this._margins.right,this.height=e.height):(this.width=e.width,this.height=this._length=r.height-this._margins.top-this._margins.bottom)}_calculatePadding(e,r,n,i){const{ticks:{align:o,padding:s},position:a}=this.options,l=this.labelRotation!==0,c=a!=="top"&&this.axis==="x";if(this.isHorizontal()){const u=this.getPixelForTick(0)-this.left,h=this.right-this.getPixelForTick(this.ticks.length-1);let f=0,d=0;l?c?(f=i*e.width,d=n*r.height):(f=n*e.height,d=i*r.width):o==="start"?d=r.width:o==="end"?f=e.width:o!=="inner"&&(f=e.width/2,d=r.width/2),this.paddingLeft=Math.max((f-u+s)*this.width/(this.width-u),0),this.paddingRight=Math.max((d-h+s)*this.width/(this.width-h),0)}else{let u=r.height/2,h=e.height/2;o==="start"?(u=0,h=e.height):o==="end"&&(u=r.height,h=0),this.paddingTop=u+s,this.paddingBottom=h+s}}_handleMargins(){this._margins&&(this._margins.left=Math.max(this.paddingLeft,this._margins.left),this._margins.top=Math.max(this.paddingTop,this._margins.top),this._margins.right=Math.max(this.paddingRight,this._margins.right),this._margins.bottom=Math.max(this.paddingBottom,this._margins.bottom))}afterFit(){Ze(this.options.afterFit,[this])}isHorizontal(){const{axis:e,position:r}=this.options;return r==="top"||r==="bottom"||e==="x"}isFullSize(){return this.options.fullSize}_convertTicksToLabels(e){let r,n;for(this.beforeTickToLabelConversion(),this.generateTickLabels(e),r=0,n=e.length;r{const N=O.gc,G=N.length/2;let U;if(G>x){for(U=0;U({width:s[_]||0,height:a[_]||0});return{first:T(0),last:T(r-1),widest:T(A),highest:T(I),widths:s,heights:a}}getLabelForValue(e){return e}getPixelForValue(e,r){return NaN}getValueForPixel(e){}getPixelForTick(e){const r=this.ticks;return e<0||e>r.length-1?null:this.getPixelForValue(r[e].value)}getPixelForDecimal(e){this._reversePixels&&(e=1-e);const r=this._startPixel+e*this._length;return Ur(this._alignToPixels?$n(this.chart,r,0):r,-32768,32767)}getDecimalForPixel(e){const r=(e-this._startPixel)/this._length;return this._reversePixels?1-r:r}getBasePixel(){return this.getPixelForValue(this.getBaseValue())}getBaseValue(){const{min:e,max:r}=this;return e<0&&r<0?r:e>0&&r>0?e:0}getContext(e){const r=this.ticks||[];if(e>=0&&ea*i?a/n:l/i:l*i0}_computeGridLineItems(e){const r=this.axis,n=this.chart,i=this.options,{grid:o,position:s,border:a}=i,l=o.offset,c=this.isHorizontal(),u=this.ticks.length+(l?1:0),h=xo(o),f=[],d=a.setContext(this.getContext()),y=d.display?d.width:0,g=y/2,p=function(z){return $n(n,z,y)};let b,v,S,k,M,A,I,T,_,x,O,N;if(s==="top")b=p(this.bottom),A=this.bottom-h,T=b-g,x=p(e.top)+g,N=e.bottom;else if(s==="bottom")b=p(this.top),x=e.top,N=p(e.bottom)-g,A=b+g,T=this.top+h;else if(s==="left")b=p(this.right),M=this.right-h,I=b-g,_=p(e.left)+g,O=e.right;else if(s==="right")b=p(this.left),_=e.left,O=p(e.right)-g,M=b+g,I=this.left+h;else if(r==="x"){if(s==="center")b=p((e.top+e.bottom)/2+.5);else if(je(s)){const z=Object.keys(s)[0],q=s[z];b=p(this.chart.scales[z].getPixelForValue(q))}x=e.top,N=e.bottom,A=b+g,T=A+h}else if(r==="y"){if(s==="center")b=p((e.left+e.right)/2);else if(je(s)){const z=Object.keys(s)[0],q=s[z];b=p(this.chart.scales[z].getPixelForValue(q))}M=b-g,I=M-h,_=e.left,O=e.right}const G=Ne(i.ticks.maxTicksLimit,u),U=Math.max(1,Math.ceil(u/G));for(v=0;v0&&(le-=$/2)}Q={left:le,top:se,width:$+V.width,height:Z+V.height,color:z.backdropColor}}p.push({label:k,font:_,textOffset:N,options:{rotation:g,color:B,strokeColor:ne,strokeWidth:H,textAlign:F,textBaseline:G,translation:[M,A],backdrop:Q}})}return p}_getXAxisLabelAlignment(){const{position:e,ticks:r}=this.options;if(-gt(this.labelRotation))return e==="top"?"left":"right";let n="center";return r.align==="start"?n="left":r.align==="end"?n="right":r.align==="inner"&&(n="inner"),n}_getYAxisLabelAlignment(e){const{position:r,ticks:{crossAlign:n,mirror:i,padding:o}}=this.options,s=e+o,a=this._getLabelSizes().widest.width;let l,c;return r==="left"?i?(c=this.right+o,n==="near"?l="left":n==="center"?(l="center",c+=a/2):(l="right",c+=a)):(c=this.right-s,n==="near"?l="right":n==="center"?(l="center",c-=a/2):(l="left",c=this.left)):r==="right"?i?(c=this.left+o,n==="near"?l="right":n==="center"?(l="center",c-=a/2):(l="left",c-=a)):(c=this.left+s,n==="near"?l="left":n==="center"?(l="center",c+=a/2):(l="right",c=this.right)):l="right",{textAlign:l,x:c}}_computeLabelArea(){if(this.options.ticks.mirror)return;const e=this.chart,r=this.options.position;return r==="left"||r==="right"?{top:0,left:this.left,bottom:e.height,right:this.right}:r==="top"||r==="bottom"?{top:this.top,left:0,bottom:this.bottom,right:e.width}:void 0}drawBackground(){const{ctx:e,options:{backgroundColor:r},left:n,top:i,width:o,height:s}=this;r&&(e.save(),e.fillStyle=r,e.fillRect(n,i,o,s),e.restore())}getLineWidthForValue(e){const r=this.options.grid;if(!this._isVisible()||!r.display)return 0;const n=this.ticks.findIndex(i=>i.value===e);return n>=0?r.setContext(this.getContext(n)).lineWidth:0}drawGrid(e){const r=this.options.grid,n=this.ctx,i=this._gridLineItems||(this._gridLineItems=this._computeGridLineItems(e));let o,s;const a=(l,c,u)=>{u.width&&u.color&&(n.save(),n.lineWidth=u.width,n.strokeStyle=u.color,n.setLineDash(u.borderDash||[]),n.lineDashOffset=u.borderDashOffset,n.beginPath(),n.moveTo(l.x,l.y),n.lineTo(c.x,c.y),n.stroke(),n.restore())};if(r.display)for(o=0,s=i.length;o{this.drawBackground(),this.drawGrid(o),this.drawTitle()}},{z:i,draw:()=>{this.drawBorder()}},{z:r,draw:o=>{this.drawLabels(o)}}]:[{z:r,draw:o=>{this.draw(o)}}]}getMatchingVisibleMetas(e){const r=this.chart.getSortedVisibleDatasetMetas(),n=this.axis+"AxisID",i=[];let o,s;for(o=0,s=r.length;o{const y=d.split("."),g=y.pop(),p=[h].concat(y).join("."),b=f[d].split("."),v=b.pop(),S=b.join(".");ht.route(p,g,S,v)})}(l,a.defaultRoutes),a.descriptors&&ht.describe(l,a.descriptors)}(e,s,n),this.override&&ht.override(e.id,e.overrides)),s}get(e){return this.items[e]}unregister(e){const r=this.items,n=e.id,i=this.scope;n in r&&delete r[n],i&&n in ht[i]&&(delete ht[i][n],this.override&&delete ci[n])}}class RM{constructor(){this.controllers=new ha(Vi,"datasets",!0),this.elements=new ha(cr,"elements"),this.plugins=new ha(Object,"plugins"),this.scales=new ha(js,"scales"),this._typedRegistries=[this.controllers,this.scales,this.elements]}add(...e){this._each("register",e)}remove(...e){this._each("unregister",e)}addControllers(...e){this._each("register",e,this.controllers)}addElements(...e){this._each("register",e,this.elements)}addPlugins(...e){this._each("register",e,this.plugins)}addScales(...e){this._each("register",e,this.scales)}getController(e){return this._get(e,this.controllers,"controller")}getElement(e){return this._get(e,this.elements,"element")}getPlugin(e){return this._get(e,this.plugins,"plugin")}getScale(e){return this._get(e,this.scales,"scale")}removeControllers(...e){this._each("unregister",e,this.controllers)}removeElements(...e){this._each("unregister",e,this.elements)}removePlugins(...e){this._each("unregister",e,this.plugins)}removeScales(...e){this._each("unregister",e,this.scales)}_each(e,r,n){[...r].forEach(i=>{const o=n||this._getRegistryForType(i);n||o.isForType(i)||o===this.plugins&&i.id?this._exec(e,o,i):Et(i,s=>{const a=n||this._getRegistryForType(s);this._exec(e,a,s)})})}_exec(e,r,n){const i=Ud(e);Ze(n["before"+i],[],n),r[e](n),Ze(n["after"+i],[],n)}_getRegistryForType(e){for(let r=0;ro.filter(a=>!s.some(l=>a.plugin.id===l.plugin.id));this._notify(i(r,n),e,"stop"),this._notify(i(n,r),e,"start")}}function jM(t,e){return e||t!==!1?t===!0?{}:t:null}function NM(t,{plugin:e,local:r},n,i){const o=t.pluginScopeKeys(e),s=t.getOptionScopes(n,o);return r&&e.defaults&&s.push(e.defaults),t.createResolver(s,i,[""],{scriptable:!1,indexable:!1,allKeys:!0})}function Wf(t,e){const r=ht.datasets[t]||{};return((e.datasets||{})[t]||{}).indexAxis||e.indexAxis||r.indexAxis||"x"}function sy(t){if(t==="x"||t==="y"||t==="r")return t}function LM(t){return t==="top"||t==="bottom"?"x":t==="left"||t==="right"?"y":void 0}function Uf(t,...e){if(sy(t))return t;for(const r of e){const n=r.axis||LM(r.position)||t.length>1&&sy(t[0].toLowerCase());if(n)return n}throw new Error(`Cannot determine type of '${t}' axis. Please provide 'axis' or 'position' option.`)}function ay(t,e,r){if(r[e+"AxisID"]===t)return{axis:e}}function BM(t,e){const r=ci[t.type]||{scales:{}},n=e.scales||{},i=Wf(t.type,e),o=Object.create(null);return Object.keys(n).forEach(s=>{const a=n[s];if(!je(a)||a._proxy)return;const l=Uf(s,a,function(h,f){if(f.data&&f.data.datasets){const d=f.data.datasets.filter(y=>y.xAxisID===h||y.yAxisID===h);if(d.length)return ay(h,"x",d[0])||ay(h,"y",d[0])}return{}}(s,t),ht.scales[a.type]),c=function(h,f){return h===f?"_index_":"_value_"}(l,i),u=r.scales||{};o[s]=Xo(Object.create(null),[{axis:l},a,u[l],u[c]])}),t.data.datasets.forEach(s=>{const a=s.type||t.type,l=s.indexAxis||Wf(a,e),c=(ci[a]||{}).scales||{};Object.keys(c).forEach(u=>{const h=function(d,y){let g=d;return d==="_index_"?g=y:d==="_value_"&&(g=y==="x"?"y":"x"),g}(u,l),f=s[h+"AxisID"]||h;o[f]=o[f]||Object.create(null),Xo(o[f],[{axis:h},n[f],c[u]])})}),Object.keys(o).forEach(s=>{const a=o[s];Xo(a,[ht.scales[a.type],ht.scale])}),o}function ly(t){const e=t.options||(t.options={});e.plugins=Ne(e.plugins,{}),e.scales=BM(t,e)}function cy(t){return(t=t||{}).datasets=t.datasets||[],t.labels=t.labels||[],t}const uy=new Map,V_=new Set;function fa(t,e){let r=uy.get(t);return r||(r=e(),uy.set(t,r),V_.add(r)),r}const So=(t,e,r)=>{const n=_s(e,r);n!==void 0&&t.add(n)};class FM{constructor(e){this._config=function(r){return(r=r||{}).data=cy(r.data),ly(r),r}(e),this._scopeCache=new Map,this._resolverCache=new Map}get platform(){return this._config.platform}get type(){return this._config.type}set type(e){this._config.type=e}get data(){return this._config.data}set data(e){this._config.data=cy(e)}get options(){return this._config.options}set options(e){this._config.options=e}get plugins(){return this._config.plugins}update(){const e=this._config;this.clearCache(),ly(e)}clearCache(){this._scopeCache.clear(),this._resolverCache.clear()}datasetScopeKeys(e){return fa(e,()=>[[`datasets.${e}`,""]])}datasetAnimationScopeKeys(e,r){return fa(`${e}.transition.${r}`,()=>[[`datasets.${e}.transitions.${r}`,`transitions.${r}`],[`datasets.${e}`,""]])}datasetElementScopeKeys(e,r){return fa(`${e}-${r}`,()=>[[`datasets.${e}.elements.${r}`,`datasets.${e}`,`elements.${r}`,""]])}pluginScopeKeys(e){const r=e.id;return fa(`${this.type}-plugin-${r}`,()=>[[`plugins.${r}`,...e.additionalOptionScopes||[]]])}_cachedScopes(e,r){const n=this._scopeCache;let i=n.get(e);return i&&!r||(i=new Map,n.set(e,i)),i}getOptionScopes(e,r,n){const{options:i,type:o}=this,s=this._cachedScopes(e,n),a=s.get(r);if(a)return a;const l=new Set;r.forEach(u=>{e&&(l.add(e),u.forEach(h=>So(l,e,h))),u.forEach(h=>So(l,i,h)),u.forEach(h=>So(l,ci[o]||{},h)),u.forEach(h=>So(l,ht,h)),u.forEach(h=>So(l,Lf,h))});const c=Array.from(l);return c.length===0&&c.push(Object.create(null)),V_.has(r)&&s.set(r,c),c}chartOptionScopes(){const{options:e,type:r}=this;return[e,ci[r]||{},ht.datasets[r]||{},{type:r},ht,Lf]}resolveNamedOptions(e,r,n,i=[""]){const o={$shared:!0},{resolver:s,subPrefixes:a}=hy(this._resolverCache,e,i);let l=s;(function(c,u){const{isScriptable:h,isIndexable:f}=T_(c);for(const d of u){const y=h(d),g=f(d),p=(g||y)&&c[d];if(y&&(Ut(p)||WM(p))||g&&st(p))return!0}return!1})(s,r)&&(o.$shared=!1,l=Ci(s,n=Ut(n)?n():n,this.createResolver(e,n,a)));for(const c of r)o[c]=l[c];return o}createResolver(e,r,n=[""],i){const{resolver:o}=hy(this._resolverCache,e,n);return je(r)?Ci(o,r,void 0,i):o}}function hy(t,e,r){let n=t.get(e);n||(n=new Map,t.set(e,n));const i=r.join();let o=n.get(i);return o||(o={resolver:qd(e,r),subPrefixes:r.filter(s=>!s.toLowerCase().includes("hover"))},n.set(i,o)),o}const WM=t=>je(t)&&Object.getOwnPropertyNames(t).some(e=>Ut(t[e])),UM=["top","bottom","left","right","chartArea"];function fy(t,e){return t==="top"||t==="bottom"||UM.indexOf(t)===-1&&e==="x"}function dy(t,e){return function(r,n){return r[t]===n[t]?r[e]-n[e]:r[t]-n[t]}}function py(t){const e=t.chart,r=e.options.animation;e.notifyPlugins("afterRender"),Ze(r&&r.onComplete,[t],e)}function VM(t){const e=t.chart,r=e.options.animation;Ze(r&&r.onProgress,[t],e)}function $_(t){return Yd()&&typeof t=="string"?t=document.getElementById(t):t&&t.length&&(t=t[0]),t&&t.canvas&&(t=t.canvas),t}const $a={},gy=t=>{const e=$_(t);return Object.values($a).filter(r=>r.canvas===e).pop()};function $M(t,e,r){const n=Object.keys(t);for(const i of n){const o=+i;if(o>=e){const s=t[i];delete t[i],(r>0||o>e)&&(t[o+r]=s)}}}var sn;let $i=(sn=class{static register(...t){_r.add(...t),my()}static unregister(...t){_r.remove(...t),my()}constructor(t,e){const r=this.config=new FM(e),n=$_(t),i=gy(n);if(i)throw new Error("Canvas is already in use. Chart with ID '"+i.id+"' must be destroyed before the canvas with ID '"+i.canvas.id+"' can be reused.");const o=r.createResolver(r.chartOptionScopes(),this.getContext());this.platform=new(r.platform||function(u){return!Yd()||typeof OffscreenCanvas<"u"&&u instanceof OffscreenCanvas?xM:TM}(n)),this.platform.updateConfig(r);const s=this.platform.acquireContext(n,o.aspectRatio),a=s&&s.canvas,l=a&&a.height,c=a&&a.width;this.id=jO(),this.ctx=s,this.canvas=a,this.width=c,this.height=l,this._options=o,this._aspectRatio=this.aspectRatio,this._layers=[],this._metasets=[],this._stacks=void 0,this.boxes=[],this.currentDevicePixelRatio=void 0,this.chartArea=void 0,this._active=[],this._lastEvent=void 0,this._listeners={},this._responsiveListeners=void 0,this._sortedMetasets=[],this.scales={},this._plugins=new DM,this.$proxies={},this._hiddenIndices={},this.attached=!1,this._animationsDisabled=void 0,this.$context=void 0,this._doResize=function(u,h){let f;return function(...d){return h?(clearTimeout(f),f=setTimeout(u,h,d)):u.apply(this,d),h}}(u=>this.update(u),o.resizeDelay||0),this._dataChanges=[],$a[this.id]=this,s&&a&&(Lr.listen(this,"complete",py),Lr.listen(this,"progress",VM),this._initialize(),this.attached&&this.update())}get aspectRatio(){const{options:{aspectRatio:t,maintainAspectRatio:e},width:r,height:n,_aspectRatio:i}=this;return it(t)?e&&i?i:n?r/n:null:t}get data(){return this.config.data}set data(t){this.config.data=t}get options(){return this._options}set options(t){this.config.options=t}get registry(){return _r}_initialize(){return this.notifyPlugins("beforeInit"),this.options.responsive?this.resize():Bm(this,this.options.devicePixelRatio),this.bindEvents(),this.notifyPlugins("afterInit"),this}clear(){return Rm(this.canvas,this.ctx),this}stop(){return Lr.stop(this),this}resize(t,e){Lr.running(this)?this._resizeBeforeDraw={width:t,height:e}:this._resize(t,e)}_resize(t,e){const r=this.options,n=this.canvas,i=r.maintainAspectRatio&&this.aspectRatio,o=this.platform.getMaximumSize(n,t,e,i),s=r.devicePixelRatio||this.platform.getDevicePixelRatio(),a=this.width?"resize":"attach";this.width=o.width,this.height=o.height,this._aspectRatio=this.aspectRatio,Bm(this,s,!0)&&(this.notifyPlugins("resize",{size:o}),Ze(r.onResize,[this,o],this),this.attached&&this._doResize(a)&&this.render())}ensureScalesHaveIDs(){Et(this.options.scales||{},(t,e)=>{t.id=e})}buildOrUpdateScales(){const t=this.options,e=t.scales,r=this.scales,n=Object.keys(r).reduce((o,s)=>(o[s]=!1,o),{});let i=[];e&&(i=i.concat(Object.keys(e).map(o=>{const s=e[o],a=Uf(o,s),l=a==="r",c=a==="x";return{options:s,dposition:l?"chartArea":c?"bottom":"left",dtype:l?"radialLinear":c?"category":"linear"}}))),Et(i,o=>{const s=o.options,a=s.id,l=Uf(a,s),c=Ne(s.type,o.dtype);s.position!==void 0&&fy(s.position,l)===fy(o.dposition)||(s.position=o.dposition),n[a]=!0;let u=null;a in r&&r[a].type===c?u=r[a]:(u=new(_r.getScale(c))({id:a,type:c,ctx:this.ctx,chart:this}),r[u.id]=u),u.init(s,t)}),Et(n,(o,s)=>{o||delete r[s]}),Et(r,o=>{On.configure(this,o,o.options),On.addBox(this,o)})}_updateMetasets(){const t=this._metasets,e=this.data.datasets.length,r=t.length;if(t.sort((n,i)=>n.index-i.index),r>e){for(let n=e;ne.length&&delete this._stacks,t.forEach((r,n)=>{e.filter(i=>i===r._dataset).length===0&&this._destroyDatasetMeta(n)})}buildOrUpdateControllers(){const t=[],e=this.data.datasets;let r,n;for(this._removeUnreferencedMetasets(),r=0,n=e.length;r{this.getDatasetMeta(e).controller.reset()},this)}reset(){this._resetElements(),this.notifyPlugins("reset")}update(t){const e=this.config;e.update();const r=this._options=e.createResolver(e.chartOptionScopes(),this.getContext()),n=this._animationsDisabled=!r.animation;if(this._updateScales(),this._checkEventBindings(),this._updateHiddenIndices(),this._plugins.invalidate(),this.notifyPlugins("beforeUpdate",{mode:t,cancelable:!0})===!1)return;const i=this.buildOrUpdateControllers();this.notifyPlugins("beforeElementsUpdate");let o=0;for(let l=0,c=this.data.datasets.length;l{l.reset()}),this._updateDatasets(t),this.notifyPlugins("afterUpdate",{mode:t}),this._layers.sort(dy("z","_idx"));const{_active:s,_lastEvent:a}=this;a?this._eventHandler(a,!0):s.length&&this._updateHoverStyles(s,s,!0),this.render()}_updateScales(){Et(this.scales,t=>{On.removeBox(this,t)}),this.ensureScalesHaveIDs(),this.buildOrUpdateScales()}_checkEventBindings(){const t=this.options,e=new Set(Object.keys(this._listeners)),r=new Set(t.events);km(e,r)&&!!this._responsiveListeners===t.responsive||(this.unbindEvents(),this.bindEvents())}_updateHiddenIndices(){const{_hiddenIndices:t}=this,e=this._getUniformDataChanges()||[];for(const{method:r,start:n,count:i}of e)$M(t,n,r==="_removeElements"?-i:i)}_getUniformDataChanges(){const t=this._dataChanges;if(!t||!t.length)return;this._dataChanges=[];const e=this.data.datasets.length,r=i=>new Set(t.filter(o=>o[0]===i).map((o,s)=>s+","+o.splice(1).join(","))),n=r(0);for(let i=1;ii.split(",")).map(i=>({method:i[1],start:+i[2],count:+i[3]}))}_updateLayout(t){if(this.notifyPlugins("beforeLayout",{cancelable:!0})===!1)return;On.update(this,this.width,this.height,t);const e=this.chartArea,r=e.width<=0||e.height<=0;this._layers=[],Et(this.boxes,n=>{r&&n.position==="chartArea"||(n.configure&&n.configure(),this._layers.push(...n._layers()))},this),this._layers.forEach((n,i)=>{n._idx=i}),this.notifyPlugins("afterLayout")}_updateDatasets(t){if(this.notifyPlugins("beforeDatasetsUpdate",{mode:t,cancelable:!0})!==!1){for(let e=0,r=this.data.datasets.length;e=0;--e)this._drawDataset(t[e]);this.notifyPlugins("afterDatasetsDraw")}_drawDataset(t){const e=this.ctx,r={meta:t,index:t.index,cancelable:!0},n=D_(this,t);this.notifyPlugins("beforeDatasetDraw",r)!==!1&&(n&&Rs(e,n),t.controller.draw(),n&&Ds(e),r.cancelable=!1,this.notifyPlugins("afterDatasetDraw",r))}isPointInArea(t){return Ss(t,this.chartArea,this._minPadding)}getElementsAtEventForMode(t,e,r,n){const i=bM.modes[e];return typeof i=="function"?i(this,t,r,n):[]}getDatasetMeta(t){const e=this.data.datasets[t],r=this._metasets;let n=r.filter(i=>i&&i._dataset===e).pop();return n||(n={type:null,data:[],dataset:null,controller:null,hidden:null,xAxisID:null,yAxisID:null,order:e&&e.order||0,index:t,_dataset:e,_parsed:[],_sorted:!1},r.push(n)),n}getContext(){return this.$context||(this.$context=to(null,{chart:this,type:"chart"}))}getVisibleDatasetCount(){return this.getSortedVisibleDatasetMetas().length}isDatasetVisible(t){const e=this.data.datasets[t];if(!e)return!1;const r=this.getDatasetMeta(t);return typeof r.hidden=="boolean"?!r.hidden:!e.hidden}setDatasetVisibility(t,e){this.getDatasetMeta(t).hidden=!e}toggleDataVisibility(t){this._hiddenIndices[t]=!this._hiddenIndices[t]}getDataVisibility(t){return!this._hiddenIndices[t]}_updateVisibility(t,e,r){const n=r?"show":"hide",i=this.getDatasetMeta(t),o=i.controller._resolveAnimations(void 0,n);Gt(e)?(i.data[e].hidden=!r,this.update()):(this.setDatasetVisibility(t,r),o.update(i,{visible:r}),this.update(s=>s.datasetIndex===t?n:void 0))}hide(t,e){this._updateVisibility(t,e,!1)}show(t,e){this._updateVisibility(t,e,!0)}_destroyDatasetMeta(t){const e=this._metasets[t];e&&e.controller&&e.controller._destroy(),delete this._metasets[t]}_stop(){let t,e;for(this.stop(),Lr.remove(this),t=0,e=this.data.datasets.length;t{e.addEventListener(this,i,o),t[i]=o},n=(i,o,s)=>{i.offsetX=o,i.offsetY=s,this._eventHandler(i)};Et(this.options.events,i=>r(i,n))}bindResponsiveEvents(){this._responsiveListeners||(this._responsiveListeners={});const t=this._responsiveListeners,e=this.platform,r=(a,l)=>{e.addEventListener(this,a,l),t[a]=l},n=(a,l)=>{t[a]&&(e.removeEventListener(this,a,l),delete t[a])},i=(a,l)=>{this.canvas&&this.resize(a,l)};let o;const s=()=>{n("attach",s),this.attached=!0,this.resize(),r("resize",i),r("detach",o)};o=()=>{this.attached=!1,n("resize",i),this._stop(),this._resize(0,0),r("attach",s)},e.isAttached(this.canvas)?s():o()}unbindEvents(){Et(this._listeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._listeners={},Et(this._responsiveListeners,(t,e)=>{this.platform.removeEventListener(this,e,t)}),this._responsiveListeners=void 0}updateHoverStyle(t,e,r){const n=r?"set":"remove";let i,o,s,a;for(e==="dataset"&&(i=this.getDatasetMeta(t[0].datasetIndex),i.controller["_"+n+"DatasetHoverStyle"]()),s=0,a=t.length;s{const o=this.getDatasetMeta(n);if(!o)throw new Error("No dataset found at index "+n);return{datasetIndex:n,element:o.data[i],index:i}});!xm(r,e)&&(this._active=r,this._lastEvent=null,this._updateHoverStyles(r,e))}notifyPlugins(t,e,r){return this._plugins.notify(this,t,e,r)}isPluginEnabled(t){return this._plugins._cache.filter(e=>e.plugin.id===t).length===1}_updateHoverStyles(t,e,r){const n=this.options.hover,i=(a,l)=>a.filter(c=>!l.some(u=>c.datasetIndex===u.datasetIndex&&c.index===u.index)),o=i(e,t),s=r?t:i(t,e);o.length&&this.updateHoverStyle(o,n.mode,!1),s.length&&n.mode&&this.updateHoverStyle(s,n.mode,!0)}_eventHandler(t,e){const r={event:t,replay:e,cancelable:!0,inChartArea:this.isPointInArea(t)},n=o=>(o.options.events||this.options.events).includes(t.native.type);if(this.notifyPlugins("beforeEvent",r,n)===!1)return;const i=this._handleEvent(t,e,r.inChartArea);return r.cancelable=!1,this.notifyPlugins("afterEvent",r,n),(i||r.changed)&&this.render(),this}_handleEvent(t,e,r){const{_active:n=[],options:i}=this,o=e,s=this._getActiveElements(t,n,r,o),a=function(u){return u.type==="mouseup"||u.type==="click"||u.type==="contextmenu"}(t),l=function(u,h,f,d){return f&&u.type!=="mouseout"?d?h:u:null}(t,this._lastEvent,r,a);r&&(this._lastEvent=null,Ze(i.onHover,[t,s,this],this),a&&Ze(i.onClick,[t,s,this],this));const c=!xm(s,n);return(c||e)&&(this._active=s,this._updateHoverStyles(s,n,e)),this._lastEvent=l,c}_getActiveElements(t,e,r,n){if(t.type==="mouseout")return[];if(!r)return e;const i=this.options.hover;return this.getElementsAtEventForMode(t,i.mode,i,n)}},_e(sn,"defaults",ht),_e(sn,"instances",$a),_e(sn,"overrides",ci),_e(sn,"registry",_r),_e(sn,"version","4.5.0"),_e(sn,"getChart",gy),sn);function my(){return Et($i.instances,t=>t._plugins.invalidate())}function yy(t,e,r=e){t.lineCap=Ne(r.borderCapStyle,e.borderCapStyle),t.setLineDash(Ne(r.borderDash,e.borderDash)),t.lineDashOffset=Ne(r.borderDashOffset,e.borderDashOffset),t.lineJoin=Ne(r.borderJoinStyle,e.borderJoinStyle),t.lineWidth=Ne(r.borderWidth,e.borderWidth),t.strokeStyle=Ne(r.borderColor,e.borderColor)}function zM(t,e,r){t.lineTo(r.x,r.y)}function z_(t,e,r={}){const n=t.length,{start:i=0,end:o=n-1}=r,{start:s,end:a}=e,l=Math.max(i,s),c=Math.min(o,a),u=ia&&o>a;return{count:n,start:l,loop:e.loop,ilen:c(s+(c?a-k:k))%o,S=()=>{d!==y&&(t.lineTo(p,y),t.lineTo(p,d),t.lineTo(p,g))};for(l&&(h=i[v(0)],t.moveTo(h.x,h.y)),u=0;u<=a;++u){if(h=i[v(u)],h.skip)continue;const k=h.x,M=h.y,A=0|k;A===f?(My&&(y=M),p=(b*p+k)/++b):(S(),t.lineTo(k,M),f=A,b=0,d=y=M),g=M}S()}function Vf(t){const e=t.options,r=e.borderDash&&e.borderDash.length;return t._decimated||t._loop||e.tension||e.cubicInterpolationMode==="monotone"||e.stepped||r?HM:qM}const YM=typeof Path2D=="function";function KM(t,e,r,n){YM&&!e.options.segment?function(i,o,s,a){let l=o._path;l||(l=o._path=new Path2D,o.path(l,s,a)&&l.closePath()),yy(i,o.options),i.stroke(l)}(t,e,r,n):function(i,o,s,a){const{segments:l,options:c}=o,u=Vf(o);for(const h of l)yy(i,c,h.style),i.beginPath(),u(i,o,h,{start:s,end:s+a-1})&&i.closePath(),i.stroke()}(t,e,r,n)}class Jn extends cr{constructor(e){super(),this.animated=!0,this.options=void 0,this._chart=void 0,this._loop=void 0,this._fullLoop=void 0,this._path=void 0,this._points=void 0,this._segments=void 0,this._decimated=!1,this._pointsUpdated=!1,this._datasetIndex=void 0,e&&Object.assign(this,e)}updateControlPoints(e,r){const n=this.options;if((n.tension||n.cubicInterpolationMode==="monotone")&&!n.stepped&&!this._pointsUpdated){const i=n.spanGaps?this._loop:this._fullLoop;iM(this._points,n,e,i,r),this._pointsUpdated=!0}}set points(e){this._points=e,delete this._segments,delete this._path,this._pointsUpdated=!1}get points(){return this._points}get segments(){return this._segments||(this._segments=function(e,r){const n=e.points,i=e.options.spanGaps,o=n.length;if(!o)return[];const s=!!e._loop,{start:a,end:l}=function(c,u,h,f){let d=0,y=u-1;if(h&&!f)for(;dd&&c[y%u].skip;)y--;return y%=u,{start:d,end:y}}(n,o,s,i);return uM(e,i===!0?[{start:a,end:l,loop:s}]:function(c,u,h,f){const d=c.length,y=[];let g,p=u,b=c[u];for(g=u+1;g<=h;++g){const v=c[g%d];v.skip||v.stop?b.skip||(f=!1,y.push({start:u%d,end:(g-1)%d,loop:f}),u=p=v.stop?g:null):(p=g,b.skip&&(u=g)),b=v}return p!==null&&y.push({start:u%d,end:p%d,loop:f}),y}(n,a,le!=="borderDash"&&e!=="fill"});function by(t,e,r,n){const i=t.options,{[r]:o}=t.getProps([r],n);return Math.abs(e-o)t;e--){const n=r[e];if(!isNaN(n.x)&&!isNaN(n.y))break}return e}function vy(t,e,r,n){return t&&e?n(t[r],e[r]):t?t[r]:e?e[r]:0}function wy(t,e){let r=[],n=!1;return st(t)?(n=!0,r=t):r=function(i,o){const{x:s=null,y:a=null}=i||{},l=o.points,c=[];return o.segments.forEach(({start:u,end:h})=>{h=Gl(u,h,l);const f=l[u],d=l[h];a!==null?(c.push({x:f.x,y:a}),c.push({x:d.x,y:a})):s!==null&&(c.push({x:s,y:f.y}),c.push({x:s,y:d.y}))}),c}(t,e),r.length?new Jn({points:r,options:{tension:0},_loop:n,_fullLoop:n}):null}function _y(t){return t&&t.fill!==!1}function GM(t,e,r){let n=t[e].fill;const i=[e];let o;if(!r)return n;for(;n!==!1&&i.indexOf(n)===-1;){if(!yt(n))return n;if(o=t[n],!o)return!1;if(o.visible)return n;i.push(n),n=o.fill}return!1}function QM(t,e,r){const n=function(o){const s=o.options,a=s.fill;let l=Ne(a&&a.target,a);return l===void 0&&(l=!!s.backgroundColor),l===!1||l===null?!1:l===!0?"origin":l}(t);if(je(n))return!isNaN(n.value)&&n;let i=parseFloat(n);return yt(i)&&Math.floor(i)===i?function(o,s,a,l){return o!=="-"&&o!=="+"||(a=s+a),a===s||a<0||a>=l?!1:a}(n[0],e,i,r):["origin","start","end","stack","shape"].indexOf(n)>=0&&n}function ZM(t,e,r){const n=[];for(let i=0;i=0;--s){const a=i[s].$filler;a&&(a.line.updateControlPoints(o,a.axis),n&&a.fill&&qc(t.ctx,a,o))}},beforeDatasetsDraw(t,e,r){if(r.drawTime!=="beforeDatasetsDraw")return;const n=t.getSortedVisibleDatasetMetas();for(let i=n.length-1;i>=0;--i){const o=n[i].$filler;_y(o)&&qc(t.ctx,o,t.chartArea)}},beforeDatasetDraw(t,e,r){const n=e.meta.$filler;_y(n)&&r.drawTime==="beforeDatasetDraw"&&qc(t.ctx,n,t.chartArea)},defaults:{propagate:!0,drawTime:"beforeDatasetDraw"}};const Oy=(t,e)=>{let{boxHeight:r=e,boxWidth:n=e}=t;return t.usePointStyle&&(r=Math.min(r,e),n=t.pointStyleWidth||Math.min(n,e)),{boxWidth:n,boxHeight:r,itemHeight:Math.max(e,r)}};class My extends cr{constructor(e){super(),this._added=!1,this.legendHitBoxes=[],this._hoveredItem=null,this.doughnutMode=!1,this.chart=e.chart,this.options=e.options,this.ctx=e.ctx,this.legendItems=void 0,this.columnSizes=void 0,this.lineWidths=void 0,this.maxHeight=void 0,this.maxWidth=void 0,this.top=void 0,this.bottom=void 0,this.left=void 0,this.right=void 0,this.height=void 0,this.width=void 0,this._margins=void 0,this.position=void 0,this.weight=void 0,this.fullSize=void 0}update(e,r,n){this.maxWidth=e,this.maxHeight=r,this._margins=n,this.setDimensions(),this.buildLabels(),this.fit()}setDimensions(){this.isHorizontal()?(this.width=this.maxWidth,this.left=this._margins.left,this.right=this.width):(this.height=this.maxHeight,this.top=this._margins.top,this.bottom=this.height)}buildLabels(){const e=this.options.labels||{};let r=Ze(e.generateLabels,[this.chart],this)||[];e.filter&&(r=r.filter(n=>e.filter(n,this.chart.data))),e.sort&&(r=r.sort((n,i)=>e.sort(n,i,this.chart.data))),this.options.reverse&&r.reverse(),this.legendItems=r}fit(){const{options:e,ctx:r}=this;if(!e.display)return void(this.width=this.height=0);const n=e.labels,i=Mr(n.font),o=i.size,s=this._computeTitleHeight(),{boxWidth:a,itemHeight:l}=Oy(n,o);let c,u;r.font=i.string,this.isHorizontal()?(c=this.maxWidth,u=this._fitRows(s,o,a,l)+10):(u=this.maxHeight,c=this._fitCols(s,i,a,l)+10),this.width=Math.min(c,e.maxWidth||this.maxWidth),this.height=Math.min(u,e.maxHeight||this.maxHeight)}_fitRows(e,r,n,i){const{ctx:o,maxWidth:s,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.lineWidths=[0],u=i+a;let h=e;o.textAlign="left",o.textBaseline="middle";let f=-1,d=-u;return this.legendItems.forEach((y,g)=>{const p=n+r/2+o.measureText(y.text).width;(g===0||c[c.length-1]+p+2*a>s)&&(h+=u,c[c.length-(g>0?0:1)]=0,d+=u,f++),l[g]={left:0,top:d,row:f,width:p,height:i},c[c.length-1]+=p+a}),h}_fitCols(e,r,n,i){const{ctx:o,maxHeight:s,options:{labels:{padding:a}}}=this,l=this.legendHitBoxes=[],c=this.columnSizes=[],u=s-e;let h=a,f=0,d=0,y=0,g=0;return this.legendItems.forEach((p,b)=>{const{itemWidth:v,itemHeight:S}=function(k,M,A,I,T){const _=function(O,N,G,U){let z=O.text;return z&&typeof z!="string"&&(z=z.reduce((q,B)=>q.length>B.length?q:B)),N+G.size/2+U.measureText(z).width}(I,k,M,A),x=function(O,N,G){let U=O;return typeof N.text!="string"&&(U=Ay(N,G)),U}(T,I,M.lineHeight);return{itemWidth:_,itemHeight:x}}(n,r,o,p,i);b>0&&d+S+2*a>u&&(h+=f+a,c.push({width:f,height:d}),y+=f+a,g++,f=d=0),l[b]={left:y,top:d,col:g,width:v,height:S},f=Math.max(f,v),d+=S+a}),h+=f,c.push({width:f,height:d}),h}adjustHitBoxes(){if(!this.options.display)return;const e=this._computeTitleHeight(),{legendHitBoxes:r,options:{align:n,labels:{padding:i},rtl:o}}=this,s=Lc(o,this.left,this.width);if(this.isHorizontal()){let a=0,l=qt(n,this.left+i,this.right-this.lineWidths[a]);for(const c of r)a!==c.row&&(a=c.row,l=qt(n,this.left+i,this.right-this.lineWidths[a])),c.top+=this.top+e+i,c.left=s.leftForLtr(s.x(l),c.width),l+=c.width+i}else{let a=0,l=qt(n,this.top+e+i,this.bottom-this.columnSizes[a].height);for(const c of r)c.col!==a&&(a=c.col,l=qt(n,this.top+e+i,this.bottom-this.columnSizes[a].height)),c.top=l,c.left+=this.left+i,c.left=s.leftForLtr(s.x(c.left),c.width),l+=c.height+i}}isHorizontal(){return this.options.position==="top"||this.options.position==="bottom"}draw(){if(this.options.display){const e=this.ctx;Rs(e,this),this._draw(),Ds(e)}}_draw(){const{options:e,columnSizes:r,lineWidths:n,ctx:i}=this,{align:o,labels:s}=e,a=ht.color,l=Lc(e.rtl,this.left,this.width),c=Mr(s.font),{padding:u}=s,h=c.size,f=h/2;let d;this.drawTitle(),i.textAlign=l.textAlign("left"),i.textBaseline="middle",i.lineWidth=.5,i.font=c.string;const{boxWidth:y,boxHeight:g,itemHeight:p}=Oy(s,h),b=this.isHorizontal(),v=this._computeTitleHeight();d=b?{x:qt(o,this.left+u,this.right-n[0]),y:this.top+u+v,line:0}:{x:this.left+u,y:qt(o,this.top+v+u,this.bottom-r[0].height),line:0},function(k,M){let A,I;M!=="ltr"&&M!=="rtl"||(A=k.canvas.style,I=[A.getPropertyValue("direction"),A.getPropertyPriority("direction")],A.setProperty("direction",M,"important"),k.prevTextDirection=I)}(this.ctx,e.textDirection);const S=p+u;this.legendItems.forEach((k,M)=>{i.strokeStyle=k.fontColor,i.fillStyle=k.fontColor;const A=i.measureText(k.text).width,I=l.textAlign(k.textAlign||(k.textAlign=s.textAlign)),T=y+f+A;let _=d.x,x=d.y;if(l.setWidth(this.width),b?M>0&&_+T+u>this.right&&(x=d.y+=S,d.line++,_=d.x=qt(o,this.left+u,this.right-n[d.line])):M>0&&x+S>this.bottom&&(_=d.x=_+r[d.line].width+u,d.line++,x=d.y=qt(o,this.top+v+u,this.bottom-r[d.line].height)),function(O,N,G){if(isNaN(y)||y<=0||isNaN(g)||g<0)return;i.save();const U=Ne(G.lineWidth,1);if(i.fillStyle=Ne(G.fillStyle,a),i.lineCap=Ne(G.lineCap,"butt"),i.lineDashOffset=Ne(G.lineDashOffset,0),i.lineJoin=Ne(G.lineJoin,"miter"),i.lineWidth=U,i.strokeStyle=Ne(G.strokeStyle,a),i.setLineDash(Ne(G.lineDash,[])),s.usePointStyle){const z={radius:g*Math.SQRT2/2,pointStyle:G.pointStyle,rotation:G.rotation,borderWidth:U},q=l.xPlus(O,y/2);E_(i,z,q,N+f,s.pointStyleWidth&&y)}else{const z=N+Math.max((h-g)/2,0),q=l.leftForLtr(O,y),B=A_(G.borderRadius);i.beginPath(),Object.values(B).some(ne=>ne!==0)?O_(i,{x:q,y:z,w:y,h:g,radius:B}):i.rect(q,z,y,g),i.fill(),U!==0&&i.stroke()}i.restore()}(l.x(_),x,k),_=((O,N,G,U)=>O===(U?"left":"right")?G:O==="center"?(N+G)/2:N)(I,_+y+f,b?_+T:this.right,e.rtl),function(O,N,G){hl(i,G.text,O,N+p/2,c,{strikethrough:G.hidden,textAlign:l.textAlign(G.textAlign)})}(l.x(_),x,k),b)d.x+=T+u;else if(typeof k.text!="string"){const O=c.lineHeight;d.y+=Ay(k,O)+u}else d.y+=S}),function(k,M){M!==void 0&&(delete k.prevTextDirection,k.canvas.style.setProperty("direction",M[0],M[1]))}(this.ctx,e.textDirection)}drawTitle(){const e=this.options,r=e.title,n=Mr(r.font),i=ar(r.padding);if(!r.display)return;const o=Lc(e.rtl,this.left,this.width),s=this.ctx,a=r.position,l=n.size/2,c=i.top+l;let u,h=this.left,f=this.width;if(this.isHorizontal())f=Math.max(...this.lineWidths),u=this.top+c,h=qt(e.align,h,this.right-f);else{const y=this.columnSizes.reduce((g,p)=>Math.max(g,p.height),0);u=c+qt(e.align,this.top,this.bottom-y-e.labels.padding-this._computeTitleHeight())}const d=qt(a,h,h+f);s.textAlign=o.textAlign(S_(a)),s.textBaseline="middle",s.strokeStyle=r.color,s.fillStyle=r.color,s.font=n.string,hl(s,r.text,d,u,n)}_computeTitleHeight(){const e=this.options.title,r=Mr(e.font),n=ar(e.padding);return e.display?r.lineHeight+n.height:0}_getLegendItemAt(e,r){let n,i,o;if(Pi(e,this.left,this.right)&&Pi(r,this.top,this.bottom)){for(o=this.legendHitBoxes,n=0;nt.chart.options.color,boxWidth:40,padding:10,generateLabels(t){const e=t.data.datasets,{labels:{usePointStyle:r,pointStyle:n,textAlign:i,color:o,useBorderRadius:s,borderRadius:a}}=t.legend.options;return t._getSortedDatasetMetas().map(l=>{const c=l.controller.getStyle(r?0:void 0),u=ar(c.borderWidth);return{text:e[l.index].label,fillStyle:c.backgroundColor,fontColor:o,hidden:!l.visible,lineCap:c.borderCapStyle,lineDash:c.borderDash,lineDashOffset:c.borderDashOffset,lineJoin:c.borderJoinStyle,lineWidth:(u.width+u.height)/4,strokeStyle:c.borderColor,pointStyle:n||c.pointStyle,rotation:c.rotation,textAlign:i||c.textAlign,borderRadius:s&&(a||c.borderRadius),datasetIndex:l.index}},this)}},title:{color:t=>t.chart.options.color,display:!1,position:"center",text:""}},descriptors:{_scriptable:t=>!t.startsWith("on"),labels:{_scriptable:t=>!["generateLabels","filter","sort"].includes(t)}}};function tA(t,e){const r=[],{bounds:n,step:i,min:o,max:s,precision:a,count:l,maxTicks:c,maxDigits:u,includeBounds:h}=t,f=i||1,d=c-1,{min:y,max:g}=e,p=!it(o),b=!it(s),v=!it(l),S=(g-y)/(u+1);let k,M,A,I,T=Em((g-y)/d/f)*f;if(T<1e-14&&!p&&!b)return[{value:y},{value:g}];I=Math.ceil(g/T)-Math.floor(y/T),I>d&&(T=Em(I*T/d/f)*f),it(a)||(k=Math.pow(10,a),T=Math.ceil(T*k)/k),n==="ticks"?(M=Math.floor(y/T)*T,A=Math.ceil(g/T)*T):(M=y,A=g),p&&b&&i&&function(O,N){const G=Math.round(O);return G-N<=O&&G+N>=O}((s-o)/i,T/1e3)?(I=Math.round(Math.min((s-o)/T,c)),T=(s-o)/I,M=o,A=s):v?(M=p?o:M,A=b?s:A,I=l-1,T=(A-M)/I):(I=(A-M)/T,I=Jo(I,Math.round(I),T/1e3)?Math.round(I):Math.ceil(I));const _=Math.max(Om(T),Om(M));k=Math.pow(10,it(a)?_:a),M=Math.round(M*k)/k,A=Math.round(A*k)/k;let x=0;for(p&&(h&&M!==o?(r.push({value:o}),Ms)break;r.push({value:O})}return b&&h&&A!==s?r.length&&Jo(r[r.length-1].value,s,Ty(s,S,t))?r[r.length-1].value=s:r.push({value:s}):b&&A!==s||r.push({value:A}),r}function Ty(t,e,{horizontal:r,minRotation:n}){const i=gt(n),o=(r?Math.sin(i):Math.cos(i))||.001,s=.75*e*(""+t).length;return Math.min(e/o,s)}class rA extends js{constructor(e){super(e),this.start=void 0,this.end=void 0,this._startValue=void 0,this._endValue=void 0,this._valueRange=0}parse(e,r){return it(e)||(typeof e=="number"||e instanceof Number)&&!isFinite(+e)?null:+e}handleTickRangeOptions(){const{beginAtZero:e}=this.options,{minDefined:r,maxDefined:n}=this.getUserBounds();let{min:i,max:o}=this;const s=l=>i=r?i:l,a=l=>o=n?o:l;if(e){const l=eo(i),c=eo(o);l<0&&c<0?a(0):l>0&&c>0&&s(0)}if(i===o){let l=o===0?1:Math.abs(.05*o);a(o+l),e||s(i-l)}this.min=i,this.max=o}getTickLimit(){const e=this.options.ticks;let r,{maxTicksLimit:n,stepSize:i}=e;return i?(r=Math.ceil(this.max/i)-Math.floor(this.min/i)+1,r>1e3&&(r=1e3)):(r=this.computeTickLimit(),n=n||11),n&&(r=Math.min(n,r)),r}computeTickLimit(){return Number.POSITIVE_INFINITY}buildTicks(){const e=this.options,r=e.ticks;let n=this.getTickLimit();n=Math.max(2,n);const i=tA({maxTicks:n,bounds:e.bounds,min:e.min,max:e.max,precision:r.precision,step:r.stepSize,count:r.count,maxDigits:this._maxDigits(),horizontal:this.isHorizontal(),minRotation:r.minRotation||0,includeBounds:r.includeBounds!==!1},this._range||this);return e.bounds==="ticks"&&function(o,s,a){let l,c,u;for(l=0,c=o.length;l=e?r[n]:r[i]]=!0}}else t[e]=!0}function jy(t,e,r){const n=[],i={},o=e.length;let s,a;for(s=0;s=0&&(c[p].major=!0);return c}(t,n,i,r):n}class Ny extends js{constructor(e){super(e),this._cache={data:[],labels:[],all:[]},this._unit="day",this._majorUnit=void 0,this._offsets={},this._normalized=!1,this._parseOpts=void 0}init(e,r={}){const n=e.time||(e.time={}),i=this._adapter=new N_._date(e.adapters.date);i.init(r),Xo(n.displayFormats,i.formats()),this._parseOpts={parser:n.parser,round:n.round,isoWeekday:n.isoWeekday},super.init(e),this._normalized=r.normalized}parse(e,r){return e===void 0?null:Cy(this,e)}beforeLayout(){super.beforeLayout(),this._cache={data:[],labels:[],all:[]}}determineDataLimits(){const e=this.options,r=this._adapter,n=e.time.unit||"day";let{min:i,max:o,minDefined:s,maxDefined:a}=this.getUserBounds();function l(c){s||isNaN(c.min)||(i=Math.min(i,c.min)),a||isNaN(c.max)||(o=Math.max(o,c.max))}s&&a||(l(this._getLabelBounds()),e.bounds==="ticks"&&e.ticks.source==="labels"||l(this.getMinMax(!1))),i=yt(i)&&!isNaN(i)?i:+r.startOf(Date.now(),n),o=yt(o)&&!isNaN(o)?o:+r.endOf(Date.now(),n)+1,this.min=Math.min(i,o-1),this.max=Math.max(i+1,o)}_getLabelBounds(){const e=this.getLabelTimestamps();let r=Number.POSITIVE_INFINITY,n=Number.NEGATIVE_INFINITY;return e.length&&(r=e[0],n=e[e.length-1]),{min:r,max:n}}buildTicks(){const e=this.options,r=e.time,n=e.ticks,i=n.source==="labels"?this.getLabelTimestamps():this._generate();e.bounds==="ticks"&&i.length&&(this.min=this._userMin||i[0],this.max=this._userMax||i[i.length-1]);const o=this.min,s=function(a,l,c){let u=0,h=a.length;for(;uu&&a[h-1]>c;)h--;return u>0||h=Bt.indexOf(c);f--){const d=Bt[f];if(pl[d].common&&a._adapter.diff(h,u,d)>=l-1)return d}return Bt[c?Bt.indexOf(c):0]}(this,s.length,r.minUnit,this.min,this.max)),this._majorUnit=n.major.enabled&&this._unit!=="year"?function(a){for(let l=Bt.indexOf(a)+1,c=Bt.length;l+e.value))}initOffsets(e=[]){let r,n,i=0,o=0;this.options.offset&&e.length&&(r=this.getDecimalForValue(e[0]),i=e.length===1?1-r:(this.getDecimalForValue(e[1])-r)/2,n=this.getDecimalForValue(e[e.length-1]),o=e.length===1?n:(n-this.getDecimalForValue(e[e.length-2]))/2);const s=e.length<3?.5:.25;i=Ur(i,0,s),o=Ur(o,0,s),this._offsets={start:i,end:o,factor:1/(i+1+o)}}_generate(){const e=this._adapter,r=this.min,n=this.max,i=this.options,o=i.time,s=o.unit||Ry(o.minUnit,r,n,this._getLabelCapacity(r)),a=Ne(i.ticks.stepSize,1),l=s==="week"&&o.isoWeekday,c=li(l)||l===!0,u={};let h,f,d=r;if(c&&(d=+e.startOf(d,"isoWeek",l)),d=+e.startOf(d,c?"day":s),e.diff(n,r,s)>1e5*a)throw new Error(r+" and "+n+" are too far apart with stepSize of "+a+" "+s);const y=i.ticks.source==="data"&&this.getDataTimestamps();for(h=d,f=0;h+g)}getLabelForValue(e){const r=this._adapter,n=this.options.time;return n.tooltipFormat?r.format(e,n.tooltipFormat):r.format(e,n.displayFormats.datetime)}format(e,r){const n=this.options.time.displayFormats,i=this._unit,o=r||n[i];return this._adapter.format(e,o)}_tickFormatFunction(e,r,n,i){const o=this.options,s=o.ticks.callback;if(s)return Ze(s,[e,r,n],this);const a=o.time.displayFormats,l=this._unit,c=this._majorUnit,u=l&&a[l],h=c&&a[c],f=n[r],d=c&&h&&f&&f.major;return this._adapter.format(e,i||(d?h:u))}generateTickLabels(e){let r,n,i;for(r=0,n=e.length;r0?a:1}getDataTimestamps(){let e,r,n=this._cache.data||[];if(n.length)return n;const i=this.getMatchingVisibleMetas();if(this._normalized&&i.length)return this._cache.data=i[0].controller.getAllParsedValues(this);for(e=0,r=i.length;e({})},plugins:{type:Array,default:()=>[]},datasetIdKey:{type:String,default:"label"},updateMode:{type:String,default:void 0}},nA={ariaLabel:{type:String},ariaDescribedby:{type:String}},iA={type:{type:String,required:!0},destroyDelay:{type:Number,default:0},...H_,...nA},oA=ev[0]==="2"?(t,e)=>Object.assign(t,{attrs:e}):(t,e)=>Object.assign(t,e);function Si(t){return Os(t)?Le(t):t}function Ly(t,e){t.labels=e}function By(t,e,r){const n=[];t.datasets=e.map(i=>{const o=t.datasets.find(s=>s[r]===i[r]);return o&&i.data&&!n.includes(o)?(n.push(o),Object.assign(o,i),o):{...i}})}const sA=Pl({props:iA,setup(t,e){let{expose:r,slots:n}=e;const i=Al(null),o=pd(null);return r({chart:o}),Dl(()=>{if(!i.value)return;const{type:s,data:a,options:l,plugins:c,datasetIdKey:u}=t,h=function(d,y){const g={labels:[],datasets:[]};return Ly(g,d.labels),By(g,d.datasets,y),g}(a,u),f=function(d){return Os(arguments.length>1&&arguments[1]!==void 0?arguments[1]:d)?new Proxy(d,{}):d}(h,a);o.value=new $i(i.value,{type:s,data:f,options:{...l},plugins:c})}),jl(()=>{const s=Le(o.value);s&&(t.destroyDelay>0?setTimeout(()=>{s.destroy(),o.value=null},t.destroyDelay):(s.destroy(),o.value=null))}),ni([()=>t.options,()=>t.data],(s,a)=>{let[l,c]=s,[u,h]=a;const f=Le(o.value);if(!f)return;let d=!1;if(l){const y=Si(l),g=Si(u);y&&y!==g&&(function(p,b){const v=p.options;v&&b&&Object.assign(v,b)}(f,y),d=!0)}if(c){const y=Si(c.labels),g=Si(h.labels),p=Si(c.datasets),b=Si(h.datasets);y!==g&&(Ly(f.config.data,y),d=!0),p&&p!==b&&(By(f.config.data,p,t.datasetIdKey),d=!0)}d&&Il(()=>{(y=>{y.update(t.updateMode)})(f)})},{deep:!0}),()=>Yi("canvas",{role:"img",ariaLabel:t.ariaLabel,ariaDescribedby:t.ariaDescribedby,ref:i},[Yi("p",{},[n.default?n.default():""])])}});function aA(t,e){return $i.register(e),Pl({props:H_,setup(r,n){let{expose:i}=n;const o=pd(null),s=a=>{o.value=a==null?void 0:a.chart};return i({chart:o}),()=>Yi(sA,oA({ref:s},{type:t,...r}))}})}const t2=aA("line",Va);class gi extends Error{}class lA extends gi{constructor(e){super(`Invalid DateTime: ${e.toMessage()}`)}}class cA extends gi{constructor(e){super(`Invalid Interval: ${e.toMessage()}`)}}class uA extends gi{constructor(e){super(`Invalid Duration: ${e.toMessage()}`)}}class Ri extends gi{}class q_ extends gi{constructor(e){super(`Invalid unit ${e}`)}}class Mt extends gi{}class on extends gi{constructor(){super("Zone is an abstract class")}}const Ee="numeric",mr="short",Kt="long",gl={year:Ee,month:Ee,day:Ee},Y_={year:Ee,month:mr,day:Ee},hA={year:Ee,month:mr,day:Ee,weekday:mr},K_={year:Ee,month:Kt,day:Ee},G_={year:Ee,month:Kt,day:Ee,weekday:Kt},Q_={hour:Ee,minute:Ee},Z_={hour:Ee,minute:Ee,second:Ee},X_={hour:Ee,minute:Ee,second:Ee,timeZoneName:mr},J_={hour:Ee,minute:Ee,second:Ee,timeZoneName:Kt},e1={hour:Ee,minute:Ee,hourCycle:"h23"},t1={hour:Ee,minute:Ee,second:Ee,hourCycle:"h23"},r1={hour:Ee,minute:Ee,second:Ee,hourCycle:"h23",timeZoneName:mr},n1={hour:Ee,minute:Ee,second:Ee,hourCycle:"h23",timeZoneName:Kt},i1={year:Ee,month:Ee,day:Ee,hour:Ee,minute:Ee},o1={year:Ee,month:Ee,day:Ee,hour:Ee,minute:Ee,second:Ee},s1={year:Ee,month:mr,day:Ee,hour:Ee,minute:Ee},a1={year:Ee,month:mr,day:Ee,hour:Ee,minute:Ee,second:Ee},fA={year:Ee,month:mr,day:Ee,weekday:mr,hour:Ee,minute:Ee},l1={year:Ee,month:Kt,day:Ee,hour:Ee,minute:Ee,timeZoneName:mr},c1={year:Ee,month:Kt,day:Ee,hour:Ee,minute:Ee,second:Ee,timeZoneName:mr},u1={year:Ee,month:Kt,day:Ee,weekday:Kt,hour:Ee,minute:Ee,timeZoneName:Kt},h1={year:Ee,month:Kt,day:Ee,weekday:Kt,hour:Ee,minute:Ee,second:Ee,timeZoneName:Kt};class Ns{get type(){throw new on}get name(){throw new on}get ianaName(){return this.name}get isUniversal(){throw new on}offsetName(e,r){throw new on}formatOffset(e,r){throw new on}offset(e){throw new on}equals(e){throw new on}get isValid(){throw new on}}let Kc=null;class Ql extends Ns{static get instance(){return Kc===null&&(Kc=new Ql),Kc}get type(){return"system"}get name(){return new Intl.DateTimeFormat().resolvedOptions().timeZone}get isUniversal(){return!1}offsetName(e,{format:r,locale:n}){return _1(e,r,n)}formatOffset(e,r){return ns(this.offset(e),r)}offset(e){return-new Date(e).getTimezoneOffset()}equals(e){return e.type==="system"}get isValid(){return!0}}const Gc=new Map,dA={year:0,month:1,day:2,era:3,hour:4,minute:5,second:6},Qc=new Map;class Yr extends Ns{static create(e){let r=Qc.get(e);return r===void 0&&Qc.set(e,r=new Yr(e)),r}static resetCache(){Qc.clear(),Gc.clear()}static isValidSpecifier(e){return this.isValidZone(e)}static isValidZone(e){if(!e)return!1;try{return new Intl.DateTimeFormat("en-US",{timeZone:e}).format(),!0}catch{return!1}}constructor(e){super(),this.zoneName=e,this.valid=Yr.isValidZone(e)}get type(){return"iana"}get name(){return this.zoneName}get isUniversal(){return!1}offsetName(e,{format:r,locale:n}){return _1(e,r,n,this.name)}formatOffset(e,r){return ns(this.offset(e),r)}offset(e){if(!this.valid)return NaN;const r=new Date(e);if(isNaN(r))return NaN;const n=function(d){let y=Gc.get(d);return y===void 0&&(y=new Intl.DateTimeFormat("en-US",{hour12:!1,timeZone:d,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",era:"short"}),Gc.set(d,y)),y}(this.name);let[i,o,s,a,l,c,u]=n.formatToParts?function(d,y){const g=d.formatToParts(y),p=[];for(let b=0;b=0?f:1e3+f,(Zl({year:i,month:o,day:s,hour:l===24?0:l,minute:c,second:u,millisecond:0})-h)/6e4}equals(e){return e.type==="iana"&&e.name===this.name}get isValid(){return this.valid}}let Fy={};const $f=new Map;function zf(t,e={}){const r=JSON.stringify([t,e]);let n=$f.get(r);return n===void 0&&(n=new Intl.DateTimeFormat(t,e),$f.set(r,n)),n}const Hf=new Map,qf=new Map;let da=null;const Yf=new Map;function Wy(t){let e=Yf.get(t);return e===void 0&&(e=new Intl.DateTimeFormat(t).resolvedOptions(),Yf.set(t,e)),e}const Zc=new Map;function pa(t,e,r,n){const i=t.listingMode();return i==="error"?null:i==="en"?r(e):n(e)}class pA{constructor(e,r,n){this.padTo=n.padTo||0,this.floor=n.floor||!1;const{padTo:i,floor:o,...s}=n;if(!r||Object.keys(s).length>0){const a={useGrouping:!1,...n};n.padTo>0&&(a.minimumIntegerDigits=n.padTo),this.inf=function(l,c={}){const u=JSON.stringify([l,c]);let h=Hf.get(u);return h===void 0&&(h=new Intl.NumberFormat(l,c),Hf.set(u,h)),h}(e,a)}}format(e){if(this.inf){const r=this.floor?Math.floor(e):e;return this.inf.format(r)}return dt(this.floor?Math.floor(e):ep(e,3),this.padTo)}}class gA{constructor(e,r,n){let i;if(this.opts=n,this.originalZone=void 0,this.opts.timeZone)this.dt=e;else if(e.zone.type==="fixed"){const s=e.offset/60*-1,a=s>=0?`Etc/GMT+${s}`:`Etc/GMT${s}`;e.offset!==0&&Yr.create(a).valid?(i=a,this.dt=e):(i="UTC",this.dt=e.offset===0?e:e.setZone("UTC").plus({minutes:e.offset}),this.originalZone=e.zone)}else e.zone.type==="system"?this.dt=e:e.zone.type==="iana"?(this.dt=e,i=e.zone.name):(i="UTC",this.dt=e.setZone("UTC").plus({minutes:e.offset}),this.originalZone=e.zone);const o={...this.opts};o.timeZone=o.timeZone||i,this.dtf=zf(r,o)}format(){return this.originalZone?this.formatToParts().map(({value:e})=>e).join(""):this.dtf.format(this.dt.toJSDate())}formatToParts(){const e=this.dtf.formatToParts(this.dt.toJSDate());return this.originalZone?e.map(r=>{if(r.type==="timeZoneName"){const n=this.originalZone.offsetName(this.dt.ts,{locale:this.dt.locale,format:this.opts.timeZoneName});return{...r,value:n}}return r}):e}resolvedOptions(){return this.dtf.resolvedOptions()}}class mA{constructor(e,r,n){this.opts={style:"long",...n},!r&&b1()&&(this.rtf=function(i,o={}){const{base:s,...a}=o,l=JSON.stringify([i,a]);let c=qf.get(l);return c===void 0&&(c=new Intl.RelativeTimeFormat(i,o),qf.set(l,c)),c}(e,n))}format(e,r){return this.rtf?this.rtf.format(e,r):function(n,i,o="always",s=!1){const a={years:["year","yr."],quarters:["quarter","qtr."],months:["month","mo."],weeks:["week","wk."],days:["day","day","days"],hours:["hour","hr."],minutes:["minute","min."],seconds:["second","sec."]},l=["hours","minutes","seconds"].indexOf(n)===-1;if(o==="auto"&&l){const y=n==="days";switch(i){case 1:return y?"tomorrow":`next ${a[n][0]}`;case-1:return y?"yesterday":`last ${a[n][0]}`;case 0:return y?"today":`this ${a[n][0]}`}}const c=Object.is(i,-0)||i<0,u=Math.abs(i),h=u===1,f=a[n],d=s?h?f[1]:f[2]||f[1]:h?a[n][0]:n;return c?`${u} ${d} ago`:`in ${u} ${d}`}(r,e,this.opts.numeric,this.opts.style!=="long")}formatToParts(e,r){return this.rtf?this.rtf.formatToParts(e,r):[]}}const Uy={firstDay:1,minimalDays:4,weekend:[6,7]};class Ge{static fromOpts(e){return Ge.create(e.locale,e.numberingSystem,e.outputCalendar,e.weekSettings,e.defaultToEN)}static create(e,r,n,i,o=!1){const s=e||ut.defaultLocale,a=s||(o?"en-US":da||(da=new Intl.DateTimeFormat().resolvedOptions().locale,da)),l=r||ut.defaultNumberingSystem,c=n||ut.defaultOutputCalendar,u=Gf(i)||ut.defaultWeekSettings;return new Ge(a,l,c,u,s)}static resetCache(){da=null,$f.clear(),Hf.clear(),qf.clear(),Yf.clear(),Zc.clear()}static fromObject({locale:e,numberingSystem:r,outputCalendar:n,weekSettings:i}={}){return Ge.create(e,r,n,i)}constructor(e,r,n,i,o){const[s,a,l]=function(c){const u=c.indexOf("-x-");u!==-1&&(c=c.substring(0,u));const h=c.indexOf("-u-");if(h===-1)return[c];{let f,d;try{f=zf(c).resolvedOptions(),d=c}catch{const b=c.substring(0,h);f=zf(b).resolvedOptions(),d=b}const{numberingSystem:y,calendar:g}=f;return[d,y,g]}}(e);this.locale=s,this.numberingSystem=r||a||null,this.outputCalendar=n||l||null,this.weekSettings=i,this.intl=function(c,u,h){return(h||u)&&(c.includes("-u-")||(c+="-u"),h&&(c+=`-ca-${h}`),u&&(c+=`-nu-${u}`)),c}(this.locale,this.numberingSystem,this.outputCalendar),this.weekdaysCache={format:{},standalone:{}},this.monthsCache={format:{},standalone:{}},this.meridiemCache=null,this.eraCache={},this.specifiedLocale=o,this.fastNumbersCached=null}get fastNumbers(){var e;return this.fastNumbersCached==null&&(this.fastNumbersCached=(!(e=this).numberingSystem||e.numberingSystem==="latn")&&(e.numberingSystem==="latn"||!e.locale||e.locale.startsWith("en")||Wy(e.locale).numberingSystem==="latn")),this.fastNumbersCached}listingMode(){const e=this.isEnglish(),r=!(this.numberingSystem!==null&&this.numberingSystem!=="latn"||this.outputCalendar!==null&&this.outputCalendar!=="gregory");return e&&r?"en":"intl"}clone(e){return e&&Object.getOwnPropertyNames(e).length!==0?Ge.create(e.locale||this.specifiedLocale,e.numberingSystem||this.numberingSystem,e.outputCalendar||this.outputCalendar,Gf(e.weekSettings)||this.weekSettings,e.defaultToEN||!1):this}redefaultToEN(e={}){return this.clone({...e,defaultToEN:!0})}redefaultToSystem(e={}){return this.clone({...e,defaultToEN:!1})}months(e,r=!1){return pa(this,e,k1,()=>{const n=this.intl==="ja"||this.intl.startsWith("ja-"),i=(r&=!n)?{month:e,day:"numeric"}:{month:e},o=r?"format":"standalone";if(!this.monthsCache[o][e]){const s=n?a=>this.dtFormatter(a,i).format():a=>this.extract(a,i,"month");this.monthsCache[o][e]=function(a){const l=[];for(let c=1;c<=12;c++){const u=Ae.utc(2009,c,1);l.push(a(u))}return l}(s)}return this.monthsCache[o][e]})}weekdays(e,r=!1){return pa(this,e,M1,()=>{const n=r?{weekday:e,year:"numeric",month:"long",day:"numeric"}:{weekday:e},i=r?"format":"standalone";return this.weekdaysCache[i][e]||(this.weekdaysCache[i][e]=function(o){const s=[];for(let a=1;a<=7;a++){const l=Ae.utc(2016,11,13+a);s.push(o(l))}return s}(o=>this.extract(o,n,"weekday"))),this.weekdaysCache[i][e]})}meridiems(){return pa(this,void 0,()=>A1,()=>{if(!this.meridiemCache){const e={hour:"numeric",hourCycle:"h12"};this.meridiemCache=[Ae.utc(2016,11,13,9),Ae.utc(2016,11,13,19)].map(r=>this.extract(r,e,"dayperiod"))}return this.meridiemCache})}eras(e){return pa(this,e,T1,()=>{const r={era:e};return this.eraCache[e]||(this.eraCache[e]=[Ae.utc(-40,1,1),Ae.utc(2017,1,1)].map(n=>this.extract(n,r,"era"))),this.eraCache[e]})}extract(e,r,n){const i=this.dtFormatter(e,r).formatToParts().find(o=>o.type.toLowerCase()===n);return i?i.value:null}numberFormatter(e={}){return new pA(this.intl,e.forceSimple||this.fastNumbers,e)}dtFormatter(e,r={}){return new gA(e,this.intl,r)}relFormatter(e={}){return new mA(this.intl,this.isEnglish(),e)}listFormatter(e={}){return function(r,n={}){const i=JSON.stringify([r,n]);let o=Fy[i];return o||(o=new Intl.ListFormat(r,n),Fy[i]=o),o}(this.intl,e)}isEnglish(){return this.locale==="en"||this.locale.toLowerCase()==="en-us"||Wy(this.intl).locale.startsWith("en-us")}getWeekSettings(){return this.weekSettings?this.weekSettings:v1()?function(e){let r=Zc.get(e);if(!r){const n=new Intl.Locale(e);r="getWeekInfo"in n?n.getWeekInfo():n.weekInfo,"minimalDays"in r||(r={...Uy,...r}),Zc.set(e,r)}return r}(this.locale):Uy}getStartOfWeek(){return this.getWeekSettings().firstDay}getMinDaysInFirstWeek(){return this.getWeekSettings().minimalDays}getWeekendDays(){return this.getWeekSettings().weekend}equals(e){return this.locale===e.locale&&this.numberingSystem===e.numberingSystem&&this.outputCalendar===e.outputCalendar}toString(){return`Locale(${this.locale}, ${this.numberingSystem}, ${this.outputCalendar})`}}let Xc=null;class Nt extends Ns{static get utcInstance(){return Xc===null&&(Xc=new Nt(0)),Xc}static instance(e){return e===0?Nt.utcInstance:new Nt(e)}static parseSpecifier(e){if(e){const r=e.match(/^utc(?:([+-]\d{1,2})(?::(\d{2}))?)?$/i);if(r)return new Nt(Xl(r[1],r[2]))}return null}constructor(e){super(),this.fixed=e}get type(){return"fixed"}get name(){return this.fixed===0?"UTC":`UTC${ns(this.fixed,"narrow")}`}get ianaName(){return this.fixed===0?"Etc/UTC":`Etc/GMT${ns(-this.fixed,"narrow")}`}offsetName(){return this.name}formatOffset(e,r){return ns(this.fixed,r)}get isUniversal(){return!0}offset(){return this.fixed}equals(e){return e.type==="fixed"&&e.fixed===this.fixed}get isValid(){return!0}}class yA extends Ns{constructor(e){super(),this.zoneName=e}get type(){return"invalid"}get name(){return this.zoneName}get isUniversal(){return!1}offsetName(){return null}formatOffset(){return""}offset(){return NaN}equals(){return!1}get isValid(){return!1}}function Sn(t,e){if(Pe(t)||t===null)return e;if(t instanceof Ns)return t;if(typeof t=="string"){const r=t.toLowerCase();return r==="default"?e:r==="local"||r==="system"?Ql.instance:r==="utc"||r==="gmt"?Nt.utcInstance:Nt.parseSpecifier(r)||Yr.create(t)}return Pn(t)?Nt.instance(t):typeof t=="object"&&"offset"in t&&typeof t.offset=="function"?t:new yA(t)}const Qd={arab:"[٠-٩]",arabext:"[۰-۹]",bali:"[᭐-᭙]",beng:"[০-৯]",deva:"[०-९]",fullwide:"[0-9]",gujr:"[૦-૯]",hanidec:"[〇|一|二|三|四|五|六|七|八|九]",khmr:"[០-៩]",knda:"[೦-೯]",laoo:"[໐-໙]",limb:"[᥆-᥏]",mlym:"[൦-൯]",mong:"[᠐-᠙]",mymr:"[၀-၉]",orya:"[୦-୯]",tamldec:"[௦-௯]",telu:"[౦-౯]",thai:"[๐-๙]",tibt:"[༠-༩]",latn:"\\d"},Vy={arab:[1632,1641],arabext:[1776,1785],bali:[6992,7001],beng:[2534,2543],deva:[2406,2415],fullwide:[65296,65303],gujr:[2790,2799],khmr:[6112,6121],knda:[3302,3311],laoo:[3792,3801],limb:[6470,6479],mlym:[3430,3439],mong:[6160,6169],mymr:[4160,4169],orya:[2918,2927],tamldec:[3046,3055],telu:[3174,3183],thai:[3664,3673],tibt:[3872,3881]},bA=Qd.hanidec.replace(/[\[|\]]/g,"").split(""),Kf=new Map;function ur({numberingSystem:t},e=""){const r=t||"latn";let n=Kf.get(r);n===void 0&&(n=new Map,Kf.set(r,n));let i=n.get(e);return i===void 0&&(i=new RegExp(`${Qd[r]}${e}`),n.set(e,i)),i}let $y,zy=()=>Date.now(),Hy="system",qy=null,Yy=null,Ky=null,Gy=60,Qy=null;class ut{static get now(){return zy}static set now(e){zy=e}static set defaultZone(e){Hy=e}static get defaultZone(){return Sn(Hy,Ql.instance)}static get defaultLocale(){return qy}static set defaultLocale(e){qy=e}static get defaultNumberingSystem(){return Yy}static set defaultNumberingSystem(e){Yy=e}static get defaultOutputCalendar(){return Ky}static set defaultOutputCalendar(e){Ky=e}static get defaultWeekSettings(){return Qy}static set defaultWeekSettings(e){Qy=Gf(e)}static get twoDigitCutoffYear(){return Gy}static set twoDigitCutoffYear(e){Gy=e%100}static get throwOnInvalid(){return $y}static set throwOnInvalid(e){$y=e}static resetCaches(){Ge.resetCache(),Yr.resetCache(),Ae.resetCache(),Kf.clear()}}class fr{constructor(e,r){this.reason=e,this.explanation=r}toMessage(){return this.explanation?`${this.reason}: ${this.explanation}`:this.reason}}const f1=[0,31,59,90,120,151,181,212,243,273,304,334],d1=[0,31,60,91,121,152,182,213,244,274,305,335];function er(t,e){return new fr("unit out of range",`you specified ${e} (of type ${typeof e}) as a ${t}, which is invalid`)}function Zd(t,e,r){const n=new Date(Date.UTC(t,e-1,r));t<100&&t>=0&&n.setUTCFullYear(n.getUTCFullYear()-1900);const i=n.getUTCDay();return i===0?7:i}function p1(t,e,r){return r+(Ls(t)?d1:f1)[e-1]}function g1(t,e){const r=Ls(t)?d1:f1,n=r.findIndex(i=>irs(n,e,r)?(l=n+1,c=1):l=n,{weekYear:l,weekNumber:c,weekday:a,...Jl(t)}}function Zy(t,e=4,r=1){const{weekYear:n,weekNumber:i,weekday:o}=t,s=Xd(Zd(n,1,e),r),a=Hi(n);let l,c=7*i+o-s-7+e;c<1?(l=n-1,c+=Hi(l)):c>a?(l=n+1,c-=Hi(n)):l=n;const{month:u,day:h}=g1(l,c);return{year:l,month:u,day:h,...Jl(t)}}function Jc(t){const{year:e,month:r,day:n}=t;return{year:e,ordinal:p1(e,r,n),...Jl(t)}}function Xy(t){const{year:e,ordinal:r}=t,{month:n,day:i}=g1(e,r);return{year:e,month:n,day:i,...Jl(t)}}function Jy(t,e){if(!Pe(t.localWeekday)||!Pe(t.localWeekNumber)||!Pe(t.localWeekYear)){if(!Pe(t.weekday)||!Pe(t.weekNumber)||!Pe(t.weekYear))throw new Ri("Cannot mix locale-based week fields with ISO-based week fields");return Pe(t.localWeekday)||(t.weekday=t.localWeekday),Pe(t.localWeekNumber)||(t.weekNumber=t.localWeekNumber),Pe(t.localWeekYear)||(t.weekYear=t.localWeekYear),delete t.localWeekday,delete t.localWeekNumber,delete t.localWeekYear,{minDaysInFirstWeek:e.getMinDaysInFirstWeek(),startOfWeek:e.getStartOfWeek()}}return{minDaysInFirstWeek:4,startOfWeek:1}}function m1(t){const e=yl(t.year),r=rr(t.month,1,12),n=rr(t.day,1,bl(t.year,t.month));return e?r?!n&&er("day",t.day):er("month",t.month):er("year",t.year)}function y1(t){const{hour:e,minute:r,second:n,millisecond:i}=t,o=rr(e,0,23)||e===24&&r===0&&n===0&&i===0,s=rr(r,0,59),a=rr(n,0,59),l=rr(i,0,999);return o?s?a?!l&&er("millisecond",i):er("second",n):er("minute",r):er("hour",e)}function Pe(t){return t===void 0}function Pn(t){return typeof t=="number"}function yl(t){return typeof t=="number"&&t%1==0}function b1(){try{return typeof Intl<"u"&&!!Intl.RelativeTimeFormat}catch{return!1}}function v1(){try{return typeof Intl<"u"&&!!Intl.Locale&&("weekInfo"in Intl.Locale.prototype||"getWeekInfo"in Intl.Locale.prototype)}catch{return!1}}function eb(t,e,r){if(t.length!==0)return t.reduce((n,i)=>{const o=[e(i),i];return n&&r(n[0],o[0])===n[0]?n:o},null)[1]}function zi(t,e){return Object.prototype.hasOwnProperty.call(t,e)}function Gf(t){if(t==null)return null;if(typeof t!="object")throw new Mt("Week settings must be an object");if(!rr(t.firstDay,1,7)||!rr(t.minimalDays,1,7)||!Array.isArray(t.weekend)||t.weekend.some(e=>!rr(e,1,7)))throw new Mt("Invalid week settings");return{firstDay:t.firstDay,minimalDays:t.minimalDays,weekend:Array.from(t.weekend)}}function rr(t,e,r){return yl(t)&&t>=e&&t<=r}function dt(t,e=2){let r;return r=t<0?"-"+(""+-t).padStart(e,"0"):(""+t).padStart(e,"0"),r}function wn(t){return Pe(t)||t===null||t===""?void 0:parseInt(t,10)}function Hn(t){return Pe(t)||t===null||t===""?void 0:parseFloat(t)}function Jd(t){if(!Pe(t)&&t!==null&&t!==""){const e=1e3*parseFloat("0."+t);return Math.floor(e)}}function ep(t,e,r="round"){const n=10**e;switch(r){case"expand":return t>0?Math.ceil(t*n)/n:Math.floor(t*n)/n;case"trunc":return Math.trunc(t*n)/n;case"round":return Math.round(t*n)/n;case"floor":return Math.floor(t*n)/n;case"ceil":return Math.ceil(t*n)/n;default:throw new RangeError(`Value rounding ${r} is out of range`)}}function Ls(t){return t%4==0&&(t%100!=0||t%400==0)}function Hi(t){return Ls(t)?366:365}function bl(t,e){const r=function(n,i){return n-i*Math.floor(n/i)}(e-1,12)+1;return r===2?Ls(t+(e-r)/12)?29:28:[31,null,31,30,31,30,31,31,30,31,30,31][r-1]}function Zl(t){let e=Date.UTC(t.year,t.month-1,t.day,t.hour,t.minute,t.second,t.millisecond);return t.year<100&&t.year>=0&&(e=new Date(e),e.setUTCFullYear(t.year,t.month-1,t.day)),+e}function tb(t,e,r){return-Xd(Zd(t,1,e),r)+e-1}function rs(t,e=4,r=1){const n=tb(t,e,r),i=tb(t+1,e,r);return(Hi(t)-n+i)/7}function w1(t){return t>99?t:t>ut.twoDigitCutoffYear?1900+t:2e3+t}function _1(t,e,r,n=null){const i=new Date(t),o={hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"};n&&(o.timeZone=n);const s={timeZoneName:e,...o},a=new Intl.DateTimeFormat(r,s).formatToParts(i).find(l=>l.type.toLowerCase()==="timezonename");return a?a.value:null}function Xl(t,e){let r=parseInt(t,10);Number.isNaN(r)&&(r=0);const n=parseInt(e,10)||0;return 60*r+(r<0||Object.is(r,-0)?-n:n)}function x1(t){const e=Number(t);if(typeof t=="boolean"||t===""||!Number.isFinite(e))throw new Mt(`Invalid unit value ${t}`);return e}function vl(t,e){const r={};for(const n in t)if(zi(t,n)){const i=t[n];if(i==null)continue;r[e(n)]=x1(i)}return r}function ns(t,e){const r=Math.trunc(Math.abs(t/60)),n=Math.trunc(Math.abs(t%60)),i=t>=0?"+":"-";switch(e){case"short":return`${i}${dt(r,2)}:${dt(n,2)}`;case"narrow":return`${i}${r}${n>0?`:${n}`:""}`;case"techie":return`${i}${dt(r,2)}${dt(n,2)}`;default:throw new RangeError(`Value format ${e} is out of range for property format`)}}function Jl(t){return function(e,r){return r.reduce((n,i)=>(n[i]=e[i],n),{})}(t,["hour","minute","second","millisecond"])}const vA=["January","February","March","April","May","June","July","August","September","October","November","December"],S1=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],wA=["J","F","M","A","M","J","J","A","S","O","N","D"];function k1(t){switch(t){case"narrow":return[...wA];case"short":return[...S1];case"long":return[...vA];case"numeric":return["1","2","3","4","5","6","7","8","9","10","11","12"];case"2-digit":return["01","02","03","04","05","06","07","08","09","10","11","12"];default:return null}}const E1=["Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"],O1=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],_A=["M","T","W","T","F","S","S"];function M1(t){switch(t){case"narrow":return[..._A];case"short":return[...O1];case"long":return[...E1];case"numeric":return["1","2","3","4","5","6","7"];default:return null}}const A1=["AM","PM"],xA=["Before Christ","Anno Domini"],SA=["BC","AD"],kA=["B","A"];function T1(t){switch(t){case"narrow":return[...kA];case"short":return[...SA];case"long":return[...xA];default:return null}}function rb(t,e){let r="";for(const n of t)n.literal?r+=n.val:r+=e(n.val);return r}const EA={D:gl,DD:Y_,DDD:K_,DDDD:G_,t:Q_,tt:Z_,ttt:X_,tttt:J_,T:e1,TT:t1,TTT:r1,TTTT:n1,f:i1,ff:s1,fff:l1,ffff:u1,F:o1,FF:a1,FFF:c1,FFFF:h1};class Pt{static create(e,r={}){return new Pt(e,r)}static parseFormat(e){let r=null,n="",i=!1;const o=[];for(let s=0;s0||i)&&o.push({literal:i||/^\s+$/.test(n),val:n===""?"'":n}),r=null,n="",i=!i):i||a===r?n+=a:(n.length>0&&o.push({literal:/^\s+$/.test(n),val:n}),n=a,r=a)}return n.length>0&&o.push({literal:i||/^\s+$/.test(n),val:n}),o}static macroTokenToFormatOpts(e){return EA[e]}constructor(e,r){this.opts=r,this.loc=e,this.systemLoc=null}formatWithSystemDefault(e,r){return this.systemLoc===null&&(this.systemLoc=this.loc.redefaultToSystem()),this.systemLoc.dtFormatter(e,{...this.opts,...r}).format()}dtFormatter(e,r={}){return this.loc.dtFormatter(e,{...this.opts,...r})}formatDateTime(e,r){return this.dtFormatter(e,r).format()}formatDateTimeParts(e,r){return this.dtFormatter(e,r).formatToParts()}formatInterval(e,r){return this.dtFormatter(e.start,r).dtf.formatRange(e.start.toJSDate(),e.end.toJSDate())}resolvedOptions(e,r){return this.dtFormatter(e,r).resolvedOptions()}num(e,r=0,n=void 0){if(this.opts.forceSimple)return dt(e,r);const i={...this.opts};return r>0&&(i.padTo=r),n&&(i.signDisplay=n),this.loc.numberFormatter(i).format(e)}formatDateTimeFromString(e,r){const n=this.loc.listingMode()==="en",i=this.loc.outputCalendar&&this.loc.outputCalendar!=="gregory",o=(f,d)=>this.loc.extract(e,f,d),s=f=>e.isOffsetFixed&&e.offset===0&&f.allowZ?"Z":e.isValid?e.zone.formatOffset(e.ts,f.format):"",a=()=>n?function(f){return A1[f.hour<12?0:1]}(e):o({hour:"numeric",hourCycle:"h12"},"dayperiod"),l=(f,d)=>n?function(y,g){return k1(g)[y.month-1]}(e,f):o(d?{month:f}:{month:f,day:"numeric"},"month"),c=(f,d)=>n?function(y,g){return M1(g)[y.weekday-1]}(e,f):o(d?{weekday:f}:{weekday:f,month:"long",day:"numeric"},"weekday"),u=f=>{const d=Pt.macroTokenToFormatOpts(f);return d?this.formatWithSystemDefault(e,d):f},h=f=>n?function(d,y){return T1(y)[d.year<0?0:1]}(e,f):o({era:f},"era");return rb(Pt.parseFormat(r),f=>{switch(f){case"S":return this.num(e.millisecond);case"u":case"SSS":return this.num(e.millisecond,3);case"s":return this.num(e.second);case"ss":return this.num(e.second,2);case"uu":return this.num(Math.floor(e.millisecond/10),2);case"uuu":return this.num(Math.floor(e.millisecond/100));case"m":return this.num(e.minute);case"mm":return this.num(e.minute,2);case"h":return this.num(e.hour%12==0?12:e.hour%12);case"hh":return this.num(e.hour%12==0?12:e.hour%12,2);case"H":return this.num(e.hour);case"HH":return this.num(e.hour,2);case"Z":return s({format:"narrow",allowZ:this.opts.allowZ});case"ZZ":return s({format:"short",allowZ:this.opts.allowZ});case"ZZZ":return s({format:"techie",allowZ:this.opts.allowZ});case"ZZZZ":return e.zone.offsetName(e.ts,{format:"short",locale:this.loc.locale});case"ZZZZZ":return e.zone.offsetName(e.ts,{format:"long",locale:this.loc.locale});case"z":return e.zoneName;case"a":return a();case"d":return i?o({day:"numeric"},"day"):this.num(e.day);case"dd":return i?o({day:"2-digit"},"day"):this.num(e.day,2);case"c":case"E":return this.num(e.weekday);case"ccc":return c("short",!0);case"cccc":return c("long",!0);case"ccccc":return c("narrow",!0);case"EEE":return c("short",!1);case"EEEE":return c("long",!1);case"EEEEE":return c("narrow",!1);case"L":return i?o({month:"numeric",day:"numeric"},"month"):this.num(e.month);case"LL":return i?o({month:"2-digit",day:"numeric"},"month"):this.num(e.month,2);case"LLL":return l("short",!0);case"LLLL":return l("long",!0);case"LLLLL":return l("narrow",!0);case"M":return i?o({month:"numeric"},"month"):this.num(e.month);case"MM":return i?o({month:"2-digit"},"month"):this.num(e.month,2);case"MMM":return l("short",!1);case"MMMM":return l("long",!1);case"MMMMM":return l("narrow",!1);case"y":return i?o({year:"numeric"},"year"):this.num(e.year);case"yy":return i?o({year:"2-digit"},"year"):this.num(e.year.toString().slice(-2),2);case"yyyy":return i?o({year:"numeric"},"year"):this.num(e.year,4);case"yyyyyy":return i?o({year:"numeric"},"year"):this.num(e.year,6);case"G":return h("short");case"GG":return h("long");case"GGGGG":return h("narrow");case"kk":return this.num(e.weekYear.toString().slice(-2),2);case"kkkk":return this.num(e.weekYear,4);case"W":return this.num(e.weekNumber);case"WW":return this.num(e.weekNumber,2);case"n":return this.num(e.localWeekNumber);case"nn":return this.num(e.localWeekNumber,2);case"ii":return this.num(e.localWeekYear.toString().slice(-2),2);case"iiii":return this.num(e.localWeekYear,4);case"o":return this.num(e.ordinal);case"ooo":return this.num(e.ordinal,3);case"q":return this.num(e.quarter);case"qq":return this.num(e.quarter,2);case"X":return this.num(Math.floor(e.ts/1e3));case"x":return this.num(e.ts);default:return u(f)}})}formatDurationFromString(e,r){const n=this.opts.signMode==="negativeLargestOnly"?-1:1,i=l=>{switch(l[0]){case"S":return"milliseconds";case"s":return"seconds";case"m":return"minutes";case"h":return"hours";case"d":return"days";case"w":return"weeks";case"M":return"months";case"y":return"years";default:return null}},o=Pt.parseFormat(r),s=o.reduce((l,{literal:c,val:u})=>c?l:l.concat(u),[]),a=e.shiftTo(...s.map(i).filter(l=>l));return rb(o,((l,c)=>u=>{const h=i(u);if(h){const f=c.isNegativeDuration&&h!==c.largestUnit?n:1;let d;return d=this.opts.signMode==="negativeLargestOnly"&&h!==c.largestUnit?"never":this.opts.signMode==="all"?"always":"auto",this.num(l.get(h)*f,u.length,d)}return u})(a,{isNegativeDuration:a<0,largestUnit:Object.keys(a.values)[0]}))}}const I1=/[A-Za-z_+-]{1,256}(?::?\/[A-Za-z0-9_+-]{1,256}(?:\/[A-Za-z0-9_+-]{1,256})?)?/;function so(...t){const e=t.reduce((r,n)=>r+n.source,"");return RegExp(`^${e}$`)}function ao(...t){return e=>t.reduce(([r,n,i],o)=>{const[s,a,l]=o(e,i);return[{...r,...s},a||n,l]},[{},null,1]).slice(0,2)}function Di(t,...e){if(t==null)return[null,null];for(const[r,n]of e){const i=r.exec(t);if(i)return n(i)}return[null,null]}function P1(...t){return(e,r)=>{const n={};let i;for(i=0;id!==void 0&&(y||d&&u)?-d:d;return[{years:f(Hn(r)),months:f(Hn(n)),weeks:f(Hn(i)),days:f(Hn(o)),hours:f(Hn(s)),minutes:f(Hn(a)),seconds:f(Hn(l),l==="-0"),milliseconds:f(Jd(c),h)}]}const CA={GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function np(t,e,r,n,i,o,s){const a={year:e.length===2?w1(wn(e)):wn(e),month:S1.indexOf(r)+1,day:wn(n),hour:wn(i),minute:wn(o)};return s&&(a.second=wn(s)),t&&(a.weekday=t.length>3?E1.indexOf(t)+1:O1.indexOf(t)+1),a}const RA=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|(?:([+-]\d\d)(\d\d)))$/;function DA(t){const[,e,r,n,i,o,s,a,l,c,u,h]=t,f=np(e,i,n,r,o,s,a);let d;return d=l?CA[l]:c?0:Xl(u,h),[f,new Nt(d)]}const jA=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d\d) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d\d):(\d\d):(\d\d) GMT$/,NA=/^(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d\d)-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/,LA=/^(Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ( \d|\d\d) (\d\d):(\d\d):(\d\d) (\d{4})$/;function nb(t){const[,e,r,n,i,o,s,a]=t;return[np(e,i,n,r,o,s,a),Nt.utcInstance]}function BA(t){const[,e,r,n,i,o,s,a]=t;return[np(e,a,r,n,i,o,s),Nt.utcInstance]}const FA=so(/([+-]\d{6}|\d{4})(?:-?(\d\d)(?:-?(\d\d))?)?/,rp),WA=so(/(\d{4})-?W(\d\d)(?:-?(\d))?/,rp),UA=so(/(\d{4})-?(\d{3})/,rp),VA=so(R1),ib=ao(function(t,e){return[{year:qi(t,e),month:qi(t,e+1,1),day:qi(t,e+2,1)},null,e+3]},lo,Bs,Fs),$A=ao(OA,lo,Bs,Fs),zA=ao(MA,lo,Bs,Fs),HA=ao(lo,Bs,Fs),qA=ao(lo),YA=so(/(\d{4})-(\d\d)-(\d\d)/,AA),KA=so(D1),GA=ao(lo,Bs,Fs),ob="Invalid Duration",j1={weeks:{days:7,hours:168,minutes:10080,seconds:604800,milliseconds:6048e5},days:{hours:24,minutes:1440,seconds:86400,milliseconds:864e5},hours:{minutes:60,seconds:3600,milliseconds:36e5},minutes:{seconds:60,milliseconds:6e4},seconds:{milliseconds:1e3}},QA={years:{quarters:4,months:12,weeks:52,days:365,hours:8760,minutes:525600,seconds:31536e3,milliseconds:31536e6},quarters:{months:3,weeks:13,days:91,hours:2184,minutes:131040,seconds:7862400,milliseconds:78624e5},months:{weeks:4,days:30,hours:720,minutes:43200,seconds:2592e3,milliseconds:2592e6},...j1},ZA=365.2425,XA=30.436875,JA={years:{quarters:4,months:12,weeks:52.1775,days:ZA,hours:8765.82,minutes:525949.2,seconds:525949.2*60,milliseconds:525949.2*60*1e3},quarters:{months:3,weeks:13.044375,days:91.310625,hours:2191.455,minutes:131487.3,seconds:525949.2*60/4,milliseconds:7889237999999999e-6},months:{weeks:4.3481250000000005,days:XA,hours:730.485,minutes:43829.1,seconds:2629746,milliseconds:2629746e3},...j1},ei=["years","quarters","months","weeks","days","hours","minutes","seconds","milliseconds"],eT=ei.slice(0).reverse();function jr(t,e,r=!1){const n={values:r?e.values:{...t.values,...e.values||{}},loc:t.loc.clone(e.loc),conversionAccuracy:e.conversionAccuracy||t.conversionAccuracy,matrix:e.matrix||t.matrix};return new Ue(n)}function N1(t,e){let r=e.milliseconds??0;for(const n of eT.slice(1))e[n]&&(r+=e[n]*t[n].milliseconds);return r}function sb(t,e){const r=N1(t,e)<0?-1:1;ei.reduceRight((n,i)=>{if(Pe(e[i]))return n;if(n){const o=e[n]*r,s=t[i][n],a=Math.floor(o/s);e[i]+=a*r,e[n]-=a*s*r}return i},null),ei.reduce((n,i)=>{if(Pe(e[i]))return n;if(n){const o=e[n]%1;e[n]-=o,e[i]+=o*t[n][i]}return i},null)}function ab(t){const e={};for(const[r,n]of Object.entries(t))n!==0&&(e[r]=n);return e}class Ue{constructor(e){const r=e.conversionAccuracy==="longterm"||!1;let n=r?JA:QA;e.matrix&&(n=e.matrix),this.values=e.values,this.loc=e.loc||Ge.create(),this.conversionAccuracy=r?"longterm":"casual",this.invalid=e.invalid||null,this.matrix=n,this.isLuxonDuration=!0}static fromMillis(e,r){return Ue.fromObject({milliseconds:e},r)}static fromObject(e,r={}){if(e==null||typeof e!="object")throw new Mt("Duration.fromObject: argument expected to be an object, got "+(e===null?"null":typeof e));return new Ue({values:vl(e,Ue.normalizeUnit),loc:Ge.fromObject(r),conversionAccuracy:r.conversionAccuracy,matrix:r.matrix})}static fromDurationLike(e){if(Pn(e))return Ue.fromMillis(e);if(Ue.isDuration(e))return e;if(typeof e=="object")return Ue.fromObject(e);throw new Mt(`Unknown duration argument ${e} of type ${typeof e}`)}static fromISO(e,r){const[n]=function(i){return Di(i,[IA,PA])}(e);return n?Ue.fromObject(n,r):Ue.invalid("unparsable",`the input "${e}" can't be parsed as ISO 8601`)}static fromISOTime(e,r){const[n]=function(i){return Di(i,[TA,qA])}(e);return n?Ue.fromObject(n,r):Ue.invalid("unparsable",`the input "${e}" can't be parsed as ISO 8601`)}static invalid(e,r=null){if(!e)throw new Mt("need to specify a reason the Duration is invalid");const n=e instanceof fr?e:new fr(e,r);if(ut.throwOnInvalid)throw new uA(n);return new Ue({invalid:n})}static normalizeUnit(e){const r={year:"years",years:"years",quarter:"quarters",quarters:"quarters",month:"months",months:"months",week:"weeks",weeks:"weeks",day:"days",days:"days",hour:"hours",hours:"hours",minute:"minutes",minutes:"minutes",second:"seconds",seconds:"seconds",millisecond:"milliseconds",milliseconds:"milliseconds"}[e&&e.toLowerCase()];if(!r)throw new q_(e);return r}static isDuration(e){return e&&e.isLuxonDuration||!1}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}toFormat(e,r={}){const n={...r,floor:r.round!==!1&&r.floor!==!1};return this.isValid?Pt.create(this.loc,n).formatDurationFromString(this,e):ob}toHuman(e={}){if(!this.isValid)return ob;const r=e.showZeros!==!1,n=ei.map(i=>{const o=this.values[i];return Pe(o)||o===0&&!r?null:this.loc.numberFormatter({style:"unit",unitDisplay:"long",...e,unit:i.slice(0,-1)}).format(o)}).filter(i=>i);return this.loc.listFormatter({type:"conjunction",style:e.listStyle||"narrow",...e}).format(n)}toObject(){return this.isValid?{...this.values}:{}}toISO(){if(!this.isValid)return null;let e="P";return this.years!==0&&(e+=this.years+"Y"),this.months===0&&this.quarters===0||(e+=this.months+3*this.quarters+"M"),this.weeks!==0&&(e+=this.weeks+"W"),this.days!==0&&(e+=this.days+"D"),this.hours===0&&this.minutes===0&&this.seconds===0&&this.milliseconds===0||(e+="T"),this.hours!==0&&(e+=this.hours+"H"),this.minutes!==0&&(e+=this.minutes+"M"),this.seconds===0&&this.milliseconds===0||(e+=ep(this.seconds+this.milliseconds/1e3,3)+"S"),e==="P"&&(e+="T0S"),e}toISOTime(e={}){if(!this.isValid)return null;const r=this.toMillis();return r<0||r>=864e5?null:(e={suppressMilliseconds:!1,suppressSeconds:!1,includePrefix:!1,format:"extended",...e,includeOffset:!1},Ae.fromMillis(r,{zone:"UTC"}).toISOTime(e))}toJSON(){return this.toISO()}toString(){return this.toISO()}[Symbol.for("nodejs.util.inspect.custom")](){return this.isValid?`Duration { values: ${JSON.stringify(this.values)} }`:`Duration { Invalid, reason: ${this.invalidReason} }`}toMillis(){return this.isValid?N1(this.matrix,this.values):NaN}valueOf(){return this.toMillis()}plus(e){if(!this.isValid)return this;const r=Ue.fromDurationLike(e),n={};for(const i of ei)(zi(r.values,i)||zi(this.values,i))&&(n[i]=r.get(i)+this.get(i));return jr(this,{values:n},!0)}minus(e){if(!this.isValid)return this;const r=Ue.fromDurationLike(e);return this.plus(r.negate())}mapUnits(e){if(!this.isValid)return this;const r={};for(const n of Object.keys(this.values))r[n]=x1(e(this.values[n],n));return jr(this,{values:r},!0)}get(e){return this[Ue.normalizeUnit(e)]}set(e){return this.isValid?jr(this,{values:{...this.values,...vl(e,Ue.normalizeUnit)}}):this}reconfigure({locale:e,numberingSystem:r,conversionAccuracy:n,matrix:i}={}){return jr(this,{loc:this.loc.clone({locale:e,numberingSystem:r}),matrix:i,conversionAccuracy:n})}as(e){return this.isValid?this.shiftTo(e).get(e):NaN}normalize(){if(!this.isValid)return this;const e=this.toObject();return sb(this.matrix,e),jr(this,{values:e},!0)}rescale(){return this.isValid?jr(this,{values:ab(this.normalize().shiftToAll().toObject())},!0):this}shiftTo(...e){if(!this.isValid)return this;if(e.length===0)return this;e=e.map(s=>Ue.normalizeUnit(s));const r={},n={},i=this.toObject();let o;for(const s of ei)if(e.indexOf(s)>=0){o=s;let a=0;for(const c in n)a+=this.matrix[c][s]*n[c],n[c]=0;Pn(i[s])&&(a+=i[s]);const l=Math.trunc(a);r[s]=l,n[s]=(1e3*a-1e3*l)/1e3}else Pn(i[s])&&(n[s]=i[s]);for(const s in n)n[s]!==0&&(r[o]+=s===o?n[s]:n[s]/this.matrix[o][s]);return sb(this.matrix,r),jr(this,{values:r},!0)}shiftToAll(){return this.isValid?this.shiftTo("years","months","weeks","days","hours","minutes","seconds","milliseconds"):this}negate(){if(!this.isValid)return this;const e={};for(const r of Object.keys(this.values))e[r]=this.values[r]===0?0:-this.values[r];return jr(this,{values:e},!0)}removeZeros(){return this.isValid?jr(this,{values:ab(this.values)},!0):this}get years(){return this.isValid?this.values.years||0:NaN}get quarters(){return this.isValid?this.values.quarters||0:NaN}get months(){return this.isValid?this.values.months||0:NaN}get weeks(){return this.isValid?this.values.weeks||0:NaN}get days(){return this.isValid?this.values.days||0:NaN}get hours(){return this.isValid?this.values.hours||0:NaN}get minutes(){return this.isValid?this.values.minutes||0:NaN}get seconds(){return this.isValid?this.values.seconds||0:NaN}get milliseconds(){return this.isValid?this.values.milliseconds||0:NaN}get isValid(){return this.invalid===null}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}equals(e){if(!this.isValid||!e.isValid||!this.loc.equals(e.loc))return!1;function r(n,i){return n===void 0||n===0?i===void 0||i===0:n===i}for(const n of ei)if(!r(this.values[n],e.values[n]))return!1;return!0}}const ki="Invalid Interval";class ct{constructor(e){this.s=e.start,this.e=e.end,this.invalid=e.invalid||null,this.isLuxonInterval=!0}static invalid(e,r=null){if(!e)throw new Mt("need to specify a reason the Interval is invalid");const n=e instanceof fr?e:new fr(e,r);if(ut.throwOnInvalid)throw new cA(n);return new ct({invalid:n})}static fromDateTimes(e,r){const n=ko(e),i=ko(r),o=function(s,a){return s&&s.isValid?a&&a.isValid?ae}isBefore(e){return!!this.isValid&&this.e<=e}contains(e){return!!this.isValid&&this.s<=e&&this.e>e}set({start:e,end:r}={}){return this.isValid?ct.fromDateTimes(e||this.s,r||this.e):this}splitAt(...e){if(!this.isValid)return[];const r=e.map(ko).filter(s=>this.contains(s)).sort((s,a)=>s.toMillis()-a.toMillis()),n=[];let{s:i}=this,o=0;for(;i+this.e?this.e:s;n.push(ct.fromDateTimes(i,a)),i=a,o+=1}return n}splitBy(e){const r=Ue.fromDurationLike(e);if(!this.isValid||!r.isValid||r.as("milliseconds")===0)return[];let n,{s:i}=this,o=1;const s=[];for(;il*o));n=+a>+this.e?this.e:a,s.push(ct.fromDateTimes(i,n)),i=n,o+=1}return s}divideEqually(e){return this.isValid?this.splitBy(this.length()/e).slice(0,e):[]}overlaps(e){return this.e>e.s&&this.s=e.e}equals(e){return!(!this.isValid||!e.isValid)&&this.s.equals(e.s)&&this.e.equals(e.e)}intersection(e){if(!this.isValid)return this;const r=this.s>e.s?this.s:e.s,n=this.e=n?null:ct.fromDateTimes(r,n)}union(e){if(!this.isValid)return this;const r=this.se.e?this.e:e.e;return ct.fromDateTimes(r,n)}static merge(e){const[r,n]=e.sort((i,o)=>i.s-o.s).reduce(([i,o],s)=>o?o.overlaps(s)||o.abutsStart(s)?[i,o.union(s)]:[i.concat([o]),s]:[i,s],[[],null]);return n&&r.push(n),r}static xor(e){let r=null,n=0;const i=[],o=e.map(a=>[{time:a.s,type:"s"},{time:a.e,type:"e"}]),s=Array.prototype.concat(...o).sort((a,l)=>a.time-l.time);for(const a of s)n+=a.type==="s"?1:-1,n===1?r=a.time:(r&&+r!=+a.time&&i.push(ct.fromDateTimes(r,a.time)),r=null);return ct.merge(i)}difference(...e){return ct.xor([this].concat(e)).map(r=>this.intersection(r)).filter(r=>r&&!r.isEmpty())}toString(){return this.isValid?`[${this.s.toISO()} – ${this.e.toISO()})`:ki}[Symbol.for("nodejs.util.inspect.custom")](){return this.isValid?`Interval { start: ${this.s.toISO()}, end: ${this.e.toISO()} }`:`Interval { Invalid, reason: ${this.invalidReason} }`}toLocaleString(e=gl,r={}){return this.isValid?Pt.create(this.s.loc.clone(r),e).formatInterval(this):ki}toISO(e){return this.isValid?`${this.s.toISO(e)}/${this.e.toISO(e)}`:ki}toISODate(){return this.isValid?`${this.s.toISODate()}/${this.e.toISODate()}`:ki}toISOTime(e){return this.isValid?`${this.s.toISOTime(e)}/${this.e.toISOTime(e)}`:ki}toFormat(e,{separator:r=" – "}={}){return this.isValid?`${this.s.toFormat(e)}${r}${this.e.toFormat(e)}`:ki}toDuration(e,r){return this.isValid?this.e.diff(this.s,e,r):Ue.invalid(this.invalidReason)}mapEndpoints(e){return ct.fromDateTimes(e(this.s),e(this.e))}}class ga{static hasDST(e=ut.defaultZone){const r=Ae.now().setZone(e).set({month:12});return!e.isUniversal&&r.offset!==r.set({month:6}).offset}static isValidIANAZone(e){return Yr.isValidZone(e)}static normalizeZone(e){return Sn(e,ut.defaultZone)}static getStartOfWeek({locale:e=null,locObj:r=null}={}){return(r||Ge.create(e)).getStartOfWeek()}static getMinimumDaysInFirstWeek({locale:e=null,locObj:r=null}={}){return(r||Ge.create(e)).getMinDaysInFirstWeek()}static getWeekendWeekdays({locale:e=null,locObj:r=null}={}){return(r||Ge.create(e)).getWeekendDays().slice()}static months(e="long",{locale:r=null,numberingSystem:n=null,locObj:i=null,outputCalendar:o="gregory"}={}){return(i||Ge.create(r,n,o)).months(e)}static monthsFormat(e="long",{locale:r=null,numberingSystem:n=null,locObj:i=null,outputCalendar:o="gregory"}={}){return(i||Ge.create(r,n,o)).months(e,!0)}static weekdays(e="long",{locale:r=null,numberingSystem:n=null,locObj:i=null}={}){return(i||Ge.create(r,n,null)).weekdays(e)}static weekdaysFormat(e="long",{locale:r=null,numberingSystem:n=null,locObj:i=null}={}){return(i||Ge.create(r,n,null)).weekdays(e,!0)}static meridiems({locale:e=null}={}){return Ge.create(e).meridiems()}static eras(e="short",{locale:r=null}={}){return Ge.create(r,null,"gregory").eras(e)}static features(){return{relative:b1(),localeWeek:v1()}}}function lb(t,e){const r=i=>i.toUTC(0,{keepLocalTime:!0}).startOf("day").valueOf(),n=r(e)-r(t);return Math.floor(Ue.fromMillis(n).as("days"))}function tT(t,e,r,n){let[i,o,s,a]=function(h,f,d){const y=[["years",(S,k)=>k.year-S.year],["quarters",(S,k)=>k.quarter-S.quarter+4*(k.year-S.year)],["months",(S,k)=>k.month-S.month+12*(k.year-S.year)],["weeks",(S,k)=>{const M=lb(S,k);return(M-M%7)/7}],["days",lb]],g={},p=h;let b,v;for(const[S,k]of y)d.indexOf(S)>=0&&(b=S,g[S]=k(h,f),v=p.plus(g),v>f?(g[S]--,(h=p.plus(g))>f&&(v=h,g[S]--,h=p.plus(g))):h=v);return[h,g,v,b]}(t,e,r);const l=e-i,c=r.filter(h=>["hours","minutes","seconds","milliseconds"].indexOf(h)>=0);c.length===0&&(s0?Ue.fromMillis(l,n).shiftTo(...c).plus(u):u}function vr(t,e=r=>r){return{regex:t,deser:([r])=>e(function(n){let i=parseInt(n,10);if(isNaN(i)){i="";for(let o=0;o=l&&s<=c&&(i+=s-l)}}return parseInt(i,10)}return i}(r))}}const L1="[  ]",B1=new RegExp(L1,"g");function rT(t){return t.replace(/\./g,"\\.?").replace(B1,L1)}function cb(t){return t.replace(/\./g,"").replace(B1," ").toLowerCase()}function hr(t,e){return t===null?null:{regex:RegExp(t.map(rT).join("|")),deser:([r])=>t.findIndex(n=>cb(r)===cb(n))+e}}function ub(t,e){return{regex:t,deser:([,r,n])=>Xl(r,n),groups:e}}function ma(t){return{regex:t,deser:([e])=>e}}const nT={year:{"2-digit":"yy",numeric:"yyyyy"},month:{numeric:"M","2-digit":"MM",short:"MMM",long:"MMMM"},day:{numeric:"d","2-digit":"dd"},weekday:{short:"EEE",long:"EEEE"},dayperiod:"a",dayPeriod:"a",hour12:{numeric:"h","2-digit":"hh"},hour24:{numeric:"H","2-digit":"HH"},minute:{numeric:"m","2-digit":"mm"},second:{numeric:"s","2-digit":"ss"},timeZoneName:{long:"ZZZZZ",short:"ZZZ"}};let eu=null;function F1(t,e){return Array.prototype.concat(...t.map(r=>function(n,i){if(n.literal)return n;const o=U1(Pt.macroTokenToFormatOpts(n.val),i);return o==null||o.includes(void 0)?n:o}(r,e)))}class W1{constructor(e,r){if(this.locale=e,this.format=r,this.tokens=F1(Pt.parseFormat(r),e),this.units=this.tokens.map(i=>function(o,s){const a=ur(s),l=ur(s,"{2}"),c=ur(s,"{3}"),u=ur(s,"{4}"),h=ur(s,"{6}"),f=ur(s,"{1,2}"),d=ur(s,"{1,3}"),y=ur(s,"{1,6}"),g=ur(s,"{1,9}"),p=ur(s,"{2,4}"),b=ur(s,"{4,6}"),v=k=>{return{regex:RegExp((M=k.val,M.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"))),deser:([A])=>A,literal:!0};var M},S=(k=>{if(o.literal)return v(k);switch(k.val){case"G":return hr(s.eras("short"),0);case"GG":return hr(s.eras("long"),0);case"y":return vr(y);case"yy":case"kk":return vr(p,w1);case"yyyy":case"kkkk":return vr(u);case"yyyyy":return vr(b);case"yyyyyy":return vr(h);case"M":case"L":case"d":case"H":case"h":case"m":case"q":case"s":case"W":return vr(f);case"MM":case"LL":case"dd":case"HH":case"hh":case"mm":case"qq":case"ss":case"WW":return vr(l);case"MMM":return hr(s.months("short",!0),1);case"MMMM":return hr(s.months("long",!0),1);case"LLL":return hr(s.months("short",!1),1);case"LLLL":return hr(s.months("long",!1),1);case"o":case"S":return vr(d);case"ooo":case"SSS":return vr(c);case"u":return ma(g);case"uu":return ma(f);case"uuu":case"E":case"c":return vr(a);case"a":return hr(s.meridiems(),0);case"EEE":return hr(s.weekdays("short",!1),1);case"EEEE":return hr(s.weekdays("long",!1),1);case"ccc":return hr(s.weekdays("short",!0),1);case"cccc":return hr(s.weekdays("long",!0),1);case"Z":case"ZZ":return ub(new RegExp(`([+-]${f.source})(?::(${l.source}))?`),2);case"ZZZ":return ub(new RegExp(`([+-]${f.source})(${l.source})?`),2);case"z":return ma(/[a-z_+-/]{1,256}?/i);case" ":return ma(/[^\S\n\r]/);default:return v(k)}})(o)||{invalidReason:"missing Intl.DateTimeFormat.formatToParts support"};return S.token=o,S}(i,e)),this.disqualifyingUnit=this.units.find(i=>i.invalidReason),!this.disqualifyingUnit){const[i,o]=[`^${(n=this.units).map(s=>s.regex).reduce((s,a)=>`${s}(${a.source})`,"")}$`,n];this.regex=RegExp(i,"i"),this.handlers=o}var n}explainFromTokens(e){if(this.isValid){const[r,n]=function(a,l,c){const u=a.match(l);if(u){const h={};let f=1;for(const d in c)if(zi(c,d)){const y=c[d],g=y.groups?y.groups+1:1;!y.literal&&y.token&&(h[y.token.val[0]]=y.deser(u.slice(f,f+g))),f+=g}return[u,h]}return[u,{}]}(e,this.regex,this.handlers),[i,o,s]=n?function(a){let l,c=null;return Pe(a.z)||(c=Yr.create(a.z)),Pe(a.Z)||(c||(c=new Nt(a.Z)),l=a.Z),Pe(a.q)||(a.M=3*(a.q-1)+1),Pe(a.h)||(a.h<12&&a.a===1?a.h+=12:a.h===12&&a.a===0&&(a.h=0)),a.G===0&&a.y&&(a.y=-a.y),Pe(a.u)||(a.S=Jd(a.u)),[Object.keys(a).reduce((u,h)=>{const f=(d=>{switch(d){case"S":return"millisecond";case"s":return"second";case"m":return"minute";case"h":case"H":return"hour";case"d":return"day";case"o":return"ordinal";case"L":case"M":return"month";case"y":return"year";case"E":case"c":return"weekday";case"W":return"weekNumber";case"k":return"weekYear";case"q":return"quarter";default:return null}})(h);return f&&(u[f]=a[h]),u},{}),c,l]}(n):[null,null,void 0];if(zi(n,"a")&&zi(n,"H"))throw new Ri("Can't include meridiem when specifying 24-hour format");return{input:e,tokens:this.tokens,regex:this.regex,rawMatches:r,matches:n,result:i,zone:o,specificOffset:s}}return{input:e,tokens:this.tokens,invalidReason:this.invalidReason}}get isValid(){return!this.disqualifyingUnit}get invalidReason(){return this.disqualifyingUnit?this.disqualifyingUnit.invalidReason:null}}function hb(t,e,r){return new W1(t,r).explainFromTokens(e)}function U1(t,e){if(!t)return null;const r=Pt.create(e,t).dtFormatter((eu||(eu=Ae.fromMillis(1555555555555)),eu)),n=r.formatToParts(),i=r.resolvedOptions();return n.map(o=>function(s,a,l){const{type:c,value:u}=s;if(c==="literal"){const y=/^\s+$/.test(u);return{literal:!y,val:y?" ":u}}const h=a[c];let f=c;c==="hour"&&(f=a.hour12!=null?a.hour12?"hour12":"hour24":a.hourCycle!=null?a.hourCycle==="h11"||a.hourCycle==="h12"?"hour12":"hour24":l.hour12?"hour12":"hour24");let d=nT[f];if(typeof d=="object"&&(d=d[h]),d)return{literal:!1,val:d}}(o,t,i))}const tu="Invalid DateTime",fb=864e13;function No(t){return new fr("unsupported zone",`the zone "${t.name}" is not supported`)}function ru(t){return t.weekData===null&&(t.weekData=ml(t.c)),t.weekData}function nu(t){return t.localWeekData===null&&(t.localWeekData=ml(t.c,t.loc.getMinDaysInFirstWeek(),t.loc.getStartOfWeek())),t.localWeekData}function qn(t,e){const r={ts:t.ts,zone:t.zone,c:t.c,o:t.o,loc:t.loc,invalid:t.invalid};return new Ae({...r,...e,old:r})}function V1(t,e,r){let n=t-60*e*1e3;const i=r.offset(n);if(e===i)return[n,e];n-=60*(i-e)*1e3;const o=r.offset(n);return i===o?[n,i]:[t-60*Math.min(i,o)*1e3,Math.max(i,o)]}function ya(t,e){const r=new Date(t+=60*e*1e3);return{year:r.getUTCFullYear(),month:r.getUTCMonth()+1,day:r.getUTCDate(),hour:r.getUTCHours(),minute:r.getUTCMinutes(),second:r.getUTCSeconds(),millisecond:r.getUTCMilliseconds()}}function za(t,e,r){return V1(Zl(t),e,r)}function db(t,e){const r=t.o,n=t.c.year+Math.trunc(e.years),i=t.c.month+Math.trunc(e.months)+3*Math.trunc(e.quarters),o={...t.c,year:n,month:i,day:Math.min(t.c.day,bl(n,i))+Math.trunc(e.days)+7*Math.trunc(e.weeks)},s=Ue.fromObject({years:e.years-Math.trunc(e.years),quarters:e.quarters-Math.trunc(e.quarters),months:e.months-Math.trunc(e.months),weeks:e.weeks-Math.trunc(e.weeks),days:e.days-Math.trunc(e.days),hours:e.hours,minutes:e.minutes,seconds:e.seconds,milliseconds:e.milliseconds}).as("milliseconds"),a=Zl(o);let[l,c]=V1(a,r,t.zone);return s!==0&&(l+=s,c=t.zone.offset(l)),{ts:l,o:c}}function Ei(t,e,r,n,i,o){const{setZone:s,zone:a}=r;if(t&&Object.keys(t).length!==0||e){const l=e||a,c=Ae.fromObject(t,{...r,zone:l,specificOffset:o});return s?c:c.setZone(a)}return Ae.invalid(new fr("unparsable",`the input "${i}" can't be parsed as ${n}`))}function ba(t,e,r=!0){return t.isValid?Pt.create(Ge.create("en-US"),{allowZ:r,forceSimple:!0}).formatDateTimeFromString(t,e):null}function iu(t,e,r){const n=t.c.year>9999||t.c.year<0;let i="";if(n&&t.c.year>=0&&(i+="+"),i+=dt(t.c.year,n?6:4),r==="year")return i;if(e){if(i+="-",i+=dt(t.c.month),r==="month")return i;i+="-"}else if(i+=dt(t.c.month),r==="month")return i;return i+=dt(t.c.day),i}function pb(t,e,r,n,i,o,s){let a=!r||t.c.millisecond!==0||t.c.second!==0,l="";switch(s){case"day":case"month":case"year":break;default:if(l+=dt(t.c.hour),s==="hour")break;if(e){if(l+=":",l+=dt(t.c.minute),s==="minute")break;a&&(l+=":",l+=dt(t.c.second))}else{if(l+=dt(t.c.minute),s==="minute")break;a&&(l+=dt(t.c.second))}if(s==="second")break;!a||n&&t.c.millisecond===0||(l+=".",l+=dt(t.c.millisecond,3))}return i&&(t.isOffsetFixed&&t.offset===0&&!o?l+="Z":t.o<0?(l+="-",l+=dt(Math.trunc(-t.o/60)),l+=":",l+=dt(Math.trunc(-t.o%60))):(l+="+",l+=dt(Math.trunc(t.o/60)),l+=":",l+=dt(Math.trunc(t.o%60)))),o&&(l+="["+t.zone.ianaName+"]"),l}const $1={month:1,day:1,hour:0,minute:0,second:0,millisecond:0},iT={weekNumber:1,weekday:1,hour:0,minute:0,second:0,millisecond:0},oT={ordinal:1,hour:0,minute:0,second:0,millisecond:0},Ha=["year","month","day","hour","minute","second","millisecond"],sT=["weekYear","weekNumber","weekday","hour","minute","second","millisecond"],aT=["year","ordinal","hour","minute","second","millisecond"];function qa(t){const e={year:"year",years:"year",month:"month",months:"month",day:"day",days:"day",hour:"hour",hours:"hour",minute:"minute",minutes:"minute",quarter:"quarter",quarters:"quarter",second:"second",seconds:"second",millisecond:"millisecond",milliseconds:"millisecond",weekday:"weekday",weekdays:"weekday",weeknumber:"weekNumber",weeksnumber:"weekNumber",weeknumbers:"weekNumber",weekyear:"weekYear",weekyears:"weekYear",ordinal:"ordinal"}[t.toLowerCase()];if(!e)throw new q_(t);return e}function gb(t){switch(t.toLowerCase()){case"localweekday":case"localweekdays":return"localWeekday";case"localweeknumber":case"localweeknumbers":return"localWeekNumber";case"localweekyear":case"localweekyears":return"localWeekYear";default:return qa(t)}}function mb(t,e){const r=Sn(e.zone,ut.defaultZone);if(!r.isValid)return Ae.invalid(No(r));const n=Ge.fromObject(e);let i,o;if(Pe(t.year))i=ut.now();else{for(const l of Ha)Pe(t[l])&&(t[l]=$1[l]);const s=m1(t)||y1(t);if(s)return Ae.invalid(s);const a=function(l){if(Lo===void 0&&(Lo=ut.now()),l.type!=="iana")return l.offset(Lo);const c=l.name;let u=Qf.get(c);return u===void 0&&(u=l.offset(Lo),Qf.set(c,u)),u}(r);[i,o]=za(t,a,r)}return new Ae({ts:i,zone:r,loc:n,o})}function yb(t,e,r){const n=!!Pe(r.round)||r.round,i=Pe(r.rounding)?"trunc":r.rounding,o=(a,l)=>(a=ep(a,n||r.calendary?0:2,r.calendary?"round":i),e.loc.clone(r).relFormatter(r).format(a,l)),s=a=>r.calendary?e.hasSame(t,a)?0:e.startOf(a).diff(t.startOf(a),a).get(a):e.diff(t,a).get(a);if(r.unit)return o(s(r.unit),r.unit);for(const a of r.units){const l=s(a);if(Math.abs(l)>=1)return o(l,a)}return o(t>e?-0:0,r.units[r.units.length-1])}function bb(t){let e,r={};return t.length>0&&typeof t[t.length-1]=="object"?(r=t[t.length-1],e=Array.from(t).slice(0,t.length-1)):e=Array.from(t),[r,e]}let Lo;const Qf=new Map;class Ae{constructor(e){const r=e.zone||ut.defaultZone;let n=e.invalid||(Number.isNaN(e.ts)?new fr("invalid input"):null)||(r.isValid?null:No(r));this.ts=Pe(e.ts)?ut.now():e.ts;let i=null,o=null;if(!n)if(e.old&&e.old.ts===this.ts&&e.old.zone.equals(r))[i,o]=[e.old.c,e.old.o];else{const s=Pn(e.o)&&!e.old?e.o:r.offset(this.ts);i=ya(this.ts,s),n=Number.isNaN(i.year)?new fr("invalid input"):null,i=n?null:i,o=n?null:s}this._zone=r,this.loc=e.loc||Ge.create(),this.invalid=n,this.weekData=null,this.localWeekData=null,this.c=i,this.o=o,this.isLuxonDateTime=!0}static now(){return new Ae({})}static local(){const[e,r]=bb(arguments),[n,i,o,s,a,l,c]=r;return mb({year:n,month:i,day:o,hour:s,minute:a,second:l,millisecond:c},e)}static utc(){const[e,r]=bb(arguments),[n,i,o,s,a,l,c]=r;return e.zone=Nt.utcInstance,mb({year:n,month:i,day:o,hour:s,minute:a,second:l,millisecond:c},e)}static fromJSDate(e,r={}){const n=(i=e,Object.prototype.toString.call(i)==="[object Date]"?e.valueOf():NaN);var i;if(Number.isNaN(n))return Ae.invalid("invalid input");const o=Sn(r.zone,ut.defaultZone);return o.isValid?new Ae({ts:n,zone:o,loc:Ge.fromObject(r)}):Ae.invalid(No(o))}static fromMillis(e,r={}){if(Pn(e))return e<-fb||e>fb?Ae.invalid("Timestamp out of range"):new Ae({ts:e,zone:Sn(r.zone,ut.defaultZone),loc:Ge.fromObject(r)});throw new Mt(`fromMillis requires a numerical input, but received a ${typeof e} with value ${e}`)}static fromSeconds(e,r={}){if(Pn(e))return new Ae({ts:1e3*e,zone:Sn(r.zone,ut.defaultZone),loc:Ge.fromObject(r)});throw new Mt("fromSeconds requires a numerical input")}static fromObject(e,r={}){e=e||{};const n=Sn(r.zone,ut.defaultZone);if(!n.isValid)return Ae.invalid(No(n));const i=Ge.fromObject(r),o=vl(e,gb),{minDaysInFirstWeek:s,startOfWeek:a}=Jy(o,i),l=ut.now(),c=Pe(r.specificOffset)?n.offset(l):r.specificOffset,u=!Pe(o.ordinal),h=!Pe(o.year),f=!Pe(o.month)||!Pe(o.day),d=h||f,y=o.weekYear||o.weekNumber;if((d||u)&&y)throw new Ri("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(f&&u)throw new Ri("Can't mix ordinal dates with month/day");const g=y||o.weekday&&!d;let p,b,v=ya(l,c);g?(p=sT,b=iT,v=ml(v,s,a)):u?(p=aT,b=oT,v=Jc(v)):(p=Ha,b=$1);let S=!1;for(const x of p)Pe(o[x])?o[x]=S?b[x]:v[x]:S=!0;const k=g?function(x,O=4,N=1){const G=yl(x.weekYear),U=rr(x.weekNumber,1,rs(x.weekYear,O,N)),z=rr(x.weekday,1,7);return G?U?!z&&er("weekday",x.weekday):er("week",x.weekNumber):er("weekYear",x.weekYear)}(o,s,a):u?function(x){const O=yl(x.year),N=rr(x.ordinal,1,Hi(x.year));return O?!N&&er("ordinal",x.ordinal):er("year",x.year)}(o):m1(o),M=k||y1(o);if(M)return Ae.invalid(M);const A=g?Zy(o,s,a):u?Xy(o):o,[I,T]=za(A,c,n),_=new Ae({ts:I,zone:n,o:T,loc:i});return o.weekday&&d&&e.weekday!==_.weekday?Ae.invalid("mismatched weekday",`you can't specify both a weekday of ${o.weekday} and a date of ${_.toISO()}`):_.isValid?_:Ae.invalid(_.invalid)}static fromISO(e,r={}){const[n,i]=function(o){return Di(o,[FA,ib],[WA,$A],[UA,zA],[VA,HA])}(e);return Ei(n,i,r,"ISO 8601",e)}static fromRFC2822(e,r={}){const[n,i]=function(o){return Di(function(s){return s.replace(/\([^()]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").trim()}(o),[RA,DA])}(e);return Ei(n,i,r,"RFC 2822",e)}static fromHTTP(e,r={}){const[n,i]=function(o){return Di(o,[jA,nb],[NA,nb],[LA,BA])}(e);return Ei(n,i,r,"HTTP",r)}static fromFormat(e,r,n={}){if(Pe(e)||Pe(r))throw new Mt("fromFormat requires an input string and a format");const{locale:i=null,numberingSystem:o=null}=n,s=Ge.fromOpts({locale:i,numberingSystem:o,defaultToEN:!0}),[a,l,c,u]=function(h,f,d){const{result:y,zone:g,specificOffset:p,invalidReason:b}=hb(h,f,d);return[y,g,p,b]}(s,e,r);return u?Ae.invalid(u):Ei(a,l,n,`format ${r}`,e,c)}static fromString(e,r,n={}){return Ae.fromFormat(e,r,n)}static fromSQL(e,r={}){const[n,i]=function(o){return Di(o,[YA,ib],[KA,GA])}(e);return Ei(n,i,r,"SQL",e)}static invalid(e,r=null){if(!e)throw new Mt("need to specify a reason the DateTime is invalid");const n=e instanceof fr?e:new fr(e,r);if(ut.throwOnInvalid)throw new lA(n);return new Ae({invalid:n})}static isDateTime(e){return e&&e.isLuxonDateTime||!1}static parseFormatForOpts(e,r={}){const n=U1(e,Ge.fromObject(r));return n?n.map(i=>i?i.val:null).join(""):null}static expandFormat(e,r={}){return F1(Pt.parseFormat(e),Ge.fromObject(r)).map(n=>n.val).join("")}static resetCache(){Lo=void 0,Qf.clear()}get(e){return this[e]}get isValid(){return this.invalid===null}get invalidReason(){return this.invalid?this.invalid.reason:null}get invalidExplanation(){return this.invalid?this.invalid.explanation:null}get locale(){return this.isValid?this.loc.locale:null}get numberingSystem(){return this.isValid?this.loc.numberingSystem:null}get outputCalendar(){return this.isValid?this.loc.outputCalendar:null}get zone(){return this._zone}get zoneName(){return this.isValid?this.zone.name:null}get year(){return this.isValid?this.c.year:NaN}get quarter(){return this.isValid?Math.ceil(this.c.month/3):NaN}get month(){return this.isValid?this.c.month:NaN}get day(){return this.isValid?this.c.day:NaN}get hour(){return this.isValid?this.c.hour:NaN}get minute(){return this.isValid?this.c.minute:NaN}get second(){return this.isValid?this.c.second:NaN}get millisecond(){return this.isValid?this.c.millisecond:NaN}get weekYear(){return this.isValid?ru(this).weekYear:NaN}get weekNumber(){return this.isValid?ru(this).weekNumber:NaN}get weekday(){return this.isValid?ru(this).weekday:NaN}get isWeekend(){return this.isValid&&this.loc.getWeekendDays().includes(this.weekday)}get localWeekday(){return this.isValid?nu(this).weekday:NaN}get localWeekNumber(){return this.isValid?nu(this).weekNumber:NaN}get localWeekYear(){return this.isValid?nu(this).weekYear:NaN}get ordinal(){return this.isValid?Jc(this.c).ordinal:NaN}get monthShort(){return this.isValid?ga.months("short",{locObj:this.loc})[this.month-1]:null}get monthLong(){return this.isValid?ga.months("long",{locObj:this.loc})[this.month-1]:null}get weekdayShort(){return this.isValid?ga.weekdays("short",{locObj:this.loc})[this.weekday-1]:null}get weekdayLong(){return this.isValid?ga.weekdays("long",{locObj:this.loc})[this.weekday-1]:null}get offset(){return this.isValid?+this.o:NaN}get offsetNameShort(){return this.isValid?this.zone.offsetName(this.ts,{format:"short",locale:this.locale}):null}get offsetNameLong(){return this.isValid?this.zone.offsetName(this.ts,{format:"long",locale:this.locale}):null}get isOffsetFixed(){return this.isValid?this.zone.isUniversal:null}get isInDST(){return!this.isOffsetFixed&&(this.offset>this.set({month:1,day:1}).offset||this.offset>this.set({month:5}).offset)}getPossibleOffsets(){if(!this.isValid||this.isOffsetFixed)return[this];const e=864e5,r=6e4,n=Zl(this.c),i=this.zone.offset(n-e),o=this.zone.offset(n+e),s=this.zone.offset(n-i*r),a=this.zone.offset(n-o*r);if(s===a)return[this];const l=n-s*r,c=n-a*r,u=ya(l,s),h=ya(c,a);return u.hour===h.hour&&u.minute===h.minute&&u.second===h.second&&u.millisecond===h.millisecond?[qn(this,{ts:l}),qn(this,{ts:c})]:[this]}get isInLeapYear(){return Ls(this.year)}get daysInMonth(){return bl(this.year,this.month)}get daysInYear(){return this.isValid?Hi(this.year):NaN}get weeksInWeekYear(){return this.isValid?rs(this.weekYear):NaN}get weeksInLocalWeekYear(){return this.isValid?rs(this.localWeekYear,this.loc.getMinDaysInFirstWeek(),this.loc.getStartOfWeek()):NaN}resolvedLocaleOptions(e={}){const{locale:r,numberingSystem:n,calendar:i}=Pt.create(this.loc.clone(e),e).resolvedOptions(this);return{locale:r,numberingSystem:n,outputCalendar:i}}toUTC(e=0,r={}){return this.setZone(Nt.instance(e),r)}toLocal(){return this.setZone(ut.defaultZone)}setZone(e,{keepLocalTime:r=!1,keepCalendarTime:n=!1}={}){if((e=Sn(e,ut.defaultZone)).equals(this.zone))return this;if(e.isValid){let i=this.ts;if(r||n){const o=e.offset(this.ts),s=this.toObject();[i]=za(s,o,e)}return qn(this,{ts:i,zone:e})}return Ae.invalid(No(e))}reconfigure({locale:e,numberingSystem:r,outputCalendar:n}={}){return qn(this,{loc:this.loc.clone({locale:e,numberingSystem:r,outputCalendar:n})})}setLocale(e){return this.reconfigure({locale:e})}set(e){if(!this.isValid)return this;const r=vl(e,gb),{minDaysInFirstWeek:n,startOfWeek:i}=Jy(r,this.loc),o=!Pe(r.weekYear)||!Pe(r.weekNumber)||!Pe(r.weekday),s=!Pe(r.ordinal),a=!Pe(r.year),l=!Pe(r.month)||!Pe(r.day),c=a||l,u=r.weekYear||r.weekNumber;if((c||s)&&u)throw new Ri("Can't mix weekYear/weekNumber units with year/month/day or ordinals");if(l&&s)throw new Ri("Can't mix ordinal dates with month/day");let h;o?h=Zy({...ml(this.c,n,i),...r},n,i):Pe(r.ordinal)?(h={...this.toObject(),...r},Pe(r.day)&&(h.day=Math.min(bl(h.year,h.month),h.day))):h=Xy({...Jc(this.c),...r});const[f,d]=za(h,this.o,this.zone);return qn(this,{ts:f,o:d})}plus(e){return this.isValid?qn(this,db(this,Ue.fromDurationLike(e))):this}minus(e){return this.isValid?qn(this,db(this,Ue.fromDurationLike(e).negate())):this}startOf(e,{useLocaleWeeks:r=!1}={}){if(!this.isValid)return this;const n={},i=Ue.normalizeUnit(e);switch(i){case"years":n.month=1;case"quarters":case"months":n.day=1;case"weeks":case"days":n.hour=0;case"hours":n.minute=0;case"minutes":n.second=0;case"seconds":n.millisecond=0}if(i==="weeks")if(r){const o=this.loc.getStartOfWeek(),{weekday:s}=this;s=3&&(l+="T"),l+=pb(this,a,r,n,i,o,s),l}toISODate({format:e="extended",precision:r="day"}={}){return this.isValid?iu(this,e==="extended",qa(r)):null}toISOWeekDate(){return ba(this,"kkkk-'W'WW-c")}toISOTime({suppressMilliseconds:e=!1,suppressSeconds:r=!1,includeOffset:n=!0,includePrefix:i=!1,extendedZone:o=!1,format:s="extended",precision:a="milliseconds"}={}){return this.isValid?(a=qa(a),(i&&Ha.indexOf(a)>=3?"T":"")+pb(this,s==="extended",r,e,n,o,a)):null}toRFC2822(){return ba(this,"EEE, dd LLL yyyy HH:mm:ss ZZZ",!1)}toHTTP(){return ba(this.toUTC(),"EEE, dd LLL yyyy HH:mm:ss 'GMT'")}toSQLDate(){return this.isValid?iu(this,!0):null}toSQLTime({includeOffset:e=!0,includeZone:r=!1,includeOffsetSpace:n=!0}={}){let i="HH:mm:ss.SSS";return(r||e)&&(n&&(i+=" "),r?i+="z":e&&(i+="ZZ")),ba(this,i,!0)}toSQL(e={}){return this.isValid?`${this.toSQLDate()} ${this.toSQLTime(e)}`:null}toString(){return this.isValid?this.toISO():tu}[Symbol.for("nodejs.util.inspect.custom")](){return this.isValid?`DateTime { ts: ${this.toISO()}, zone: ${this.zone.name}, locale: ${this.locale} }`:`DateTime { Invalid, reason: ${this.invalidReason} }`}valueOf(){return this.toMillis()}toMillis(){return this.isValid?this.ts:NaN}toSeconds(){return this.isValid?this.ts/1e3:NaN}toUnixInteger(){return this.isValid?Math.floor(this.ts/1e3):NaN}toJSON(){return this.toISO()}toBSON(){return this.toJSDate()}toObject(e={}){if(!this.isValid)return{};const r={...this.c};return e.includeConfig&&(r.outputCalendar=this.outputCalendar,r.numberingSystem=this.loc.numberingSystem,r.locale=this.loc.locale),r}toJSDate(){return new Date(this.isValid?this.ts:NaN)}diff(e,r="milliseconds",n={}){if(!this.isValid||!e.isValid)return Ue.invalid("created by diffing an invalid DateTime");const i={locale:this.locale,numberingSystem:this.numberingSystem,...n},o=(l=r,Array.isArray(l)?l:[l]).map(Ue.normalizeUnit),s=e.valueOf()>this.valueOf(),a=tT(s?this:e,s?e:this,o,i);var l;return s?a.negate():a}diffNow(e="milliseconds",r={}){return this.diff(Ae.now(),e,r)}until(e){return this.isValid?ct.fromDateTimes(this,e):this}hasSame(e,r,n){if(!this.isValid)return!1;const i=e.valueOf(),o=this.setZone(e.zone,{keepLocalTime:!0});return o.startOf(r,n)<=i&&i<=o.endOf(r,n)}equals(e){return this.isValid&&e.isValid&&this.valueOf()===e.valueOf()&&this.zone.equals(e.zone)&&this.loc.equals(e.loc)}toRelative(e={}){if(!this.isValid)return null;const r=e.base||Ae.fromObject({},{zone:this.zone}),n=e.padding?thisr.valueOf(),Math.min)}static max(...e){if(!e.every(Ae.isDateTime))throw new Mt("max requires all arguments be DateTimes");return eb(e,r=>r.valueOf(),Math.max)}static fromFormatExplain(e,r,n={}){const{locale:i=null,numberingSystem:o=null}=n;return hb(Ge.fromOpts({locale:i,numberingSystem:o,defaultToEN:!0}),e,r)}static fromStringExplain(e,r,n={}){return Ae.fromFormatExplain(e,r,n)}static buildFormatParser(e,r={}){const{locale:n=null,numberingSystem:i=null}=r,o=Ge.fromOpts({locale:n,numberingSystem:i,defaultToEN:!0});return new W1(o,e)}static fromFormatParser(e,r,n={}){if(Pe(e)||Pe(r))throw new Mt("fromFormatParser requires an input string and a format parser");const{locale:i=null,numberingSystem:o=null}=n,s=Ge.fromOpts({locale:i,numberingSystem:o,defaultToEN:!0});if(!s.equals(r.locale))throw new Mt(`fromFormatParser called with a locale of ${s}, but the format parser was created for ${r.locale}`);const{result:a,zone:l,specificOffset:c,invalidReason:u}=r.explainFromTokens(e);return u?Ae.invalid(u):Ei(a,l,n,`format ${r.format}`,e,c)}static get DATE_SHORT(){return gl}static get DATE_MED(){return Y_}static get DATE_MED_WITH_WEEKDAY(){return hA}static get DATE_FULL(){return K_}static get DATE_HUGE(){return G_}static get TIME_SIMPLE(){return Q_}static get TIME_WITH_SECONDS(){return Z_}static get TIME_WITH_SHORT_OFFSET(){return X_}static get TIME_WITH_LONG_OFFSET(){return J_}static get TIME_24_SIMPLE(){return e1}static get TIME_24_WITH_SECONDS(){return t1}static get TIME_24_WITH_SHORT_OFFSET(){return r1}static get TIME_24_WITH_LONG_OFFSET(){return n1}static get DATETIME_SHORT(){return i1}static get DATETIME_SHORT_WITH_SECONDS(){return o1}static get DATETIME_MED(){return s1}static get DATETIME_MED_WITH_SECONDS(){return a1}static get DATETIME_MED_WITH_WEEKDAY(){return fA}static get DATETIME_FULL(){return l1}static get DATETIME_FULL_WITH_SECONDS(){return c1}static get DATETIME_HUGE(){return u1}static get DATETIME_HUGE_WITH_SECONDS(){return h1}}function ko(t){if(Ae.isDateTime(t))return t;if(t&&t.valueOf&&Pn(t.valueOf()))return Ae.fromJSDate(t);if(t&&typeof t=="object")return Ae.fromObject(t);throw new Mt(`Unknown datetime argument: ${t}, of type ${typeof t}`)}/*! + * chartjs-adapter-luxon v1.3.1 + * https://www.chartjs.org + * (c) 2023 chartjs-adapter-luxon Contributors + * Released under the MIT license + */const lT={datetime:Ae.DATETIME_MED_WITH_SECONDS,millisecond:"h:mm:ss.SSS a",second:Ae.TIME_WITH_SECONDS,minute:Ae.TIME_SIMPLE,hour:{hour:"numeric"},day:{day:"numeric",month:"short"},week:"DD",month:{month:"short",year:"numeric"},quarter:"'Q'q - yyyy",year:{year:"numeric"}};N_._date.override({_id:"luxon",_create:function(t){return Ae.fromMillis(t,this.options)},init(t){this.options.locale||(this.options.locale=t.locale)},formats:function(){return lT},parse:function(t,e){const r=this.options,n=typeof t;return t===null||n==="undefined"?null:(n==="number"?t=this._create(t):n==="string"?t=typeof e=="string"?Ae.fromFormat(t,e,r):Ae.fromISO(t,r):t instanceof Date?t=Ae.fromJSDate(t,r):n!=="object"||t instanceof Ae||(t=Ae.fromObject(t,r)),t.isValid?t.valueOf():null)},format:function(t,e){const r=this._create(t);return typeof e=="string"?r.toFormat(e):r.toLocaleString(e)},add:function(t,e,r){const n={};return n[r]=e,this._create(t).plus(n).valueOf()},diff:function(t,e,r){return this._create(t).diff(this._create(e)).as(r).valueOf()},startOf:function(t,e,r){if(e==="isoWeek"){r=Math.trunc(Math.min(Math.max(0,r),6));const n=this._create(t);return n.minus({days:(n.weekday-r+7)%7}).startOf("day").valueOf()}return e?this._create(t).startOf(e).valueOf():t},endOf:function(t,e){return this._create(t).endOf(e).valueOf()}});/*! +* chartjs-plugin-annotation v3.1.0 +* https://www.chartjs.org/chartjs-plugin-annotation/index + * (c) 2024 chartjs-plugin-annotation Contributors + * Released under the MIT License + */const vb={modes:{point:(t,e)=>va(t,e,{intersect:!0}),nearest:(t,e,r)=>function(n,i,o){let s=Number.POSITIVE_INFINITY;return va(n,i,o).reduce((a,l)=>{const c=l.getCenterPoint(),u=function(f,d,y){return y==="x"?{x:f.x,y:d.y}:y==="y"?{x:d.x,y:f.y}:d}(i,c,o.axis),h=xs(i,u);return ha._index-l._index).slice(0,1)}(t,e,r),x:(t,e,r)=>va(t,e,{intersect:r.intersect,axis:"x"}),y:(t,e,r)=>va(t,e,{intersect:r.intersect,axis:"y"})}};function Zf(t,e,r){return(vb.modes[r.mode]||vb.modes.nearest)(t,e,r)}function va(t,e,r){return t.filter(n=>r.intersect?n.inRange(e.x,e.y):function(i,o,s){return s!=="x"&&s!=="y"?i.inRange(o.x,o.y,"x",!0)||i.inRange(o.x,o.y,"y",!0):i.inRange(o.x,o.y,s,!0)}(n,e,r.axis))}function ui(t,e,r){const n=Math.cos(r),i=Math.sin(r),o=e.x,s=e.y;return{x:o+n*(t.x-o)-i*(t.y-s),y:s+i*(t.x-o)+n*(t.y-s)}}const cT=(t,e)=>e>t||t.length>e.length&&t.slice(0,e.length)===e,ji=.001,ec=(t,e,r)=>Math.min(r,Math.max(e,t)),z1=(t,e)=>t.value>=t.start-e&&t.value<=t.end+e;function uT(t,e,r){for(const n of Object.keys(t))t[n]=ec(t[n],e,r);return t}function H1(t,{x:e,y:r,x2:n,y2:i},o,{borderWidth:s,hitTolerance:a}){const l=(s+a)/2,c=t.x>=e-l-ji&&t.x<=n+l+ji,u=t.y>=r-l-ji&&t.y<=i+l+ji;return o==="x"?c:(o==="y"||c)&&u}function q1(t,{rect:e,center:r},n,{rotation:i,borderWidth:o,hitTolerance:s}){return H1(ui(t,r,gt(-i)),e,n,{borderWidth:o,hitTolerance:s})}function mi(t,e){const{centerX:r,centerY:n}=t.getProps(["centerX","centerY"],e);return{x:r,y:n}}const Y1=t=>typeof t=="string"&&t.endsWith("%"),K1=t=>parseFloat(t)/100,G1=t=>ec(K1(t),0,1),Eo=(t,e)=>({x:t,y:e,x2:t,y2:e,width:0,height:0}),hT={box:t=>Eo(t.centerX,t.centerY),doughnutLabel:t=>Eo(t.centerX,t.centerY),ellipse:t=>({centerX:t.centerX,centerY:t.centerX,radius:0,width:0,height:0}),label:t=>Eo(t.centerX,t.centerY),line:t=>Eo(t.x,t.y),point:t=>({centerX:t.centerX,centerY:t.centerY,radius:0,width:0,height:0}),polygon:t=>Eo(t.centerX,t.centerY)};function ip(t,e){return e==="start"?0:e==="end"?t:Y1(e)?G1(e)*t:t/2}function Mn(t,e,r=!0){return typeof e=="number"?e:Y1(e)?(r?G1(e):K1(e))*t:t}function Q1(t,e,{borderWidth:r,position:n,xAdjust:i,yAdjust:o},s){const a=je(s),l=e.width+(a?s.width:0)+r,c=e.height+(a?s.height:0)+r,u=op(n),h=wb(t.x,l,i,u.x),f=wb(t.y,c,o,u.y);return{x:h,y:f,x2:h+l,y2:f+c,width:l,height:c,centerX:h+l/2,centerY:f+c/2}}function op(t,e="center"){return je(t)?{x:Ne(t.x,e),y:Ne(t.y,e)}:{x:t=Ne(t,e),y:t}}const Z1=(t,e)=>t&&t.autoFit&&e<1;function X1(t,e){const r=t.font,n=st(r)?r:[r];return Z1(t,e)?n.map(function(i){const o=Mr(i);return o.size=Math.floor(i.size*e),o.lineHeight=i.lineHeight,Mr(o)}):n.map(i=>Mr(i))}function J1(t){return t&&(Gt(t.xValue)||Gt(t.yValue))}function wb(t,e,r=0,n){return t-ip(e,n)+r}function co(t,e,r){const n=r.init;if(n)return n===!0?xb(e,r):function(i,o,s){const a=Ze(s.init,[{chart:i,properties:o,options:s}]);if(a===!0)return xb(o,s);if(je(a))return a}(t,e,r)}function _b(t,e,r){let n=!1;return e.forEach(i=>{Ut(t[i])?(n=!0,r[i]=t[i]):Gt(r[i])&&delete r[i]}),n}function xb(t,e){const r=e.type||"line";return hT[r](t)}const ou=new Map;function tc(t){if(t&&typeof t=="object"){const e=t.toString();return e==="[object HTMLImageElement]"||e==="[object HTMLCanvasElement]"}}function rc(t,{x:e,y:r},n){n&&(t.translate(e,r),t.rotate(gt(n)),t.translate(-e,-r))}function Kr(t,e){if(e&&e.borderWidth)return t.lineCap=e.borderCapStyle||"butt",t.setLineDash(e.borderDash),t.lineDashOffset=e.borderDashOffset,t.lineJoin=e.borderJoinStyle||"miter",t.lineWidth=e.borderWidth,t.strokeStyle=e.borderColor,!0}function uo(t,e){t.shadowColor=e.backgroundShadowColor,t.shadowBlur=e.shadowBlur,t.shadowOffsetX=e.shadowOffsetX,t.shadowOffsetY=e.shadowOffsetY}function nc(t,e){const r=e.content;if(tc(r))return{width:Mn(r.width,e.width),height:Mn(r.height,e.height)};const n=X1(e),i=e.textStrokeWidth,o=st(r)?r:[r],s=o.join()+(a=>a.reduce(function(l,c){return l+c.string},""))(n)+i+(t._measureText?"-spriting":"");return ou.has(s)||ou.set(s,function(a,l,c,u){a.save();const h=l.length;let f=0,d=u;for(let y=0;y0)return h.lineJoin="round",h.miterLimit=2,h.lineWidth=f.textStrokeWidth,h.strokeStyle=f.textStrokeColor,!0}(t,r)&&function(h,{x:f,y:d},y,g){h.beginPath();let p=0;y.forEach(function(b,v){const S=g[Math.min(v,g.length-1)],k=S.lineHeight;h.font=S.string,h.strokeText(b,f,d+k/2+p),p+=k}),h.stroke()}(t,{x:c,y:u},o,s),function(h,{x:f,y:d},y,{fonts:g,colors:p}){let b=0;y.forEach(function(v,S){const k=p[Math.min(S,p.length-1)],M=g[Math.min(S,g.length-1)],A=M.lineHeight;h.beginPath(),h.font=M.string,h.fillStyle=k,h.fillText(v,f,d+A/2+b),b+=A,h.fill()})}(t,{x:c,y:u},o,{fonts:s,colors:l}),t.restore()}function fT(t,e,r,n){const{radius:i,options:o}=e,s=o.pointStyle,a=o.rotation;let l=(a||0)*Vd;if(tc(s))return t.save(),t.translate(r,n),t.rotate(l),t.drawImage(s,-s.width/2,-s.height/2,s.width,s.height),void t.restore();(c=>isNaN(c)||c<=0)(i)||function(c,{x:u,y:h,radius:f,rotation:d,style:y,rad:g}){let p,b,v,S;switch(c.beginPath(),y){default:c.arc(u,h,f,0,pt),c.closePath();break;case"triangle":c.moveTo(u+Math.sin(g)*f,h-Math.cos(g)*f),g+=cl,c.lineTo(u+Math.sin(g)*f,h-Math.cos(g)*f),g+=cl,c.lineTo(u+Math.sin(g)*f,h-Math.cos(g)*f),c.closePath();break;case"rectRounded":S=.516*f,v=f-S,p=Math.cos(g+Xt)*v,b=Math.sin(g+Xt)*v,c.arc(u-p,h-b,S,g-Qe,g-xt),c.arc(u+b,h-p,S,g-xt,g),c.arc(u+p,h+b,S,g,g+xt),c.arc(u-b,h+p,S,g+xt,g+Qe),c.closePath();break;case"rect":if(!d){v=Math.SQRT1_2*f,c.rect(u-v,h-v,2*v,2*v);break}g+=Xt;case"rectRot":p=Math.cos(g)*f,b=Math.sin(g)*f,c.moveTo(u-p,h-b),c.lineTo(u+b,h-p),c.lineTo(u+p,h+b),c.lineTo(u-b,h+p),c.closePath();break;case"crossRot":g+=Xt;case"cross":p=Math.cos(g)*f,b=Math.sin(g)*f,c.moveTo(u-p,h-b),c.lineTo(u+p,h+b),c.moveTo(u+b,h-p),c.lineTo(u-b,h+p);break;case"star":p=Math.cos(g)*f,b=Math.sin(g)*f,c.moveTo(u-p,h-b),c.lineTo(u+p,h+b),c.moveTo(u+b,h-p),c.lineTo(u-b,h+p),g+=Xt,p=Math.cos(g)*f,b=Math.sin(g)*f,c.moveTo(u-p,h-b),c.lineTo(u+p,h+b),c.moveTo(u+b,h-p),c.lineTo(u-b,h+p);break;case"line":p=Math.cos(g)*f,b=Math.sin(g)*f,c.moveTo(u-p,h-b),c.lineTo(u+p,h+b);break;case"dash":c.moveTo(u,h),c.lineTo(u+Math.cos(g)*f,h+Math.sin(g)*f)}c.fill()}(t,{x:r,y:n,radius:i,rotation:a,style:s,rad:l})}const Sb=["left","bottom","top","right"];function dT(t,e){const{pointX:r,pointY:n,options:i}=e,o=i.callout,s=o&&o.display&&function(f,d){const y=d.position;return Sb.includes(y)?y:function(g,p){const{x:b,y:v,x2:S,y2:k,width:M,height:A,pointX:I,pointY:T,centerX:_,centerY:x,rotation:O}=g,N={x:_,y:x},G=p.start,U=Mn(M,G),z=Mn(A,G),q=[b,b+U,b+U,S],B=[v+z,k,v,k],ne=[];for(let H=0;H<4;H++){const Q=ui({x:q[H],y:B[H]},N,gt(O));ne.push({position:Sb[H],distance:xs(Q,{x:I,y:T})})}return ne.sort((H,Q)=>H.distance-Q.distance)[0].position}(f,d)}(e,o);if(!s||function(f,d,y){const{pointX:g,pointY:p}=f,b=d.margin;let v=g,S=p;return y==="left"?v+=b:y==="right"?v-=b:y==="top"?S+=b:y==="bottom"&&(S-=b),f.inRange(v,S)}(e,o,s))return;if(t.save(),t.beginPath(),!Kr(t,o))return t.restore();const{separatorStart:a,separatorEnd:l}=function(f,d){const{x:y,y:g,x2:p,y2:b}=f,v=function(M,A){const{width:I,height:T,options:_}=M,x=_.callout.margin+_.borderWidth/2;return A==="right"?I+x:A==="bottom"?T+x:-x}(f,d);let S,k;return d==="left"||d==="right"?(S={x:y+v,y:g},k={x:S.x,y:b}):(S={x:y,y:g+v},k={x:p,y:S.y}),{separatorStart:S,separatorEnd:k}}(e,s),{sideStart:c,sideEnd:u}=function(f,d,y){const{y:g,width:p,height:b,options:v}=f,S=v.callout.start,k=function(I,T){const _=T.side;return I==="left"||I==="top"?-_:_}(d,v.callout);let M,A;return d==="left"||d==="right"?(M={x:y.x,y:g+Mn(b,S)},A={x:M.x+k,y:M.y}):(M={x:y.x+Mn(p,S),y:y.y},A={x:M.x,y:M.y+k}),{sideStart:M,sideEnd:A}}(e,s,a);(o.margin>0||i.borderWidth===0)&&(t.moveTo(a.x,a.y),t.lineTo(l.x,l.y)),t.moveTo(c.x,c.y),t.lineTo(u.x,u.y);const h=ui({x:r,y:n},e.getCenterPoint(),gt(-e.rotation));t.lineTo(h.x,h.y),t.stroke(),t.restore()}const kb={xScaleID:{min:"xMin",max:"xMax",start:"left",end:"right",startProp:"x",endProp:"x2"},yScaleID:{min:"yMin",max:"yMax",start:"bottom",end:"top",startProp:"y",endProp:"y2"}};function ro(t,e,r){return yt(e=typeof e=="number"?e:t.parse(e))?t.getPixelForValue(e):r}function hi(t,e,r){const n=e[r];if(n||r==="scaleID")return n;const i=r.charAt(0),o=Object.values(t).filter(s=>s.axis&&s.axis===i);return o.length?o[0].id:i}function rx(t,e){if(t){const r=t.options.reverse;return{start:ro(t,e.min,r?e.end:e.start),end:ro(t,e.max,r?e.start:e.end)}}}function nx(t,e){const{chartArea:r,scales:n}=t,i=n[hi(n,e,"xScaleID")],o=n[hi(n,e,"yScaleID")];let s=r.width/2,a=r.height/2;return i&&(s=ro(i,e.xValue,i.left+i.width/2)),o&&(a=ro(o,e.yValue,o.top+o.height/2)),{x:s,y:a}}function sp(t,e){const r=t.scales,n=r[hi(r,e,"xScaleID")],i=r[hi(r,e,"yScaleID")];if(!n&&!i)return{};let{left:o,right:s}=n||t.chartArea,{top:a,bottom:l}=i||t.chartArea;const c=Eb(n,{min:e.xMin,max:e.xMax,start:o,end:s});o=c.start,s=c.end;const u=Eb(i,{min:e.yMin,max:e.yMax,start:l,end:a});return a=u.start,l=u.end,{x:o,y:a,x2:s,y2:l,width:s-o,height:l-a,centerX:o+(s-o)/2,centerY:a+(l-a)/2}}function ix(t,e){if(!J1(e)){const r=sp(t,e);let n=e.radius;n&&!isNaN(n)||(n=Math.min(r.width,r.height)/2,e.radius=n);const i=2*n,o=r.centerX+e.xAdjust,s=r.centerY+e.yAdjust;return{x:o-n,y:s-n,x2:o+n,y2:s+n,centerX:o,centerY:s,width:i,height:i,radius:n}}return function(r,n){const i=nx(r,n),o=2*n.radius;return{x:i.x-n.radius+n.xAdjust,y:i.y-n.radius+n.yAdjust,x2:i.x+n.radius+n.xAdjust,y2:i.y+n.radius+n.yAdjust,centerX:i.x+n.xAdjust,centerY:i.y+n.yAdjust,radius:n.radius,width:o,height:o}}(t,e)}function pT(t,e){const{scales:r,chartArea:n}=t,i=r[e.scaleID],o={x:n.left,y:n.top,x2:n.right,y2:n.bottom};return i?function(s,a,l){const c=ro(s,l.value,NaN),u=ro(s,l.endValue,c);s.isHorizontal()?(a.x=c,a.x2=u):(a.y=c,a.y2=u)}(i,o,e):function(s,a,l){for(const c of Object.keys(kb)){const u=s[hi(s,l,c)];if(u){const{min:h,max:f,start:d,end:y,startProp:g,endProp:p}=kb[c],b=rx(u,{min:l[h],max:l[f],start:u[d],end:u[y]});a[g]=b.start,a[p]=b.end}}}(r,o,e),o}function ox(t,e){const r=sp(t,e);return r.initProperties=co(t,r,e),r.elements=[{type:"label",optionScope:"label",properties:gT(t,r,e),initProperties:r.initProperties}],r}function Eb(t,e){const r=rx(t,e)||e;return{start:Math.min(r.start,r.end),end:Math.max(r.start,r.end)}}function Ob(t,e){const{start:r,end:n,borderWidth:i}=t,{position:o,padding:{start:s,end:a},adjust:l}=e;return r+i/2+l+ip(n-i-r-s-a-e.size,o)}function gT(t,e,r){const n=r.label;n.backgroundColor="transparent",n.callout.display=!1;const i=op(n.position),o=ar(n.padding),s=nc(t.ctx,n),a=function({properties:h,options:f},d,y,g){const{x:p,x2:b,width:v}=h;return Ob({start:p,end:b,borderWidth:f.borderWidth},{position:y.x,padding:{start:g.left,end:g.right},adjust:f.label.xAdjust,size:d.width})}({properties:e,options:r},s,i,o),l=function({properties:h,options:f},d,y,g){const{y:p,y2:b,height:v}=h;return Ob({start:p,end:b,borderWidth:f.borderWidth},{position:y.y,padding:{start:g.top,end:g.bottom},adjust:f.label.yAdjust,size:d.height})}({properties:e,options:r},s,i,o),c=s.width+o.width,u=s.height+o.height;return{x:a,y:l,x2:a+c,y2:l+u,width:c,height:u,centerX:a+c/2,centerY:l+u/2,rotation:n.rotation}}const Xf=["enter","leave"],ap=Xf.concat("click");function mT(t,e,r){if(t.listened)switch(e.type){case"mousemove":case"mouseout":return function(n,i,o){if(!n.moveListened)return;let s;s=i.type==="mousemove"?Zf(n.visibleElements,i,o.interaction):[];const a=n.hovered;n.hovered=s;const l={state:n,event:i};let c=Mb(l,"leave",a,s);return Mb(l,"enter",s,a)||c}(t,e,r);case"click":return function(n,i,o){const s=n.listeners,a=Zf(n.visibleElements,i,o.interaction);let l;for(const c of a)l=sx(c.options.click||s.click,c,i)||l;return l}(t,e,r)}}function Mb({state:t,event:e},r,n,i){let o;for(const s of n)i.indexOf(s)<0&&(o=sx(s.options[r]||t.listeners[r],s,e)||o);return o}function sx(t,e,r){return Ze(t,[e.$context,r])===!0}const wl=["afterDraw","beforeDraw"];function Ab(t,e,r){if(t.hooked)return Ze(e.options[r]||t.hooks[r],[e.$context])}function yT(t,e,r){const n=function(o,s,a){const l=s.axis,c=s.id,u=l+"ScaleID",h={min:Ne(s.min,Number.NEGATIVE_INFINITY),max:Ne(s.max,Number.POSITIVE_INFINITY)};for(const f of a)f.scaleID===c?Ib(f,s,["value","endValue"],h):hi(o,f,u)===c&&Ib(f,s,[l+"Min",l+"Max",l+"Value"],h);return h}(t.scales,e,r);let i=Tb(e,n,"min","suggestedMin");i=Tb(e,n,"max","suggestedMax")||i,i&&Ut(e.handleTickRangeOptions)&&e.handleTickRangeOptions()}function Tb(t,e,r,n){if(yt(e[r])&&!function(i,o,s){return Gt(i[o])||Gt(i[s])}(t.options,r,n)){const i=t[r]!==e[r];return t[r]=e[r],i}}function bT(t,e){for(const r of["scaleID","xScaleID","yScaleID"]){const n=hi(e,t,r);n&&!e[n]&&vT(t,r)}}function vT(t,e){if(e==="scaleID")return!0;const r=e.charAt(0);for(const n of["Min","Max","Value"])if(Gt(t[r+n]))return!0;return!1}function Ib(t,e,r,n){for(const i of r){const o=t[i];if(Gt(o)){const s=e.parse(o);n.min=Math.min(n.min,s),n.max=Math.max(n.max,s)}}}class Ni extends cr{inRange(e,r,n,i){const{x:o,y:s}=ui({x:e,y:r},this.getCenterPoint(i),gt(-this.options.rotation));return H1({x:o,y:s},this.getProps(["x","y","x2","y2"],i),n,this.options)}getCenterPoint(e){return mi(this,e)}draw(e){e.save(),rc(e,this.getCenterPoint(),this.options.rotation),ex(e,this,this.options),e.restore()}get label(){return this.elements&&this.elements[0]}resolveElementProperties(e,r){return ox(e,r)}}Ni.id="boxAnnotation",Ni.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:0,borderShadowColor:"transparent",borderWidth:1,display:!0,init:void 0,hitTolerance:0,label:{backgroundColor:"transparent",borderWidth:0,callout:{display:!1},color:"black",content:null,display:!1,drawTime:void 0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:"bold"},height:void 0,hitTolerance:void 0,opacity:void 0,padding:6,position:"center",rotation:void 0,textAlign:"start",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,yAdjust:0,z:void 0},rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},Ni.defaultRoutes={borderColor:"color",backgroundColor:"color"},Ni.descriptors={label:{_fallback:!0}};class Ya extends cr{inRange(e,r,n,i){return q1({x:e,y:r},{rect:this.getProps(["x","y","x2","y2"],i),center:this.getCenterPoint(i)},n,{rotation:this.rotation,borderWidth:0,hitTolerance:this.options.hitTolerance})}getCenterPoint(e){return mi(this,e)}draw(e){const r=this.options;r.display&&r.content&&(function(n,i){const{_centerX:o,_centerY:s,_radius:a,_startAngle:l,_endAngle:c,_counterclockwise:u,options:h}=i;n.save();const f=Kr(n,h);n.fillStyle=h.backgroundColor,n.beginPath(),n.arc(o,s,a,l,c,u),n.closePath(),n.fill(),f&&n.stroke(),n.restore()}(e,this),e.save(),rc(e,this.getCenterPoint(),this.rotation),tx(e,this,r,this._fitRatio),e.restore())}resolveElementProperties(e,r){const n=function(d,y){return d.getSortedVisibleDatasetMetas().reduce(function(g,p){const b=p.controller;return b instanceof Do&&function(v,S,k){if(!S.autoHide)return!0;for(let M=0;M=90?p:g},void 0)}(e,r);if(!n)return{};const{controllerMeta:i,point:o,radius:s}=function({chartArea:d},y,g){const{left:p,top:b,right:v,bottom:S}=d,{innerRadius:k,offsetX:M,offsetY:A}=g.controller,I=(p+v)/2+M,T=(b+S)/2+A,_={left:Math.max(I-k,p),right:Math.min(I+k,v),top:Math.max(T-k,b),bottom:Math.min(T+k,S)},x={x:(_.left+_.right)/2,y:(_.top+_.bottom)/2},O=y.spacing+y.borderWidth/2,N=k-O,G=x.y>T,U=G?b+O:S-O,z=function(B,ne,H,Q){const F=Math.pow(H-B,2),V=Math.pow(Q,2),Z=-2*ne,$=Math.pow(ne,2)+F-V,se=Math.pow(Z,2)-4*$;if(se<=0)return{_startAngle:0,_endAngle:pt};const le=(-Z-Math.sqrt(se))/2,ue=(-Z+Math.sqrt(se))/2;return{_startAngle:Nf({x:ne,y:H},{x:le,y:B}).angle,_endAngle:Nf({x:ne,y:H},{x:ue,y:B}).angle}}(U,I,T,N);return{controllerMeta:{_centerX:I,_centerY:T,_radius:N,_counterclockwise:G,...z},point:x,radius:Math.min(k,Math.min(_.right-_.left,_.bottom-_.top)/2)}}(e,r,n);let a=nc(e.ctx,r);const l=function({width:d,height:y},g){const p=Math.sqrt(Math.pow(d,2)+Math.pow(y,2));return 2*g/p}(a,s);Z1(r,l)&&(a={width:a.width*l,height:a.height*l});const{position:c,xAdjust:u,yAdjust:h}=r,f=Q1(o,a,{borderWidth:0,position:c,xAdjust:u,yAdjust:h});return{initProperties:co(e,f,r),...f,...i,rotation:r.rotation,_fitRatio:l}}}Ya.id="doughnutLabelAnnotation",Ya.defaults={autoFit:!0,autoHide:!0,backgroundColor:"transparent",backgroundShadowColor:"transparent",borderColor:"transparent",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderShadowColor:"transparent",borderWidth:0,color:"black",content:null,display:!0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:void 0},height:void 0,hitTolerance:0,init:void 0,opacity:void 0,position:"center",rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,spacing:1,textAlign:"center",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,yAdjust:0},Ya.defaultRoutes={};class is extends cr{inRange(e,r,n,i){return q1({x:e,y:r},{rect:this.getProps(["x","y","x2","y2"],i),center:this.getCenterPoint(i)},n,{rotation:this.rotation,borderWidth:this.options.borderWidth,hitTolerance:this.options.hitTolerance})}getCenterPoint(e){return mi(this,e)}draw(e){const r=this.options,n=!Gt(this._visible)||this._visible;r.display&&r.content&&n&&(e.save(),rc(e,this.getCenterPoint(),this.rotation),dT(e,this),ex(e,this,r),tx(e,function({x:i,y:o,width:s,height:a,options:l}){const c=l.borderWidth/2,u=ar(l.padding);return{x:i+u.left+c,y:o+u.top+c,width:s-u.left-u.right-l.borderWidth,height:a-u.top-u.bottom-l.borderWidth}}(this),r),e.restore())}resolveElementProperties(e,r){let n;if(J1(r))n=nx(e,r);else{const{centerX:s,centerY:a}=sp(e,r);n={x:s,y:a}}const i=ar(r.padding),o=Q1(n,nc(e.ctx,r),r,i);return{initProperties:co(e,o,r),pointX:n.x,pointY:n.y,...o,rotation:r.rotation}}}is.id="labelAnnotation",is.defaults={adjustScaleRange:!0,backgroundColor:"transparent",backgroundShadowColor:"transparent",borderCapStyle:"butt",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:0,borderShadowColor:"transparent",borderWidth:0,callout:{borderCapStyle:"butt",borderColor:void 0,borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderWidth:1,display:!1,margin:5,position:"auto",side:5,start:"50%"},color:"black",content:null,display:!0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:void 0},height:void 0,hitTolerance:0,init:void 0,opacity:void 0,padding:6,position:"center",rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,textAlign:"center",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,xMax:void 0,xMin:void 0,xScaleID:void 0,xValue:void 0,yAdjust:0,yMax:void 0,yMin:void 0,yScaleID:void 0,yValue:void 0,z:0},is.defaultRoutes={borderColor:"color"};const lp=(t,e,r)=>({x:t.x+r*(e.x-t.x),y:t.y+r*(e.y-t.y)}),Jf=(t,e,r)=>lp(e,r,Math.abs((t-e.y)/(r.y-e.y))).x,Pb=(t,e,r)=>lp(e,r,Math.abs((t-e.x)/(r.x-e.x))).y,Oo=t=>t*t,Cb=(t,e,r,n)=>(1-n)*(1-n)*t+2*(1-n)*n*e+n*n*r,su=(t,e,r,n)=>({x:Cb(t.x,e.x,r.x,n),y:Cb(t.y,e.y,r.y,n)}),Rb=(t,e,r,n)=>2*(1-n)*(e-t)+2*n*(r-e),Db=(t,e,r,n)=>-Math.atan2(Rb(t.x,e.x,r.x,n),Rb(t.y,e.y,r.y,n))+.5*Qe;class os extends cr{inRange(e,r,n,i){const o=(this.options.borderWidth+this.options.hitTolerance)/2;if(n!=="x"&&n!=="y"){const s={mouseX:e,mouseY:r},{path:a,ctx:l}=this;if(a){Kr(l,this.options),l.lineWidth+=this.options.hitTolerance;const{chart:c}=this.$context,u=e*c.currentDevicePixelRatio,h=r*c.currentDevicePixelRatio,f=l.isPointInStroke(a,u,h)||au(this,s,i);return l.restore(),f}return function(c,{mouseX:u,mouseY:h},f=.001,d){const{x:y,y:g,x2:p,y2:b}=c.getProps(["x","y","x2","y2"],d),v=p-y,S=b-g,k=Oo(v)+Oo(S),M=k===0?-1:((u-y)*v+(h-g)*S)/k;let A,I;return M<0?(A=y,I=g):M>1?(A=p,I=b):(A=y+M*v,I=g+M*S),Oo(u-A)+Oo(h-I)<=f}(this,s,Oo(o),i)||au(this,s,i)}return function(s,{mouseX:a,mouseY:l},c,{hitSize:u,useFinalPosition:h}){const f=((d,y,{x:g,y:p,x2:b,y2:v},S)=>S==="y"?{start:Math.min(p,v),end:Math.max(p,v),value:y}:{start:Math.min(g,b),end:Math.max(g,b),value:d})(a,l,s.getProps(["x","y","x2","y2"],h),c);return z1(f,u)||au(s,{mouseX:a,mouseY:l},h,c)}(this,{mouseX:e,mouseY:r},n,{hitSize:o,useFinalPosition:i})}getCenterPoint(e){return mi(this,e)}draw(e){const{x:r,y:n,x2:i,y2:o,cp:s,options:a}=this;if(e.save(),!Kr(e,a))return e.restore();uo(e,a);const l=Math.sqrt(Math.pow(i-r,2)+Math.pow(o-n,2));if(a.curve&&s)return function(y,g,p,b){const{x:v,y:S,x2:k,y2:M,options:A}=g,{startOpts:I,endOpts:T,startAdjust:_,endAdjust:x}=Fb(g),O={x:v,y:S},N={x:k,y:M},G=Db(O,p,N,0),U=Db(O,p,N,1)-Qe,z=su(O,p,N,_/b),q=su(O,p,N,1-x/b),B=new Path2D;y.beginPath(),B.moveTo(z.x,z.y),B.quadraticCurveTo(p.x,p.y,q.x,q.y),y.shadowColor=A.borderShadowColor,y.stroke(B),g.path=B,g.ctx=y,Ub(y,z,{angle:G,adjust:_},I),Ub(y,q,{angle:U,adjust:x},T)}(e,this,s,l),e.restore();const{startOpts:c,endOpts:u,startAdjust:h,endAdjust:f}=Fb(this),d=Math.atan2(o-n,i-r);e.translate(r,n),e.rotate(d),e.beginPath(),e.moveTo(0+h,0),e.lineTo(l-f,0),e.shadowColor=a.borderShadowColor,e.stroke(),ed(e,0,h,c),ed(e,l,-f,u),e.restore()}get label(){return this.elements&&this.elements[0]}resolveElementProperties(e,r){const n=pT(e,r),{x:i,y:o,x2:s,y2:a}=n,l=function({x:h,y:f,x2:d,y2:y},{top:g,right:p,bottom:b,left:v}){return!(hp&&d>p||fb&&y>b)}(n,e.chartArea),c=l?function(h,f,d){const{x:y,y:g}=Nb(h,f,d),{x:p,y:b}=Nb(f,h,d);return{x:y,y:g,x2:p,y2:b,width:Math.abs(p-y),height:Math.abs(b-g)}}({x:i,y:o},{x:s,y:a},e.chartArea):{x:i,y:o,x2:s,y2:a,width:Math.abs(s-i),height:Math.abs(a-o)};if(c.centerX=(s+i)/2,c.centerY=(a+o)/2,c.initProperties=co(e,c,r),r.curve){const h={x:c.x,y:c.y},f={x:c.x2,y:c.y2};c.cp=function(d,y,g){const{x:p,y:b,x2:v,y2:S,centerX:k,centerY:M}=d,A=Math.atan2(S-b,v-p),I=op(y.controlPoint,0);return ui({x:k+Mn(g,I.x,!1),y:M+Mn(g,I.y,!1)},{x:k,y:M},A)}(c,r,xs(h,f))}const u=function(h,f,d){const y=d.borderWidth,g=ar(d.padding),p=nc(h.ctx,d),b=p.width+g.width+y,v=p.height+g.height+y;return function(S,k,M,A){const{width:I,height:T,padding:_}=M,{xAdjust:x,yAdjust:O}=k,N={x:S.x,y:S.y},G={x:S.x2,y:S.y2},U=k.rotation==="auto"?function(V){const{x:Z,y:$,x2:se,y2:le}=V,ue=Math.atan2(le-$,se-Z);return ue>Qe/2?ue-Qe:uei&&(e=Pb(i,{x:t,y:e},r),t=i),eo&&(t=Jf(o,{x:t,y:e},r),e=o),{x:t,y:e}}function au(t,{mouseX:e,mouseY:r},n,i){const o=t.label;return o.options.display&&o.inRange(e,r,i,n)}function Lb(t,e,r,n){const{labelSize:i,padding:o}=e,s=t.w*n.dx,a=t.h*n.dy,l=s>0&&(i.w/2+o.left-n.x)/s,c=a>0&&(i.h/2+o.top-n.y)/a;return ec(Math.max(l,c),0,.25)}function Bb(t,e){const{size:r,min:n,max:i,padding:o}=e,s=r/2;return r>i-n?(i+n)/2:(n>=t-o-s&&(t=n+o+s),i<=t+o+s&&(t=i-o-s),t)}function Fb(t){const e=t.options,r=e.arrowHeads&&e.arrowHeads.start,n=e.arrowHeads&&e.arrowHeads.end;return{startOpts:r,endOpts:n,startAdjust:Wb(t,r),endAdjust:Wb(t,n)}}function Wb(t,e){if(!e||!e.display)return 0;const{length:r,width:n}=e,i=t.options.borderWidth/2,o={x:r,y:n+i};return Math.abs(Jf(0,o,{x:0,y:i}))}function ed(t,e,r,n){if(!n||!n.display)return;const{length:i,width:o,fill:s,backgroundColor:a,borderColor:l}=n,c=Math.abs(e-i)+r;t.beginPath(),uo(t,n),Kr(t,n),t.moveTo(c,-o),t.lineTo(e+r,0),t.lineTo(c,o),s===!0?(t.fillStyle=a||l,t.closePath(),t.fill(),t.shadowColor="transparent"):t.shadowColor=n.borderShadowColor,t.stroke()}function Ub(t,{x:e,y:r},{angle:n,adjust:i},o){o&&o.display&&(t.save(),t.translate(e,r),t.rotate(n),ed(t,0,-i,o),t.restore())}os.defaults={adjustScaleRange:!0,arrowHeads:{display:!1,end:Object.assign({},jb),fill:!1,length:12,start:Object.assign({},jb),width:6},borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:2,curve:!1,controlPoint:{y:"-50%"},display:!0,endValue:void 0,init:void 0,hitTolerance:0,label:{backgroundColor:"rgba(0,0,0,0.8)",backgroundShadowColor:"transparent",borderCapStyle:"butt",borderColor:"black",borderDash:[],borderDashOffset:0,borderJoinStyle:"miter",borderRadius:6,borderShadowColor:"transparent",borderWidth:0,callout:Object.assign({},is.defaults.callout),color:"#fff",content:null,display:!1,drawTime:void 0,font:{family:void 0,lineHeight:void 0,size:void 0,style:void 0,weight:"bold"},height:void 0,hitTolerance:void 0,opacity:void 0,padding:6,position:"center",rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,textAlign:"center",textStrokeColor:void 0,textStrokeWidth:0,width:void 0,xAdjust:0,yAdjust:0,z:void 0},scaleID:void 0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,value:void 0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},os.descriptors={arrowHeads:{start:{_fallback:!0},end:{_fallback:!0},_fallback:!0}},os.defaultRoutes={borderColor:"color"};class Bo extends cr{inRange(e,r,n,i){const o=this.options.rotation,s=(this.options.borderWidth+this.options.hitTolerance)/2;if(n!=="x"&&n!=="y")return function(d,y,g,p){const{width:b,height:v,centerX:S,centerY:k}=y,M=b/2,A=v/2;if(M<=0||A<=0)return!1;const I=gt(g||0),T=Math.cos(I),_=Math.sin(I),x=Math.pow(T*(d.x-S)+_*(d.y-k),2),O=Math.pow(_*(d.x-S)-T*(d.y-k),2);return x/Math.pow(M+p,2)+O/Math.pow(A+p,2)<=1.0001}({x:e,y:r},this.getProps(["width","height","centerX","centerY"],i),o,s);const{x:a,y:l,x2:c,y2:u}=this.getProps(["x","y","x2","y2"],i),h=n==="y"?{start:l,end:u}:{start:a,end:c},f=ui({x:e,y:r},this.getCenterPoint(i),gt(-o));return f[n]>=h.start-s-ji&&f[n]<=h.end+s+ji}getCenterPoint(e){return mi(this,e)}draw(e){const{width:r,height:n,centerX:i,centerY:o,options:s}=this;e.save(),rc(e,this.getCenterPoint(),s.rotation),uo(e,this.options),e.beginPath(),e.fillStyle=s.backgroundColor;const a=Kr(e,s);e.ellipse(i,o,n/2,r/2,Qe/2,0,2*Qe),e.fill(),a&&(e.shadowColor=s.borderShadowColor,e.stroke()),e.restore()}get label(){return this.elements&&this.elements[0]}resolveElementProperties(e,r){return ox(e,r)}}Bo.id="ellipseAnnotation",Bo.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:1,display:!0,hitTolerance:0,init:void 0,label:Object.assign({},Ni.defaults.label),rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xMax:void 0,xMin:void 0,xScaleID:void 0,yMax:void 0,yMin:void 0,yScaleID:void 0,z:0},Bo.defaultRoutes={borderColor:"color",backgroundColor:"color"},Bo.descriptors={label:{_fallback:!0}};class Ka extends cr{inRange(e,r,n,i){const{x:o,y:s,x2:a,y2:l,width:c}=this.getProps(["x","y","x2","y2","width"],i),u=(this.options.borderWidth+this.options.hitTolerance)/2;return n!=="x"&&n!=="y"?function(h,f,d,y){return!(!h||!f||d<=0)&&Math.pow(h.x-f.x,2)+Math.pow(h.y-f.y,2)<=Math.pow(d+y,2)}({x:e,y:r},this.getCenterPoint(i),c/2,u):z1(n==="y"?{start:s,end:l,value:r}:{start:o,end:a,value:e},u)}getCenterPoint(e){return mi(this,e)}draw(e){const r=this.options,n=r.borderWidth;if(r.radius<.1)return;e.save(),e.fillStyle=r.backgroundColor,uo(e,r);const i=Kr(e,r);fT(e,this,this.centerX,this.centerY),i&&!tc(r.pointStyle)&&(e.shadowColor=r.borderShadowColor,e.stroke()),e.restore(),r.borderWidth=n}resolveElementProperties(e,r){const n=ix(e,r);return n.initProperties=co(e,n,r),n}}Ka.id="pointAnnotation",Ka.defaults={adjustScaleRange:!0,backgroundShadowColor:"transparent",borderDash:[],borderDashOffset:0,borderShadowColor:"transparent",borderWidth:1,display:!0,hitTolerance:0,init:void 0,pointStyle:"circle",radius:10,rotation:0,shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0,xAdjust:0,xMax:void 0,xMin:void 0,xScaleID:void 0,xValue:void 0,yAdjust:0,yMax:void 0,yMin:void 0,yScaleID:void 0,yValue:void 0,z:0},Ka.defaultRoutes={borderColor:"color",backgroundColor:"color"};class Ga extends cr{inRange(e,r,n,i){if(n!=="x"&&n!=="y")return this.options.radius>=.1&&this.elements.length>1&&function(c,u,h,f){let d=!1,y=c[c.length-1].getProps(["bX","bY"],f);for(const g of c){const p=g.getProps(["bX","bY"],f);p.bY>h!=y.bY>h&&u<(y.bX-p.bX)*(h-p.bY)/(y.bY-p.bY)+p.bX&&(d=!d),y=p}return d}(this.elements,e,r,i);const o=ui({x:e,y:r},this.getCenterPoint(i),gt(-this.options.rotation)),s=this.elements.map(c=>n==="y"?c.bY:c.bX),a=Math.min(...s),l=Math.max(...s);return o[n]>=a&&o[n]<=l}getCenterPoint(e){return mi(this,e)}draw(e){const{elements:r,options:n}=this;e.save(),e.beginPath(),e.fillStyle=n.backgroundColor,uo(e,n);const i=Kr(e,n);let o=!0;for(const s of r)o?(e.moveTo(s.x,s.y),o=!1):e.lineTo(s.x,s.y);e.closePath(),e.fill(),i&&(e.shadowColor=n.borderShadowColor,e.stroke()),e.restore()}resolveElementProperties(e,r){const n=ix(e,r),{sides:i,rotation:o}=r,s=[],a=2*Qe/i;let l=o*Vd;for(let c=0;c{ht.describe(`elements.${Cn[t].id}`,{_fallback:"plugins.annotation.common"})});const _T={update:Object.assign},xT=ap.concat(wl),Vb=(t,e)=>je(e)?rd(t,e):t,td=t=>t==="color"||t==="font";function cp(t="line"){return Cn[t]?t:"line"}function ST(t,e,r,n){const i=function(a,l,c){return c==="reset"||c==="none"||c==="resize"?_T:new j_(a,l)}(t,r.animations,n),o=e.annotations,s=function(a,l){const c=l.length,u=a.length;if(uc&&a.splice(c,u-c);return a}(e.elements,o);for(let a=0;aVb(s,i)):r[n]=Vb(o,i)}return r}function OT(t,e,r,n){return e.$context||(e.$context=Object.assign(Object.create(t.getContext()),{element:e,get elements(){return r.filter(i=>i&&i.options)},id:n.id,type:"annotation"}))}const gn=new Map,$b=t=>t.type!=="doughnutLabel",MT=ap.concat(wl);var r2={id:"annotation",version:"3.1.0",beforeRegister(){(function(t,e,r,n=!0){const i=r.split(".");let o=0;for(const s of e.split(".")){const a=i[o++];if(parseInt(s,10){const s=i[o];je(s)&&(s.id=o,n.push(s))}):st(i)&&n.push(...i),function(o,s){for(const a of o)bT(a,s)}(n.filter($b),t.scales)},afterDataLimits(t,e){const r=gn.get(t);yT(t,e.scale,r.annotations.filter($b).filter(n=>n.display&&n.adjustScaleRange))},afterUpdate(t,e,r){const n=gn.get(t);(function(i,o,s){o.listened=_b(s,ap,o.listeners),o.moveListened=!1,Xf.forEach(a=>{Ut(s[a])&&(o.moveListened=!0)}),o.listened&&o.moveListened||o.annotations.forEach(a=>{!o.listened&&Ut(a.click)&&(o.listened=!0),o.moveListened||Xf.forEach(l=>{Ut(a[l])&&(o.listened=!0,o.moveListened=!0)})})})(0,n,r),ST(t,n,r,e.mode),n.visibleElements=n.elements.filter(i=>!i.skip&&i.options.display),function(i,o,s){const a=o.visibleElements;o.hooked=_b(s,wl,o.hooks),o.hooked||a.forEach(l=>{o.hooked||wl.forEach(c=>{Ut(l.options[c])&&(o.hooked=!0)})})}(0,n,r)},beforeDatasetsDraw(t,e,r){Mo(t,"beforeDatasetsDraw",r.clip)},afterDatasetsDraw(t,e,r){Mo(t,"afterDatasetsDraw",r.clip)},beforeDatasetDraw(t,e,r){Mo(t,e.index,r.clip)},beforeDraw(t,e,r){Mo(t,"beforeDraw",r.clip)},afterDraw(t,e,r){Mo(t,"afterDraw",r.clip)},beforeEvent(t,e,r){mT(gn.get(t),e.event,r)&&(e.changed=!0)},afterDestroy(t){gn.delete(t)},getAnnotations(t){const e=gn.get(t);return e?e.elements:[]},_getAnnotationElementsAtEventForMode:(t,e,r)=>Zf(t,e,r),defaults:{animations:{numbers:{properties:["x","y","x2","y2","width","height","centerX","centerY","pointX","pointY","radius"],type:"number"},colors:{properties:["backgroundColor","borderColor"],type:"color"}},clip:!0,interaction:{mode:void 0,axis:void 0,intersect:void 0},common:{drawTime:"afterDatasetsDraw",init:!1,label:{}}},descriptors:{_indexable:!1,_scriptable:t=>!MT.includes(t)&&t!=="init",annotations:{_allKeys:!1,_fallback:(t,e)=>`elements.${Cn[cp(e.type)].id}`},interaction:{_fallback:!0},common:{label:{_indexable:td,_fallback:!0},_indexable:td}},additionalOptionScopes:[""]};function Mo(t,e,r){const{ctx:n,chartArea:i}=t,o=gn.get(t);r&&Rs(n,i);const s=function(a,l){const c=[];for(const u of a)if(u.options.drawTime===l&&c.push({element:u,main:!0}),u.elements&&u.elements.length)for(const h of u.elements)h.options.display&&h.options.drawTime===l&&c.push({element:h});return c}(o.visibleElements,e).sort((a,l)=>a.element.options.z-l.element.options.z);for(const a of s)AT(n,i,o,a);r&&Ds(n)}function AT(t,e,r,n){const i=n.element;n.main?(Ab(r,i,"beforeDraw"),i.draw(t,e),Ab(r,i,"afterDraw")):i.draw(t,e)}export{NT as A,Tr as B,Dl as C,IT as D,vS as E,Ft as F,LT as G,KT as H,CT as I,ZT as J,QT as K,GT as L,qT as M,YT as N,$T as O,t2 as P,$i as Q,_k as R,e2 as S,UT as T,Va as U,Jn as V,zc as W,Iy as X,Ny as Y,JT as Z,r2 as _,XT as a,PT as b,Jt as c,Pl as d,FT as e,WT as f,lu as g,Yi as h,Z0 as i,BT as j,wS as k,bu as l,_S as m,DT as n,yu as o,RT as p,qx as q,Es as r,Wt as s,HT as t,zT as u,VT as v,ni as w,xx as x,jT as y,Ol as z}; diff --git a/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-C1Wk2aFl.js b/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-C1Wk2aFl.js new file mode 100644 index 0000000000..8a615bc5ed --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-C1Wk2aFl.js @@ -0,0 +1,562 @@ +import{g as fn,d as $a,c as V,w as Za,h as Qa}from"./vendor-Bzn5cd2Y.js";/*! + * Font Awesome Free 7.0.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2025 Fonticons, Inc. + */function Le(e,n){(n==null||n>e.length)&&(n=e.length);for(var a=0,t=Array(n);a=e.length?{done:!0}:{done:!1,value:e[t++]}},e:function(u){throw u},f:r}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}var i,o=!0,f=!1;return{s:function(){a=a.call(e)},n:function(){var u=a.next();return o=u.done,u},e:function(u){f=!0,i=u},f:function(){try{o||a.return==null||a.return()}finally{if(f)throw i}}}}function y(e,n,a){return(n=Qn(n))in e?Object.defineProperty(e,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[n]=a,e}function cn(e,n){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable})),a.push.apply(a,t)}return a}function s(e){for(var n=1;n0;)n+="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[62*Math.random()|0];return n}function ee(e){for(var n=[],a=(e||[]).length>>>0;a--;)n[a]=e[a];return n}function nn(e){return e.classList?ee(e.classList):(e.getAttribute("class")||"").split(" ").filter(function(n){return n})}function xn(e){return"".concat(e).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function ye(e){return Object.keys(e||{}).reduce(function(n,a){return n+"".concat(a,": ").concat(e[a].trim(),";")},"")}function an(e){return e.size!==D.size||e.x!==D.x||e.y!==D.y||e.rotate!==D.rotate||e.flipX||e.flipY}function Ma(){var e="fa",n=Sa,a=h.cssPrefix,t=h.replacementClass,r=`:root, :host { + --fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free"; + --fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free"; + --fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro"; + --fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro"; + --fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone"; + --fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone"; + --fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone"; + --fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone"; + --fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands"; + --fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp"; + --fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp"; + --fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp"; + --fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp"; + --fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone"; + --fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone"; + --fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone"; + --fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone"; + --fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab"; + --fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press"; + --fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard"; + --fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint"; + --fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog"; + --fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo"; + --fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch"; + --fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly"; + --fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill"; + --fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo"; + --fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel"; +} + +.svg-inline--fa { + box-sizing: content-box; + display: var(--fa-display, inline-block); + height: 1em; + overflow: visible; + vertical-align: -0.125em; + width: var(--fa-width, 1.25em); +} +.svg-inline--fa.fa-2xs { + vertical-align: 0.1em; +} +.svg-inline--fa.fa-xs { + vertical-align: 0em; +} +.svg-inline--fa.fa-sm { + vertical-align: -0.0714285714em; +} +.svg-inline--fa.fa-lg { + vertical-align: -0.2em; +} +.svg-inline--fa.fa-xl { + vertical-align: -0.25em; +} +.svg-inline--fa.fa-2xl { + vertical-align: -0.3125em; +} +.svg-inline--fa.fa-pull-left, +.svg-inline--fa .fa-pull-start { + float: inline-start; + margin-inline-end: var(--fa-pull-margin, 0.3em); +} +.svg-inline--fa.fa-pull-right, +.svg-inline--fa .fa-pull-end { + float: inline-end; + margin-inline-start: var(--fa-pull-margin, 0.3em); +} +.svg-inline--fa.fa-li { + width: var(--fa-li-width, 2em); + inset-inline-start: calc(-1 * var(--fa-li-width, 2em)); + inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */ +} + +.fa-layers-counter, .fa-layers-text { + display: inline-block; + position: absolute; + text-align: center; +} + +.fa-layers { + display: inline-block; + height: 1em; + position: relative; + text-align: center; + vertical-align: -0.125em; + width: var(--fa-width, 1.25em); +} +.fa-layers .svg-inline--fa { + inset: 0; + margin: auto; + position: absolute; + transform-origin: center center; +} + +.fa-layers-text { + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + transform-origin: center center; +} + +.fa-layers-counter { + background-color: var(--fa-counter-background-color, #ff253a); + border-radius: var(--fa-counter-border-radius, 1em); + box-sizing: border-box; + color: var(--fa-inverse, #fff); + line-height: var(--fa-counter-line-height, 1); + max-width: var(--fa-counter-max-width, 5em); + min-width: var(--fa-counter-min-width, 1.5em); + overflow: hidden; + padding: var(--fa-counter-padding, 0.25em 0.5em); + right: var(--fa-right, 0); + text-overflow: ellipsis; + top: var(--fa-top, 0); + transform: scale(var(--fa-counter-scale, 0.25)); + transform-origin: top right; +} + +.fa-layers-bottom-right { + bottom: var(--fa-bottom, 0); + right: var(--fa-right, 0); + top: auto; + transform: scale(var(--fa-layers-scale, 0.25)); + transform-origin: bottom right; +} + +.fa-layers-bottom-left { + bottom: var(--fa-bottom, 0); + left: var(--fa-left, 0); + right: auto; + top: auto; + transform: scale(var(--fa-layers-scale, 0.25)); + transform-origin: bottom left; +} + +.fa-layers-top-right { + top: var(--fa-top, 0); + right: var(--fa-right, 0); + transform: scale(var(--fa-layers-scale, 0.25)); + transform-origin: top right; +} + +.fa-layers-top-left { + left: var(--fa-left, 0); + right: auto; + top: var(--fa-top, 0); + transform: scale(var(--fa-layers-scale, 0.25)); + transform-origin: top left; +} + +.fa-1x { + font-size: 1em; +} + +.fa-2x { + font-size: 2em; +} + +.fa-3x { + font-size: 3em; +} + +.fa-4x { + font-size: 4em; +} + +.fa-5x { + font-size: 5em; +} + +.fa-6x { + font-size: 6em; +} + +.fa-7x { + font-size: 7em; +} + +.fa-8x { + font-size: 8em; +} + +.fa-9x { + font-size: 9em; +} + +.fa-10x { + font-size: 10em; +} + +.fa-2xs { + font-size: calc(10 / 16 * 1em); /* converts a 10px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 10 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 10 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-xs { + font-size: calc(12 / 16 * 1em); /* converts a 12px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 12 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 12 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-sm { + font-size: calc(14 / 16 * 1em); /* converts a 14px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 14 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 14 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-lg { + font-size: calc(20 / 16 * 1em); /* converts a 20px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 20 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 20 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-xl { + font-size: calc(24 / 16 * 1em); /* converts a 24px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 24 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 24 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-2xl { + font-size: calc(32 / 16 * 1em); /* converts a 32px size into an em-based value that's relative to the scale's 16px base */ + line-height: calc(1 / 32 * 1em); /* sets the line-height of the icon back to that of it's parent */ + vertical-align: calc((6 / 32 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */ +} + +.fa-width-auto { + --fa-width: auto; +} + +.fa-fw, +.fa-width-fixed { + --fa-width: 1.25em; +} + +.fa-ul { + list-style-type: none; + margin-inline-start: var(--fa-li-margin, 2.5em); + padding-inline-start: 0; +} +.fa-ul > li { + position: relative; +} + +.fa-li { + inset-inline-start: calc(-1 * var(--fa-li-width, 2em)); + position: absolute; + text-align: center; + width: var(--fa-li-width, 2em); + line-height: inherit; +} + +/* Heads Up: Bordered Icons will not be supported in the future! + - This feature will be deprecated in the next major release of Font Awesome (v8)! + - You may continue to use it in this version *v7), but it will not be supported in Font Awesome v8. +*/ +/* Notes: +* --@{v.$css-prefix}-border-width = 1/16 by default (to render as ~1px based on a 16px default font-size) +* --@{v.$css-prefix}-border-padding = + ** 3/16 for vertical padding (to give ~2px of vertical whitespace around an icon considering it's vertical alignment) + ** 4/16 for horizontal padding (to give ~4px of horizontal whitespace around an icon) +*/ +.fa-border { + border-color: var(--fa-border-color, #eee); + border-radius: var(--fa-border-radius, 0.1em); + border-style: var(--fa-border-style, solid); + border-width: var(--fa-border-width, 0.0625em); + box-sizing: var(--fa-border-box-sizing, content-box); + padding: var(--fa-border-padding, 0.1875em 0.25em); +} + +.fa-pull-left, +.fa-pull-start { + float: inline-start; + margin-inline-end: var(--fa-pull-margin, 0.3em); +} + +.fa-pull-right, +.fa-pull-end { + float: inline-end; + margin-inline-start: var(--fa-pull-margin, 0.3em); +} + +.fa-beat { + animation-name: fa-beat; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-bounce { + animation-name: fa-bounce; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); +} + +.fa-fade { + animation-name: fa-fade; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-beat-fade { + animation-name: fa-beat-fade; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); +} + +.fa-flip { + animation-name: fa-flip; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, ease-in-out); +} + +.fa-shake { + animation-name: fa-shake; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin { + animation-name: fa-spin; + animation-delay: var(--fa-animation-delay, 0s); + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 2s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, linear); +} + +.fa-spin-reverse { + --fa-animation-direction: reverse; +} + +.fa-pulse, +.fa-spin-pulse { + animation-name: fa-spin; + animation-direction: var(--fa-animation-direction, normal); + animation-duration: var(--fa-animation-duration, 1s); + animation-iteration-count: var(--fa-animation-iteration-count, infinite); + animation-timing-function: var(--fa-animation-timing, steps(8)); +} + +@media (prefers-reduced-motion: reduce) { + .fa-beat, + .fa-bounce, + .fa-fade, + .fa-beat-fade, + .fa-flip, + .fa-pulse, + .fa-shake, + .fa-spin, + .fa-spin-pulse { + animation: none !important; + transition: none !important; + } +} +@keyframes fa-beat { + 0%, 90% { + transform: scale(1); + } + 45% { + transform: scale(var(--fa-beat-scale, 1.25)); + } +} +@keyframes fa-bounce { + 0% { + transform: scale(1, 1) translateY(0); + } + 10% { + transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); + } + 30% { + transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); + } + 50% { + transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); + } + 57% { + transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); + } + 64% { + transform: scale(1, 1) translateY(0); + } + 100% { + transform: scale(1, 1) translateY(0); + } +} +@keyframes fa-fade { + 50% { + opacity: var(--fa-fade-opacity, 0.4); + } +} +@keyframes fa-beat-fade { + 0%, 100% { + opacity: var(--fa-beat-fade-opacity, 0.4); + transform: scale(1); + } + 50% { + opacity: 1; + transform: scale(var(--fa-beat-fade-scale, 1.125)); + } +} +@keyframes fa-flip { + 50% { + transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); + } +} +@keyframes fa-shake { + 0% { + transform: rotate(-15deg); + } + 4% { + transform: rotate(15deg); + } + 8%, 24% { + transform: rotate(-18deg); + } + 12%, 28% { + transform: rotate(18deg); + } + 16% { + transform: rotate(-22deg); + } + 20% { + transform: rotate(22deg); + } + 32% { + transform: rotate(-12deg); + } + 36% { + transform: rotate(12deg); + } + 40%, 100% { + transform: rotate(0deg); + } +} +@keyframes fa-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} +.fa-rotate-90 { + transform: rotate(90deg); +} + +.fa-rotate-180 { + transform: rotate(180deg); +} + +.fa-rotate-270 { + transform: rotate(270deg); +} + +.fa-flip-horizontal { + transform: scale(-1, 1); +} + +.fa-flip-vertical { + transform: scale(1, -1); +} + +.fa-flip-both, +.fa-flip-horizontal.fa-flip-vertical { + transform: scale(-1, -1); +} + +.fa-rotate-by { + transform: rotate(var(--fa-rotate-angle, 0)); +} + +.svg-inline--fa .fa-primary { + fill: var(--fa-primary-color, currentColor); + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa .fa-secondary { + fill: var(--fa-secondary-color, currentColor); + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-primary { + opacity: var(--fa-secondary-opacity, 0.4); +} + +.svg-inline--fa.fa-swap-opacity .fa-secondary { + opacity: var(--fa-primary-opacity, 1); +} + +.svg-inline--fa mask .fa-primary, +.svg-inline--fa mask .fa-secondary { + fill: black; +} + +.svg-inline--fa.fa-inverse { + fill: var(--fa-inverse, #fff); +} + +.fa-stack { + display: inline-block; + height: 2em; + line-height: 2em; + position: relative; + vertical-align: middle; + width: 2.5em; +} + +.fa-inverse { + color: var(--fa-inverse, #fff); +} + +.svg-inline--fa.fa-stack-1x { + height: 1em; + width: 1.25em; +} +.svg-inline--fa.fa-stack-2x { + height: 2em; + width: 2.5em; +} + +.fa-stack-1x, +.fa-stack-2x { + bottom: 0; + left: 0; + margin: auto; + position: absolute; + right: 0; + top: 0; + z-index: var(--fa-stack-z-index, auto); +}`;if(a!==e||t!==n){var i=new RegExp("\\.".concat(e,"\\-"),"g"),o=new RegExp("\\--".concat(e,"\\-"),"g"),f=new RegExp("\\.".concat(n),"g");r=r.replace(i,".".concat(a,"-")).replace(o,"--".concat(a,"-")).replace(f,".".concat(t))}return r}var wn=!1;function Ae(){h.autoAddCss&&!wn&&(function(e){if(e&&B){var n=k.createElement("style");n.setAttribute("type","text/css"),n.innerHTML=e;for(var a=k.head.childNodes,t=null,r=a.length-1;r>-1;r--){var i=a[r],o=(i.tagName||"").toUpperCase();["STYLE","LINK"].indexOf(o)>-1&&(t=i)}k.head.insertBefore(n,t)}}(Ma()),wn=!0)}var bt={mixout:function(){return{dom:{css:Ma,insertCss:Ae}}},hooks:function(){return{beforeDOMElementCreation:function(){Ae()},beforeI2svg:function(){Ae()}}}},R=H||{};R[T]||(R[T]={}),R[T].styles||(R[T].styles={}),R[T].hooks||(R[T].hooks={}),R[T].shims||(R[T].shims=[]);var F=R[T],Ia=[],Ca=function(){k.removeEventListener("DOMContentLoaded",Ca),tn=1,Ia.map(function(e){return e()})},tn=!1;function se(e){var n=e.tag,a=e.attributes,t=a===void 0?{}:a,r=e.children,i=r===void 0?[]:r;return typeof e=="string"?xn(e):"<".concat(n," ").concat(function(o){return Object.keys(o||{}).reduce(function(f,u){return f+"".concat(u,'="').concat(xn(o[u]),'" ')},"").trim()}(t),">").concat(i.map(se).join(""),"")}function kn(e,n,a){if(e&&e[n]&&e[n][a])return{prefix:n,iconName:a,icon:e[n][a]}}B&&((tn=(k.documentElement.doScroll?/^loaded|^c/:/^loaded|^i|^c/).test(k.readyState))||k.addEventListener("DOMContentLoaded",Ca));var Ne=function(e,n,a,t){var r,i,o,f=Object.keys(e),u=f.length,l=n;for(a===void 0?(r=1,o=e[f[0]]):(r=0,o=a);r2&&arguments[2]!==void 0?arguments[2]:{}).skipHooks,t=a!==void 0&&a,r=Sn(n);typeof F.hooks.addPack!="function"||t?F.styles[e]=s(s({},F.styles[e]||{}),r):F.hooks.addPack(e,Sn(n)),e==="fas"&&La("fa",n)}var ie=F.styles,yt=F.shims,Ea=Object.keys(Na),xt=Ea.reduce(function(e,n){return e[n]=Object.keys(Na[n]),e},{}),rn=null,Da={},Ta={},Ra={},Wa={},Ba={};function wt(e,n){var a,t=n.split("-"),r=t[0],i=t.slice(1).join("-");return r!==e||i===""||(a=i,~vt.indexOf(a))?null:i}var zn,Ya=function(){var e=function(t){return Ne(ie,function(r,i,o){return r[o]=Ne(i,t,{}),r},{})};Da=e(function(t,r,i){return r[3]&&(t[r[3]]=i),r[2]&&r[2].filter(function(o){return typeof o=="number"}).forEach(function(o){t[o.toString(16)]=i}),t}),Ta=e(function(t,r,i){return t[i]=i,r[2]&&r[2].filter(function(o){return typeof o=="string"}).forEach(function(o){t[o]=i}),t}),Ba=e(function(t,r,i){var o=r[2];return t[i]=i,o.forEach(function(f){t[f]=i}),t});var n="far"in ie||h.autoFetchSvg,a=Ne(yt,function(t,r){var i=r[0],o=r[1],f=r[2];return o!=="far"||n||(o="fas"),typeof i=="string"&&(t.names[i]={prefix:o,iconName:f}),typeof i=="number"&&(t.unicodes[i.toString(16)]={prefix:o,iconName:f}),t},{names:{},unicodes:{}});Ra=a.names,Wa=a.unicodes,rn=xe(h.styleDefault,{family:h.familyDefault})};function He(e,n){return(Da[e]||{})[n]}function U(e,n){return(Ba[e]||{})[n]}function Ha(e){return Ra[e]||{prefix:null,iconName:null}}function J(){return rn}zn=function(e){rn=xe(e.styleDefault,{family:h.familyDefault})},Ye.push(zn),Ya();function xe(e){var n=(arguments.length>1&&arguments[1]!==void 0?arguments[1]:{}).family,a=n===void 0?O:n,t=mt[a][e];if(a===oe&&!e)return"fad";var r=bn[a][e]||bn[a][t],i=e in F.styles?e:null;return r||i||null}function jn(e){return e.sort().filter(function(n,a,t){return t.indexOf(n)===a})}var An=wa.concat(xa);function we(e){var n,a,t=(arguments.length>1&&arguments[1]!==void 0?arguments[1]:{}).skipLookups,r=t!==void 0&&t,i=null,o=jn(e.filter(function(m){return An.includes(m)})),f=jn(e.filter(function(m){return!An.includes(m)})),u=be(o.filter(function(m){return i=m,!ia.includes(m)}),1)[0],l=u===void 0?null:u,c=function(m){var p=O,b=Ea.reduce(function(g,v){return g[v]="".concat(h.cssPrefix,"-").concat(v),g},{});return ya.forEach(function(g){(m.includes(b[g])||m.some(function(v){return xt[g].includes(v)}))&&(p=g)}),p}(o),d=s(s({},(n=[],a=null,f.forEach(function(m){var p=wt(h.cssPrefix,m);p?a=p:m&&n.push(m)}),{iconName:a,rest:n})),{},{prefix:xe(l,{family:c})});return s(s(s({},d),function(m){var p=m.values,b=m.family,g=m.canonical,v=m.givenPrefix,w=v===void 0?"":v,S=m.styles,C=S===void 0?{}:S,E=m.config,x=E===void 0?{}:E,M=b===oe,j=p.includes("fa-duotone")||p.includes("fad"),A=x.familyDefault==="duotone",I=g.prefix==="fad"||g.prefix==="fa-duotone";if(!M&&(j||A||I)&&(g.prefix="fad"),(p.includes("fa-brands")||p.includes("fab"))&&(g.prefix="fab"),!g.prefix&&kt.includes(b)&&(Object.keys(C).find(function(N){return St.includes(N)})||x.autoFetchSvg)){var z=nt.get(b).defaultShortPrefixId;g.prefix=z,g.iconName=U(g.prefix,g.iconName)||g.iconName}return g.prefix!=="fa"&&w!=="fa"||(g.prefix=J()||"fas"),g}({values:e,family:c,styles:ie,config:h,canonical:d,givenPrefix:i})),function(m,p,b){var g=b.prefix,v=b.iconName;if(m||!g||!v)return{prefix:g,iconName:v};var w=p==="fa"?Ha(v):{},S=U(g,v);return v=w.iconName||S||v,(g=w.prefix||g)!=="far"||ie.far||!ie.fas||h.autoFetchSvg||(g="fas"),{prefix:g,iconName:v}}(r,i,d))}var kt=ya.filter(function(e){return e!==O||e!==oe}),St=Object.keys(Ee).filter(function(e){return e!==O}).map(function(e){return Object.keys(Ee[e])}).flat(),zt=function(){return et(function e(){(function(n,a){if(!(n instanceof a))throw new TypeError("Cannot call a class as a function")})(this,e),this.definitions={}},[{key:"add",value:function(){for(var e=this,n=arguments.length,a=new Array(n),t=0;t0&&u.forEach(function(l){typeof l=="string"&&(e[i][l]=f)}),e[i][o]=f}),e}}])}(),Nn=[],Z={},Q={},jt=Object.keys(Q);function Je(e,n){for(var a=arguments.length,t=new Array(a>2?a-2:0),r=2;r1?n-1:0),t=1;t0&&arguments[0]!==void 0?arguments[0]:{};return B?(_("beforeI2svg",e),K("pseudoElements2svg",e),K("i2svg",e)):Promise.reject(new Error("Operation requires a DOM of some kind."))},watch:function(){var e,n=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},a=n.autoReplaceSvgRoot;h.autoReplaceSvg===!1&&(h.autoReplaceSvg=!0),h.observeMutations=!0,e=function(){Nt({autoReplaceSvgRoot:a}),_("watch",n)},B&&(tn?setTimeout(e,0):Ia.push(e))}},fe={noAuto:function(){h.autoReplaceSvg=!1,h.observeMutations=!1,_("noAuto")},config:h,dom:At,parse:{icon:function(e){if(e===null)return null;if(Ze(e)==="object"&&e.prefix&&e.iconName)return{prefix:e.prefix,iconName:U(e.prefix,e.iconName)||e.iconName};if(Array.isArray(e)&&e.length===2){var n=e[1].indexOf("fa-")===0?e[1].slice(3):e[1],a=xe(e[0]);return{prefix:a,iconName:U(a,n)||n}}if(typeof e=="string"&&(e.indexOf("".concat(h.cssPrefix,"-"))>-1||e.match(pt))){var t=we(e.split(" "),{skipLookups:!0});return{prefix:t.prefix||J(),iconName:U(t.prefix,t.iconName)||t.iconName}}if(typeof e=="string"){var r=J();return{prefix:r,iconName:U(r,e)||e}}}},library:Ja,findIconDefinition:Ke,toHtml:se},Nt=function(){var e=(arguments.length>0&&arguments[0]!==void 0?arguments[0]:{}).autoReplaceSvgRoot,n=e===void 0?k:e;(Object.keys(F.styles).length>0||h.autoFetchSvg)&&B&&h.autoReplaceSvg&&fe.dom.i2svg({node:n})};function ke(e,n){return Object.defineProperty(e,"abstract",{get:n}),Object.defineProperty(e,"html",{get:function(){return e.abstract.map(function(a){return se(a)})}}),Object.defineProperty(e,"node",{get:function(){if(B){var a=k.createElement("div");return a.innerHTML=e.html,a.children}}}),e}function on(e){var n=e.icons,a=n.main,t=n.mask,r=e.prefix,i=e.iconName,o=e.transform,f=e.symbol,u=e.maskId,l=e.extra,c=e.watchable,d=c!==void 0&&c,m=t.found?t:a,p=m.width,b=m.height,g=[h.replacementClass,i?"".concat(h.cssPrefix,"-").concat(i):""].filter(function(x){return l.classes.indexOf(x)===-1}).filter(function(x){return x!==""||!!x}).concat(l.classes).join(" "),v={children:[],attributes:s(s({},l.attributes),{},{"data-prefix":r,"data-icon":i,class:g,role:l.attributes.role||"img",viewBox:"0 0 ".concat(p," ").concat(b)})};(function(x){return["aria-label","aria-labelledby","title","role"].some(function(M){return M in x})})(l.attributes)||l.attributes["aria-hidden"]||(v.attributes["aria-hidden"]="true"),d&&(v.attributes[q]="");var w=s(s({},v),{},{prefix:r,iconName:i,main:a,mask:t,maskId:u,transform:o,symbol:f,styles:s({},l.styles)}),S=t.found&&a.found?K("generateAbstractMask",w)||{children:[],attributes:{}}:K("generateAbstractIcon",w)||{children:[],attributes:{}},C=S.children,E=S.attributes;return w.children=C,w.attributes=E,f?function(x){var M=x.prefix,j=x.iconName,A=x.children,I=x.attributes,z=x.symbol,N=z===!0?"".concat(M,"-").concat(h.cssPrefix,"-").concat(j):z;return[{tag:"svg",attributes:{style:"display: none;"},children:[{tag:"symbol",attributes:s(s({},I),{},{id:N}),children:A}]}]}(w):function(x){var M=x.children,j=x.main,A=x.mask,I=x.attributes,z=x.styles,N=x.transform;if(an(N)&&j.found&&!A.found){var Y={x:j.width/j.height/2,y:.5};I.style=ye(s(s({},z),{},{"transform-origin":"".concat(Y.x+N.x/16,"em ").concat(Y.y+N.y/16,"em")}))}return[{tag:"svg",attributes:I,children:M}]}(w)}function Pn(e){var n=e.content,a=e.width,t=e.height,r=e.transform,i=e.extra,o=e.watchable,f=o!==void 0&&o,u=s(s({},i.attributes),{},{class:i.classes.join(" ")});f&&(u[q]="");var l=s({},i.styles);an(r)&&(l.transform=function(m){var p=m.transform,b=m.width,g=b===void 0?16:b,v=m.height,w=v===void 0?16:v,S="";return S+=ta?"translate(".concat(p.x/X-g/2,"em, ").concat(p.y/X-w/2,"em) "):"translate(calc(-50% + ".concat(p.x/X,"em), calc(-50% + ").concat(p.y/X,"em)) "),S+="scale(".concat(p.size/X*(p.flipX?-1:1),", ").concat(p.size/X*(p.flipY?-1:1),") "),S+"rotate(".concat(p.rotate,"deg) ")}({transform:r,width:a,height:t}),l["-webkit-transform"]=l.transform);var c=ye(l);c.length>0&&(u.style=c);var d=[];return d.push({tag:"span",attributes:u,children:[n]}),d}var Pe=F.styles;function Ue(e){var n=e[0],a=e[1],t=be(e.slice(4),1)[0];return{found:!0,width:n,height:a,icon:Array.isArray(t)?{tag:"g",attributes:{class:"".concat(h.cssPrefix,"-").concat(je.GROUP)},children:[{tag:"path",attributes:{class:"".concat(h.cssPrefix,"-").concat(je.SECONDARY),fill:"currentColor",d:t[0]}},{tag:"path",attributes:{class:"".concat(h.cssPrefix,"-").concat(je.PRIMARY),fill:"currentColor",d:t[1]}}]}:{tag:"path",attributes:{fill:"currentColor",d:t}}}}var Pt={found:!1,width:512,height:512};function qe(e,n){var a=n;return n==="fa"&&h.styleDefault!==null&&(n=J()),new Promise(function(t,r){if(a==="fa"){var i=Ha(e)||{};e=i.iconName||e,n=i.prefix||n}if(e&&n&&Pe[n]&&Pe[n][e])return t(Ue(Pe[n][e]));!ja&&h.showMissingIcons,t(s(s({},Pt),{},{icon:h.showMissingIcons&&e&&K("missingIconAbstract")||{}}))})}var On=function(){},_e=h.measurePerformance&&ce&&ce.mark&&ce.measure?ce:{mark:On,measure:On},te='FA "7.0.0"',Ot=function(e){_e.mark("".concat(te," ").concat(e," ends")),_e.measure("".concat(te," ").concat(e),"".concat(te," ").concat(e," begins"),"".concat(te," ").concat(e," ends"))},ln=function(e){return _e.mark("".concat(te," ").concat(e," begins")),function(){return Ot(e)}},ge=function(){};function Mn(e){return typeof(e.getAttribute?e.getAttribute(q):null)=="string"}function Mt(e){return k.createElementNS("http://www.w3.org/2000/svg",e)}function It(e){return k.createElement(e)}function Ka(e){var n=(arguments.length>1&&arguments[1]!==void 0?arguments[1]:{}).ceFn,a=n===void 0?e.tag==="svg"?Mt:It:n;if(typeof e=="string")return k.createTextNode(e);var t=a(e.tag);return Object.keys(e.attributes||[]).forEach(function(r){t.setAttribute(r,e.attributes[r])}),(e.children||[]).forEach(function(r){t.appendChild(Ka(r,{ceFn:a}))}),t}var he={replace:function(e){var n=e[0];if(n.parentNode)if(e[1].forEach(function(t){n.parentNode.insertBefore(Ka(t),n)}),n.getAttribute(q)===null&&h.keepOriginalSource){var a=k.createComment(function(t){var r=" ".concat(t.outerHTML," ");return"".concat(r,"Font Awesome fontawesome.com ")}(n));n.parentNode.replaceChild(a,n)}else n.remove()},nest:function(e){var n=e[0],a=e[1];if(~nn(n).indexOf(h.replacementClass))return he.replace(e);var t=new RegExp("".concat(h.cssPrefix,"-.*"));if(delete a[0].attributes.id,a[0].attributes.class){var r=a[0].attributes.class.split(" ").reduce(function(o,f){return f===h.replacementClass||f.match(t)?o.toSvg.push(f):o.toNode.push(f),o},{toNode:[],toSvg:[]});a[0].attributes.class=r.toSvg.join(" "),r.toNode.length===0?n.removeAttribute("class"):n.setAttribute("class",r.toNode.join(" "))}var i=a.map(function(o){return se(o)}).join(` +`);n.setAttribute(q,""),n.innerHTML=i}};function In(e){e()}function Ua(e,n){var a=typeof n=="function"?n:ge;if(e.length===0)a();else{var t=In;h.mutateApproach==="async"&&(t=H.requestAnimationFrame||In),t(function(){var r=h.autoReplaceSvg===!0?he.replace:he[h.autoReplaceSvg]||he.replace,i=ln("mutate");e.map(r),i(),a()})}}var sn=!1;function qa(){sn=!0}function Ve(){sn=!1}var ve=null;function Cn(e){if(pn&&h.observeMutations){var n=e.treeCallback,a=n===void 0?ge:n,t=e.nodeCallback,r=t===void 0?ge:t,i=e.pseudoElementsCallback,o=i===void 0?ge:i,f=e.observeMutationsRoot,u=f===void 0?k:f;ve=new pn(function(l){if(!sn){var c=J();ee(l).forEach(function(d){if(d.type==="childList"&&d.addedNodes.length>0&&!Mn(d.addedNodes[0])&&(h.searchPseudoElements&&o(d.target),a(d.target)),d.type==="attributes"&&d.target.parentNode&&h.searchPseudoElements&&o([d.target],!0),d.type==="attributes"&&Mn(d.target)&&~ht.indexOf(d.attributeName))if(d.attributeName==="class"&&function(v){var w=v.getAttribute?v.getAttribute(Te):null,S=v.getAttribute?v.getAttribute(Re):null;return w&&S}(d.target)){var m=we(nn(d.target)),p=m.prefix,b=m.iconName;d.target.setAttribute(Te,p||c),b&&d.target.setAttribute(Re,b)}else(g=d.target)&&g.classList&&g.classList.contains&&g.classList.contains(h.replacementClass)&&r(d.target);var g})}}),B&&ve.observe(u,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}}function Ct(e){var n,a,t=e.getAttribute("data-prefix"),r=e.getAttribute("data-icon"),i=e.innerText!==void 0?e.innerText.trim():"",o=we(nn(e));return o.prefix||(o.prefix=J()),t&&r&&(o.prefix=t,o.iconName=r),o.iconName&&o.prefix||(o.prefix&&i.length>0&&(o.iconName=(n=o.prefix,a=e.innerText,(Ta[n]||{})[a]||He(o.prefix,Fa(e.innerText)))),!o.iconName&&h.autoFetchSvg&&e.firstChild&&e.firstChild.nodeType===Node.TEXT_NODE&&(o.iconName=e.firstChild.data)),o}function Fn(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{styleParser:!0},a=Ct(e),t=a.iconName,r=a.prefix,i=a.rest,o=function(l){return ee(l.attributes).reduce(function(c,d){return c.name!=="class"&&c.name!=="style"&&(c[d.name]=d.value),c},{})}(e),f=Je("parseNodeAttributes",{},e),u=n.styleParser?function(l){var c=l.getAttribute("style"),d=[];return c&&(d=c.split(";").reduce(function(m,p){var b=p.split(":"),g=b[0],v=b.slice(1);return g&&v.length>0&&(m[g]=v.join(":").trim()),m},{})),d}(e):[];return s({iconName:t,prefix:r,transform:D,mask:{iconName:null,prefix:null,rest:[]},maskId:null,symbol:!1,extra:{classes:i,styles:u,attributes:o}},f)}var Ft=F.styles;function _a(e){var n=h.autoReplaceSvg==="nest"?Fn(e,{styleParser:!1}):Fn(e);return~n.extra.classes.indexOf(Pa)?K("generateLayersText",e,n):K("generateSvgReplacementMutation",e,n)}function Ln(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;if(!B)return Promise.resolve();var a=k.documentElement.classList,t=function(c){return a.add("".concat(vn,"-").concat(c))},r=function(c){return a.remove("".concat(vn,"-").concat(c))},i=h.autoFetchSvg?[].concat(L(xa),L(wa)):ia.concat(Object.keys(Ft));i.includes("fa")||i.push("fa");var o=[".".concat(Pa,":not([").concat(q,"])")].concat(i.map(function(c){return".".concat(c,":not([").concat(q,"])")})).join(", ");if(o.length===0)return Promise.resolve();var f=[];try{f=ee(e.querySelectorAll(o))}catch{}if(!(f.length>0))return Promise.resolve();t("pending"),r("complete");var u=ln("onTree"),l=f.reduce(function(c,d){try{var m=_a(d);m&&c.push(m)}catch(p){ja||p.name}return c},[]);return new Promise(function(c,d){Promise.all(l).then(function(m){Ua(m,function(){t("active"),t("complete"),r("pending"),typeof n=="function"&&n(),u(),c()})}).catch(function(m){u(),d(m)})})}function Lt(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;_a(e).then(function(a){a&&Ua([a],n)})}var Et=function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},a=n.transform,t=a===void 0?D:a,r=n.symbol,i=r!==void 0&&r,o=n.mask,f=o===void 0?null:o,u=n.maskId,l=u===void 0?null:u,c=n.classes,d=c===void 0?[]:c,m=n.attributes,p=m===void 0?{}:m,b=n.styles,g=b===void 0?{}:b;if(e){var v=e.prefix,w=e.iconName,S=e.icon;return ke(s({type:"icon"},e),function(){return _("beforeDOMElementCreation",{iconDefinition:e,params:n}),on({icons:{main:Ue(S),mask:f?Ue(f.icon):{found:!1,width:null,height:null,icon:{}}},prefix:v,iconName:w,transform:s(s({},D),t),symbol:i,maskId:l,extra:{attributes:p,styles:g,classes:d}})})}},Dt={mixout:function(){return{icon:(e=Et,function(n){var a=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},t=(n||{}).icon?n:Ke(n||{}),r=a.mask;return r&&(r=(r||{}).icon?r:Ke(r||{})),e(t,s(s({},a),{},{mask:r}))})};var e},hooks:function(){return{mutationObserverCallbacks:function(e){return e.treeCallback=Ln,e.nodeCallback=Lt,e}}},provides:function(e){e.i2svg=function(n){var a=n.node,t=a===void 0?k:a,r=n.callback;return Ln(t,r===void 0?function(){}:r)},e.generateSvgReplacementMutation=function(n,a){var t=a.iconName,r=a.prefix,i=a.transform,o=a.symbol,f=a.mask,u=a.maskId,l=a.extra;return new Promise(function(c,d){Promise.all([qe(t,r),f.iconName?qe(f.iconName,f.prefix):Promise.resolve({found:!1,width:512,height:512,icon:{}})]).then(function(m){var p=be(m,2),b=p[0],g=p[1];c([n,on({icons:{main:b,mask:g},prefix:r,iconName:t,transform:i,symbol:o,maskId:u,extra:l,watchable:!0})])}).catch(d)})},e.generateAbstractIcon=function(n){var a,t=n.children,r=n.attributes,i=n.main,o=n.transform,f=ye(n.styles);return f.length>0&&(r.style=f),an(o)&&(a=K("generateAbstractTransformGrouping",{main:i,transform:o,containerWidth:i.width,iconWidth:i.width})),t.push(a||i.icon),{children:t,attributes:r}}}},Tt={mixout:function(){return{layer:function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},a=n.classes,t=a===void 0?[]:a;return ke({type:"layer"},function(){_("beforeDOMElementCreation",{assembler:e,params:n});var r=[];return e(function(i){Array.isArray(i)?i.map(function(o){r=r.concat(o.abstract)}):r=r.concat(i.abstract)}),[{tag:"span",attributes:{class:["".concat(h.cssPrefix,"-layers")].concat(L(t)).join(" ")},children:r}]})}}}},Rt={mixout:function(){return{counter:function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};n.title;var a=n.classes,t=a===void 0?[]:a,r=n.attributes,i=r===void 0?{}:r,o=n.styles,f=o===void 0?{}:o;return ke({type:"counter",content:e},function(){return _("beforeDOMElementCreation",{content:e,params:n}),function(u){var l=u.content,c=u.extra,d=s(s({},c.attributes),{},{class:c.classes.join(" ")}),m=ye(c.styles);m.length>0&&(d.style=m);var p=[];return p.push({tag:"span",attributes:d,children:[l]}),p}({content:e.toString(),extra:{attributes:i,styles:f,classes:["".concat(h.cssPrefix,"-layers-counter")].concat(L(t))}})})}}}},Wt={mixout:function(){return{text:function(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},a=n.transform,t=a===void 0?D:a,r=n.classes,i=r===void 0?[]:r,o=n.attributes,f=o===void 0?{}:o,u=n.styles,l=u===void 0?{}:u;return ke({type:"text",content:e},function(){return _("beforeDOMElementCreation",{content:e,params:n}),Pn({content:e,transform:s(s({},D),t),extra:{attributes:f,styles:l,classes:["".concat(h.cssPrefix,"-layers-text")].concat(L(i))}})})}}},provides:function(e){e.generateLayersText=function(n,a){var t=a.transform,r=a.extra,i=null,o=null;if(ta){var f=parseInt(getComputedStyle(n).fontSize,10),u=n.getBoundingClientRect();i=u.width/f,o=u.height/f}return Promise.resolve([n,Pn({content:n.innerHTML,width:i,height:o,transform:t,extra:r,watchable:!0})])}}},En=new RegExp('"',"ug"),Dn=[1105920,1112319],Tn=s(s(s(s({},{FontAwesome:{normal:"fas",400:"fas"}}),{"Font Awesome 7 Free":{900:"fas",400:"far"},"Font Awesome 7 Pro":{900:"fas",400:"far",normal:"far",300:"fal",100:"fat"},"Font Awesome 7 Brands":{400:"fab",normal:"fab"},"Font Awesome 7 Duotone":{900:"fad",400:"fadr",normal:"fadr",300:"fadl",100:"fadt"},"Font Awesome 7 Sharp":{900:"fass",400:"fasr",normal:"fasr",300:"fasl",100:"fast"},"Font Awesome 7 Sharp Duotone":{900:"fasds",400:"fasdr",normal:"fasdr",300:"fasdl",100:"fasdt"},"Font Awesome 7 Jelly":{400:"fajr",normal:"fajr"},"Font Awesome 7 Jelly Fill":{400:"fajfr",normal:"fajfr"},"Font Awesome 7 Jelly Duo":{400:"fajdr",normal:"fajdr"},"Font Awesome 7 Slab":{400:"faslr",normal:"faslr"},"Font Awesome 7 Slab Press":{400:"faslpr",normal:"faslpr"},"Font Awesome 7 Thumbprint":{300:"fatl",normal:"fatl"},"Font Awesome 7 Notdog":{900:"fans",normal:"fans"},"Font Awesome 7 Notdog Duo":{900:"fands",normal:"fands"},"Font Awesome 7 Etch":{900:"faes",normal:"faes"},"Font Awesome 7 Chisel":{400:"facr",normal:"facr"},"Font Awesome 7 Whiteboard":{600:"fawsb",normal:"fawsb"}}),{"Font Awesome 5 Free":{900:"fas",400:"far"},"Font Awesome 5 Pro":{900:"fas",400:"far",normal:"far",300:"fal"},"Font Awesome 5 Brands":{400:"fab",normal:"fab"},"Font Awesome 5 Duotone":{900:"fad"}}),{"Font Awesome Kit":{400:"fak",normal:"fak"},"Font Awesome Kit Duotone":{400:"fakd",normal:"fakd"}}),Xe=Object.keys(Tn).reduce(function(e,n){return e[n.toLowerCase()]=Tn[n],e},{}),Bt=Object.keys(Xe).reduce(function(e,n){var a=Xe[n];return e[n]=a[900]||L(Object.entries(a))[0][1],e},{});function Rn(e,n){var a="".concat("data-fa-pseudo-element-pending").concat(n.replace(":","-"));return new Promise(function(t,r){if(e.getAttribute(a)!==null)return t();var i,o,f,u=ee(e.children).filter(function(A){return A.getAttribute(De)===n})[0],l=H.getComputedStyle(e,n),c=l.getPropertyValue("font-family"),d=c.match(gt),m=l.getPropertyValue("font-weight"),p=l.getPropertyValue("content");if(u&&!d)return e.removeChild(u),t();if(d&&p!=="none"&&p!==""){var b=l.getPropertyValue("content"),g=function(A,I){var z=A.replace(/^['"]|['"]$/g,"").toLowerCase(),N=parseInt(I),Y=isNaN(N)?"normal":N;return(Xe[z]||{})[Y]||Bt[z]}(c,m),v=function(A){return Fa(L(A.replace(En,""))[0]||"")}(b),w=d[0].startsWith("FontAwesome"),S=function(A){var I=A.getPropertyValue("font-feature-settings").includes("ss01"),z=A.getPropertyValue("content").replace(En,""),N=z.codePointAt(0),Y=N>=Dn[0]&&N<=Dn[1],Se=z.length===2&&z[0]===z[1];return Y||Se||I}(l),C=He(g,v),E=C;if(w){var x=(o=Wa[i=v],f=He("fas",i),o||(f?{prefix:"fas",iconName:f}:null)||{prefix:null,iconName:null});x.iconName&&x.prefix&&(C=x.iconName,g=x.prefix)}if(!C||S||u&&u.getAttribute(Te)===g&&u.getAttribute(Re)===E)t();else{e.setAttribute(a,E),u&&e.removeChild(u);var M={iconName:null,prefix:null,transform:D,symbol:!1,mask:{iconName:null,prefix:null,rest:[]},maskId:null,extra:{classes:[],styles:{},attributes:{}}},j=M.extra;j.attributes[De]=n,qe(C,g).then(function(A){var I=on(s(s({},M),{},{icons:{main:A,mask:{prefix:null,iconName:null,rest:[]}},prefix:g,iconName:E,extra:j,watchable:!0})),z=k.createElementNS("http://www.w3.org/2000/svg","svg");n==="::before"?e.insertBefore(z,e.firstChild):e.appendChild(z),z.outerHTML=I.map(function(N){return se(N)}).join(` +`),e.removeAttribute(a),t()}).catch(r)}}else t()})}function Yt(e){return Promise.all([Rn(e,"::before"),Rn(e,"::after")])}function Ht(e){return!(e.parentNode===document.head||~dt.indexOf(e.tagName.toUpperCase())||e.getAttribute(De)||e.parentNode&&e.parentNode.tagName==="svg")}var Jt=function(e){return!!e&&za.some(function(n){return e.includes(n)})},Kt=function(e){if(!e)return[];for(var n=new Set,a=[e],t=function(){var c=i[r];a=a.flatMap(function(d){return d.split(c).map(function(m){return m.replace(/,\s*$/,"").trim()})})},r=0,i=[/(?=\s:)/,new RegExp("(?<=\\)\\)?[^,]*,)")];r1&&arguments[1]!==void 0&&arguments[1])n=e;else if(h.searchPseudoElementsFullScan)n=e.querySelectorAll("*");else{var a,t=new Set,r=pe(document.styleSheets);try{for(r.s();!(a=r.n()).done;){var i=a.value;try{var o,f=pe(i.cssRules);try{for(f.s();!(o=f.n()).done;){var u,l=o.value,c=pe(Kt(l.selectorText));try{for(c.s();!(u=c.n()).done;){var d=u.value;t.add(d)}}catch(p){c.e(p)}finally{c.f()}}}catch(p){f.e(p)}finally{f.f()}}catch{h.searchPseudoElementsWarnings}}}catch(p){r.e(p)}finally{r.f()}if(!t.size)return;var m=Array.from(t).join(", ");try{n=e.querySelectorAll(m)}catch{}}return new Promise(function(p,b){var g=ee(n).filter(Ht).map(Yt),v=ln("searchPseudoElements");qa(),Promise.all(g).then(function(){v(),Ve(),p()}).catch(function(){v(),Ve(),b()})})}}var Bn=!1,Yn=function(e){return e.toLowerCase().split(" ").reduce(function(n,a){var t=a.toLowerCase().split("-"),r=t[0],i=t.slice(1).join("-");if(r&&i==="h")return n.flipX=!0,n;if(r&&i==="v")return n.flipY=!0,n;if(i=parseFloat(i),isNaN(i))return n;switch(r){case"grow":n.size=n.size+i;break;case"shrink":n.size=n.size-i;break;case"left":n.x=n.x-i;break;case"right":n.x=n.x+i;break;case"up":n.y=n.y-i;break;case"down":n.y=n.y+i;break;case"rotate":n.rotate=n.rotate+i}return n},{size:16,x:0,y:0,flipX:!1,flipY:!1,rotate:0})},Ut={mixout:function(){return{parse:{transform:function(e){return Yn(e)}}}},hooks:function(){return{parseNodeAttributes:function(e,n){var a=n.getAttribute("data-fa-transform");return a&&(e.transform=Yn(a)),e}}},provides:function(e){e.generateAbstractTransformGrouping=function(n){var a=n.main,t=n.transform,r=n.containerWidth,i=n.iconWidth,o={transform:"translate(".concat(r/2," 256)")},f="translate(".concat(32*t.x,", ").concat(32*t.y,") "),u="scale(".concat(t.size/16*(t.flipX?-1:1),", ").concat(t.size/16*(t.flipY?-1:1),") "),l="rotate(".concat(t.rotate," 0 0)"),c={outer:o,inner:{transform:"".concat(f," ").concat(u," ").concat(l)},path:{transform:"translate(".concat(i/2*-1," -256)")}};return{tag:"g",attributes:s({},c.outer),children:[{tag:"g",attributes:s({},c.inner),children:[{tag:a.icon.tag,children:a.icon.children,attributes:s(s({},a.icon.attributes),c.path)}]}]}}}},Oe={x:0,y:0,width:"100%",height:"100%"};function Hn(e){var n=!(arguments.length>1&&arguments[1]!==void 0)||arguments[1];return e.attributes&&(e.attributes.fill||n)&&(e.attributes.fill="black"),e}var ne,qt={hooks:function(){return{parseNodeAttributes:function(e,n){var a=n.getAttribute("data-fa-mask"),t=a?we(a.split(" ").map(function(r){return r.trim()})):{prefix:null,iconName:null,rest:[]};return t.prefix||(t.prefix=J()),e.mask=t,e.maskId=n.getAttribute("data-fa-mask-id"),e}}},provides:function(e){e.generateAbstractMask=function(n){var a,t=n.children,r=n.attributes,i=n.main,o=n.mask,f=n.maskId,u=n.transform,l=i.width,c=i.icon,d=o.width,m=o.icon,p=function(M){var j=M.transform,A=M.containerWidth,I=M.iconWidth,z={transform:"translate(".concat(A/2," 256)")},N="translate(".concat(32*j.x,", ").concat(32*j.y,") "),Y="scale(".concat(j.size/16*(j.flipX?-1:1),", ").concat(j.size/16*(j.flipY?-1:1),") "),Se="rotate(".concat(j.rotate," 0 0)");return{outer:z,inner:{transform:"".concat(N," ").concat(Y," ").concat(Se)},path:{transform:"translate(".concat(I/2*-1," -256)")}}}({transform:u,containerWidth:d,iconWidth:l}),b={tag:"rect",attributes:s(s({},Oe),{},{fill:"white"})},g=c.children?{children:c.children.map(Hn)}:{},v={tag:"g",attributes:s({},p.inner),children:[Hn(s({tag:c.tag,attributes:s(s({},c.attributes),p.path)},g))]},w={tag:"g",attributes:s({},p.outer),children:[v]},S="mask-".concat(f||yn()),C="clip-".concat(f||yn()),E={tag:"mask",attributes:s(s({},Oe),{},{id:S,maskUnits:"userSpaceOnUse",maskContentUnits:"userSpaceOnUse"}),children:[b,w]},x={tag:"defs",children:[{tag:"clipPath",attributes:{id:C},children:(a=m,a.tag==="g"?a.children:[a])},E]};return t.push(x,{tag:"rect",attributes:s({fill:"currentColor","clip-path":"url(#".concat(C,")"),mask:"url(#".concat(S,")")},Oe)}),{children:t,attributes:r}}}};ne=fe,Nn=[bt,Dt,Tt,Rt,Wt,{hooks:function(){return{mutationObserverCallbacks:function(e){return e.pseudoElementsCallback=Wn,e}}},provides:function(e){e.pseudoElements2svg=function(n){var a=n.node,t=a===void 0?k:a;h.searchPseudoElements&&Wn(t)}}},{mixout:function(){return{dom:{unwatch:function(){qa(),Bn=!0}}}},hooks:function(){return{bootstrap:function(){Cn(Je("mutationObserverCallbacks",{}))},noAuto:function(){ve&&ve.disconnect()},watch:function(e){var n=e.observeMutationsRoot;Bn?Ve():Cn(Je("mutationObserverCallbacks",{observeMutationsRoot:n}))}}}},Ut,qt,{provides:function(e){var n=!1;H.matchMedia&&(n=H.matchMedia("(prefers-reduced-motion: reduce)").matches),e.missingIconAbstract=function(){var a=[],t={fill:"currentColor"},r={attributeType:"XML",repeatCount:"indefinite",dur:"2s"};a.push({tag:"path",attributes:s(s({},t),{},{d:"M156.5,447.7l-12.6,29.5c-18.7-9.5-35.9-21.2-51.5-34.9l22.7-22.7C127.6,430.5,141.5,440,156.5,447.7z M40.6,272H8.5 c1.4,21.2,5.4,41.7,11.7,61.1L50,321.2C45.1,305.5,41.8,289,40.6,272z M40.6,240c1.4-18.8,5.2-37,11.1-54.1l-29.5-12.6 C14.7,194.3,10,216.7,8.5,240H40.6z M64.3,156.5c7.8-14.9,17.2-28.8,28.1-41.5L69.7,92.3c-13.7,15.6-25.5,32.8-34.9,51.5 L64.3,156.5z M397,419.6c-13.9,12-29.4,22.3-46.1,30.4l11.9,29.8c20.7-9.9,39.8-22.6,56.9-37.6L397,419.6z M115,92.4 c13.9-12,29.4-22.3,46.1-30.4l-11.9-29.8c-20.7,9.9-39.8,22.6-56.8,37.6L115,92.4z M447.7,355.5c-7.8,14.9-17.2,28.8-28.1,41.5 l22.7,22.7c13.7-15.6,25.5-32.9,34.9-51.5L447.7,355.5z M471.4,272c-1.4,18.8-5.2,37-11.1,54.1l29.5,12.6 c7.5-21.1,12.2-43.5,13.6-66.8H471.4z M321.2,462c-15.7,5-32.2,8.2-49.2,9.4v32.1c21.2-1.4,41.7-5.4,61.1-11.7L321.2,462z M240,471.4c-18.8-1.4-37-5.2-54.1-11.1l-12.6,29.5c21.1,7.5,43.5,12.2,66.8,13.6V471.4z M462,190.8c5,15.7,8.2,32.2,9.4,49.2h32.1 c-1.4-21.2-5.4-41.7-11.7-61.1L462,190.8z M92.4,397c-12-13.9-22.3-29.4-30.4-46.1l-29.8,11.9c9.9,20.7,22.6,39.8,37.6,56.9 L92.4,397z M272,40.6c18.8,1.4,36.9,5.2,54.1,11.1l12.6-29.5C317.7,14.7,295.3,10,272,8.5V40.6z M190.8,50 c15.7-5,32.2-8.2,49.2-9.4V8.5c-21.2,1.4-41.7,5.4-61.1,11.7L190.8,50z M442.3,92.3L419.6,115c12,13.9,22.3,29.4,30.5,46.1 l29.8-11.9C470,128.5,457.3,109.4,442.3,92.3z M397,92.4l22.7-22.7c-15.6-13.7-32.8-25.5-51.5-34.9l-12.6,29.5 C370.4,72.1,384.4,81.5,397,92.4z"})});var i=s(s({},r),{},{attributeName:"opacity"}),o={tag:"circle",attributes:s(s({},t),{},{cx:"256",cy:"364",r:"28"}),children:[]};return n||o.children.push({tag:"animate",attributes:s(s({},r),{},{attributeName:"r",values:"28;14;28;28;14;28;"})},{tag:"animate",attributes:s(s({},i),{},{values:"1;0;1;1;0;1;"})}),a.push(o),a.push({tag:"path",attributes:s(s({},t),{},{opacity:"1",d:"M263.7,312h-16c-6.6,0-12-5.4-12-12c0-71,77.4-63.9,77.4-107.8c0-20-17.8-40.2-57.4-40.2c-29.1,0-44.3,9.6-59.2,28.7 c-3.9,5-11.1,6-16.2,2.4l-13.1-9.2c-5.6-3.9-6.9-11.8-2.6-17.2c21.2-27.2,46.4-44.7,91.2-44.7c52.3,0,97.4,29.8,97.4,80.2 c0,67.6-77.4,63.5-77.4,107.8C275.7,306.6,270.3,312,263.7,312z"}),children:n?[]:[{tag:"animate",attributes:s(s({},i),{},{values:"1;0;0;0;0;1;"})}]}),n||a.push({tag:"path",attributes:s(s({},t),{},{opacity:"0",d:"M232.5,134.5l7,168c0.3,6.4,5.6,11.5,12,11.5h9c6.4,0,11.7-5.1,12-11.5l7-168c0.3-6.8-5.2-12.5-12-12.5h-23 C237.7,122,232.2,127.7,232.5,134.5z"}),children:[{tag:"animate",attributes:s(s({},i),{},{values:"0;0;1;1;0;0;"})}]}),{tag:"g",attributes:{class:"missing"},children:a}}}},{hooks:function(){return{parseNodeAttributes:function(e,n){var a=n.getAttribute("data-fa-symbol"),t=a!==null&&(a===""||a);return e.symbol=t,e}}}}],Z={},Object.keys(Q).forEach(function(e){jt.indexOf(e)===-1&&delete Q[e]}),Nn.forEach(function(e){var n=e.mixout?e.mixout():{};if(Object.keys(n).forEach(function(t){typeof n[t]=="function"&&(ne[t]=n[t]),Ze(n[t])==="object"&&Object.keys(n[t]).forEach(function(r){ne[t]||(ne[t]={}),ne[t][r]=n[t][r]})}),e.hooks){var a=e.hooks();Object.keys(a).forEach(function(t){Z[t]||(Z[t]=[]),Z[t].push(a[t])})}e.provides&&e.provides(Q)});var Zt=fe.library,Ge=fe.parse,_t=fe.icon;function P(e,n,a){return(n=function(t){var r=function(i,o){if(typeof i!="object"||!i)return i;var f=i[Symbol.toPrimitive];if(f!==void 0){var u=f.call(i,o);if(typeof u!="object")return u;throw new TypeError("@@toPrimitive must return a primitive value.")}return(o==="string"?String:Number)(i)}(t,"string");return typeof r=="symbol"?r:r+""}(n))in e?Object.defineProperty(e,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[n]=a,e}function Jn(e,n){var a=Object.keys(e);if(Object.getOwnPropertySymbols){var t=Object.getOwnPropertySymbols(e);n&&(t=t.filter(function(r){return Object.getOwnPropertyDescriptor(e,r).enumerable})),a.push.apply(a,t)}return a}function W(e){for(var n=1;n0||!Array.isArray(n)&&n?P({},e,n):{}}var Ie,Kn,G,ue,Ce,de,ae,Un,qn,_n,Vn,Xn,Gn,$n,me,Fe,Vt=typeof globalThis<"u"?globalThis:typeof window<"u"?window:fn!==void 0?fn:typeof self<"u"?self:{},Va={exports:{}};Ie=Va,Kn=Vt,G=function(e,n,a){if(!qn(n)||Vn(n)||Xn(n)||Gn(n)||Un(n))return n;var t,r=0,i=0;if(_n(n))for(t=[],i=n.length;r1&&arguments[1]!==void 0?arguments[1]:{},a=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};if(typeof e=="string")return e;var t=(e.children||[]).map(function(u){return Xa(u)}),r=Object.keys(e.attributes||{}).reduce(function(u,l){var c=e.attributes[l];switch(l){case"class":u.class=c.split(/\s+/).reduce(function(d,m){return d[m]=!0,d},{});break;case"style":u.style=c.split(";").map(function(d){return d.trim()}).filter(function(d){return d}).reduce(function(d,m){var p=m.indexOf(":"),b=Xt.camelize(m.slice(0,p)),g=m.slice(p+1).trim();return d[b]=g,d},{});break;default:u.attrs[l]=c}return u},{attrs:{},class:{},style:{}});a.class;var i=a.style,o=i===void 0?{}:i,f=function(u,l){if(u==null)return{};var c,d,m=function(b,g){if(b==null)return{};var v={};for(var w in b)if({}.hasOwnProperty.call(b,w)){if(g.indexOf(w)!==-1)continue;v[w]=b[w]}return v}(u,l);if(Object.getOwnPropertySymbols){var p=Object.getOwnPropertySymbols(u);for(d=0;d-1}},icon:{type:[Object,Array,String],required:!0},mask:{type:[Object,Array,String],default:null},maskId:{type:String,default:null},listItem:{type:Boolean,default:!1},pull:{type:String,default:null,validator:function(e){return["right","left"].indexOf(e)>-1}},pulse:{type:Boolean,default:!1},rotation:{type:[String,Number],default:null,validator:function(e){return[90,180,270].indexOf(Number.parseInt(e,10))>-1}},rotateBy:{type:Boolean,default:!1},swapOpacity:{type:Boolean,default:!1},size:{type:String,default:null,validator:function(e){return["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"].indexOf(e)>-1}},spin:{type:Boolean,default:!1},transform:{type:[String,Object],default:null},symbol:{type:[Boolean,String],default:!1},title:{type:String,default:null},titleId:{type:String,default:null},inverse:{type:Boolean,default:!1},bounce:{type:Boolean,default:!1},shake:{type:Boolean,default:!1},beat:{type:Boolean,default:!1},fade:{type:Boolean,default:!1},beatFade:{type:Boolean,default:!1},flash:{type:Boolean,default:!1},spinPulse:{type:Boolean,default:!1},spinReverse:{type:Boolean,default:!1},widthAuto:{type:Boolean,default:!1}},setup:function(e,n){var a=n.attrs,t=V(function(){return Zn(e.icon)}),r=V(function(){return Me("classes",function(l){var c,d=(P(P(P(P(P(P(P(P(P(P(c={"fa-spin":l.spin,"fa-pulse":l.pulse,"fa-fw":l.fixedWidth,"fa-border":l.border,"fa-li":l.listItem,"fa-inverse":l.inverse,"fa-flip":l.flip===!0,"fa-flip-horizontal":l.flip==="horizontal"||l.flip==="both","fa-flip-vertical":l.flip==="vertical"||l.flip==="both"},"fa-".concat(l.size),l.size!==null),"fa-rotate-".concat(l.rotation),l.rotation!==null),"fa-rotate-by",l.rotateBy),"fa-pull-".concat(l.pull),l.pull!==null),"fa-swap-opacity",l.swapOpacity),"fa-bounce",l.bounce),"fa-shake",l.shake),"fa-beat",l.beat),"fa-fade",l.fade),"fa-beat-fade",l.beatFade),P(P(P(P(c,"fa-flash",l.flash),"fa-spin-pulse",l.spinPulse),"fa-spin-reverse",l.spinReverse),"fa-width-auto",l.widthAuto));return Object.keys(d).map(function(m){return d[m]?m:null}).filter(function(m){return m})}(e))}),i=V(function(){return Me("transform",typeof e.transform=="string"?Ge.transform(e.transform):e.transform)}),o=V(function(){return Me("mask",Zn(e.mask))}),f=V(function(){var l=W(W(W(W({},r.value),i.value),o.value),{},{symbol:e.symbol,maskId:e.maskId});return l.title=e.title,l.titleId=e.titleId,_t(t.value,l)});Za(f,function(l){if(!l)return function(){var c;!Ga&&console&&typeof console.error=="function"&&(c=console).error.apply(c,arguments)}("Could not find one or more icon(s)",t.value,o.value)},{immediate:!0});var u=V(function(){return f.value?Xa(f.value.abstract[0],{},a):null});return function(){return u.value}}}),er={prefix:"fas",iconName:"right-long",icon:[576,512,["long-arrow-alt-right"],"f30b","M566.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-128 128c-9.2 9.2-22.9 11.9-34.9 6.9S384 396.9 384 384l0-64-336 0c-26.5 0-48-21.5-48-48l0-32c0-26.5 21.5-48 48-48l336 0 0-64c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l128 128z"]},nr={prefix:"fas",iconName:"calculator",icon:[384,512,[128425],"f1ec","M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-384c0-35.3-28.7-64-64-64L64 0zM96 64l192 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L96 160c-17.7 0-32-14.3-32-32l0-32c0-17.7 14.3-32 32-32zm16 168a24 24 0 1 1 -48 0 24 24 0 1 1 48 0zm80 24a24 24 0 1 1 0-48 24 24 0 1 1 0 48zm128-24a24 24 0 1 1 -48 0 24 24 0 1 1 48 0zM88 352a24 24 0 1 1 0-48 24 24 0 1 1 0 48zm128-24a24 24 0 1 1 -48 0 24 24 0 1 1 48 0zm80 24a24 24 0 1 1 0-48 24 24 0 1 1 0 48zM64 424c0-13.3 10.7-24 24-24l112 0c13.3 0 24 10.7 24 24s-10.7 24-24 24L88 448c-13.3 0-24-10.7-24-24zm232-24c13.3 0 24 10.7 24 24s-10.7 24-24 24-24-10.7-24-24 10.7-24 24-24z"]},ar={prefix:"fas",iconName:"car",icon:[512,512,[128664,"automobile"],"f1b9","M135.2 117.4l-26.1 74.6 293.8 0-26.1-74.6C372.3 104.6 360.2 96 346.6 96L165.4 96c-13.6 0-25.7 8.6-30.2 21.4zM39.6 196.8L74.8 96.3C88.3 57.8 124.6 32 165.4 32l181.2 0c40.8 0 77.1 25.8 90.6 64.3l35.2 100.5c23.2 9.6 39.6 32.5 39.6 59.2l0 192c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-32-320 0 0 32c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32L0 256c0-26.7 16.4-49.6 39.6-59.2zM128 304a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm288 32a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"]},tr={prefix:"fas",iconName:"calendar-days",icon:[448,512,["calendar-alt"],"f073","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 32 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l32 0 0-32c0-17.7 14.3-32 32-32zM64 240l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm128 0l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm144-16c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zM64 368l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm144-16c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zm112 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16z"]},rr={prefix:"fas",iconName:"power-off",icon:[512,512,[9211],"f011","M288 0c0-17.7-14.3-32-32-32S224-17.7 224 0l0 256c0 17.7 14.3 32 32 32s32-14.3 32-32L288 0zM146.3 98.4c14.5-10.1 18-30.1 7.9-44.6s-30.1-18-44.6-7.9C43.4 92.1 0 169 0 256 0 397.4 114.6 512 256 512S512 397.4 512 256c0-87-43.4-163.9-109.7-210.1-14.5-10.1-34.4-6.6-44.6 7.9s-6.6 34.4 7.9 44.6c49.8 34.8 82.3 92.4 82.3 157.6 0 106-86 192-192 192S64 362 64 256c0-65.2 32.5-122.9 82.3-157.6z"]},ir={prefix:"fas",iconName:"delete-left",icon:[640,512,[9003,"backspace"],"f55a","M576 128c0-35.3-28.7-64-64-64L205.3 64c-17 0-33.3 6.7-45.3 18.7L9.4 233.4c-6 6-9.4 14.1-9.4 22.6s3.4 16.6 9.4 22.6L160 429.3c12 12 28.3 18.7 45.3 18.7L512 448c35.3 0 64-28.7 64-64l0-256zM284.1 188.1c9.4-9.4 24.6-9.4 33.9 0l33.9 33.9 33.9-33.9c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-33.9 33.9 33.9 33.9c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-33.9-33.9-33.9 33.9c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l33.9-33.9-33.9-33.9c-9.4-9.4-9.4-24.6 0-33.9z"]},or={prefix:"fas",iconName:"pen-to-square",icon:[512,512,["edit"],"f044","M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L368 46.1 465.9 144 490.3 119.6c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L432 177.9 334.1 80 172.4 241.7zM96 64C43 64 0 107 0 160L0 416c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 64z"]},lr={prefix:"fas",iconName:"clock",icon:[512,512,[128339,"clock-four"],"f017","M256 0a256 256 0 1 1 0 512 256 256 0 1 1 0-512zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"]},sr={prefix:"fas",iconName:"circle-xmark",icon:[512,512,[61532,"times-circle","xmark-circle"],"f057","M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM167 167c9.4-9.4 24.6-9.4 33.9 0l55 55 55-55c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-55 55 55 55c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-55-55-55 55c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l55-55-55-55c-9.4-9.4-9.4-24.6 0-33.9z"]},fr={prefix:"fas",iconName:"plug-circle-bolt",icon:[640,512,[],"e55b","M192-32c17.7 0 32 14.3 32 32l0 96 128 0 0-96c0-17.7 14.3-32 32-32s32 14.3 32 32l0 96 64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l0 48.7c-98.6 8.1-176 90.7-176 191.3 0 27.3 5.7 53.3 16 76.9l0 3.1c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-66.7C165.2 398.1 96 319.1 96 224l0-64c-17.7 0-32-14.3-32-32S78.3 96 96 96l64 0 0-96c0-17.7 14.3-32 32-32zM352 400a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm177.4-77c-5.8-4.2-13.8-4-19.4 .5l-80 64c-5.3 4.2-7.4 11.4-5.1 17.8S433.2 416 440 416l32.9 0-15.9 42.4c-2.5 6.7-.2 14.3 5.6 18.6s13.8 4 19.4-.5l80-64c5.3-4.2 7.4-11.4 5.1-17.8S558.8 384 552 384l-32.9 0 15.9-42.4c2.5-6.7 .2-14.3-5.6-18.6z"]},cr={prefix:"fas",iconName:"calendar-day",icon:[448,512,[],"f783","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 32 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l32 0 0-32c0-17.7 14.3-32 32-32zm0 256c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l64 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-64 0z"]},ur={prefix:"fas",iconName:"car-battery",icon:[512,512,["battery-car"],"f5df","M80 64c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l96 0c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l16 0c35.3 0 64 28.7 64 64l0 256c0 35.3-28.7 64-64 64L64 448c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l16 0zM392 184c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 32-32 0c-13.3 0-24 10.7-24 24s10.7 24 24 24l32 0 0 32c0 13.3 10.7 24 24 24s24-10.7 24-24l0-32 32 0c13.3 0 24-10.7 24-24s-10.7-24-24-24l-32 0 0-32zM64 240c0 13.3 10.7 24 24 24l112 0c13.3 0 24-10.7 24-24s-10.7-24-24-24L88 216c-13.3 0-24 10.7-24 24z"]},dr={prefix:"fas",iconName:"wrench",icon:[576,512,[128295],"f0ad","M509.4 98.6c7.6-7.6 20.3-5.7 24.1 4.3 6.8 17.7 10.5 37 10.5 57.1 0 88.4-71.6 160-160 160-17.5 0-34.4-2.8-50.2-8L146.9 498.9c-28.1 28.1-73.7 28.1-101.8 0s-28.1-73.7 0-101.8L232 210.2c-5.2-15.8-8-32.6-8-50.2 0-88.4 71.6-160 160-160 20.1 0 39.4 3.7 57.1 10.5 10 3.8 11.8 16.5 4.3 24.1l-88.7 88.7c-3 3-4.7 7.1-4.7 11.3l0 41.4c0 8.8 7.2 16 16 16l41.4 0c4.2 0 8.3-1.7 11.3-4.7l88.7-88.7z"]},mr={prefix:"fas",iconName:"eraser",icon:[576,512,[],"f12d","M178.5 416l123 0 65.3-65.3-173.5-173.5-126.7 126.7 112 112zM224 480l-45.5 0c-17 0-33.3-6.7-45.3-18.7L17 345C6.1 334.1 0 319.4 0 304s6.1-30.1 17-41L263 17C273.9 6.1 288.6 0 304 0s30.1 6.1 41 17L527 199c10.9 10.9 17 25.6 17 41s-6.1 30.1-17 41l-135 135 120 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0z"]},pr={prefix:"fas",iconName:"charging-station",icon:[576,512,[],"f5e7","M64 64C64 28.7 92.7 0 128 0L288 0c35.3 0 64 28.7 64 64l0 224c44.2 0 80 35.8 80 80l0 12c0 11 9 20 20 20s20-9 20-20l0-127.7c-32.5-10.2-56-40.5-56-76.3l0-32c0-8.8 7.2-16 16-16l16 0 0-48c0-8.8 7.2-16 16-16s16 7.2 16 16l0 48 32 0 0-48c0-8.8 7.2-16 16-16s16 7.2 16 16l0 48 16 0c8.8 0 16 7.2 16 16l0 32c0 35.8-23.5 66.1-56 76.3L520 380c0 37.6-30.4 68-68 68s-68-30.4-68-68l0-12c0-17.7-14.3-32-32-32l0 129.4c9.3 3.3 16 12.2 16 22.6 0 13.3-10.7 24-24 24L72 512c-13.3 0-24-10.7-24-24 0-10.5 6.7-19.3 16-22.6L64 64zm82.7 125.7l39 0-20.9 66.9c-2.4 7.6 3.3 15.4 11.3 15.4 2.9 0 5.6-1 7.8-2.9l94.6-82c3.1-2.7 4.9-6.6 4.9-10.7 0-7.8-6.3-14.1-14.1-14.1l-39 0 20.9-66.9c2.4-7.6-3.3-15.4-11.3-15.4-2.9 0-5.6 1-7.8 2.9l-94.6 82c-3.1 2.7-4.9 6.6-4.9 10.7 0 7.8 6.3 14.1 14.1 14.1z"]},gr={prefix:"fas",iconName:"house",icon:[512,512,[127968,63498,63500,"home","home-alt","home-lg-alt"],"f015","M277.8 8.6c-12.3-11.4-31.3-11.4-43.5 0l-224 208c-9.6 9-12.8 22.9-8 35.1S18.8 272 32 272l16 0 0 176c0 35.3 28.7 64 64 64l288 0c35.3 0 64-28.7 64-64l0-176 16 0c13.2 0 25-8.1 29.8-20.3s1.6-26.2-8-35.1l-224-208zM240 320l32 0c26.5 0 48 21.5 48 48l0 96-128 0 0-96c0-26.5 21.5-48 48-48z"]},hr={prefix:"fas",iconName:"gauge-high",icon:[512,512,[62461,"tachometer-alt","tachometer-alt-fast"],"f625","M0 256a256 256 0 1 1 512 0 256 256 0 1 1 -512 0zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64 0-16.2-6-31.1-16-42.3l69.5-138.9c5.9-11.9 1.1-26.3-10.7-32.2s-26.3-1.1-32.2 10.7L261.1 288.2c-1.7-.1-3.4-.2-5.1-.2-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"]},vr={prefix:"fas",iconName:"right-left",icon:[512,512,["exchange-alt"],"f362","M502.6 150.6l-96 96c-9.2 9.2-22.9 11.9-34.9 6.9S352 236.9 352 224l0-64-320 0c-17.7 0-32-14.3-32-32S14.3 96 32 96l320 0 0-64c0-12.9 7.8-24.6 19.8-29.6s25.7-2.2 34.9 6.9l96 96c12.5 12.5 12.5 32.8 0 45.3zm-397.3 352l-96-96c-12.5-12.5-12.5-32.8 0-45.3l96-96c9.2-9.2 22.9-11.9 34.9-6.9S160 275.1 160 288l0 64 320 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-320 0 0 64c0 12.9-7.8 24.6-19.8 29.6s-25.7 2.2-34.9-6.9z"]},br={prefix:"fas",iconName:"lock-open",icon:[576,512,[],"f3c1","M384 96c0-35.3 28.7-64 64-64s64 28.7 64 64l0 32c0 17.7 14.3 32 32 32s32-14.3 32-32l0-32c0-70.7-57.3-128-128-128S320 25.3 320 96l0 64-160 0c-35.3 0-64 28.7-64 64l0 224c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-224c0-35.3-28.7-64-64-64l-32 0 0-64z"]},yr={prefix:"fas",iconName:"plug-circle-xmark",icon:[640,512,[],"e560","M192-32c17.7 0 32 14.3 32 32l0 96 128 0 0-96c0-17.7 14.3-32 32-32s32 14.3 32 32l0 96 64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l0 48.7c-98.6 8.1-176 90.7-176 191.3 0 27.3 5.7 53.3 16 76.9l0 3.1c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-66.7C165.2 398.1 96 319.1 96 224l0-64c-17.7 0-32-14.3-32-32S78.3 96 96 96l64 0 0-96c0-17.7 14.3-32 32-32zM496 256a144 144 0 1 1 0 288 144 144 0 1 1 0-288zm59.3 107.3c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0l-36.7 36.7-36.7-36.7c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6l36.7 36.7-36.7 36.7c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0l36.7-36.7 36.7 36.7c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6l-36.7-36.7 36.7-36.7z"]},xr={prefix:"fas",iconName:"solar-panel",icon:[576,512,[],"f5ba","M121.8 32c-30 0-56 20.8-62.5 50.1L9.6 306.1C.7 346.1 31.1 384 72 384l184.1 0 0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l192 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64 184.1 0c40.9 0 71.4-37.9 62.5-77.9l-49.8-224C510.4 52.8 484.5 32 454.5 32L121.8 32zM245.6 96l85.2 0 7.3 88-99.8 0 7.3-88zm-55.5 88l-87.8 0 19.6-88 75.6 0-7.3 88zM91.6 232l94.5 0-7.3 88-106.7 0 19.6-88zm142.6 0l107.8 0 7.3 88-122.5 0 7.3-88zm156 0l94.5 0 19.6 88-106.7 0-7.3-88zM474 184l-87.8 0-7.3-88 75.6 0 19.6 88z"]},wr={prefix:"fas",iconName:"plug-circle-check",icon:[640,512,[],"e55c","M192-32c17.7 0 32 14.3 32 32l0 96 128 0 0-96c0-17.7 14.3-32 32-32s32 14.3 32 32l0 96 64 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l0 48.7c-98.6 8.1-176 90.7-176 191.3 0 27.3 5.7 53.3 16 76.9l0 3.1c0 17.7-14.3 32-32 32s-32-14.3-32-32l0-66.7C165.2 398.1 96 319.1 96 224l0-64c-17.7 0-32-14.3-32-32S78.3 96 96 96l64 0 0-96c0-17.7 14.3-32 32-32zM352 400a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm201.4-60.9c-7.1-5.2-17.2-3.6-22.4 3.5l-53 72.9-26.8-26.8c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6l40 40c3.3 3.3 7.9 5 12.6 4.6s8.9-2.8 11.7-6.5l64-88c5.2-7.1 3.6-17.2-3.5-22.3z"]},kr={prefix:"fas",iconName:"star",icon:[576,512,[11088,61446],"f005","M309.5-18.9c-4.1-8-12.4-13.1-21.4-13.1s-17.3 5.1-21.4 13.1L193.1 125.3 33.2 150.7c-8.9 1.4-16.3 7.7-19.1 16.3s-.5 18 5.8 24.4l114.4 114.5-25.2 159.9c-1.4 8.9 2.3 17.9 9.6 23.2s16.9 6.1 25 2L288.1 417.6 432.4 491c8 4.1 17.7 3.3 25-2s11-14.2 9.6-23.2L441.7 305.9 556.1 191.4c6.4-6.4 8.6-15.8 5.8-24.4s-10.1-14.9-19.1-16.3L383 125.3 309.5-18.9z"]},Sr={prefix:"fas",iconName:"triangle-exclamation",icon:[512,512,[9888,"exclamation-triangle","warning"],"f071","M256 0c14.7 0 28.2 8.1 35.2 21l216 400c6.7 12.4 6.4 27.4-.8 39.5S486.1 480 472 480L40 480c-14.1 0-27.1-7.4-34.4-19.5s-7.5-27.1-.8-39.5l216-400c7-12.9 20.5-21 35.2-21zm0 168c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm26.7 216a26.7 26.7 0 1 0 -53.3 0 26.7 26.7 0 1 0 53.3 0z"]},zr={prefix:"fas",iconName:"lock",icon:[384,512,[128274],"f023","M128 96l0 64 128 0 0-64c0-35.3-28.7-64-64-64s-64 28.7-64 64zM64 160l0-64C64 25.3 121.3-32 192-32S320 25.3 320 96l0 64c35.3 0 64 28.7 64 64l0 224c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 224c0-35.3 28.7-64 64-64z"]},jr={prefix:"fas",iconName:"bolt",icon:[448,512,[9889,"zap"],"f0e7","M338.8-9.9c11.9 8.6 16.3 24.2 10.9 37.8L271.3 224 416 224c13.5 0 25.5 8.4 30.1 21.1s.7 26.9-9.6 35.5l-288 240c-11.3 9.4-27.4 9.9-39.3 1.3s-16.3-24.2-10.9-37.8L176.7 288 32 288c-13.5 0-25.5-8.4-30.1-21.1s-.7-26.9 9.6-35.5l288-240c11.3-9.4 27.4-9.9 39.3-1.3z"]},Ar={prefix:"fas",iconName:"arrow-rotate-left",icon:[512,512,[8634,"arrow-left-rotate","arrow-rotate-back","arrow-rotate-backward","undo"],"f0e2","M256 64c-56.8 0-107.9 24.7-143.1 64l47.1 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L32 192c-17.7 0-32-14.3-32-32L0 32C0 14.3 14.3 0 32 0S64 14.3 64 32l0 54.7C110.9 33.6 179.5 0 256 0 397.4 0 512 114.6 512 256S397.4 512 256 512c-87 0-163.9-43.4-210.1-109.7-10.1-14.5-6.6-34.4 7.9-44.6s34.4-6.6 44.6 7.9c34.8 49.8 92.4 82.3 157.6 82.3 106 0 192-86 192-192S362 64 256 64z"]},Nr={prefix:"fas",iconName:"coins",icon:[512,512,[],"f51e","M128 96l0-16c0-44.2 86-80 192-80S512 35.8 512 80l0 16c0 30.6-41.3 57.2-102 70.7-2.4-2.8-4.9-5.5-7.4-8-15.5-15.3-35.5-26.9-56.4-35.5-41.9-17.5-96.5-27.1-154.2-27.1-21.9 0-43.3 1.4-63.8 4.1-.2-1.3-.2-2.7-.2-4.1zM432 353l0-46.2c15.1-3.9 29.3-8.5 42.2-13.9 13.2-5.5 26.1-12.2 37.8-20.3l0 15.4c0 26.8-31.5 50.5-80 65zm0-96l0-33c0-4.5-.4-8.8-1-13 15.5-3.9 30-8.6 43.2-14.2s26.1-12.2 37.8-20.3l0 15.4c0 26.8-31.5 50.5-80 65zM0 240l0-16c0-44.2 86-80 192-80s192 35.8 192 80l0 16c0 44.2-86 80-192 80S0 284.2 0 240zm384 96c0 44.2-86 80-192 80S0 380.2 0 336l0-15.4c11.6 8.1 24.5 14.7 37.8 20.3 41.9 17.5 96.5 27.1 154.2 27.1s112.3-9.7 154.2-27.1c13.2-5.5 26.1-12.2 37.8-20.3l0 15.4zm0 80.6l0 15.4c0 44.2-86 80-192 80S0 476.2 0 432l0-15.4c11.6 8.1 24.5 14.7 37.8 20.3 41.9 17.5 96.5 27.1 154.2 27.1s112.3-9.7 154.2-27.1c13.2-5.5 26.1-12.2 37.8-20.3z"]},Pr={prefix:"fas",iconName:"calendar-week",icon:[448,512,[],"f784","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 32 0c35.3 0 64 28.7 64 64l0 288c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l32 0 0-32c0-17.7 14.3-32 32-32zm0 256c-17.7 0-32 14.3-32 32l0 64c0 17.7 14.3 32 32 32l192 0c17.7 0 32-14.3 32-32l0-64c0-17.7-14.3-32-32-32l-192 0z"]},Or={prefix:"fas",iconName:"circle-info",icon:[512,512,["info-circle"],"f05a","M256 512a256 256 0 1 0 0-512 256 256 0 1 0 0 512zM224 160a32 32 0 1 1 64 0 32 32 0 1 1 -64 0zm-8 64l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24z"]},Mr={prefix:"far",iconName:"clock",icon:[512,512,[128339,"clock-four"],"f017","M464 256a208 208 0 1 1 -416 0 208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0 256 256 0 1 0 -512 0zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"]},Ir={prefix:"far",iconName:"star",icon:[576,512,[11088,61446],"f005","M288.1-32c9 0 17.3 5.1 21.4 13.1L383 125.3 542.9 150.7c8.9 1.4 16.3 7.7 19.1 16.3s.5 18-5.8 24.4L441.7 305.9 467 465.8c1.4 8.9-2.3 17.9-9.6 23.2s-17 6.1-25 2L288.1 417.6 143.8 491c-8 4.1-17.7 3.3-25-2s-11-14.2-9.6-23.2L134.4 305.9 20 191.4c-6.4-6.4-8.6-15.8-5.8-24.4s10.1-14.9 19.1-16.3l159.9-25.4 73.6-144.2c4.1-8 12.4-13.1 21.4-13.1zm0 76.8L230.3 158c-3.5 6.8-10 11.6-17.6 12.8l-125.5 20 89.8 89.9c5.4 5.4 7.9 13.1 6.7 20.7l-19.8 125.5 113.3-57.6c6.8-3.5 14.9-3.5 21.8 0l113.3 57.6-19.8-125.5c-1.2-7.6 1.3-15.3 6.7-20.7l89.8-89.9-125.5-20c-7.6-1.2-14.1-6-17.6-12.8L288.1 44.8z"]};/*! + * Font Awesome Free 7.0.0 by @fontawesome - https://fontawesome.com + * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) + * Copyright 2025 Fonticons, Inc. + */export{er as A,Nr as B,yr as C,wr as D,fr as E,Qt as F,Ar as G,rr as H,mr as a,zr as b,br as c,hr as d,ur as e,ir as f,xr as g,gr as h,pr as i,nr as j,dr as k,Zt as l,ar as m,or as n,sr as o,Sr as p,Or as q,kr as r,Ir as s,lr as t,Mr as u,jr as v,cr as w,Pr as x,tr as y,vr as z}; diff --git a/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-CpQlJZ13.js b/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-CpQlJZ13.js deleted file mode 100644 index 729d35f31f..0000000000 --- a/packages/modules/display_themes/cards/web/assets/vendor-fortawesome-CpQlJZ13.js +++ /dev/null @@ -1,564 +0,0 @@ -import{g as ce,d as Lt,c as K,w as Ot,h as Mt}from"./vendor-BMrK3KHF.js";/*! - * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */function Nt(n,e,t){return(e=function(a){var r=function(i,o){if(typeof i!="object"||!i)return i;var l=i[Symbol.toPrimitive];if(l!==void 0){var f=l.call(i,o);if(typeof f!="object")return f;throw new TypeError("@@toPrimitive must return a primitive value.")}return(o==="string"?String:Number)(i)}(a,"string");return typeof r=="symbol"?r:r+""}(e))in n?Object.defineProperty(n,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):n[e]=t,n}function le(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(r){return Object.getOwnPropertyDescriptor(n,r).enumerable})),t.push.apply(t,a)}return t}function s(n){for(var e=1;e{};let $n={},Ke={},Xe=null,Ve={mark:fe,measure:fe};try{typeof window<"u"&&($n=window),typeof document<"u"&&(Ke=document),typeof MutationObserver<"u"&&(Xe=MutationObserver),typeof performance<"u"&&(Ve=performance)}catch{}const{userAgent:ue=""}=$n.navigator||{},Y=$n,v=Ke,me=Xe,sn=Ve;Y.document;const R=!!v.documentElement&&!!v.head&&typeof v.addEventListener=="function"&&typeof v.createElement=="function",Ge=~ue.indexOf("MSIE")||~ue.indexOf("Trident/");var Ze={classic:{fa:"solid",fas:"solid","fa-solid":"solid",far:"regular","fa-regular":"regular",fal:"light","fa-light":"light",fat:"thin","fa-thin":"thin",fab:"brands","fa-brands":"brands"},duotone:{fa:"solid",fad:"solid","fa-solid":"solid","fa-duotone":"solid",fadr:"regular","fa-regular":"regular",fadl:"light","fa-light":"light",fadt:"thin","fa-thin":"thin"},sharp:{fa:"solid",fass:"solid","fa-solid":"solid",fasr:"regular","fa-regular":"regular",fasl:"light","fa-light":"light",fast:"thin","fa-thin":"thin"},"sharp-duotone":{fa:"solid",fasds:"solid","fa-solid":"solid",fasdr:"regular","fa-regular":"regular",fasdl:"light","fa-light":"light",fasdt:"thin","fa-thin":"thin"}},Je=["fa-classic","fa-duotone","fa-sharp","fa-sharp-duotone"],O="classic",hn="duotone",Qe=[O,hn,"sharp","sharp-duotone"],Pt=new Map([["classic",{defaultShortPrefixId:"fas",defaultStyleId:"solid",styleIds:["solid","regular","light","thin","brands"],futureStyleIds:[],defaultFontWeight:900}],["sharp",{defaultShortPrefixId:"fass",defaultStyleId:"solid",styleIds:["solid","regular","light","thin"],futureStyleIds:[],defaultFontWeight:900}],["duotone",{defaultShortPrefixId:"fad",defaultStyleId:"solid",styleIds:["solid","regular","light","thin"],futureStyleIds:[],defaultFontWeight:900}],["sharp-duotone",{defaultShortPrefixId:"fasds",defaultStyleId:"solid",styleIds:["solid","regular","light","thin"],futureStyleIds:[],defaultFontWeight:900}]]),St=["fak","fa-kit","fakd","fa-kit-duotone"],Ct={fak:"kit","fa-kit":"kit"},jt={fakd:"kit-duotone","fa-kit-duotone":"kit-duotone"},Et=["fak","fakd"],It={kit:"fak"},Ft={"kit-duotone":"fakd"},cn={GROUP:"duotone-group",SWAP_OPACITY:"swap-opacity",PRIMARY:"primary",SECONDARY:"secondary"},Dt=["fak","fa-kit","fakd","fa-kit-duotone"],En={classic:{fab:"fa-brands",fad:"fa-duotone",fal:"fa-light",far:"fa-regular",fas:"fa-solid",fat:"fa-thin"},duotone:{fadr:"fa-regular",fadl:"fa-light",fadt:"fa-thin"},sharp:{fass:"fa-solid",fasr:"fa-regular",fasl:"fa-light",fast:"fa-thin"},"sharp-duotone":{fasds:"fa-solid",fasdr:"fa-regular",fasdl:"fa-light",fasdt:"fa-thin"}},In=["fa","fas","far","fal","fat","fad","fadr","fadl","fadt","fab","fass","fasr","fasl","fast","fasds","fasdr","fasdl","fasdt","fa-classic","fa-duotone","fa-sharp","fa-sharp-duotone","fa-solid","fa-regular","fa-light","fa-thin","fa-duotone","fa-brands"],$e=[1,2,3,4,5,6,7,8,9,10],Rt=$e.concat([11,12,13,14,15,16,17,18,19,20]),Tt=[...Object.keys({classic:["fas","far","fal","fat","fad"],duotone:["fadr","fadl","fadt"],sharp:["fass","fasr","fasl","fast"],"sharp-duotone":["fasds","fasdr","fasdl","fasdt"]}),"solid","regular","light","thin","duotone","brands","2xs","xs","sm","lg","xl","2xl","beat","border","fade","beat-fade","bounce","flip-both","flip-horizontal","flip-vertical","flip","fw","inverse","layers-counter","layers-text","layers","li","pull-left","pull-right","pulse","rotate-180","rotate-270","rotate-90","rotate-by","shake","spin-pulse","spin-reverse","spin","stack-1x","stack-2x","stack","ul",cn.GROUP,cn.SWAP_OPACITY,cn.PRIMARY,cn.SECONDARY].concat($e.map(n=>"".concat(n,"x"))).concat(Rt.map(n=>"w-".concat(n)));const I="___FONT_AWESOME___",Fn=16,nt="svg-inline--fa",U="data-fa-i2svg",Dn="data-fa-pseudo-element",Rn="data-prefix",Tn="data-icon",de="fontawesome-i2svg",Bt=["HTML","HEAD","STYLE","SCRIPT"],et=(()=>{try{return!0}catch{return!1}})();function an(n){return new Proxy(n,{get:(e,t)=>t in e?e[t]:e[O]})}const tt=s({},Ze);tt[O]=s(s(s(s({},{"fa-duotone":"duotone"}),Ze[O]),Ct),jt);const Yt=an(tt),Bn=s({},{classic:{solid:"fas",regular:"far",light:"fal",thin:"fat",brands:"fab"},duotone:{solid:"fad",regular:"fadr",light:"fadl",thin:"fadt"},sharp:{solid:"fass",regular:"fasr",light:"fasl",thin:"fast"},"sharp-duotone":{solid:"fasds",regular:"fasdr",light:"fasdl",thin:"fasdt"}});Bn[O]=s(s(s(s({},{duotone:"fad"}),Bn[O]),It),Ft);const pe=an(Bn),Yn=s({},En);Yn[O]=s(s({},Yn[O]),{fak:"fa-kit"});const ne=an(Yn),zn=s({},{classic:{"fa-brands":"fab","fa-duotone":"fad","fa-light":"fal","fa-regular":"far","fa-solid":"fas","fa-thin":"fat"},duotone:{"fa-regular":"fadr","fa-light":"fadl","fa-thin":"fadt"},sharp:{"fa-solid":"fass","fa-regular":"fasr","fa-light":"fasl","fa-thin":"fast"},"sharp-duotone":{"fa-solid":"fasds","fa-regular":"fasdr","fa-light":"fasdl","fa-thin":"fasdt"}});zn[O]=s(s({},zn[O]),{"fa-kit":"fak"}),an(zn);const Wt=/fa(s|r|l|t|d|dr|dl|dt|b|k|kd|ss|sr|sl|st|sds|sdr|sdl|sdt)?[\-\ ]/,at="fa-layers-text",Ht=/Font ?Awesome ?([56 ]*)(Solid|Regular|Light|Thin|Duotone|Brands|Free|Pro|Sharp Duotone|Sharp|Kit)?.*/i;an(s({},{classic:{900:"fas",400:"far",normal:"far",300:"fal",100:"fat"},duotone:{900:"fad",400:"fadr",300:"fadl",100:"fadt"},sharp:{900:"fass",400:"fasr",300:"fasl",100:"fast"},"sharp-duotone":{900:"fasds",400:"fasdr",300:"fasdl",100:"fasdt"}}));const _t=["class","data-prefix","data-icon","data-fa-transform","data-fa-mask"],An={GROUP:"duotone-group",PRIMARY:"primary",SECONDARY:"secondary"},Ut=["kit",...Tt],nn=Y.FontAwesomeConfig||{};v&&typeof v.querySelector=="function"&&[["data-family-prefix","familyPrefix"],["data-css-prefix","cssPrefix"],["data-family-default","familyDefault"],["data-style-default","styleDefault"],["data-replacement-class","replacementClass"],["data-auto-replace-svg","autoReplaceSvg"],["data-auto-add-css","autoAddCss"],["data-auto-a11y","autoA11y"],["data-search-pseudo-elements","searchPseudoElements"],["data-observe-mutations","observeMutations"],["data-mutate-approach","mutateApproach"],["data-keep-original-source","keepOriginalSource"],["data-measure-performance","measurePerformance"],["data-show-missing-icons","showMissingIcons"]].forEach(n=>{let[e,t]=n;const a=function(r){return r===""||r!=="false"&&(r==="true"||r)}(function(r){var i=v.querySelector("script["+r+"]");if(i)return i.getAttribute(r)}(e));a!=null&&(nn[t]=a)});const rt={styleDefault:"solid",familyDefault:O,cssPrefix:"fa",replacementClass:nt,autoReplaceSvg:!0,autoAddCss:!0,autoA11y:!0,searchPseudoElements:!1,observeMutations:!0,mutateApproach:"async",keepOriginalSource:!0,measurePerformance:!1,showMissingIcons:!0};nn.familyPrefix&&(nn.cssPrefix=nn.familyPrefix);const V=s(s({},rt),nn);V.autoReplaceSvg||(V.observeMutations=!1);const p={};Object.keys(rt).forEach(n=>{Object.defineProperty(p,n,{enumerable:!0,set:function(e){V[n]=e,Wn.forEach(t=>t(p))},get:function(){return V[n]}})}),Object.defineProperty(p,"familyPrefix",{enumerable:!0,set:function(n){V.cssPrefix=n,Wn.forEach(e=>e(p))},get:function(){return V.cssPrefix}}),Y.FontAwesomeConfig=p;const Wn=[],B=Fn,C={size:16,x:0,y:0,rotate:0,flipX:!1,flipY:!1};function tn(){let n=12,e="";for(;n-- >0;)e+="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"[62*Math.random()|0];return e}function J(n){const e=[];for(let t=(n||[]).length>>>0;t--;)e[t]=n[t];return e}function ee(n){return n.classList?J(n.classList):(n.getAttribute("class")||"").split(" ").filter(e=>e)}function ge(n){return"".concat(n).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">")}function bn(n){return Object.keys(n||{}).reduce((e,t)=>e+"".concat(t,": ").concat(n[t].trim(),";"),"")}function te(n){return n.size!==C.size||n.x!==C.x||n.y!==C.y||n.rotate!==C.rotate||n.flipX||n.flipY}function it(){const n="fa",e=nt,t=p.cssPrefix,a=p.replacementClass;let r=`:root, :host { - --fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free"; - --fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free"; - --fa-font-light: normal 300 1em/1 "Font Awesome 6 Pro"; - --fa-font-thin: normal 100 1em/1 "Font Awesome 6 Pro"; - --fa-font-duotone: normal 900 1em/1 "Font Awesome 6 Duotone"; - --fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 6 Duotone"; - --fa-font-duotone-light: normal 300 1em/1 "Font Awesome 6 Duotone"; - --fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 6 Duotone"; - --fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands"; - --fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 6 Sharp"; - --fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 6 Sharp"; - --fa-font-sharp-light: normal 300 1em/1 "Font Awesome 6 Sharp"; - --fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 6 Sharp"; - --fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 6 Sharp Duotone"; - --fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 6 Sharp Duotone"; - --fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 6 Sharp Duotone"; - --fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 6 Sharp Duotone"; -} - -svg:not(:root).svg-inline--fa, svg:not(:host).svg-inline--fa { - overflow: visible; - box-sizing: content-box; -} - -.svg-inline--fa { - display: var(--fa-display, inline-block); - height: 1em; - overflow: visible; - vertical-align: -0.125em; -} -.svg-inline--fa.fa-2xs { - vertical-align: 0.1em; -} -.svg-inline--fa.fa-xs { - vertical-align: 0em; -} -.svg-inline--fa.fa-sm { - vertical-align: -0.0714285705em; -} -.svg-inline--fa.fa-lg { - vertical-align: -0.2em; -} -.svg-inline--fa.fa-xl { - vertical-align: -0.25em; -} -.svg-inline--fa.fa-2xl { - vertical-align: -0.3125em; -} -.svg-inline--fa.fa-pull-left { - margin-right: var(--fa-pull-margin, 0.3em); - width: auto; -} -.svg-inline--fa.fa-pull-right { - margin-left: var(--fa-pull-margin, 0.3em); - width: auto; -} -.svg-inline--fa.fa-li { - width: var(--fa-li-width, 2em); - top: 0.25em; -} -.svg-inline--fa.fa-fw { - width: var(--fa-fw-width, 1.25em); -} - -.fa-layers svg.svg-inline--fa { - bottom: 0; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; -} - -.fa-layers-counter, .fa-layers-text { - display: inline-block; - position: absolute; - text-align: center; -} - -.fa-layers { - display: inline-block; - height: 1em; - position: relative; - text-align: center; - vertical-align: -0.125em; - width: 1em; -} -.fa-layers svg.svg-inline--fa { - transform-origin: center center; -} - -.fa-layers-text { - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - transform-origin: center center; -} - -.fa-layers-counter { - background-color: var(--fa-counter-background-color, #ff253a); - border-radius: var(--fa-counter-border-radius, 1em); - box-sizing: border-box; - color: var(--fa-inverse, #fff); - line-height: var(--fa-counter-line-height, 1); - max-width: var(--fa-counter-max-width, 5em); - min-width: var(--fa-counter-min-width, 1.5em); - overflow: hidden; - padding: var(--fa-counter-padding, 0.25em 0.5em); - right: var(--fa-right, 0); - text-overflow: ellipsis; - top: var(--fa-top, 0); - transform: scale(var(--fa-counter-scale, 0.25)); - transform-origin: top right; -} - -.fa-layers-bottom-right { - bottom: var(--fa-bottom, 0); - right: var(--fa-right, 0); - top: auto; - transform: scale(var(--fa-layers-scale, 0.25)); - transform-origin: bottom right; -} - -.fa-layers-bottom-left { - bottom: var(--fa-bottom, 0); - left: var(--fa-left, 0); - right: auto; - top: auto; - transform: scale(var(--fa-layers-scale, 0.25)); - transform-origin: bottom left; -} - -.fa-layers-top-right { - top: var(--fa-top, 0); - right: var(--fa-right, 0); - transform: scale(var(--fa-layers-scale, 0.25)); - transform-origin: top right; -} - -.fa-layers-top-left { - left: var(--fa-left, 0); - right: auto; - top: var(--fa-top, 0); - transform: scale(var(--fa-layers-scale, 0.25)); - transform-origin: top left; -} - -.fa-1x { - font-size: 1em; -} - -.fa-2x { - font-size: 2em; -} - -.fa-3x { - font-size: 3em; -} - -.fa-4x { - font-size: 4em; -} - -.fa-5x { - font-size: 5em; -} - -.fa-6x { - font-size: 6em; -} - -.fa-7x { - font-size: 7em; -} - -.fa-8x { - font-size: 8em; -} - -.fa-9x { - font-size: 9em; -} - -.fa-10x { - font-size: 10em; -} - -.fa-2xs { - font-size: 0.625em; - line-height: 0.1em; - vertical-align: 0.225em; -} - -.fa-xs { - font-size: 0.75em; - line-height: 0.0833333337em; - vertical-align: 0.125em; -} - -.fa-sm { - font-size: 0.875em; - line-height: 0.0714285718em; - vertical-align: 0.0535714295em; -} - -.fa-lg { - font-size: 1.25em; - line-height: 0.05em; - vertical-align: -0.075em; -} - -.fa-xl { - font-size: 1.5em; - line-height: 0.0416666682em; - vertical-align: -0.125em; -} - -.fa-2xl { - font-size: 2em; - line-height: 0.03125em; - vertical-align: -0.1875em; -} - -.fa-fw { - text-align: center; - width: 1.25em; -} - -.fa-ul { - list-style-type: none; - margin-left: var(--fa-li-margin, 2.5em); - padding-left: 0; -} -.fa-ul > li { - position: relative; -} - -.fa-li { - left: calc(-1 * var(--fa-li-width, 2em)); - position: absolute; - text-align: center; - width: var(--fa-li-width, 2em); - line-height: inherit; -} - -.fa-border { - border-color: var(--fa-border-color, #eee); - border-radius: var(--fa-border-radius, 0.1em); - border-style: var(--fa-border-style, solid); - border-width: var(--fa-border-width, 0.08em); - padding: var(--fa-border-padding, 0.2em 0.25em 0.15em); -} - -.fa-pull-left { - float: left; - margin-right: var(--fa-pull-margin, 0.3em); -} - -.fa-pull-right { - float: right; - margin-left: var(--fa-pull-margin, 0.3em); -} - -.fa-beat { - animation-name: fa-beat; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, ease-in-out); -} - -.fa-bounce { - animation-name: fa-bounce; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1)); -} - -.fa-fade { - animation-name: fa-fade; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); -} - -.fa-beat-fade { - animation-name: fa-beat-fade; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1)); -} - -.fa-flip { - animation-name: fa-flip; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, ease-in-out); -} - -.fa-shake { - animation-name: fa-shake; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, linear); -} - -.fa-spin { - animation-name: fa-spin; - animation-delay: var(--fa-animation-delay, 0s); - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 2s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, linear); -} - -.fa-spin-reverse { - --fa-animation-direction: reverse; -} - -.fa-pulse, -.fa-spin-pulse { - animation-name: fa-spin; - animation-direction: var(--fa-animation-direction, normal); - animation-duration: var(--fa-animation-duration, 1s); - animation-iteration-count: var(--fa-animation-iteration-count, infinite); - animation-timing-function: var(--fa-animation-timing, steps(8)); -} - -@media (prefers-reduced-motion: reduce) { - .fa-beat, -.fa-bounce, -.fa-fade, -.fa-beat-fade, -.fa-flip, -.fa-pulse, -.fa-shake, -.fa-spin, -.fa-spin-pulse { - animation-delay: -1ms; - animation-duration: 1ms; - animation-iteration-count: 1; - transition-delay: 0s; - transition-duration: 0s; - } -} -@keyframes fa-beat { - 0%, 90% { - transform: scale(1); - } - 45% { - transform: scale(var(--fa-beat-scale, 1.25)); - } -} -@keyframes fa-bounce { - 0% { - transform: scale(1, 1) translateY(0); - } - 10% { - transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0); - } - 30% { - transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em)); - } - 50% { - transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0); - } - 57% { - transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em)); - } - 64% { - transform: scale(1, 1) translateY(0); - } - 100% { - transform: scale(1, 1) translateY(0); - } -} -@keyframes fa-fade { - 50% { - opacity: var(--fa-fade-opacity, 0.4); - } -} -@keyframes fa-beat-fade { - 0%, 100% { - opacity: var(--fa-beat-fade-opacity, 0.4); - transform: scale(1); - } - 50% { - opacity: 1; - transform: scale(var(--fa-beat-fade-scale, 1.125)); - } -} -@keyframes fa-flip { - 50% { - transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg)); - } -} -@keyframes fa-shake { - 0% { - transform: rotate(-15deg); - } - 4% { - transform: rotate(15deg); - } - 8%, 24% { - transform: rotate(-18deg); - } - 12%, 28% { - transform: rotate(18deg); - } - 16% { - transform: rotate(-22deg); - } - 20% { - transform: rotate(22deg); - } - 32% { - transform: rotate(-12deg); - } - 36% { - transform: rotate(12deg); - } - 40%, 100% { - transform: rotate(0deg); - } -} -@keyframes fa-spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} -.fa-rotate-90 { - transform: rotate(90deg); -} - -.fa-rotate-180 { - transform: rotate(180deg); -} - -.fa-rotate-270 { - transform: rotate(270deg); -} - -.fa-flip-horizontal { - transform: scale(-1, 1); -} - -.fa-flip-vertical { - transform: scale(1, -1); -} - -.fa-flip-both, -.fa-flip-horizontal.fa-flip-vertical { - transform: scale(-1, -1); -} - -.fa-rotate-by { - transform: rotate(var(--fa-rotate-angle, 0)); -} - -.fa-stack { - display: inline-block; - vertical-align: middle; - height: 2em; - position: relative; - width: 2.5em; -} - -.fa-stack-1x, -.fa-stack-2x { - bottom: 0; - left: 0; - margin: auto; - position: absolute; - right: 0; - top: 0; - z-index: var(--fa-stack-z-index, auto); -} - -.svg-inline--fa.fa-stack-1x { - height: 1em; - width: 1.25em; -} -.svg-inline--fa.fa-stack-2x { - height: 2em; - width: 2.5em; -} - -.fa-inverse { - color: var(--fa-inverse, #fff); -} - -.sr-only, -.fa-sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} - -.sr-only-focusable:not(:focus), -.fa-sr-only-focusable:not(:focus) { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} - -.svg-inline--fa .fa-primary { - fill: var(--fa-primary-color, currentColor); - opacity: var(--fa-primary-opacity, 1); -} - -.svg-inline--fa .fa-secondary { - fill: var(--fa-secondary-color, currentColor); - opacity: var(--fa-secondary-opacity, 0.4); -} - -.svg-inline--fa.fa-swap-opacity .fa-primary { - opacity: var(--fa-secondary-opacity, 0.4); -} - -.svg-inline--fa.fa-swap-opacity .fa-secondary { - opacity: var(--fa-primary-opacity, 1); -} - -.svg-inline--fa mask .fa-primary, -.svg-inline--fa mask .fa-secondary { - fill: black; -}`;if(t!==n||a!==e){const i=new RegExp("\\.".concat(n,"\\-"),"g"),o=new RegExp("\\--".concat(n,"\\-"),"g"),l=new RegExp("\\.".concat(e),"g");r=r.replace(i,".".concat(t,"-")).replace(o,"--".concat(t,"-")).replace(l,".".concat(a))}return r}let he=!1;function Ln(){p.autoAddCss&&!he&&(function(n){if(!n||!R)return;const e=v.createElement("style");e.setAttribute("type","text/css"),e.innerHTML=n;const t=v.head.childNodes;let a=null;for(let r=t.length-1;r>-1;r--){const i=t[r],o=(i.tagName||"").toUpperCase();["STYLE","LINK"].indexOf(o)>-1&&(a=i)}v.head.insertBefore(e,a)}(it()),he=!0)}var qt={mixout:()=>({dom:{css:it,insertCss:Ln}}),hooks:()=>({beforeDOMElementCreation(){Ln()},beforeI2svg(){Ln()}})};const F=Y||{};F[I]||(F[I]={}),F[I].styles||(F[I].styles={}),F[I].hooks||(F[I].hooks={}),F[I].shims||(F[I].shims=[]);var j=F[I];const ot=[],st=function(){v.removeEventListener("DOMContentLoaded",st),pn=1,ot.map(n=>n())};let pn=!1;function rn(n){const{tag:e,attributes:t={},children:a=[]}=n;return typeof n=="string"?ge(n):"<".concat(e," ").concat(function(r){return Object.keys(r||{}).reduce((i,o)=>i+"".concat(o,'="').concat(ge(r[o]),'" '),"").trim()}(t),">").concat(a.map(rn).join(""),"")}function be(n,e,t){if(n&&n[e]&&n[e][t])return{prefix:e,iconName:t,icon:n[e][t]}}R&&(pn=(v.documentElement.doScroll?/^loaded|^c/:/^loaded|^i|^c/).test(v.readyState),pn||v.addEventListener("DOMContentLoaded",st));var On=function(n,e,t,a){var r,i,o,l=Object.keys(n),f=l.length,c=e;for(t===void 0?(r=1,o=n[l[0]]):(r=0,o=t);r=55296&&o<=56319&&r{const a=n[t];return a.icon?e[a.iconName]=a.icon:e[t]=a,e},{})}function Hn(n,e){let t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};const{skipHooks:a=!1}=t,r=ye(e);typeof j.hooks.addPack!="function"||a?j.styles[n]=s(s({},j.styles[n]||{}),r):j.hooks.addPack(n,ye(e)),n==="fas"&&Hn("fa",e)}const{styles:en,shims:Kt}=j,lt=Object.keys(ne),Xt=lt.reduce((n,e)=>(n[e]=Object.keys(ne[e]),n),{});let ae=null,ft={},ut={},mt={},dt={},pt={};function Vt(n,e){const t=e.split("-"),a=t[0],r=t.slice(1).join("-");return a!==n||r===""||(i=r,~Ut.indexOf(i))?null:r;var i}const gt=()=>{const n=a=>On(en,(r,i,o)=>(r[o]=On(i,a,{}),r),{});ft=n((a,r,i)=>(r[3]&&(a[r[3]]=i),r[2]&&r[2].filter(o=>typeof o=="number").forEach(o=>{a[o.toString(16)]=i}),a)),ut=n((a,r,i)=>(a[i]=i,r[2]&&r[2].filter(o=>typeof o=="string").forEach(o=>{a[o]=i}),a)),pt=n((a,r,i)=>{const o=r[2];return a[i]=i,o.forEach(l=>{a[l]=i}),a});const e="far"in en||p.autoFetchSvg,t=On(Kt,(a,r)=>{const i=r[0];let o=r[1];const l=r[2];return o!=="far"||e||(o="fas"),typeof i=="string"&&(a.names[i]={prefix:o,iconName:l}),typeof i=="number"&&(a.unicodes[i.toString(16)]={prefix:o,iconName:l}),a},{names:{},unicodes:{}});mt=t.names,dt=t.unicodes,ae=yn(p.styleDefault,{family:p.familyDefault})};var ve;function _n(n,e){return(ft[n]||{})[e]}function _(n,e){return(pt[n]||{})[e]}function ht(n){return mt[n]||{prefix:null,iconName:null}}function W(){return ae}ve=n=>{ae=yn(n.styleDefault,{family:p.familyDefault})},Wn.push(ve),gt();function yn(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{family:t=O}=e,a=Yt[t][n];if(t===hn&&!n)return"fad";const r=pe[t][n]||pe[t][a],i=n in j.styles?n:null;return r||i||null}function xe(n){return n.sort().filter((e,t,a)=>a.indexOf(e)===t)}function vn(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{skipLookups:t=!1}=e;let a=null;const r=In.concat(Dt),i=xe(n.filter(d=>r.includes(d))),o=xe(n.filter(d=>!In.includes(d))),l=i.filter(d=>(a=d,!Je.includes(d))),[f=null]=l,c=function(d){let u=O;const h=lt.reduce((g,b)=>(g[b]="".concat(p.cssPrefix,"-").concat(b),g),{});return Qe.forEach(g=>{(d.includes(h[g])||d.some(b=>Xt[g].includes(b)))&&(u=g)}),u}(i),m=s(s({},function(d){let u=[],h=null;return d.forEach(g=>{const b=Vt(p.cssPrefix,g);b?h=b:g&&u.push(g)}),{iconName:h,rest:u}}(o)),{},{prefix:yn(f,{family:c})});return s(s(s({},m),function(d){const{values:u,family:h,canonical:g,givenPrefix:b="",styles:k={},config:z={}}=d,x=h===hn,w=u.includes("fa-duotone")||u.includes("fad"),M=z.familyDefault==="duotone",y=g.prefix==="fad"||g.prefix==="fa-duotone";if(!x&&(w||M||y)&&(g.prefix="fad"),(u.includes("fa-brands")||u.includes("fab"))&&(g.prefix="fab"),!g.prefix&&Gt.includes(h)&&(Object.keys(k).find(A=>Zt.includes(A))||z.autoFetchSvg)){const A=Pt.get(h).defaultShortPrefixId;g.prefix=A,g.iconName=_(g.prefix,g.iconName)||g.iconName}return g.prefix!=="fa"&&b!=="fa"||(g.prefix=W()||"fas"),g}({values:n,family:c,styles:en,config:p,canonical:m,givenPrefix:a})),function(d,u,h){let{prefix:g,iconName:b}=h;if(d||!g||!b)return{prefix:g,iconName:b};const k=u==="fa"?ht(b):{},z=_(g,b);return b=k.iconName||z||b,g=k.prefix||g,g!=="far"||en.far||!en.fas||p.autoFetchSvg||(g="fas"),{prefix:g,iconName:b}}(t,a,m))}const Gt=Qe.filter(n=>n!==O||n!==hn),Zt=Object.keys(En).filter(n=>n!==O).map(n=>Object.keys(En[n])).flat();let ke=[],G={};const Z={},Jt=Object.keys(Z);function Un(n,e){for(var t=arguments.length,a=new Array(t>2?t-2:0),r=2;r{e=i.apply(null,[e,...a])}),e}function q(n){for(var e=arguments.length,t=new Array(e>1?e-1:0),a=1;a{r.apply(null,t)})}function H(){const n=arguments[0],e=Array.prototype.slice.call(arguments,1);return Z[n]?Z[n].apply(null,e):void 0}function qn(n){n.prefix==="fa"&&(n.prefix="fas");let{iconName:e}=n;const t=n.prefix||W();if(e)return e=_(t,e)||e,be(bt.definitions,t,e)||be(j.styles,t,e)}const bt=new class{constructor(){this.definitions={}}add(){for(var n=arguments.length,e=new Array(n),t=0;t{this.definitions[r]=s(s({},this.definitions[r]||{}),a[r]),Hn(r,a[r]);const i=ne[O][r];i&&Hn(i,a[r]),gt()})}reset(){this.definitions={}}_pullDefinitions(n,e){const t=e.prefix&&e.iconName&&e.icon?{0:e}:e;return Object.keys(t).map(a=>{const{prefix:r,iconName:i,icon:o}=t[a],l=o[2];n[r]||(n[r]={}),l.length>0&&l.forEach(f=>{typeof f=="string"&&(n[r][f]=o)}),n[r][i]=o}),n}},Qt={i2svg:function(){let n=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return R?(q("beforeI2svg",n),H("pseudoElements2svg",n),H("i2svg",n)):Promise.reject(new Error("Operation requires a DOM of some kind."))},watch:function(){let n=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};const{autoReplaceSvgRoot:e}=n;var t;p.autoReplaceSvg===!1&&(p.autoReplaceSvg=!0),p.observeMutations=!0,t=()=>{$t({autoReplaceSvgRoot:e}),q("watch",n)},R&&(pn?setTimeout(t,0):ot.push(t))}},on={noAuto:()=>{p.autoReplaceSvg=!1,p.observeMutations=!1,q("noAuto")},config:p,dom:Qt,parse:{icon:n=>{if(n===null)return null;if(typeof n=="object"&&n.prefix&&n.iconName)return{prefix:n.prefix,iconName:_(n.prefix,n.iconName)||n.iconName};if(Array.isArray(n)&&n.length===2){const e=n[1].indexOf("fa-")===0?n[1].slice(3):n[1],t=yn(n[0]);return{prefix:t,iconName:_(t,e)||e}}if(typeof n=="string"&&(n.indexOf("".concat(p.cssPrefix,"-"))>-1||n.match(Wt))){const e=vn(n.split(" "),{skipLookups:!0});return{prefix:e.prefix||W(),iconName:_(e.prefix,e.iconName)||e.iconName}}if(typeof n=="string"){const e=W();return{prefix:e,iconName:_(e,n)||n}}}},library:bt,findIconDefinition:qn,toHtml:rn},$t=function(){let n=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};const{autoReplaceSvgRoot:e=v}=n;(Object.keys(j.styles).length>0||p.autoFetchSvg)&&R&&p.autoReplaceSvg&&on.dom.i2svg({node:e})};function xn(n,e){return Object.defineProperty(n,"abstract",{get:e}),Object.defineProperty(n,"html",{get:function(){return n.abstract.map(t=>rn(t))}}),Object.defineProperty(n,"node",{get:function(){if(!R)return;const t=v.createElement("div");return t.innerHTML=n.html,t.children}}),n}function re(n){const{icons:{main:e,mask:t},prefix:a,iconName:r,transform:i,symbol:o,title:l,maskId:f,titleId:c,extra:m,watchable:d=!1}=n,{width:u,height:h}=t.found?t:e,g=Et.includes(a),b=[p.replacementClass,r?"".concat(p.cssPrefix,"-").concat(r):""].filter(y=>m.classes.indexOf(y)===-1).filter(y=>y!==""||!!y).concat(m.classes).join(" ");let k={children:[],attributes:s(s({},m.attributes),{},{"data-prefix":a,"data-icon":r,class:b,role:m.attributes.role||"img",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 ".concat(u," ").concat(h)})};const z=g&&!~m.classes.indexOf("fa-fw")?{width:"".concat(u/h*16*.0625,"em")}:{};d&&(k.attributes[U]=""),l&&(k.children.push({tag:"title",attributes:{id:k.attributes["aria-labelledby"]||"title-".concat(c||tn())},children:[l]}),delete k.attributes.title);const x=s(s({},k),{},{prefix:a,iconName:r,main:e,mask:t,maskId:f,transform:i,symbol:o,styles:s(s({},z),m.styles)}),{children:w,attributes:M}=t.found&&e.found?H("generateAbstractMask",x)||{children:[],attributes:{}}:H("generateAbstractIcon",x)||{children:[],attributes:{}};return x.children=w,x.attributes=M,o?function(y){let{prefix:A,iconName:L,children:E,attributes:T,symbol:S}=y;const P=S===!0?"".concat(A,"-").concat(p.cssPrefix,"-").concat(L):S;return[{tag:"svg",attributes:{style:"display: none;"},children:[{tag:"symbol",attributes:s(s({},T),{},{id:P}),children:E}]}]}(x):function(y){let{children:A,main:L,mask:E,attributes:T,styles:S,transform:P}=y;if(te(P)&&L.found&&!E.found){const{width:kn,height:wn}=L,se={x:kn/wn/2,y:.5};T.style=bn(s(s({},S),{},{"transform-origin":"".concat(se.x+P.x/16,"em ").concat(se.y+P.y/16,"em")}))}return[{tag:"svg",attributes:T,children:A}]}(x)}function we(n){const{content:e,width:t,height:a,transform:r,title:i,extra:o,watchable:l=!1}=n,f=s(s(s({},o.attributes),i?{title:i}:{}),{},{class:o.classes.join(" ")});l&&(f[U]="");const c=s({},o.styles);te(r)&&(c.transform=function(u){let{transform:h,width:g=Fn,height:b=Fn,startCentered:k=!1}=u,z="";return z+=k&&Ge?"translate(".concat(h.x/B-g/2,"em, ").concat(h.y/B-b/2,"em) "):k?"translate(calc(-50% + ".concat(h.x/B,"em), calc(-50% + ").concat(h.y/B,"em)) "):"translate(".concat(h.x/B,"em, ").concat(h.y/B,"em) "),z+="scale(".concat(h.size/B*(h.flipX?-1:1),", ").concat(h.size/B*(h.flipY?-1:1),") "),z+="rotate(".concat(h.rotate,"deg) "),z}({transform:r,startCentered:!0,width:t,height:a}),c["-webkit-transform"]=c.transform);const m=bn(c);m.length>0&&(f.style=m);const d=[];return d.push({tag:"span",attributes:f,children:[e]}),i&&d.push({tag:"span",attributes:{class:"sr-only"},children:[i]}),d}const{styles:Mn}=j;function Kn(n){const e=n[0],t=n[1],[a]=n.slice(4);let r=null;return r=Array.isArray(a)?{tag:"g",attributes:{class:"".concat(p.cssPrefix,"-").concat(An.GROUP)},children:[{tag:"path",attributes:{class:"".concat(p.cssPrefix,"-").concat(An.SECONDARY),fill:"currentColor",d:a[0]}},{tag:"path",attributes:{class:"".concat(p.cssPrefix,"-").concat(An.PRIMARY),fill:"currentColor",d:a[1]}}]}:{tag:"path",attributes:{fill:"currentColor",d:a}},{found:!0,width:e,height:t,icon:r}}const na={found:!1,width:512,height:512};function Xn(n,e){let t=e;return e==="fa"&&p.styleDefault!==null&&(e=W()),new Promise((a,r)=>{if(t==="fa"){const i=ht(n)||{};n=i.iconName||n,e=i.prefix||e}if(n&&e&&Mn[e]&&Mn[e][n])return a(Kn(Mn[e][n]));!et&&p.showMissingIcons,a(s(s({},na),{},{icon:p.showMissingIcons&&n&&H("missingIconAbstract")||{}}))})}const ze=()=>{},Vn=p.measurePerformance&&sn&&sn.mark&&sn.measure?sn:{mark:ze,measure:ze},$='FA "6.7.2"',ea=n=>{Vn.mark("".concat($," ").concat(n," ends")),Vn.measure("".concat($," ").concat(n),"".concat($," ").concat(n," begins"),"".concat($," ").concat(n," ends"))};var ie=n=>(Vn.mark("".concat($," ").concat(n," begins")),()=>ea(n));const mn=()=>{};function Ae(n){return typeof(n.getAttribute?n.getAttribute(U):null)=="string"}function ta(n){return v.createElementNS("http://www.w3.org/2000/svg",n)}function aa(n){return v.createElement(n)}function yt(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{ceFn:t=n.tag==="svg"?ta:aa}=e;if(typeof n=="string")return v.createTextNode(n);const a=t(n.tag);return Object.keys(n.attributes||[]).forEach(function(r){a.setAttribute(r,n.attributes[r])}),(n.children||[]).forEach(function(r){a.appendChild(yt(r,{ceFn:t}))}),a}const dn={replace:function(n){const e=n[0];if(e.parentNode)if(n[1].forEach(t=>{e.parentNode.insertBefore(yt(t),e)}),e.getAttribute(U)===null&&p.keepOriginalSource){let t=v.createComment(function(a){let r=" ".concat(a.outerHTML," ");return r="".concat(r,"Font Awesome fontawesome.com "),r}(e));e.parentNode.replaceChild(t,e)}else e.remove()},nest:function(n){const e=n[0],t=n[1];if(~ee(e).indexOf(p.replacementClass))return dn.replace(n);const a=new RegExp("".concat(p.cssPrefix,"-.*"));if(delete t[0].attributes.id,t[0].attributes.class){const i=t[0].attributes.class.split(" ").reduce((o,l)=>(l===p.replacementClass||l.match(a)?o.toSvg.push(l):o.toNode.push(l),o),{toNode:[],toSvg:[]});t[0].attributes.class=i.toSvg.join(" "),i.toNode.length===0?e.removeAttribute("class"):e.setAttribute("class",i.toNode.join(" "))}const r=t.map(i=>rn(i)).join(` -`);e.setAttribute(U,""),e.innerHTML=r}};function Le(n){n()}function vt(n,e){const t=typeof e=="function"?e:mn;if(n.length===0)t();else{let a=Le;p.mutateApproach==="async"&&(a=Y.requestAnimationFrame||Le),a(()=>{const r=p.autoReplaceSvg===!0?dn.replace:dn[p.autoReplaceSvg]||dn.replace,i=ie("mutate");n.map(r),i(),t()})}}let oe=!1;function xt(){oe=!0}function Gn(){oe=!1}let gn=null;function Oe(n){if(!me||!p.observeMutations)return;const{treeCallback:e=mn,nodeCallback:t=mn,pseudoElementsCallback:a=mn,observeMutationsRoot:r=v}=n;gn=new me(i=>{if(oe)return;const o=W();J(i).forEach(l=>{if(l.type==="childList"&&l.addedNodes.length>0&&!Ae(l.addedNodes[0])&&(p.searchPseudoElements&&a(l.target),e(l.target)),l.type==="attributes"&&l.target.parentNode&&p.searchPseudoElements&&a(l.target.parentNode),l.type==="attributes"&&Ae(l.target)&&~_t.indexOf(l.attributeName))if(l.attributeName==="class"&&function(c){const m=c.getAttribute?c.getAttribute(Rn):null,d=c.getAttribute?c.getAttribute(Tn):null;return m&&d}(l.target)){const{prefix:c,iconName:m}=vn(ee(l.target));l.target.setAttribute(Rn,c||o),m&&l.target.setAttribute(Tn,m)}else(f=l.target)&&f.classList&&f.classList.contains&&f.classList.contains(p.replacementClass)&&t(l.target);var f})}),R&&gn.observe(r,{childList:!0,attributes:!0,characterData:!0,subtree:!0})}function ra(n){const e=n.getAttribute("data-prefix"),t=n.getAttribute("data-icon"),a=n.innerText!==void 0?n.innerText.trim():"";let r=vn(ee(n));return r.prefix||(r.prefix=W()),e&&t&&(r.prefix=e,r.iconName=t),r.iconName&&r.prefix||(r.prefix&&a.length>0&&(r.iconName=(i=r.prefix,o=n.innerText,(ut[i]||{})[o]||_n(r.prefix,ct(n.innerText)))),!r.iconName&&p.autoFetchSvg&&n.firstChild&&n.firstChild.nodeType===Node.TEXT_NODE&&(r.iconName=n.firstChild.data)),r;var i,o}function Me(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{styleParser:!0};const{iconName:t,prefix:a,rest:r}=ra(n),i=function(f){const c=J(f.attributes).reduce((u,h)=>(u.name!=="class"&&u.name!=="style"&&(u[h.name]=h.value),u),{}),m=f.getAttribute("title"),d=f.getAttribute("data-fa-title-id");return p.autoA11y&&(m?c["aria-labelledby"]="".concat(p.replacementClass,"-title-").concat(d||tn()):(c["aria-hidden"]="true",c.focusable="false")),c}(n),o=Un("parseNodeAttributes",{},n);let l=e.styleParser?function(f){const c=f.getAttribute("style");let m=[];return c&&(m=c.split(";").reduce((d,u)=>{const h=u.split(":"),g=h[0],b=h.slice(1);return g&&b.length>0&&(d[g]=b.join(":").trim()),d},{})),m}(n):[];return s({iconName:t,title:n.getAttribute("title"),titleId:n.getAttribute("data-fa-title-id"),prefix:a,transform:C,mask:{iconName:null,prefix:null,rest:[]},maskId:null,symbol:!1,extra:{classes:r,styles:l,attributes:i}},o)}const{styles:ia}=j;function kt(n){const e=p.autoReplaceSvg==="nest"?Me(n,{styleParser:!1}):Me(n);return~e.extra.classes.indexOf(at)?H("generateLayersText",n,e):H("generateSvgReplacementMutation",n,e)}function Ne(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;if(!R)return Promise.resolve();const t=v.documentElement.classList,a=m=>t.add("".concat(de,"-").concat(m)),r=m=>t.remove("".concat(de,"-").concat(m)),i=p.autoFetchSvg?[...St,...In]:Je.concat(Object.keys(ia));i.includes("fa")||i.push("fa");const o=[".".concat(at,":not([").concat(U,"])")].concat(i.map(m=>".".concat(m,":not([").concat(U,"])"))).join(", ");if(o.length===0)return Promise.resolve();let l=[];try{l=J(n.querySelectorAll(o))}catch{}if(!(l.length>0))return Promise.resolve();a("pending"),r("complete");const f=ie("onTree"),c=l.reduce((m,d)=>{try{const u=kt(d);u&&m.push(u)}catch(u){et||u.name}return m},[]);return new Promise((m,d)=>{Promise.all(c).then(u=>{vt(u,()=>{a("active"),a("complete"),r("pending"),typeof e=="function"&&e(),f(),m()})}).catch(u=>{f(),d(u)})})}function oa(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:null;kt(n).then(t=>{t&&vt([t],e)})}const sa=function(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{transform:t=C,symbol:a=!1,mask:r=null,maskId:i=null,title:o=null,titleId:l=null,classes:f=[],attributes:c={},styles:m={}}=e;if(!n)return;const{prefix:d,iconName:u,icon:h}=n;return xn(s({type:"icon"},n),()=>(q("beforeDOMElementCreation",{iconDefinition:n,params:e}),p.autoA11y&&(o?c["aria-labelledby"]="".concat(p.replacementClass,"-title-").concat(l||tn()):(c["aria-hidden"]="true",c.focusable="false")),re({icons:{main:Kn(h),mask:r?Kn(r.icon):{found:!1,width:null,height:null,icon:{}}},prefix:d,iconName:u,transform:s(s({},C),t),symbol:a,title:o,maskId:i,titleId:l,extra:{attributes:c,styles:m,classes:f}})))};var ca={mixout(){return{icon:(n=sa,function(e){let t=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const a=(e||{}).icon?e:qn(e||{});let{mask:r}=t;return r&&(r=(r||{}).icon?r:qn(r||{})),n(a,s(s({},t),{},{mask:r}))})};var n},hooks:()=>({mutationObserverCallbacks:n=>(n.treeCallback=Ne,n.nodeCallback=oa,n)}),provides(n){n.i2svg=function(e){const{node:t=v,callback:a=()=>{}}=e;return Ne(t,a)},n.generateSvgReplacementMutation=function(e,t){const{iconName:a,title:r,titleId:i,prefix:o,transform:l,symbol:f,mask:c,maskId:m,extra:d}=t;return new Promise((u,h)=>{Promise.all([Xn(a,o),c.iconName?Xn(c.iconName,c.prefix):Promise.resolve({found:!1,width:512,height:512,icon:{}})]).then(g=>{let[b,k]=g;u([e,re({icons:{main:b,mask:k},prefix:o,iconName:a,transform:l,symbol:f,maskId:m,title:r,titleId:i,extra:d,watchable:!0})])}).catch(h)})},n.generateAbstractIcon=function(e){let{children:t,attributes:a,main:r,transform:i,styles:o}=e;const l=bn(o);let f;return l.length>0&&(a.style=l),te(i)&&(f=H("generateAbstractTransformGrouping",{main:r,transform:i,containerWidth:r.width,iconWidth:r.width})),t.push(f||r.icon),{children:t,attributes:a}}}},la={mixout:()=>({layer(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{classes:t=[]}=e;return xn({type:"layer"},()=>{q("beforeDOMElementCreation",{assembler:n,params:e});let a=[];return n(r=>{Array.isArray(r)?r.map(i=>{a=a.concat(i.abstract)}):a=a.concat(r.abstract)}),[{tag:"span",attributes:{class:["".concat(p.cssPrefix,"-layers"),...t].join(" ")},children:a}]})}})},fa={mixout:()=>({counter(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{title:t=null,classes:a=[],attributes:r={},styles:i={}}=e;return xn({type:"counter",content:n},()=>(q("beforeDOMElementCreation",{content:n,params:e}),function(o){const{content:l,title:f,extra:c}=o,m=s(s(s({},c.attributes),f?{title:f}:{}),{},{class:c.classes.join(" ")}),d=bn(c.styles);d.length>0&&(m.style=d);const u=[];return u.push({tag:"span",attributes:m,children:[l]}),f&&u.push({tag:"span",attributes:{class:"sr-only"},children:[f]}),u}({content:n.toString(),title:t,extra:{attributes:r,styles:i,classes:["".concat(p.cssPrefix,"-layers-counter"),...a]}})))}})},ua={mixout:()=>({text(n){let e=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};const{transform:t=C,title:a=null,classes:r=[],attributes:i={},styles:o={}}=e;return xn({type:"text",content:n},()=>(q("beforeDOMElementCreation",{content:n,params:e}),we({content:n,transform:s(s({},C),t),title:a,extra:{attributes:i,styles:o,classes:["".concat(p.cssPrefix,"-layers-text"),...r]}})))}}),provides(n){n.generateLayersText=function(e,t){const{title:a,transform:r,extra:i}=t;let o=null,l=null;if(Ge){const f=parseInt(getComputedStyle(e).fontSize,10),c=e.getBoundingClientRect();o=c.width/f,l=c.height/f}return p.autoA11y&&!a&&(i.attributes["aria-hidden"]="true"),Promise.resolve([e,we({content:e.innerHTML,width:o,height:l,transform:r,title:a,extra:i,watchable:!0})])}}};const ma=new RegExp('"',"ug"),Pe=[1105920,1112319],Se=s(s(s(s({},{FontAwesome:{normal:"fas",400:"fas"}}),{"Font Awesome 6 Free":{900:"fas",400:"far"},"Font Awesome 6 Pro":{900:"fas",400:"far",normal:"far",300:"fal",100:"fat"},"Font Awesome 6 Brands":{400:"fab",normal:"fab"},"Font Awesome 6 Duotone":{900:"fad",400:"fadr",normal:"fadr",300:"fadl",100:"fadt"},"Font Awesome 6 Sharp":{900:"fass",400:"fasr",normal:"fasr",300:"fasl",100:"fast"},"Font Awesome 6 Sharp Duotone":{900:"fasds",400:"fasdr",normal:"fasdr",300:"fasdl",100:"fasdt"}}),{"Font Awesome 5 Free":{900:"fas",400:"far"},"Font Awesome 5 Pro":{900:"fas",400:"far",normal:"far",300:"fal"},"Font Awesome 5 Brands":{400:"fab",normal:"fab"},"Font Awesome 5 Duotone":{900:"fad"}}),{"Font Awesome Kit":{400:"fak",normal:"fak"},"Font Awesome Kit Duotone":{400:"fakd",normal:"fakd"}}),Zn=Object.keys(Se).reduce((n,e)=>(n[e.toLowerCase()]=Se[e],n),{}),da=Object.keys(Zn).reduce((n,e)=>{const t=Zn[e];return n[e]=t[900]||[...Object.entries(t)][0][1],n},{});function Ce(n,e){const t="".concat("data-fa-pseudo-element-pending").concat(e.replace(":","-"));return new Promise((a,r)=>{if(n.getAttribute(t)!==null)return a();const i=J(n.children).filter(d=>d.getAttribute(Dn)===e)[0],o=Y.getComputedStyle(n,e),l=o.getPropertyValue("font-family"),f=l.match(Ht),c=o.getPropertyValue("font-weight"),m=o.getPropertyValue("content");if(i&&!f)return n.removeChild(i),a();if(f&&m!=="none"&&m!==""){const d=o.getPropertyValue("content");let u=function(x,w){const M=x.replace(/^['"]|['"]$/g,"").toLowerCase(),y=parseInt(w),A=isNaN(y)?"normal":y;return(Zn[M]||{})[A]||da[M]}(l,c);const{value:h,isSecondary:g}=function(x){const w=x.replace(ma,""),M=function(L,E){const T=L.length;let S,P=L.charCodeAt(E);return P>=55296&&P<=56319&&T>E+1&&(S=L.charCodeAt(E+1),S>=56320&&S<=57343)?1024*(P-55296)+S-56320+65536:P}(w,0),y=M>=Pe[0]&&M<=Pe[1],A=w.length===2&&w[0]===w[1];return{value:ct(A?w[0]:w),isSecondary:y||A}}(d),b=f[0].startsWith("FontAwesome");let k=_n(u,h),z=k;if(b){const x=function(w){const M=dt[w],y=_n("fas",w);return M||(y?{prefix:"fas",iconName:y}:null)||{prefix:null,iconName:null}}(h);x.iconName&&x.prefix&&(k=x.iconName,u=x.prefix)}if(!k||g||i&&i.getAttribute(Rn)===u&&i.getAttribute(Tn)===z)a();else{n.setAttribute(t,z),i&&n.removeChild(i);const x={iconName:null,title:null,titleId:null,prefix:null,transform:C,symbol:!1,mask:{iconName:null,prefix:null,rest:[]},maskId:null,extra:{classes:[],styles:{},attributes:{}}},{extra:w}=x;w.attributes[Dn]=e,Xn(k,u).then(M=>{const y=re(s(s({},x),{},{icons:{main:M,mask:{prefix:null,iconName:null,rest:[]}},prefix:u,iconName:z,extra:w,watchable:!0})),A=v.createElementNS("http://www.w3.org/2000/svg","svg");e==="::before"?n.insertBefore(A,n.firstChild):n.appendChild(A),A.outerHTML=y.map(L=>rn(L)).join(` -`),n.removeAttribute(t),a()}).catch(r)}}else a()})}function pa(n){return Promise.all([Ce(n,"::before"),Ce(n,"::after")])}function ga(n){return!(n.parentNode===document.head||~Bt.indexOf(n.tagName.toUpperCase())||n.getAttribute(Dn)||n.parentNode&&n.parentNode.tagName==="svg")}function je(n){if(R)return new Promise((e,t)=>{const a=J(n.querySelectorAll("*")).filter(ga).map(pa),r=ie("searchPseudoElements");xt(),Promise.all(a).then(()=>{r(),Gn(),e()}).catch(()=>{r(),Gn(),t()})})}let Ee=!1;const Ie=n=>n.toLowerCase().split(" ").reduce((e,t)=>{const a=t.toLowerCase().split("-"),r=a[0];let i=a.slice(1).join("-");if(r&&i==="h")return e.flipX=!0,e;if(r&&i==="v")return e.flipY=!0,e;if(i=parseFloat(i),isNaN(i))return e;switch(r){case"grow":e.size=e.size+i;break;case"shrink":e.size=e.size-i;break;case"left":e.x=e.x-i;break;case"right":e.x=e.x+i;break;case"up":e.y=e.y-i;break;case"down":e.y=e.y+i;break;case"rotate":e.rotate=e.rotate+i}return e},{size:16,x:0,y:0,flipX:!1,flipY:!1,rotate:0}),Nn={x:0,y:0,width:"100%",height:"100%"};function Fe(n){let e=!(arguments.length>1&&arguments[1]!==void 0)||arguments[1];return n.attributes&&(n.attributes.fill||e)&&(n.attributes.fill="black"),n}(function(n,e){let{mixoutsTo:t}=e;ke=n,G={},Object.keys(Z).forEach(a=>{Jt.indexOf(a)===-1&&delete Z[a]}),ke.forEach(a=>{const r=a.mixout?a.mixout():{};if(Object.keys(r).forEach(i=>{typeof r[i]=="function"&&(t[i]=r[i]),typeof r[i]=="object"&&Object.keys(r[i]).forEach(o=>{t[i]||(t[i]={}),t[i][o]=r[i][o]})}),a.hooks){const i=a.hooks();Object.keys(i).forEach(o=>{G[o]||(G[o]=[]),G[o].push(i[o])})}a.provides&&a.provides(Z)})})([qt,ca,la,fa,ua,{hooks:()=>({mutationObserverCallbacks:n=>(n.pseudoElementsCallback=je,n)}),provides(n){n.pseudoElements2svg=function(e){const{node:t=v}=e;p.searchPseudoElements&&je(t)}}},{mixout:()=>({dom:{unwatch(){xt(),Ee=!0}}}),hooks:()=>({bootstrap(){Oe(Un("mutationObserverCallbacks",{}))},noAuto(){gn&&gn.disconnect()},watch(n){const{observeMutationsRoot:e}=n;Ee?Gn():Oe(Un("mutationObserverCallbacks",{observeMutationsRoot:e}))}})},{mixout:()=>({parse:{transform:n=>Ie(n)}}),hooks:()=>({parseNodeAttributes(n,e){const t=e.getAttribute("data-fa-transform");return t&&(n.transform=Ie(t)),n}}),provides(n){n.generateAbstractTransformGrouping=function(e){let{main:t,transform:a,containerWidth:r,iconWidth:i}=e;const o={transform:"translate(".concat(r/2," 256)")},l="translate(".concat(32*a.x,", ").concat(32*a.y,") "),f="scale(".concat(a.size/16*(a.flipX?-1:1),", ").concat(a.size/16*(a.flipY?-1:1),") "),c="rotate(".concat(a.rotate," 0 0)"),m={outer:o,inner:{transform:"".concat(l," ").concat(f," ").concat(c)},path:{transform:"translate(".concat(i/2*-1," -256)")}};return{tag:"g",attributes:s({},m.outer),children:[{tag:"g",attributes:s({},m.inner),children:[{tag:t.icon.tag,children:t.icon.children,attributes:s(s({},t.icon.attributes),m.path)}]}]}}}},{hooks:()=>({parseNodeAttributes(n,e){const t=e.getAttribute("data-fa-mask"),a=t?vn(t.split(" ").map(r=>r.trim())):{prefix:null,iconName:null,rest:[]};return a.prefix||(a.prefix=W()),n.mask=a,n.maskId=e.getAttribute("data-fa-mask-id"),n}}),provides(n){n.generateAbstractMask=function(e){let{children:t,attributes:a,main:r,mask:i,maskId:o,transform:l}=e;const{width:f,icon:c}=r,{width:m,icon:d}=i,u=function(A){let{transform:L,containerWidth:E,iconWidth:T}=A;const S={transform:"translate(".concat(E/2," 256)")},P="translate(".concat(32*L.x,", ").concat(32*L.y,") "),kn="scale(".concat(L.size/16*(L.flipX?-1:1),", ").concat(L.size/16*(L.flipY?-1:1),") "),wn="rotate(".concat(L.rotate," 0 0)");return{outer:S,inner:{transform:"".concat(P," ").concat(kn," ").concat(wn)},path:{transform:"translate(".concat(T/2*-1," -256)")}}}({transform:l,containerWidth:m,iconWidth:f}),h={tag:"rect",attributes:s(s({},Nn),{},{fill:"white"})},g=c.children?{children:c.children.map(Fe)}:{},b={tag:"g",attributes:s({},u.inner),children:[Fe(s({tag:c.tag,attributes:s(s({},c.attributes),u.path)},g))]},k={tag:"g",attributes:s({},u.outer),children:[b]},z="mask-".concat(o||tn()),x="clip-".concat(o||tn()),w={tag:"mask",attributes:s(s({},Nn),{},{id:z,maskUnits:"userSpaceOnUse",maskContentUnits:"userSpaceOnUse"}),children:[h,k]},M={tag:"defs",children:[{tag:"clipPath",attributes:{id:x},children:(y=d,y.tag==="g"?y.children:[y])},w]};var y;return t.push(M,{tag:"rect",attributes:s({fill:"currentColor","clip-path":"url(#".concat(x,")"),mask:"url(#".concat(z,")")},Nn)}),{children:t,attributes:a}}}},{provides(n){let e=!1;Y.matchMedia&&(e=Y.matchMedia("(prefers-reduced-motion: reduce)").matches),n.missingIconAbstract=function(){const t=[],a={fill:"currentColor"},r={attributeType:"XML",repeatCount:"indefinite",dur:"2s"};t.push({tag:"path",attributes:s(s({},a),{},{d:"M156.5,447.7l-12.6,29.5c-18.7-9.5-35.9-21.2-51.5-34.9l22.7-22.7C127.6,430.5,141.5,440,156.5,447.7z M40.6,272H8.5 c1.4,21.2,5.4,41.7,11.7,61.1L50,321.2C45.1,305.5,41.8,289,40.6,272z M40.6,240c1.4-18.8,5.2-37,11.1-54.1l-29.5-12.6 C14.7,194.3,10,216.7,8.5,240H40.6z M64.3,156.5c7.8-14.9,17.2-28.8,28.1-41.5L69.7,92.3c-13.7,15.6-25.5,32.8-34.9,51.5 L64.3,156.5z M397,419.6c-13.9,12-29.4,22.3-46.1,30.4l11.9,29.8c20.7-9.9,39.8-22.6,56.9-37.6L397,419.6z M115,92.4 c13.9-12,29.4-22.3,46.1-30.4l-11.9-29.8c-20.7,9.9-39.8,22.6-56.8,37.6L115,92.4z M447.7,355.5c-7.8,14.9-17.2,28.8-28.1,41.5 l22.7,22.7c13.7-15.6,25.5-32.9,34.9-51.5L447.7,355.5z M471.4,272c-1.4,18.8-5.2,37-11.1,54.1l29.5,12.6 c7.5-21.1,12.2-43.5,13.6-66.8H471.4z M321.2,462c-15.7,5-32.2,8.2-49.2,9.4v32.1c21.2-1.4,41.7-5.4,61.1-11.7L321.2,462z M240,471.4c-18.8-1.4-37-5.2-54.1-11.1l-12.6,29.5c21.1,7.5,43.5,12.2,66.8,13.6V471.4z M462,190.8c5,15.7,8.2,32.2,9.4,49.2h32.1 c-1.4-21.2-5.4-41.7-11.7-61.1L462,190.8z M92.4,397c-12-13.9-22.3-29.4-30.4-46.1l-29.8,11.9c9.9,20.7,22.6,39.8,37.6,56.9 L92.4,397z M272,40.6c18.8,1.4,36.9,5.2,54.1,11.1l12.6-29.5C317.7,14.7,295.3,10,272,8.5V40.6z M190.8,50 c15.7-5,32.2-8.2,49.2-9.4V8.5c-21.2,1.4-41.7,5.4-61.1,11.7L190.8,50z M442.3,92.3L419.6,115c12,13.9,22.3,29.4,30.5,46.1 l29.8-11.9C470,128.5,457.3,109.4,442.3,92.3z M397,92.4l22.7-22.7c-15.6-13.7-32.8-25.5-51.5-34.9l-12.6,29.5 C370.4,72.1,384.4,81.5,397,92.4z"})});const i=s(s({},r),{},{attributeName:"opacity"}),o={tag:"circle",attributes:s(s({},a),{},{cx:"256",cy:"364",r:"28"}),children:[]};return e||o.children.push({tag:"animate",attributes:s(s({},r),{},{attributeName:"r",values:"28;14;28;28;14;28;"})},{tag:"animate",attributes:s(s({},i),{},{values:"1;0;1;1;0;1;"})}),t.push(o),t.push({tag:"path",attributes:s(s({},a),{},{opacity:"1",d:"M263.7,312h-16c-6.6,0-12-5.4-12-12c0-71,77.4-63.9,77.4-107.8c0-20-17.8-40.2-57.4-40.2c-29.1,0-44.3,9.6-59.2,28.7 c-3.9,5-11.1,6-16.2,2.4l-13.1-9.2c-5.6-3.9-6.9-11.8-2.6-17.2c21.2-27.2,46.4-44.7,91.2-44.7c52.3,0,97.4,29.8,97.4,80.2 c0,67.6-77.4,63.5-77.4,107.8C275.7,306.6,270.3,312,263.7,312z"}),children:e?[]:[{tag:"animate",attributes:s(s({},i),{},{values:"1;0;0;0;0;1;"})}]}),e||t.push({tag:"path",attributes:s(s({},a),{},{opacity:"0",d:"M232.5,134.5l7,168c0.3,6.4,5.6,11.5,12,11.5h9c6.4,0,11.7-5.1,12-11.5l7-168c0.3-6.8-5.2-12.5-12-12.5h-23 C237.7,122,232.2,127.7,232.5,134.5z"}),children:[{tag:"animate",attributes:s(s({},i),{},{values:"0;0;1;1;0;0;"})}]}),{tag:"g",attributes:{class:"missing"},children:t}}}},{hooks:()=>({parseNodeAttributes(n,e){const t=e.getAttribute("data-fa-symbol"),a=t!==null&&(t===""||t);return n.symbol=a,n}})}],{mixoutsTo:on});const za=on.library,Jn=on.parse,ha=on.icon;function De(n,e){var t=Object.keys(n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(n);e&&(a=a.filter(function(r){return Object.getOwnPropertyDescriptor(n,r).enumerable})),t.push.apply(t,a)}return t}function D(n){for(var e=1;e=0)continue;f[c]=o[c]}return f}(n,e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(n);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(n,t)&&(r[t]=n[t])}return r}var Pn,Re,X,ln,Sn,fn,Q,Te,Be,Ye,We,He,_e,Ue,un,Cn,va=typeof globalThis<"u"?globalThis:typeof window<"u"?window:ce!==void 0?ce:typeof self<"u"?self:{},wt={exports:{}};Pn=wt,Re=va,X=function(n,e,t){if(!Be(e)||We(e)||He(e)||_e(e)||Te(e))return e;var a,r=0,i=0;if(Ye(e))for(a=[],i=e.length;r1&&arguments[1]!==void 0?arguments[1]:{},t=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};if(typeof n=="string")return n;var a=(n.children||[]).map(function(f){return zt(f)}),r=Object.keys(n.attributes||{}).reduce(function(f,c){var m=n.attributes[c];switch(c){case"class":f.class=m.split(/\s+/).reduce(function(d,u){return d[u]=!0,d},{});break;case"style":f.style=m.split(";").map(function(d){return d.trim()}).filter(function(d){return d}).reduce(function(d,u){var h=u.indexOf(":"),g=xa.camelize(u.slice(0,h)),b=u.slice(h+1).trim();return d[g]=b,d},{});break;default:f.attrs[c]=m}return f},{attrs:{},class:{},style:{}});t.class;var i=t.style,o=i===void 0?{}:i,l=ya(t,ka);return Mt(n.tag,D(D(D({},e),{},{class:r.class,style:D(D({},r.style),o)},r.attrs),l),a)}var At=!1;try{At=!0}catch{}function jn(n,e){return Array.isArray(e)&&e.length>0||!Array.isArray(e)&&e?N({},n,e):{}}function qe(n){return n&&Qn(n)==="object"&&n.prefix&&n.iconName&&n.icon?n:Jn.icon?Jn.icon(n):n===null?null:Qn(n)==="object"&&n.prefix&&n.iconName?n:Array.isArray(n)&&n.length===2?{prefix:n[0],iconName:n[1]}:typeof n=="string"?{prefix:"fas",iconName:n}:void 0}var Aa=Lt({name:"FontAwesomeIcon",props:{border:{type:Boolean,default:!1},fixedWidth:{type:Boolean,default:!1},flip:{type:[Boolean,String],default:!1,validator:function(n){return[!0,!1,"horizontal","vertical","both"].indexOf(n)>-1}},icon:{type:[Object,Array,String],required:!0},mask:{type:[Object,Array,String],default:null},maskId:{type:String,default:null},listItem:{type:Boolean,default:!1},pull:{type:String,default:null,validator:function(n){return["right","left"].indexOf(n)>-1}},pulse:{type:Boolean,default:!1},rotation:{type:[String,Number],default:null,validator:function(n){return[90,180,270].indexOf(Number.parseInt(n,10))>-1}},swapOpacity:{type:Boolean,default:!1},size:{type:String,default:null,validator:function(n){return["2xs","xs","sm","lg","xl","2xl","1x","2x","3x","4x","5x","6x","7x","8x","9x","10x"].indexOf(n)>-1}},spin:{type:Boolean,default:!1},transform:{type:[String,Object],default:null},symbol:{type:[Boolean,String],default:!1},title:{type:String,default:null},titleId:{type:String,default:null},inverse:{type:Boolean,default:!1},bounce:{type:Boolean,default:!1},shake:{type:Boolean,default:!1},beat:{type:Boolean,default:!1},fade:{type:Boolean,default:!1},beatFade:{type:Boolean,default:!1},flash:{type:Boolean,default:!1},spinPulse:{type:Boolean,default:!1},spinReverse:{type:Boolean,default:!1}},setup:function(n,e){var t=e.attrs,a=K(function(){return qe(n.icon)}),r=K(function(){return jn("classes",function(c){var m,d=(N(N(N(N(N(N(N(N(N(N(m={"fa-spin":c.spin,"fa-pulse":c.pulse,"fa-fw":c.fixedWidth,"fa-border":c.border,"fa-li":c.listItem,"fa-inverse":c.inverse,"fa-flip":c.flip===!0,"fa-flip-horizontal":c.flip==="horizontal"||c.flip==="both","fa-flip-vertical":c.flip==="vertical"||c.flip==="both"},"fa-".concat(c.size),c.size!==null),"fa-rotate-".concat(c.rotation),c.rotation!==null),"fa-pull-".concat(c.pull),c.pull!==null),"fa-swap-opacity",c.swapOpacity),"fa-bounce",c.bounce),"fa-shake",c.shake),"fa-beat",c.beat),"fa-fade",c.fade),"fa-beat-fade",c.beatFade),"fa-flash",c.flash),N(N(m,"fa-spin-pulse",c.spinPulse),"fa-spin-reverse",c.spinReverse));return Object.keys(d).map(function(u){return d[u]?u:null}).filter(function(u){return u})}(n))}),i=K(function(){return jn("transform",typeof n.transform=="string"?Jn.transform(n.transform):n.transform)}),o=K(function(){return jn("mask",qe(n.mask))}),l=K(function(){return ha(a.value,D(D(D(D({},r.value),i.value),o.value),{},{symbol:n.symbol,title:n.title,titleId:n.titleId,maskId:n.maskId}))});Ot(l,function(c){if(!c)return function(){var m;!At&&console&&typeof console.error=="function"&&(m=console).error.apply(m,arguments)}("Could not find one or more icon(s)",a.value,o.value)},{immediate:!0});var f=K(function(){return l.value?zt(l.value.abstract[0],{},t):null});return function(){return f.value}}});/*! - * Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com - * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) - * Copyright 2024 Fonticons, Inc. - */const La={prefix:"fas",iconName:"calendar-days",icon:[448,512,["calendar-alt"],"f073","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 48 0c26.5 0 48 21.5 48 48l0 48L0 160l0-48C0 85.5 21.5 64 48 64l48 0 0-32c0-17.7 14.3-32 32-32zM0 192l448 0 0 272c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 192zm64 80l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm128 0l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm144-16c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zM64 400l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16zm144-16c-8.8 0-16 7.2-16 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0zm112 16l0 32c0 8.8 7.2 16 16 16l32 0c8.8 0 16-7.2 16-16l0-32c0-8.8-7.2-16-16-16l-32 0c-8.8 0-16 7.2-16 16z"]},Oa={prefix:"fas",iconName:"lock",icon:[448,512,[128274],"f023","M144 144l0 48 160 0 0-48c0-44.2-35.8-80-80-80s-80 35.8-80 80zM80 192l0-48C80 64.5 144.5 0 224 0s144 64.5 144 144l0 48 16 0c35.3 0 64 28.7 64 64l0 192c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 256c0-35.3 28.7-64 64-64l16 0z"]},Ma={prefix:"fas",iconName:"pen-to-square",icon:[512,512,["edit"],"f044","M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L362.3 51.7l97.9 97.9 30.1-30.1c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L437.7 172.3 339.7 74.3 172.4 241.7zM96 64C43 64 0 107 0 160L0 416c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 64z"]},Na={prefix:"fas",iconName:"star",icon:[576,512,[11088,61446],"f005","M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"]},Pa={prefix:"fas",iconName:"charging-station",icon:[576,512,[],"f5e7","M96 0C60.7 0 32 28.7 32 64l0 384c-17.7 0-32 14.3-32 32s14.3 32 32 32l288 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l0-144 16 0c22.1 0 40 17.9 40 40l0 32c0 39.8 32.2 72 72 72s72-32.2 72-72l0-123.7c32.5-10.2 56-40.5 56-76.3l0-32c0-8.8-7.2-16-16-16l-16 0 0-48c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 48-32 0 0-48c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 48-16 0c-8.8 0-16 7.2-16 16l0 32c0 35.8 23.5 66.1 56 76.3L472 376c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-32c0-48.6-39.4-88-88-88l-16 0 0-192c0-35.3-28.7-64-64-64L96 0zM216.9 82.7c6 4 8.5 11.5 6.3 18.3l-25 74.9 57.8 0c6.7 0 12.7 4.2 15 10.4s.5 13.3-4.6 17.7l-112 96c-5.5 4.7-13.4 5.1-19.3 1.1s-8.5-11.5-6.3-18.3l25-74.9L96 208c-6.7 0-12.7-4.2-15-10.4s-.5-13.3 4.6-17.7l112-96c5.5-4.7 13.4-5.1 19.3-1.1z"]},Sa={prefix:"fas",iconName:"car-battery",icon:[512,512,["battery-car"],"f5df","M80 96c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l96 0c0-17.7 14.3-32 32-32l64 0c17.7 0 32 14.3 32 32l16 0c35.3 0 64 28.7 64 64l0 224c0 35.3-28.7 64-64 64L64 448c-35.3 0-64-28.7-64-64L0 160c0-35.3 28.7-64 64-64l16 0zm304 96c0-8.8-7.2-16-16-16s-16 7.2-16 16l0 32-32 0c-8.8 0-16 7.2-16 16s7.2 16 16 16l32 0 0 32c0 8.8 7.2 16 16 16s16-7.2 16-16l0-32 32 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-32 0 0-32zM80 240c0 8.8 7.2 16 16 16l96 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-96 0c-8.8 0-16 7.2-16 16z"]},Ca={prefix:"fas",iconName:"plug-circle-bolt",icon:[576,512,[],"e55b","M96 0C78.3 0 64 14.3 64 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM288 0c-17.7 0-32 14.3-32 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM32 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l0 32c0 77.4 55 142 128 156.8l0 67.2c0 17.7 14.3 32 32 32s32-14.3 32-32l0-67.2c12.3-2.5 24.1-6.4 35.1-11.5c-2.1-10.8-3.1-21.9-3.1-33.3c0-80.3 53.8-148 127.3-169.2c.5-2.2 .7-4.5 .7-6.8c0-17.7-14.3-32-32-32L32 160zM432 512a144 144 0 1 0 0-288 144 144 0 1 0 0 288zm47.9-225c4.3 3.7 5.4 9.9 2.6 14.9L452.4 356l35.6 0c5.2 0 9.8 3.3 11.4 8.2s-.1 10.3-4.2 13.4l-96 72c-4.5 3.4-10.8 3.2-15.1-.6s-5.4-9.9-2.6-14.9L411.6 380 376 380c-5.2 0-9.8-3.3-11.4-8.2s.1-10.3 4.2-13.4l96-72c4.5-3.4 10.8-3.2 15.1 .6z"]},ja={prefix:"fas",iconName:"solar-panel",icon:[640,512,[],"f5ba","M122.2 0C91.7 0 65.5 21.5 59.5 51.4L8.3 307.4C.4 347 30.6 384 71 384l217 0 0 64-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l192 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0 0-64 217 0c40.4 0 70.7-36.9 62.8-76.6l-51.2-256C574.5 21.5 548.3 0 517.8 0L122.2 0zM260.9 64l118.2 0 10.4 104-139 0L260.9 64zM202.3 168l-100.8 0L122.2 64l90.4 0L202.3 168zM91.8 216l105.6 0L187.1 320 71 320 91.8 216zm153.9 0l148.6 0 10.4 104-169.4 0 10.4-104zm196.8 0l105.6 0L569 320l-116 0L442.5 216zm96-48l-100.8 0L427.3 64l90.4 0 31.4-6.3L517.8 64l20.8 104z"]},Ea={prefix:"fas",iconName:"lock-open",icon:[576,512,[],"f3c1","M352 144c0-44.2 35.8-80 80-80s80 35.8 80 80l0 48c0 17.7 14.3 32 32 32s32-14.3 32-32l0-48C576 64.5 511.5 0 432 0S288 64.5 288 144l0 48L64 192c-35.3 0-64 28.7-64 64L0 448c0 35.3 28.7 64 64 64l320 0c35.3 0 64-28.7 64-64l0-192c0-35.3-28.7-64-64-64l-32 0 0-48z"]},Ia={prefix:"fas",iconName:"wrench",icon:[512,512,[128295],"f0ad","M352 320c88.4 0 160-71.6 160-160c0-15.3-2.2-30.1-6.2-44.2c-3.1-10.8-16.4-13.2-24.3-5.3l-76.8 76.8c-3 3-7.1 4.7-11.3 4.7L336 192c-8.8 0-16-7.2-16-16l0-57.4c0-4.2 1.7-8.3 4.7-11.3l76.8-76.8c7.9-7.9 5.4-21.2-5.3-24.3C382.1 2.2 367.3 0 352 0C263.6 0 192 71.6 192 160c0 19.1 3.4 37.5 9.5 54.5L19.9 396.1C7.2 408.8 0 426.1 0 444.1C0 481.6 30.4 512 67.9 512c18 0 35.3-7.2 48-19.9L297.5 310.5c17 6.2 35.4 9.5 54.5 9.5zM80 408a24 24 0 1 1 0 48 24 24 0 1 1 0-48z"]},Fa={prefix:"fas",iconName:"circle-info",icon:[512,512,["info-circle"],"f05a","M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336l24 0 0-64-24 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0c13.3 0 24 10.7 24 24l0 88 8 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-80 0c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"]},Da={prefix:"fas",iconName:"arrow-rotate-left",icon:[512,512,[8634,"arrow-left-rotate","arrow-rotate-back","arrow-rotate-backward","undo"],"f0e2","M125.7 160l50.3 0c17.7 0 32 14.3 32 32s-14.3 32-32 32L48 224c-17.7 0-32-14.3-32-32L16 64c0-17.7 14.3-32 32-32s32 14.3 32 32l0 51.2L97.6 97.6c87.5-87.5 229.3-87.5 316.8 0s87.5 229.3 0 316.8s-229.3 87.5-316.8 0c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0c62.5 62.5 163.8 62.5 226.3 0s62.5-163.8 0-226.3s-163.8-62.5-226.3 0L125.7 160z"]},Ra={prefix:"fas",iconName:"plug-circle-check",icon:[576,512,[],"e55c","M96 0C78.3 0 64 14.3 64 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM288 0c-17.7 0-32 14.3-32 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM32 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l0 32c0 77.4 55 142 128 156.8l0 67.2c0 17.7 14.3 32 32 32s32-14.3 32-32l0-67.2c12.3-2.5 24.1-6.4 35.1-11.5c-2.1-10.8-3.1-21.9-3.1-33.3c0-80.3 53.8-148 127.3-169.2c.5-2.2 .7-4.5 .7-6.8c0-17.7-14.3-32-32-32L32 160zM576 368a144 144 0 1 0 -288 0 144 144 0 1 0 288 0zm-76.7-43.3c6.2 6.2 6.2 16.4 0 22.6l-72 72c-6.2 6.2-16.4 6.2-22.6 0l-40-40c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L416 385.4l60.7-60.7c6.2-6.2 16.4-6.2 22.6 0z"]},Ta={prefix:"fas",iconName:"clock",icon:[512,512,[128339,"clock-four"],"f017","M256 0a256 256 0 1 1 0 512A256 256 0 1 1 256 0zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"]},Ba={prefix:"fas",iconName:"power-off",icon:[512,512,[9211],"f011","M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 224c0 17.7 14.3 32 32 32s32-14.3 32-32l0-224zM143.5 120.6c13.6-11.3 15.4-31.5 4.1-45.1s-31.5-15.4-45.1-4.1C49.7 115.4 16 181.8 16 256c0 132.5 107.5 240 240 240s240-107.5 240-240c0-74.2-33.8-140.6-86.6-184.6c-13.6-11.3-33.8-9.4-45.1 4.1s-9.4 33.8 4.1 45.1c38.9 32.3 63.5 81 63.5 135.4c0 97.2-78.8 176-176 176s-176-78.8-176-176c0-54.4 24.7-103.1 63.5-135.4z"]},Ya={prefix:"fas",iconName:"calculator",icon:[384,512,[128425],"f1ec","M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-384c0-35.3-28.7-64-64-64L64 0zM96 64l192 0c17.7 0 32 14.3 32 32l0 32c0 17.7-14.3 32-32 32L96 160c-17.7 0-32-14.3-32-32l0-32c0-17.7 14.3-32 32-32zm32 160a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zM96 352a32 32 0 1 1 0-64 32 32 0 1 1 0 64zM64 416c0-17.7 14.3-32 32-32l96 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-96 0c-17.7 0-32-14.3-32-32zM192 256a32 32 0 1 1 0-64 32 32 0 1 1 0 64zm32 64a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zm64-64a32 32 0 1 1 0-64 32 32 0 1 1 0 64zm32 64a32 32 0 1 1 -64 0 32 32 0 1 1 64 0zM288 448a32 32 0 1 1 0-64 32 32 0 1 1 0 64z"]},Wa={prefix:"fas",iconName:"delete-left",icon:[576,512,[9003,"backspace"],"f55a","M576 128c0-35.3-28.7-64-64-64L205.3 64c-17 0-33.3 6.7-45.3 18.7L9.4 233.4c-6 6-9.4 14.1-9.4 22.6s3.4 16.6 9.4 22.6L160 429.3c12 12 28.3 18.7 45.3 18.7L512 448c35.3 0 64-28.7 64-64l0-256zM271 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z"]},Ha={prefix:"fas",iconName:"house",icon:[576,512,[127968,63498,63500,"home","home-alt","home-lg-alt"],"f015","M575.8 255.5c0 18-15 32.1-32 32.1l-32 0 .7 160.2c0 2.7-.2 5.4-.5 8.1l0 16.2c0 22.1-17.9 40-40 40l-16 0c-1.1 0-2.2 0-3.3-.1c-1.4 .1-2.8 .1-4.2 .1L416 512l-24 0c-22.1 0-40-17.9-40-40l0-24 0-64c0-17.7-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32l0 64 0 24c0 22.1-17.9 40-40 40l-24 0-31.9 0c-1.5 0-3-.1-4.5-.2c-1.2 .1-2.4 .2-3.6 .2l-16 0c-22.1 0-40-17.9-40-40l0-112c0-.9 0-1.9 .1-2.8l0-69.7-32 0c-18 0-32-14-32-32.1c0-9 3-17 10-24L266.4 8c7-7 15-8 22-8s15 2 21 7L564.8 231.5c8 7 12 15 11 24z"]},_a={prefix:"fas",iconName:"calendar-week",icon:[448,512,[],"f784","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 48 0c26.5 0 48 21.5 48 48l0 48L0 160l0-48C0 85.5 21.5 64 48 64l48 0 0-32c0-17.7 14.3-32 32-32zM0 192l448 0 0 272c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 192zm80 64c-8.8 0-16 7.2-16 16l0 64c0 8.8 7.2 16 16 16l288 0c8.8 0 16-7.2 16-16l0-64c0-8.8-7.2-16-16-16L80 256z"]},Ua={prefix:"fas",iconName:"bolt",icon:[448,512,[9889,"zap"],"f0e7","M349.4 44.6c5.9-13.7 1.5-29.7-10.6-38.5s-28.6-8-39.9 1.8l-256 224c-10 8.8-13.6 22.9-8.9 35.3S50.7 288 64 288l111.5 0L98.6 467.4c-5.9 13.7-1.5 29.7 10.6 38.5s28.6 8 39.9-1.8l256-224c10-8.8 13.6-22.9 8.9-35.3s-16.6-20.7-30-20.7l-111.5 0L349.4 44.6z"]},qa={prefix:"fas",iconName:"car",icon:[512,512,[128664,"automobile"],"f1b9","M135.2 117.4L109.1 192l293.8 0-26.1-74.6C372.3 104.6 360.2 96 346.6 96L165.4 96c-13.6 0-25.7 8.6-30.2 21.4zM39.6 196.8L74.8 96.3C88.3 57.8 124.6 32 165.4 32l181.2 0c40.8 0 77.1 25.8 90.6 64.3l35.2 100.5c23.2 9.6 39.6 32.5 39.6 59.2l0 144 0 48c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-48L96 400l0 48c0 17.7-14.3 32-32 32l-32 0c-17.7 0-32-14.3-32-32l0-48L0 256c0-26.7 16.4-49.6 39.6-59.2zM128 288a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm288 32a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"]},Ka={prefix:"fas",iconName:"plug-circle-xmark",icon:[576,512,[],"e560","M96 0C78.3 0 64 14.3 64 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM288 0c-17.7 0-32 14.3-32 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM32 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l0 32c0 77.4 55 142 128 156.8l0 67.2c0 17.7 14.3 32 32 32s32-14.3 32-32l0-67.2c12.3-2.5 24.1-6.4 35.1-11.5c-2.1-10.8-3.1-21.9-3.1-33.3c0-80.3 53.8-148 127.3-169.2c.5-2.2 .7-4.5 .7-6.8c0-17.7-14.3-32-32-32L32 160zM432 512a144 144 0 1 0 0-288 144 144 0 1 0 0 288zm59.3-180.7L454.6 368l36.7 36.7c6.2 6.2 6.2 16.4 0 22.6s-16.4 6.2-22.6 0L432 390.6l-36.7 36.7c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6L409.4 368l-36.7-36.7c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L432 345.4l36.7-36.7c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6z"]},Xa={prefix:"fas",iconName:"eraser",icon:[576,512,[],"f12d","M290.7 57.4L57.4 290.7c-25 25-25 65.5 0 90.5l80 80c12 12 28.3 18.7 45.3 18.7L288 480l9.4 0L512 480c17.7 0 32-14.3 32-32s-14.3-32-32-32l-124.1 0L518.6 285.3c25-25 25-65.5 0-90.5L381.3 57.4c-25-25-65.5-25-90.5 0zM297.4 416l-9.4 0-105.4 0-80-80L227.3 211.3 364.7 348.7 297.4 416z"]},Va={prefix:"fas",iconName:"gauge-high",icon:[512,512,[62461,"tachometer-alt","tachometer-alt-fast"],"f625","M0 256a256 256 0 1 1 512 0A256 256 0 1 1 0 256zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM256 416c35.3 0 64-28.7 64-64c0-17.4-6.9-33.1-18.1-44.6L366 161.7c5.3-12.1-.2-26.3-12.3-31.6s-26.3 .2-31.6 12.3L257.9 288c-.6 0-1.3 0-1.9 0c-35.3 0-64 28.7-64 64s28.7 64 64 64zM176 144a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM96 288a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm352-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"]},Ga={prefix:"fas",iconName:"triangle-exclamation",icon:[512,512,[9888,"exclamation-triangle","warning"],"f071","M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"]},Za={prefix:"fas",iconName:"calendar-day",icon:[448,512,[],"f783","M128 0c17.7 0 32 14.3 32 32l0 32 128 0 0-32c0-17.7 14.3-32 32-32s32 14.3 32 32l0 32 48 0c26.5 0 48 21.5 48 48l0 48L0 160l0-48C0 85.5 21.5 64 48 64l48 0 0-32c0-17.7 14.3-32 32-32zM0 192l448 0 0 272c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 192zm80 64c-8.8 0-16 7.2-16 16l0 96c0 8.8 7.2 16 16 16l96 0c8.8 0 16-7.2 16-16l0-96c0-8.8-7.2-16-16-16l-96 0z"]},Ja={prefix:"fas",iconName:"circle-xmark",icon:[512,512,[61532,"times-circle","xmark-circle"],"f057","M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z"]},Qa={prefix:"far",iconName:"star",icon:[576,512,[11088,61446],"f005","M287.9 0c9.2 0 17.6 5.2 21.6 13.5l68.6 141.3 153.2 22.6c9 1.3 16.5 7.6 19.3 16.3s.5 18.1-5.9 24.5L433.6 328.4l26.2 155.6c1.5 9-2.2 18.1-9.7 23.5s-17.3 6-25.3 1.7l-137-73.2L151 509.1c-8.1 4.3-17.9 3.7-25.3-1.7s-11.2-14.5-9.7-23.5l26.2-155.6L31.1 218.2c-6.5-6.4-8.7-15.9-5.9-24.5s10.3-14.9 19.3-16.3l153.2-22.6L266.3 13.5C270.4 5.2 278.7 0 287.9 0zm0 79L235.4 187.2c-3.5 7.1-10.2 12.1-18.1 13.3L99 217.9 184.9 303c5.5 5.5 8.1 13.3 6.8 21L171.4 443.7l105.2-56.2c7.1-3.8 15.6-3.8 22.6 0l105.2 56.2L384.2 324.1c-1.3-7.7 1.2-15.5 6.8-21l85.9-85.1L358.6 200.5c-7.8-1.2-14.6-6.1-18.1-13.3L287.9 79z"]},$a={prefix:"far",iconName:"clock",icon:[512,512,[128339,"clock-four"],"f017","M464 256A208 208 0 1 1 48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 120l0 136c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 243.2 280 120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"]};export{Ra as A,Ca as B,Da as C,Ba as D,Aa as F,Xa as a,Oa as b,Ea as c,Va as d,Sa as e,Wa as f,ja as g,Ha as h,Pa as i,Ya as j,Ia as k,za as l,qa as m,Ma as n,Ja as o,Ga as p,Fa as q,Na as r,Qa as s,Ta as t,$a as u,Ua as v,Za as w,_a as x,La as y,Ka as z}; diff --git a/packages/modules/display_themes/cards/web/assets/vendor-inkline-C_NPDnDu.js b/packages/modules/display_themes/cards/web/assets/vendor-inkline-C_NPDnDu.js deleted file mode 100644 index e6d1e8e5bc..0000000000 --- a/packages/modules/display_themes/cards/web/assets/vendor-inkline-C_NPDnDu.js +++ /dev/null @@ -1 +0,0 @@ -import{h as nt,w as Bt,r as zt,d as c,a as At,b as $,v as x,e as o,f,i as m,j as d,m as h,o as l,k as P,l as V,n as Y,p as A,q as y,s as I,t as E,u as Q,T as L,x as _,y as G,z,F as j,A as X,c as se,B as Vt,C as Ot,D as Lt,E as Dt,G as Nt}from"./vendor-BMrK3KHF.js";const p=(e,t)=>{const i=e.__vccOpts||e;for(const[a,s]of t)i[a]=s;return i};function g(e){let t=e.color;return t||(t=e.$inkline.options.colorMode==="system"?typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.$inkline.options.colorMode),{[`-${t}`]:!0}}function lt(e,t){if(!e||!t)return!1;if(t.indexOf(" ")!==-1)throw new Error("Class name should not contain spaces.");return e.classList?e.classList.contains(t):(" "+e.className+" ").indexOf(" "+t+" ")>-1}function _e(e,t){if(!e)return;let i=e.className;const a=(t||"").split(" ");for(let s=0,r=a.length;s"-"+s.toLowerCase())}function rt(e,t){return["string","number"].indexOf(typeof t)>-1&&t!==""?`${He(e)}-${t}`:He(e)}function Re(e){return e.charAt(0).toUpperCase()+e.slice(1)}function U(e){if(Array.isArray(e)){const t=e.slice().map(U),i=Object.keys(t);return Object.keys(e).filter(a=>!i.includes(a)).forEach(a=>{t[a]=e[a]}),t}return typeof e=="object"?Object.keys(e).reduce((t,i)=>(t[i]=U(e[i]),t),{}):e}function Fe(e){if(e.tabIndex>0||e.tabIndex===0&&e.getAttribute("tabIndex")!==null)return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&e.rel!=="ignore";case"INPUT":return e.type!=="hidden"&&e.type!=="file";case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}function Tt(e){if(!Fe(e))return!1;try{e.focus()}catch{}return typeof window<"u"&&document.activeElement===e}function ot(e){for(let t=0;t"u")return;if(e.currentStyle)return e.currentStyle[t];const i=window.getComputedStyle(e,null);return i.getPropertyValue?i.getPropertyValue(t):i[t]}const dt=e=>e instanceof Function,J={xs:[0,575],sm:[576,767],md:[768,991],lg:[992,1199],xl:[1200,1399],xxl:[1400,1/0]},je=["","xs","sm","md","lg","xl","xxl"],Pt={tab:["Tab",9],enter:["Enter",13],esc:["Escape",27],space:[" ","Space",32],left:["ArrowLeft","Left",37],up:["ArrowUp","Up",38],right:["ArrowRight","Right",39],down:["ArrowDown","Down",40]},Qe={pristine:!0,dirty:!1,untouched:!0,touched:!1,valid:!0,invalid:!1,errors:[]},Rt={value:"",validators:[]},ut=["value","validators","pristine","dirty","untouched","touched","valid","invalid","errors"],k=(e,t)=>{const i=t.key||t.keyIdentifier||t.keyCode;return Pt[e].indexOf(i)!==-1};function Et(e,t,i){e&&t&&e.removeEventListener(t,i,!1)}function Mt(e,t,i){e&&t&&e.detachEvent("on"+t,i)}const T=typeof window>"u"?()=>{}:window.document.removeEventListener?Et:Mt;function _t(e,t,i){e&&t&&i&&e.addEventListener(t,i,!1)}function Ft(e,t,i){e&&t&&i&&e.attachEvent("on"+t,i)}const O=typeof window>"u"?()=>{}:window.document.addEventListener?_t:Ft,ct=e=>e.map(t=>t.type==="element"?nt(t.name,t.attributes,ct(t.children)):t.value);function pt(e,t){if(!e||!t)return;const i=t.split(" ");let a=" "+e.className+" ";for(let s=0,r=i.length;si&&i[a],e)}function ne(e,t,i){return t&&t.split(".").reduce((a,s)=>(Object.keys(i).forEach(r=>{a[s][r]=i[r]}),a&&a[s]),e),Object.keys(i).forEach(a=>{e[a]=i[a]}),e}function S(e){return`${e?`${e}-`:""}${Math.random().toString(36).substr(2,9)}`}const D={"en-US":/^[A-Z]+$/i,"bg-BG":/^[А-Я]+$/i,"cs-CZ":/^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[A-ZÆØÅ]+$/i,"de-DE":/^[A-ZÄÖÜß]+$/i,"el-GR":/^[Α-ω]+$/i,"es-ES":/^[A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[A-ZÀÉÈÌÎÓÒÙ]+$/i,"nb-NO":/^[A-ZÆØÅ]+$/i,"nl-NL":/^[A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[A-ZÆØÅ]+$/i,"hu-HU":/^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"pl-PL":/^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[А-ЯЁ]+$/i,"sl-SI":/^[A-ZČĆĐŠŽ]+$/i,"sk-SK":/^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,"sr-RS@latin":/^[A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[A-ZÅÄÖ]+$/i,"tr-TR":/^[A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[А-ЩЬЮЯЄIЇҐі]+$/i,"ku-IQ":/^[ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,ar:/^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/},N={"en-US":/^[0-9A-Z]+$/i,"bg-BG":/^[0-9А-Я]+$/i,"cs-CZ":/^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[0-9A-ZÆØÅ]+$/i,"de-DE":/^[0-9A-ZÄÖÜß]+$/i,"el-GR":/^[0-9Α-ω]+$/i,"es-ES":/^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,"hu-HU":/^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"nb-NO":/^[0-9A-ZÆØÅ]+$/i,"nl-NL":/^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[0-9A-ZÆØÅ]+$/i,"pl-PL":/^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[0-9А-ЯЁ]+$/i,"sl-SI":/^[0-9A-ZČĆĐŠŽ]+$/i,"sk-SK":/^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,"sr-RS@latin":/^[0-9A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[0-9А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[0-9A-ZÅÄÖ]+$/i,"tr-TR":/^[0-9A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,"ku-IQ":/^[٠١٢٣٤٥٦٧٨٩0-9ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,ar:/^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/};["AU","GB","HK","IN","NZ","ZA","ZM"].forEach(e=>{D[`en-${e}`]=D["en-US"],N[`en-${e}`]=N["en-US"]}),["AE","BH","DZ","EG","IQ","JO","KW","LB","LY","MA","QM","QA","SA","SD","SY","TN","YE"].forEach(e=>{D[`ar-${e}`]=D.ar,N[`ar-${e}`]=N.ar}),D["pt-BR"]=D["pt-PT"],N["pt-BR"]=N["pt-PT"],D["pl-Pl"]=D["pl-PL"],N["pl-Pl"]=N["pl-PL"];const Ye=/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,jt={alpha:function(e,t={}){const i=t.locale||"en-US",a=s=>{let r=String(s);return t.allowDashes&&(r=r.replace(/-/g,"")),t.allowSpaces&&(r=r.replace(/ /g,"")),r};return e.constructor===Array?e.every(s=>D[i].test(a(s))):D[i].test(a(e))},alphanumeric:function(e,t={}){const i=t.locale||"en-US",a=s=>{let r=String(s);return t.allowDashes&&(r=r.replace(/-/g,"")),t.allowSpaces&&(r=r.replace(/ /g,"")),r};return e.constructor===Array?e.every(s=>N[i].test(a(s))):N[i].test(a(e))},custom:function(e,t={validator:()=>!0}){return e.constructor===Array?e.every(i=>t.validator(i)):t.validator(e)},number:function(e,t={allowNegative:!1,allowDecimal:!1}){let i="\\d+";t.allowNegative&&(i="[-]?"+i),t.allowDecimal&&(i+="([\\.\\,]\\d+)?");const a=new RegExp(`^${i}$`);return e.constructor===Array?e.every(s=>a.test(s)):a.test(e)},email:function(e){return e.constructor===Array?e.every(t=>!t||Ye.test(String(t))):!e||Ye.test(String(e))},max:function(e,t={value:0}){if(e==null)return!1;const i=a=>Number(a);return Array.isArray(e)?e.every(a=>i(a)<=t.value):i(e)<=t.value},maxLength:function(e,t={value:0}){return e!=null&&(e.constructor===Array?e.length<=t.value:typeof e=="object"?Object.keys(e).length<=t.value:String(e).length<=t.value)},min:function(e,t={value:0}){if(e==null)return!1;const i=a=>Number(a);return Array.isArray(e)?e.every(a=>i(a)>=t.value):i(e)>=t.value},minLength:function(e,t={value:0}){return e!=null&&(e.constructor===Array?e.length>=t.value:typeof e=="object"?Object.keys(e).length>=t.value:String(e).length>=t.value)},required:function(e,t={invalidateFalse:!1}){return e!=null&&(e.constructor===Array?!!e.length:typeof e=="boolean"?!t.invalidateFalse||e:!!String(e).trim().length)},sameAs:function(e,t={}){if(!t.target)return!1;const i=M(t.schema(),t.target);if(!i)throw new Error(`Could not find target with name '${t.target}' in 'sameAs' validator.`);return e===i.value}};function ht(e){const t=Object.keys(e).length===0||Array.isArray(e.validators)||e.hasOwnProperty("value")?{...Qe,...Rt}:Qe;return Object.entries(t).forEach(([i,a])=>{e.hasOwnProperty(i)||(e[i]=a)}),Object.keys(e).filter(i=>!ut.includes(i)).forEach(i=>{(typeof e[i]=="object"||Array.isArray(e[i]))&&(e[i]=ht(e[i]))}),e}const Ee={locale:"en",messages:{en:{validation:{alpha:e=>{let t;switch(!0){case(e.allowSpaces&&e.allowDashes):t="letters, spaces, and dashes";break;case e.allowSpaces:t="letters and spaces";break;case e.allowDashes:t="letters and dashes";break;default:t="letters"}return`Please enter ${t} only.`},alphanumeric:e=>{let t;switch(!0){case(e.allowSpaces&&e.allowDashes):t="letters, numbers, spaces, and dashes";break;case e.allowSpaces:t="letters, numbers, and spaces";break;case e.allowDashes:t="letters, numbers, and dashes";break;default:t="letters and numbers"}return`Please enter ${t} only.`},number:e=>{let t;switch(!0){case(e.allowNegative&&e.allowDecimal):t="positive or negative decimal numbers";break;case e.allowNegative:t="positive or negative numbers";break;case e.allowDecimal:t="decimal numbers";break;default:t="numbers"}return`Please enter ${t} only.`},email:()=>"Please enter a valid email address.",max:()=>"Please enter a maximum value of {value}.",maxLength:()=>"Please enter up to {value} characters.",min:()=>"Please enter a minimum value of {value}.",minLength:()=>"Please enter at least {value} characters.",required:()=>"Please enter a value for this field.",sameAs:()=>"Please make sure that the two values match.",custom:()=>"Please enter a correct value for this field."}}}};function Gt(e,t=""){const i=[];return e.valid=(e.validators||[]).reduce((a,s)=>{const r=typeof s=="string"?{name:s}:s,n=jt[r.name](e.value,r);if(!n){const{name:b,message:B,...F}=r,w={name:t.split(".").pop(),value:e.value,...F},xt=(B instanceof Function?B():B)||function(We,ie={}){const ae=M(Ee.messages[Ee.locale],We),Ct=dt(ae)?ae(ie):ae||We;return Object.keys(ie).reduce((St,Ue)=>St.replace(new RegExp(`{${Ue}}`,"g"),`${ie[Ue]}`),Ct)}(`validation.${b}`,w);i.push({name:b,message:xt,path:t})}return a&&n},!0),e.invalid=!e.valid,e.errors=i,e}function mt(e,t=""){return e.valid=Object.keys(e).filter(i=>!ut.includes(i)).reduce((i,a)=>(Object.keys(e[a]).length===0||e[a].validators||e[a].value?e[a]=Gt(e[a],`${t}`?`${t}.${a}`:a):e[a]=mt(e[a],`${t}`?`${t}.${a}`:a),i&&e[a].valid),!0),e.invalid=!e.valid,e}function le(e){return mt(e,"")}const Zt=Object.freeze(Object.defineProperty({__proto__:null,inkCaretDown:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"caret-down",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 11c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1z"},children:[]}]},inkCheck:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"check",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M23.625 3.5l-13.125 13.125-6.125-6.125-4.375 4.375 10.5 10.5 17.5-17.5z"},children:[]}]},inkChevronDown:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"chevron-down",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M26.297 12.625l-11.594 11.578c-0.391 0.391-1.016 0.391-1.406 0l-11.594-11.578c-0.391-0.391-0.391-1.031 0-1.422l2.594-2.578c0.391-0.391 1.016-0.391 1.406 0l8.297 8.297 8.297-8.297c0.391-0.391 1.016-0.391 1.406 0l2.594 2.578c0.391 0.391 0.391 1.031 0 1.422z"},children:[]}]},inkCircle:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"24",height:"28",viewBox:"0 0 24 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"circle",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"},children:[]}]},inkDanger:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"danger",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M14 2.625c-3.038 0-5.895 1.183-8.043 3.332s-3.332 5.005-3.332 8.043c0 3.038 1.183 5.895 3.332 8.043s5.005 3.332 8.043 3.332c3.038 0 5.895-1.183 8.043-3.332s3.332-5.005 3.332-8.043c0-3.038-1.183-5.895-3.332-8.043s-5.005-3.332-8.043-3.332zM14 0v0c7.732 0 14 6.268 14 14s-6.268 14-14 14c-7.732 0-14-6.268-14-14s6.268-14 14-14zM12.25 19.25h3.5v3.5h-3.5zM12.25 5.25h3.5v10.5h-3.5z"},children:[]}]},inkInfo:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"info",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M12.25 8.313c0-0.722 0.591-1.313 1.313-1.313h0.875c0.722 0 1.313 0.591 1.313 1.313v0.875c0 0.722-0.591 1.313-1.313 1.313h-0.875c-0.722 0-1.313-0.591-1.313-1.313v-0.875z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M17.5 21h-7v-1.75h1.75v-5.25h-1.75v-1.75h5.25v7h1.75z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M14 0c-7.732 0-14 6.268-14 14s6.268 14 14 14 14-6.268 14-14-6.268-14-14-14zM14 25.375c-6.282 0-11.375-5.093-11.375-11.375s5.093-11.375 11.375-11.375 11.375 5.093 11.375 11.375-5.093 11.375-11.375 11.375z"},children:[]}]},inkMinus:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"minus",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M0 11.375v5.25c0 0.483 0.392 0.875 0.875 0.875h26.25c0.483 0 0.875-0.392 0.875-0.875v-5.25c0-0.483-0.392-0.875-0.875-0.875h-26.25c-0.483 0-0.875 0.392-0.875 0.875z"},children:[]}]},inkPlus:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"plus",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.125 10.5h-9.625v-9.625c0-0.483-0.392-0.875-0.875-0.875h-5.25c-0.483 0-0.875 0.392-0.875 0.875v9.625h-9.625c-0.483 0-0.875 0.392-0.875 0.875v5.25c0 0.483 0.392 0.875 0.875 0.875h9.625v9.625c0 0.483 0.392 0.875 0.875 0.875h5.25c0.483 0 0.875-0.392 0.875-0.875v-9.625h9.625c0.483 0 0.875-0.392 0.875-0.875v-5.25c0-0.483-0.392-0.875-0.875-0.875z"},children:[]}]},inkSearch:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"search",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.132 23.827l-6.632-5.641c-0.686-0.617-1.419-0.9-2.011-0.873 1.566-1.834 2.511-4.213 2.511-6.813 0-5.799-4.701-10.5-10.5-10.5s-10.5 4.701-10.5 10.5 4.701 10.5 10.5 10.5c2.6 0 4.98-0.946 6.813-2.511-0.027 0.592 0.256 1.326 0.873 2.011l5.641 6.632c0.966 1.073 2.544 1.164 3.506 0.201s0.872-2.54-0.201-3.506zM10.5 17.5c-3.866 0-7-3.134-7-7s3.134-7 7-7 7 3.134 7 7-3.134 7-7 7z"},children:[]}]},inkSort:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 17c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1zM16 11c0 0.547-0.453 1-1 1h-14c-0.547 0-1-0.453-1-1 0-0.266 0.109-0.516 0.297-0.703l7-7c0.187-0.187 0.438-0.297 0.703-0.297s0.516 0.109 0.703 0.297l7 7c0.187 0.187 0.297 0.438 0.297 0.703z"},children:[]}]},inkSortAsc:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort-asc",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 11c0 0.547-0.453 1-1 1h-14c-0.547 0-1-0.453-1-1 0-0.266 0.109-0.516 0.297-0.703l7-7c0.187-0.187 0.438-0.297 0.703-0.297s0.516 0.109 0.703 0.297l7 7c0.187 0.187 0.297 0.438 0.297 0.703z"},children:[]}]},inkSortDesc:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort-desc",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 17c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1z"},children:[]}]},inkTimes:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"times",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.745 22.495c-0-0-0-0-0-0l-8.494-8.494 8.494-8.494c0-0 0-0 0-0 0.091-0.091 0.158-0.198 0.2-0.312 0.116-0.311 0.050-0.675-0.2-0.925l-4.013-4.013c-0.25-0.25-0.614-0.316-0.925-0.2-0.114 0.042-0.221 0.109-0.312 0.2 0 0-0 0-0 0l-8.494 8.494-8.494-8.494c-0-0-0-0-0-0-0.091-0.091-0.198-0.158-0.312-0.2-0.311-0.116-0.675-0.050-0.925 0.2l-4.013 4.013c-0.25 0.25-0.316 0.614-0.2 0.925 0.042 0.114 0.109 0.221 0.2 0.312 0 0 0 0 0 0l8.494 8.494-8.494 8.494c-0 0-0 0-0 0-0.091 0.091-0.157 0.198-0.2 0.312-0.116 0.311-0.050 0.675 0.2 0.925l4.013 4.013c0.25 0.25 0.614 0.316 0.925 0.2 0.114-0.042 0.221-0.109 0.312-0.2 0-0 0-0 0-0l8.494-8.494 8.494 8.494c0 0 0 0 0 0 0.092 0.091 0.198 0.158 0.312 0.2 0.311 0.116 0.675 0.050 0.925-0.2l4.013-4.013c0.25-0.25 0.316-0.614 0.2-0.925-0.042-0.114-0.109-0.221-0.2-0.312z"},children:[]}]},inkWarning:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"warning",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M14 2.537l11.733 23.385h-23.467l11.733-23.385zM14 0c-0.603 0-1.207 0.407-1.665 1.221l-11.951 23.819c-0.916 1.628-0.137 2.96 1.731 2.96h23.77c1.868 0 2.647-1.332 1.731-2.96h0l-11.951-23.819c-0.458-0.814-1.061-1.221-1.665-1.221v0z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M15.75 22.75c0 0.966-0.784 1.75-1.75 1.75s-1.75-0.784-1.75-1.75c0-0.966 0.784-1.75 1.75-1.75s1.75 0.784 1.75 1.75z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M14 19.25c-0.966 0-1.75-0.784-1.75-1.75v-5.25c0-0.966 0.784-1.75 1.75-1.75s1.75 0.784 1.75 1.75v5.25c0 0.966-0.784 1.75-1.75 1.75z"},children:[]}]}},Symbol.toStringTag,{value:"Module"})),Je="inkline-color-mode",re=e=>{let t;t=e==="system"?matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e,pt(document.body,"-light -dark"),_e(document.body,`-${t}`)},Kt={components:{},icons:{},colorMode:"system",locale:"en",validateOn:["input","blur"],color:"",size:"",routerComponent:"router-link",componentOptions:{}};function qt({icons:e,components:t,...i}){return{form:a=>ht(a),setLocale(a){(function(s){Ee.locale=s})(a)},options:zt(i)}}const W={prototype:void 0},yn={install(e,t={}){const i={...Kt,...t};for(const r in i.components)e.component(i.components[r].name,i.components[r]);if(typeof window<"u"){const r=localStorage.getItem(Je);r&&(i.colorMode=r)}const a=qt(i);W.prototype=a,e.config.globalProperties.$inkline=a,e.provide("inkline",a);const s={...Zt,...i.icons};if(e.provide("inklineIcons",s),typeof window<"u"){Bt(()=>a.options.colorMode,b=>{re(b),localStorage.setItem(Je,b)});const r=()=>{a.options.colorMode==="system"&&re(a.options.colorMode)},n=matchMedia("(prefers-color-scheme: dark)");n.addEventListener?n.addEventListener("change",r):n.addListener(r),_e(document.body,"inkline"),re(i.colorMode)}}};function u(e,t,i=""){return()=>{var a;return W.prototype?(a=W.prototype.options.componentOptions[e])!=null&&a[t]?W.prototype.options.componentOptions[e][t]:W.prototype.options[t]:i}}function v(e){return["","xs","sm","md","lg","xl","xxl"].includes(e)}const ft=c({props:{collapse:{type:[String,Boolean],default:"md"},modelValue:{type:Boolean,default:!1}},emits:["update:modelValue"],data(){return{open:this.modelValue,windowWidth:typeof window<"u"?window.innerWidth:0}},computed:{collapsibleClasses(){return{"-open":this.open,"-collapsible":this.collapsible,[`-collapse-${this.collapse}`]:!!this.collapse}},collapsible(){return this.collapse===!0||this.collapse===!1?this.collapse:this.windowWidth<=J[this.collapse][1]}},watch:{modelValue(e){this.open=e}},created(){typeof window<"u"&&(O(window,"resize",this.onWindowResize),this.onWindowResize())},beforeUnmount(){typeof window<"u"&&T(window,"resize",this.onWindowResize)},methods:{setOpen(e){this.open=e,this.$emit("update:modelValue",this.open)},toggleOpen(){this.open=!this.open,this.$emit("update:modelValue",this.open)},onWindowResize(){if(this.collapse===!0||this.collapse===!1||typeof window>"u")return;const e=window.innerWidth;this.windowWidth<=J[this.collapse][1]&&e>J[this.collapse][1]&&this.setOpen(!1),this.windowWidth=window.innerWidth}}}),R=c({inject:{formGroup:{default:()=>({})},form:{default:()=>({})}},computed:{isDisabled(){return this.disabled||this.form.isDisabled||this.formGroup.isDisabled},isReadonly(){return this.readonly||this.form.isReadonly||this.formGroup.isReadonly},parent(){return this.formGroup.$?this.formGroup:this.form},schema(){const e=this.parent.schema||{};return this.name!==""?M(e,`${this.name}`):e}}}),Z=c({props:{tag:{type:String,default:"a"}},computed:{isTag(){return this.$attrs.to?this.routerComponent:this.$attrs.href?"a":this.tag},isComponent(){return this.isTag===this.routerComponent},routerComponent(){return this.$inkline.options.routerComponent}}}),Wt=e=>({name:"offset",options:{offset:[0,e]}}),bt=({offset:e})=>[Wt(e),{name:"arrow",options:{padding:6}},{name:"preventOverflow",options:{padding:8}},{name:"computeStyles",options:{gpuAcceleration:!1,adaptive:!1}}],ee=c({props:{placement:{type:String,default:"auto"},offset:{type:Number,default:6},popperOptions:{type:Object,default:()=>({})}},data:()=>({popperInstance:void 0}),watch:{placement(e){this.popperInstance&&this.popperInstance.setOptions({placement:e})}},beforeUnmount(){this.destroyPopper()},methods:{createPopper(){if(typeof window>"u")return;const e=bt({offset:this.offset});this.popperInstance=At(this.$refs.wrapper,this.$refs.popup,{strategy:"fixed",placement:this.placement,modifiers:e,...this.popperOptions})},destroyPopper(){this.popperInstance&&(this.popperInstance.destroy(),this.popperInstance=void 0)}}}),Ge=c({props:{disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:void 0},trigger:{type:Array,default:()=>["hover","click","focus"]},interactable:{type:Boolean,default:!0},hoverHideDelay:{type:Number,default:300}},emits:["update:modelValue","click-outside"],data(){return{visible:this.modelValue,triggerStack:0,hoverHideTransition:!1}},watch:{modelValue(e){e?this.show():this.hide()}},mounted(){if(!this.$slots.default)throw new Error("Popup components require one child element to be used as trigger.");this.addEventListeners()},beforeUnmount(){this.removeEventListeners()},methods:{show(){this.disabled||this.visible||(this.triggerStack+=1,this.visible=!0,this.createPopper(),this.$emit("update:modelValue",!0))},hide(){!this.disabled&&this.visible&&(this.triggerStack-=1,this.triggerStack<=0&&(this.triggerStack=0,this.visible=!1,this.$emit("update:modelValue",!1)))},hoverShow(){this.hoverHideTransition=!1,this.show()},hoverHide(){this.hoverHideTransition=!0,setTimeout(()=>{this.hoverHideTransition&&this.hide()},this.hoverHideDelay)},onClick(){this.visible?this.hide():this.show()},onClickOutside(e){this.visible&&this.$emit("click-outside",e),this.modelValue||this.hide()},addEventListeners(){[].concat(this.trigger).forEach(e=>{switch(e){case"hover":O(this.$refs.trigger,"mouseenter",this.interactable?this.hoverShow:this.show),O(this.$refs.trigger,"mouseleave",this.interactable?this.hoverHide:this.hide),this.interactable&&(O(this.$refs.popup,"mouseenter",this.hoverShow),O(this.$refs.popup,"mouseleave",this.hoverHide));break;case"click":O(this.$refs.trigger,"click",this.onClick);break;case"focus":for(const t of this.$refs.trigger.children)O(t,"focus",this.show),O(t,"blur",this.hide)}})},removeEventListeners(){[].concat(this.trigger).forEach(e=>{switch(e){case"hover":T(this.$refs.trigger,"mouseenter",this.interactable?this.hoverShow:this.show),T(this.$refs.trigger,"mouseleave",this.interactable?this.hoverHide:this.hide),this.interactable&&(T(this.$refs.popup,"mouseenter",this.hoverShow),T(this.$refs.popup,"mouseleave",this.hoverHide));break;case"click":T(this.$refs.trigger,"click",this.onClick);break;case"focus":for(const t of this.$refs.trigger.children)T(t,"focus",this.show),T(t,"blur",this.hide)}})},focusTrigger(){for(const e of this.$refs.trigger.children)if(ot(e)){e.focus();break}}}}),oe="IAlert",Ut=c({name:oe,inheritAttrs:!1,props:{size:{type:String,default:u(oe,"size"),validator:v},color:{type:String,default:u(oe,"color")},modelValue:{type:Boolean,default:!0},dismissible:{type:Boolean,default:!1},dismissAriaLabel:{type:String,default:"Dismiss"}},emits:["update:modelValue"],data:()=>({dismissed:!1}),computed:{classes(){return{[`-${this.color}`]:!!this.color,[`-${this.size}`]:!!this.size,"-dismissible":this.dismissible,"-with-icon":!!this.$slots.icon}}},watch:{modelValue(e){this.dismissed=!e}},methods:{dismiss(){this.dismissed=!0,this.$emit("update:modelValue",!1)}}}),Ht={key:0,class:"icon",role:"img","aria-hidden":"true"},Qt={class:"content"},Yt=["aria-label"],Jt=p(Ut,[["render",function(e,t,i,a,s,r){return $((l(),o("div",h({class:["alert",e.classes],role:"alert"},e.$attrs),[e.$slots.icon?(l(),o("span",Ht,[d(e.$slots,"icon")])):f("",!0),m("div",Qt,[d(e.$slots,"default")]),e.dismissible?(l(),o("span",{key:1,class:"dismiss",role:"button","aria-label":e.dismissAriaLabel,onClick:t[0]||(t[0]=(...n)=>e.dismiss&&e.dismiss(...n))},[d(e.$slots,"dismiss",{},()=>[t[1]||(t[1]=P("×"))])],8,Yt)):f("",!0)],16)),[[x,!e.dismissed]])}]]),de="IBadge",Xt=p(c({name:de,inheritAttrs:!1,props:{color:{type:String,default:u(de,"color")},size:{type:String,default:u(de,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["badge",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),ue="IBreadcrumb",ei=c({name:ue,inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Breadcrumbs"},color:{type:String,default:u(ue,"color")},size:{type:String,default:u(ue,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),ti=["aria-label"],ii=p(ei,[["render",function(e,t,i,a,s,r){return l(),o("nav",h({class:["breadcrumb",e.classes],"aria-label":e.ariaLabel},e.$attrs),[m("ol",null,[d(e.$slots,"default")])],16,ti)}]]),ai=c({name:"IBreadcrumbItem",mixins:[Z],inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},href:{type:String,default:""},to:{type:[String,Object],default:""},tabindex:{type:[Number,String],default:0}},computed:{classes(){return{"-active":this.active,"-disabled":this.disabled}},tabIndex(){return this.disabled||this.active?-1:this.tabindex}}}),si=["is","href","to","tabindex","aria-current"],ni=p(ai,[["render",function(e,t,i,a,s,r){return l(),o("li",h({class:["breadcrumb-item",e.classes]},e.$attrs),[m("a",{is:e.isTag,href:e.href,to:e.to,tabindex:e.tabIndex,"aria-current":e.active?"location":null},[d(e.$slots,"default",{},void 0,!0)],8,si)],16)}],["__scopeId","data-v-28fa6b16"]]),ce="ILoader",li=c({name:ce,inheritAttrs:!1,props:{color:{type:String,default:u(ce,"color")},size:{type:String,default:u(ce,"size")}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),ri={key:0,class:"loader-text"},yt=p(li,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["loader",e.classes],role:"img","aria-hidden":"true"}),[e.$slots.default?(l(),o("span",ri,[d(e.$slots,"default")])):f("",!0),t[0]||(t[0]=m("svg",{viewBox:"25 25 50 50"},[m("circle",{cx:"50",cy:"50",r:"20",fill:"none","stroke-width":"4","stroke-miterlimit":"10"})],-1))],16)}]]),pe="IButton",vt=p(c({name:pe,components:{ILoader:yt},mixins:[Z],inject:{buttonGroup:{default:()=>({})},form:{default:()=>({})},formGroup:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},block:{type:Boolean,default:!1},circle:{type:Boolean,default:!1},color:{type:String,default:u(pe,"color")},disabled:{type:Boolean,default:!1},link:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},outline:{type:Boolean,default:!1},tag:{type:String,default:"button"},tabindex:{type:[Number,String],default:0},size:{type:String,default:u(pe,"size"),validator:v}},computed:{ariaBusy(){return this.role!=="button"?null:this.loading?"true":"false"},ariaDisabled(){return this.role!=="button"?null:this.disabled?"true":"false"},ariaPressed(){return this.role!=="button"?null:this.active?"true":"false"},classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-active":this.active,"-block":this.block,"-circle":this.circle,"-disabled":this.isDisabled,"-link":this.link,"-outline":this.outline}},isDisabled(){return this.disabled||this.buttonGroup.disabled||this.form.disabled||this.formGroup.disabled},role(){return this.$attrs.to||this.$attrs.href?"link":"button"},tabIndex(){return this.isDisabled?-1:this.tabindex}}}),[["render",function(e,t,i,a,s,r){const n=A("i-loader");return l(),V(Y(e.isTag),h(e.$attrs,{class:["button",e.classes],tag:e.tag,role:e.role,tabindex:e.tabIndex,disabled:e.isDisabled||e.loading,"aria-disabled":e.ariaDisabled,"aria-pressed":e.ariaPressed,"aria-busy":e.ariaBusy,"aria-live":"polite"}),{default:y(()=>[e.loading?d(e.$slots,"loading",{key:0},()=>[I(n)]):f("",!0),e.loading?f("",!0):d(e.$slots,"default",{key:1})]),_:3},16,["tag","role","tabindex","class","disabled","aria-disabled","aria-pressed","aria-busy"])}]]),oi=c({name:"IButtonGroup",inject:{form:{default:()=>({})},buttonGroup:{default:()=>({})},formGroup:{default:()=>({})}},provide(){return{buttonGroup:this}},inheritAttrs:!1,props:{vertical:{type:Boolean,default:!1},block:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1}},computed:{classes(){return{"-vertical":this.vertical,"-block":this.block,"-disabled":this.isDisabled}},isDisabled(){return this.disabled||this.buttonGroup.disabled||this.form.disabled||this.formGroup.disabled}}}),di=["aria-disabled"],ui=p(oi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["button-group",e.classes],role:"group","aria-disabled":e.isDisabled},e.$attrs),[d(e.$slots,"default")],16,di)}]]),H={};for(const e of je){e!==""&&(H[e]={type:[String,Boolean,Number],default:!1});for(const t of["first","last"])H[`${t}${Re(e)}`]={type:Boolean,default:!1};for(const t of["offset","push","pull"])H[`${t}${Re(e)}`]={type:[String,Number],default:""}}const Ze=p(c({name:"IColumn",inheritAttrs:!1,props:H,computed:{classes(){return Object.keys(H).reduce((e,t)=>(this[t]&&(e[rt(`-${t}`,this[t])]=!0),e),{})}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["column",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),Ke=p(c({name:"IContainer",inheritAttrs:!1,props:{fluid:{type:Boolean,default:!1}},computed:{classes(){return{"-fluid":this.fluid}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["container",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),he="ICard",ci=c({name:he,inheritAttrs:!1,props:{color:{type:String,default:u(he,"color")},size:{type:String,default:u(he,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),pi={key:0,class:"card-header"},hi={key:1,class:"card-body"},mi={key:2,class:"card-footer"},fi=p(ci,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["card",e.classes]},e.$attrs),[e.$slots.header?(l(),o("header",pi,[d(e.$slots,"header")])):f("",!0),d(e.$slots,"image"),e.$slots.default?(l(),o("div",hi,[d(e.$slots,"default")])):f("",!0),e.$slots.footer?(l(),o("footer",mi,[d(e.$slots,"footer")])):f("",!0)],16)}]]),me="ICheckbox",bi=c({name:me,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(me,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:!1},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("checkbox")},native:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(me,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-native":this.native}},checked(){return this.formGroup.checked?this.formGroup.checked.includes(this.value):this.schema?this.schema.value:this.modelValue},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i,a,s;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),(s=(a=this.formGroup).onChange)==null||s.call(a,this.value),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),yi=["aria-checked"],vi=["checked","name","disabled","readonly",".indeterminate"],gi=["aria-checked","aria-disabled","aria-readonly","tabindex"],ki=p(bi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["checkbox",e.classes],"aria-checked":e.checked?"true":"false",role:"checkbox"},e.$attrs),[m("input",{ref:"input",type:"checkbox",checked:e.checked,tabindex:"-1",name:e.name,disabled:e.isDisabled,readonly:e.isReadonly,".indeterminate":e.indeterminate,"aria-hidden":"true",onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,vi),m("label",{class:"checkbox-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n)),onClick:t[2]||(t[2]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,gi)],16,yi)}]]),fe="ICheckboxGroup",$i=c({name:fe,mixins:[R],provide(){return{formGroup:this}},inheritAttrs:!1,props:{color:{type:String,default:u(fe,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},modelValue:{default:()=>[]},name:{type:[String,Number],default:()=>S("checkbox-group")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(fe,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},checked(){return this.schema?this.schema.value:this.modelValue}},methods:{onChange(e){var a,s;const t=[...this.modelValue],i=t.findIndex(r=>r===e);i!==-1?t.splice(i,1):t.push(e),(s=(a=this.parent).onInput)==null||s.call(a,this.name,t),this.$emit("update:modelValue",t)}}}),wi=["name"],Ii=p($i,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["form-group checkbox-group",e.classes],name:e.name,role:"checkboxgroup"},e.$attrs),[d(e.$slots,"default",{},void 0,!0)],16,wi)}],["__scopeId","data-v-4043b2ce"]]),be="ICollapsible",xi=c({name:be,provide(){return{collapsible:this}},inheritAttrs:!1,props:{accordion:{type:Boolean,default:!1},color:{type:String,default:u(be,"color")},size:{type:String,default:u(be,"size"),validator:v},modelValue:{type:Array,default:()=>[]}},emits:["update:modelValue"],data(){return{activeItems:[].concat(this.modelValue)}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},watch:{modelValue(e){this.activeItems=[].concat(e)}},methods:{onItemClick(e){if(this.accordion)return this.activeItems=this.activeItems.indexOf(e.name)>-1?[]:[e.name],this.activeItems;const t=this.activeItems.indexOf(e.name);t>-1?this.activeItems.splice(t,1):this.activeItems.push(e.name),this.$emit("update:modelValue",this.activeItems)}}}),Ci=p(xi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["collapsible",e.classes],role:"tablist","aria-multiselectable":"true"},e.$attrs),[d(e.$slots,"default")],16)}]]),gt=p(c({name:"IExpandTransition",methods:{onEnter(e){const t=q(e,"width");e.style.width=t,e.style.position="absolute",e.style.visibility="hidden",e.style.height="auto";const i=q(e,"height");e.style.width=null,e.style.position=null,e.style.visibility=null,e.style.height=0,q(e,"height"),setTimeout(()=>{e.style.height=i})},onAfterEnter(e){e.style.height="auto"},onLeave(e){e.style.height=q(e,"height"),q(e,"height"),setTimeout(()=>{e.style.height=0})}}}),[["render",function(e,t,i,a,s,r){return l(),V(L,{name:"expand",onEnter:e.onEnter,onAfterEnter:e.onAfterEnter,onLeave:e.onLeave},{default:y(()=>[d(e.$slots,"default",{},void 0,!0)]),_:3},8,["onEnter","onAfterEnter","onLeave"])}],["__scopeId","data-v-1e395af5"]]),Si=c({name:"ICollapsibleItem",components:{IExpandTransition:gt},inject:{collapsible:{default:()=>({activeItems:[]})}},inheritAttrs:!1,props:{name:{type:String,default:()=>S("collapsible-item")},title:{type:String,default:""}},computed:{active(){return this.collapsible.activeItems.indexOf(this.name)>-1},classes(){return{"-active":this.active}}},methods:{onClick(){this.collapsible.onItemClick(this)}}}),Bi=["name"],zi=["id","aria-expanded","aria-controls","aria-describedby"],Ai=["id","aria-hidden","aria-labelledby"],Vi={class:"content"},Oi=p(Si,[["render",function(e,t,i,a,s,r){const n=A("i-expand-transition");return l(),o("div",h({class:["collapsible-item",e.classes],name:e.name},e.$attrs),[m("a",{class:"collapsible-header",role:"tab",id:`collapsible-item-heading-${e.name}`,"aria-expanded":e.active?"true":"false","aria-controls":`collapsible-item-content-${e.name}`,"aria-describedby":`collapsible-item-content-${e.name}`,tabindex:"0",onClick:t[0]||(t[0]=(...b)=>e.onClick&&e.onClick(...b)),onKeydown:[t[1]||(t[1]=E(Q((...b)=>e.onClick&&e.onClick(...b),["prevent"]),["space"])),t[2]||(t[2]=E(Q((...b)=>e.onClick&&e.onClick(...b),["prevent"]),["enter"]))]},[d(e.$slots,"header",{},()=>[P(_(e.title),1)]),t[3]||(t[3]=m("i",{class:"icon"},null,-1))],40,zi),I(n,null,{default:y(()=>[$(m("div",{class:"collapsible-body",role:"tabpanel",id:`collapsible-item-content-${e.name}`,"aria-hidden":e.active?"false":"true","aria-labelledby":`collapsible-item-heading-${e.name}`},[m("div",Vi,[d(e.$slots,"default")])],8,Ai),[[x,e.active]])]),_:3})],16,Bi)}]]),Li=(e,t)=>i=>{(function(a){return!!a&&!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)})(e)&&i.target&&(e===i.target||e.contains(i.target)||t.value(i))},K={beforeMount(e,t){typeof window<"u"&&O(window.document,"mousedown",Li(e,t))}},ye="IDropdown",Di=c({name:ye,directives:{ClickOutside:K},mixins:[ee,Ge],provide(){return{dropdown:this}},inject:{navbar:{default:()=>({onItemClick:()=>{}})},sidebar:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{animationDuration:{type:Number,default:300},color:{type:String,default:u(ye,"color")},disabled:{type:Boolean,default:!1},hideOnItemClick:{type:Boolean,default:!0},keydownTrigger:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},keydownItem:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},modelValue:{type:Boolean,default:!1},arrow:{type:Boolean,default:!0},placement:{type:String,default:"bottom"},trigger:{type:[String,Array],default:()=>["click"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!0},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(ye,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},mounted(){for(const e of this.$refs.trigger.children)O(e,"keydown",this.onTriggerKeyDown);O(this.$refs.popup,"keydown",this.onItemKeyDown)},beforeUnmount(){for(const e of this.$refs.trigger.children)T(e,"keydown",this.onTriggerKeyDown);T(this.$refs.popup,"keydown",this.onItemKeyDown)},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)},getFocusableItems(){const e=[];for(const t of this.$refs.body.children)Fe(t)&&e.push(t);return e},onTriggerKeyDown(e){if(this.keydownTrigger.length===0)return;const t=this.getFocusableItems(),i=t.findIndex(s=>s.active),a=t[i>-1?i:0];switch(!0){case(k("up",e)&&this.keydownTrigger.includes("up")):case(k("down",e)&&this.keydownTrigger.includes("down")):this.show(),setTimeout(()=>{a.focus()},this.visible?0:this.animationDuration),e.preventDefault(),e.stopPropagation();break;case(k("enter",e)&&this.keydownTrigger.includes("enter")):case(k("space",e)&&this.keydownTrigger.includes("space")):this.onClick(),this.visible||setTimeout(()=>{a.focus()},this.animationDuration),e.preventDefault();break;case(k("tab",e)&&this.keydownTrigger.includes("tab")):case(k("esc",e)&&this.keydownTrigger.includes("esc")):this.hide()}},onItemKeyDown(e){if(this.keydownItem.length!==0)switch(!0){case(k("up",e)&&this.keydownItem.includes("up")):case(k("down",e)&&this.keydownItem.includes("down")):const t=this.getFocusableItems(),i=t.findIndex(r=>r===e.target),a=t.length-1;let s;s=k("up",e)?i>0?i-1:0:i{e.onItemClick()})}}}),Ni={class:"dropdown-trigger",ref:"trigger"},Ti=["aria-hidden"],Pi={key:0,"data-popper-arrow":""},Ri={key:1,class:"dropdown-header"},Ei={key:2,class:"dropdown-body",ref:"body"},Mi={key:3,class:"dropdown-footer"},_i=p(Di,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h({class:"dropdown-wrapper",ref:"wrapper","aria-haspopup":"true",onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))},e.$attrs),[m("div",Ni,[d(e.$slots,"default")],512),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:z(["dropdown",e.classes]),role:"menu",ref:"popup","aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",Pi)):f("",!0),e.$slots.header?(l(),o("div",Ri,[d(e.$slots,"header")])):f("",!0),e.$slots.body?(l(),o("div",Ei,[d(e.$slots,"body")],512)):f("",!0),e.$slots.footer?(l(),o("div",Mi,[d(e.$slots,"footer")])):f("",!0)],10,Ti),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16)),[[n,e.onClickOutside]])}]]),Fi=p(c({name:"IDropdownDivider",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:"dropdown-divider",role:"separator"},e.$attrs),null,16)}]]),ji=p(c({name:"IDropdownItem",mixins:[Z],inject:{dropdown:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},plaintext:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{classes(){return{"-active":this.active,"-disabled":this.disabled,"-plaintext":this.plaintext}},role(){return this.$attrs.to||this.$attrs.href?"link":"menuitem"},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(e){var t,i;(i=(t=this.dropdown).onItemClick)==null||i.call(t,this,e)}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["dropdown-item",e.classes],role:e.role,tag:e.tag,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.disabled,"aria-pressed":e.active,onClick:e.onClick}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["class","role","tag","tabindex","disabled","aria-disabled","aria-pressed","onClick"])}]]),Xe="IForm",Gi=c({name:Xe,mixins:[R],provide(){return{form:this}},inheritAttrs:!1,props:{color:{type:String,default:""},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},name:{type:String,default:()=>S("form")},modelValue:{type:Object,default:()=>null},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Xe,"size"),validator:v}},emits:["update:modelValue","submit"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},schema(){return this.modelValue?this.modelValue:M(this.formGroup.schema||this.form.schema||{},this.name)}},methods:{onBlur(e,t){var i,a;if((a=(i=this.parent).onBlur)==null||a.call(i,this.name?`${this.name}.${e}`:e,t),this.modelValue){let s=U(this.modelValue);s=ne(s,e,{untouched:!1,touched:!0}),this.shouldValidate(e,"blur")&&(s=le(s)),this.$emit("update:modelValue",s)}},onInput(e,t){var i,a;if((a=(i=this.parent).onInput)==null||a.call(i,this.name?`${this.name}.${e}`:e,t),this.modelValue){let s=U(this.modelValue);s=function(r,n,b,B){return M(r,n)[b]=B,r}(s,e,"value",t),s=ne(s,e,{pristine:!1,dirty:!0}),this.shouldValidate(e,"input")&&(s=le(s)),this.$emit("update:modelValue",s)}},onSubmit(e){if(e.preventDefault(),this.modelValue){let t=U(this.modelValue);if(t=ne(le(t),"",{untouched:!1,touched:!0}),this.$emit("update:modelValue",t),t.invalid)return}this.$emit("submit",e)},shouldValidate(e,t){const i=M(this.modelValue,e);return(i.validateOn?[].concat(i.validateOn):this.$inkline.options.validateOn).includes(t)}}}),Zi=["name","readonly","disabled"],Ki=p(Gi,[["render",function(e,t,i,a,s,r){return l(),o("form",h(e.$attrs,{class:["form",e.classes],role:"form",name:e.name,readonly:e.isReadonly,disabled:e.isDisabled,onSubmit:t[0]||(t[0]=(...n)=>e.onSubmit&&e.onSubmit(...n))}),[d(e.$slots,"default",{},void 0,!0)],16,Zi)}],["__scopeId","data-v-575ccba7"]]),ve="IFormGroup",qi=c({name:ve,mixins:[R],provide(){return{formGroup:this}},props:{color:{type:String,default:u(ve,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},name:{type:String,default:""},readonly:{type:Boolean,default:!1},required:{type:Boolean,default:!1},size:{type:String,default:u(ve,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline,"-required":this.required}}},methods:{onBlur(e,t){var i,a;(a=(i=this.parent)==null?void 0:i.onBlur)==null||a.call(i,this.name?`${this.name}.${e}`:e,t)},onInput(e,t){var i,a;(a=(i=this.parent)==null?void 0:i.onInput)==null||a.call(i,this.name?`${this.name}.${e}`:e,t)}}}),Wi=["name"],Ui=p(qi,[["render",function(e,t,i,a,s,r){return l(),o("fieldset",{class:z(["form-group",e.classes]),name:e.name,role:"group"},[d(e.$slots,"default")],10,Wi)}]]),Hi=c({name:"IFormError",inject:{formGroup:{default:()=>({})},form:{default:()=>({})}},props:{for:{type:String,default:""},visible:{type:[Array,String],default:()=>["touched","dirty","invalid"]}},computed:{parent(){return this.formGroup.$?this.formGroup:this.form},schema(){return this.for!==""?M(this.parent.schema||{},`${this.for}`):this.parent.schema||{}},errors(){return this.schema.errors||[]},isVisible(){let e=!0;return this.schema&&this.visible&&[].concat(this.visible).forEach(t=>{e=e&&this.schema[t]}),e}}}),Qi={key:0,class:"form-error","aria-live":"polite"},Yi=p(Hi,[["render",function(e,t,i,a,s,r){return e.schema?$((l(),V(L,{key:0,name:"fade-in-transition"},{default:y(()=>[e.errors.length>0?(l(),o("ul",Qi,[(l(!0),o(j,null,X(e.errors,n=>(l(),o("li",null,_(n.message),1))),256))])):f("",!0)]),_:1},512)),[[x,e.isVisible]]):f("",!0)}]]),et="IFormLabel",Ji=c({name:et,mixins:[R],props:{for:{type:String,default:""},placement:{type:String,default:""},size:{type:String,default:u(et,"size"),validator:v}},computed:{classes(){return{[`-${this.size}`]:!!this.size,[`-${this.placement}`]:!!this.placement}},forAttr(){return this.for}},methods:{getNextSibling(){return this.$el.nextSibling.querySelector("input, textarea")},onClick(){var e;this.for||((e=this.getNextSibling())==null||e.focus())}}}),Xi=["for"],ea=p(Ji,[["render",function(e,t,i,a,s,r){return l(),o("label",h(e.$attrs,{class:["form-label",e.classes],for:e.forAttr,onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),[d(e.$slots,"default")],16,Xi)}]]),tt="IHamburgerMenu",kt=p(c({name:tt,inheritAttrs:!1,props:{animation:{type:String,default:"close"},color:{type:String,default:u(tt,"color")},modelValue:{type:Boolean,default:!1}},emits:["update:modelValue"],computed:{classes(){return{...g(this),"-active":this.modelValue,[`-${this.animation}`]:!0}}},methods:{onClick(){this.$emit("update:modelValue",!this.modelValue)}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["hamburger-menu",e.classes],onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),t[1]||(t[1]=[m("span",{class:"hamburger-menu-bars"},null,-1)]),16)}]]),Me={};for(const e of je)for(const t of["start","center","end","top","middle","bottom","around","between","reverse"])Me[`${t}${Re(e)}`]={type:Boolean,default:!1};const qe=p(c({name:"IRow",inheritAttrs:!1,props:{noGutter:{type:Boolean,default:!1},noCollapse:{type:Boolean,default:!1},...Me},computed:{classes(){const e=Object.keys(Me).reduce((t,i)=>(this[i]&&(t[rt(`-${i}`,this[i])]=!0),t),{});return{"-no-gutter":this.noGutter,"-no-collapse":this.noCollapse,...e}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["row",e.classes]}),[d(e.$slots,"default")],16)}]]),ge="IHeader",ta=p(c({name:ge,components:{IContainer:Ke,IRow:qe,IColumn:Ze},inheritAttrs:!1,props:{color:{type:String,default:u(ge,"color")},cover:{type:Boolean,default:!1},fluid:{type:Boolean,default:!1},fullscreen:{type:Boolean,default:!1},size:{type:String,default:u(ge,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-cover":this.cover,"-fullscreen":this.fullscreen}}}}),[["render",function(e,t,i,a,s,r){const n=A("i-column"),b=A("i-row"),B=A("i-container");return l(),o("header",h(e.$attrs,{class:["header",e.classes]}),[I(B,{fluid:e.fluid},{default:y(()=>[I(b,null,{default:y(()=>[I(n,null,{default:y(()=>[d(e.$slots,"default")]),_:3})]),_:3})]),_:3},8,["fluid"])],16)}]]),it="IIcon",$t=p(c({name:it,inheritAttrs:!1,props:{name:{type:String,default:""},size:{type:String,default:u(it,"size"),validator:v}},setup(e){const t=Vt("inklineIcons"),i=se(()=>function(r,n="dash"){const b=n==="dash"?/-([a-z0-9])/g:/_([a-z0-9])/g;return r.replace(b,(B,F)=>F.toUpperCase())}(e.name)),a=se(()=>t[i.value]),s=se(()=>({"inkline-icon":!0,[`-${e.size}`]:!!e.size}));return Ot(()=>{i.value&&t[i.value]}),()=>{var r,n;return nt("svg",{class:s.value,...(r=a.value)==null?void 0:r.attributes},ct(((n=a.value)==null?void 0:n.children)||[]))}}}),[["render",function(e,t,i,a,s,r){const n=A("icon");return l(),V(n,h(e.$attrs,{size:e.size}),null,16,["size"])}]]),ke="IInput",ia=c({name:ke,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(ke,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},error:{type:[Array,Boolean],default:()=>["touched","dirty","invalid"]},id:{type:String,default:void 0},modelValue:{type:[String,Number],default:""},name:{type:[String,Number],default:()=>S("input")},plaintext:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(ke,"size"),validator:v},tabindex:{type:[Number,String],default:0},type:{type:String,default:"text"},clearAriaLabel:{type:String,default:"Clear"}},emits:["update:modelValue","clear"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-error":this.hasError,"-readonly":this.isReadonly,"-prefixed":!!this.$slots.prefix,"-suffixed":!!this.$slots.suffix,"-prepended":!!this.$slots.prepend,"-appended":!!this.$slots.append}},hasError(){if(typeof this.error=="boolean")return this.error;if(this.schema&&this.error){let e=!0;return[].concat(this.error).forEach(t=>{e=e&&this.schema[t]}),e}return!1},tabIndex(){return this.isDisabled?-1:this.tabindex},isClearable(){return this.clearable&&!this.isDisabled&&!this.isReadonly&&this.value!==""},value(){return this.schema?this.schema.value:this.modelValue}},methods:{onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)},onInput(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.value),this.$emit("update:modelValue",e.target.value)},onClear(e){this.$emit("update:modelValue",""),this.$emit("clear",e)},focus(){this.$refs.input.focus()}}}),aa={key:0,class:"input-prepend"},sa={class:"input"},na={key:0,class:"input-prefix"},la=["value","name","id","type","tabindex","disabled","aria-disabled","readonly","aria-readonly"],ra={key:1,class:"input-suffix"},oa=["aria-label","aria-hidden"],da={key:1,class:"input-append"},te=p(ia,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["input-wrapper",e.classes])},[e.$slots.prepend?(l(),o("div",aa,[d(e.$slots,"prepend")])):f("",!0),m("div",sa,[e.$slots.prefix?(l(),o("span",na,[d(e.$slots,"prefix")])):f("",!0),m("input",h(e.$attrs,{value:e.value,ref:"input",name:e.name,id:e.id,type:e.type,tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly||e.plaintext,"aria-readonly":!(!e.isReadonly&&!e.plaintext)&&"true",onInput:t[0]||(t[0]=(...n)=>e.onInput&&e.onInput(...n)),onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n))}),null,16,la),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",ra,[d(e.$slots,"clearable",{clear:e.onClear},()=>[e.clearable?$((l(),o("i",{key:0,class:"input-clear",role:"button","aria-label":e.clearAriaLabel,"aria-hidden":e.isClearable?"false":"true",onClick:t[2]||(t[2]=(...n)=>e.onClear&&e.onClear(...n))},null,8,oa)),[[x,e.isClearable]]):f("",!0)]),d(e.$slots,"suffix")])):f("",!0)]),e.$slots.append?(l(),o("div",da,[d(e.$slots,"append")])):f("",!0)],2)}]]),$e="INumberInput",ua=c({name:$e,components:{IButton:vt},extends:te,inheritAttrs:!1,props:{color:{type:String,default:u($e,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},id:{type:String,default:""},modelValue:{type:[String,Number],default:""},name:{type:[String,Number],default:()=>S("input")},readonly:{type:Boolean,default:!1},size:{type:String,default:u($e,"size"),validator:v},tabindex:{type:[Number,String],default:0},min:{type:[Number,String],default:-1/0},max:{type:[Number,String],default:1/0},precision:{type:Number,default:0},step:{type:Number,default:1}},emits:["update:modelValue"],watch:{modelValue:{immediate:!0,handler(e){var i,a;let t=(e||"").toString().replace(/^[^0-9-]/,"").replace(/^(-)[^0-9]/,"$1").replace(new RegExp(`^(-?[0-9]+)[^0-9${this.precision>0?".":""}]`),"$1");this.precision>0&&(t=t.replace(/^(-?[0-9]+\.)[^0-9]/,"$1").replace(new RegExp(`^(-?[0-9]+\\.[0-9]{0,${this.precision}}).*`),"$1")),parseFloat(t)>=parseFloat(this.max)&&(t=this.max.toString()),parseFloat(t)<=parseFloat(this.min)&&(t=this.min.toString()),(a=(i=this.parent).onInput)==null||a.call(i,this.name,t),this.$emit("update:modelValue",t)}}},methods:{decrease(){this.$emit("update:modelValue",this.formatPrecision((Number(this.modelValue)-this.step).toString()))},increase(){this.$emit("update:modelValue",this.formatPrecision((Number(this.modelValue)+this.step).toString()))},formatPrecision(e){const t=e.split(".");let i=t[1]||"";for(let a=i.length;a0?`${t[0]}.${i}`:t[0]},onBlurFormatPrecision(e){var t,i;this.$emit("update:modelValue",this.formatPrecision(Number(this.modelValue).toString())),(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),ca={class:"input-prepend"},pa={class:"input"},ha={key:0,class:"input-prefix"},ma=["value","name","id","tabindex","disabled","aria-disabled","readonly","aria-readonly"],fa={key:1,class:"input-suffix"},ba=["aria-label","aria-hidden"],ya={class:"input-append"},va=p(ua,[["render",function(e,t,i,a,s,r){const n=A("i-button");return l(),o("div",{class:z(["input-wrapper -prepended -appended",e.classes])},[m("div",ca,[d(e.$slots,"prepend"),I(n,{type:"button",color:e.color,size:e.size,disabled:e.disabled,class:"input-button-decrease",onClick:e.decrease},{default:y(()=>t[3]||(t[3]=[P(" - ")])),_:1},8,["color","size","disabled","onClick"])]),m("div",pa,[e.$slots.prefix?(l(),o("span",ha,[d(e.$slots,"prefix")])):f("",!0),m("input",h(e.$attrs,{value:e.value,ref:"input",name:e.name,id:e.id,type:"text",tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly,"aria-readonly":!!e.isReadonly&&"true",onInput:t[0]||(t[0]=(...b)=>e.onInput&&e.onInput(...b)),onBlur:t[1]||(t[1]=(...b)=>e.onBlur&&e.onBlur(...b))}),null,16,ma),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",fa,[d(e.$slots,"clearable",{clear:e.onClear},()=>[e.clearable?$((l(),o("i",{key:0,class:"input-clear",role:"button","aria-label":e.clearAriaLabel,"aria-hidden":e.isClearable?"false":"true",onClick:t[2]||(t[2]=(...b)=>e.onClear&&e.onClear(...b))},null,8,ba)),[[x,e.isClearable]]):f("",!0)]),d(e.$slots,"suffix")])):f("",!0)]),m("div",ya,[I(n,{type:"button",color:e.color,size:e.size,disabled:e.disabled,class:"input-button-increase",onClick:e.increase},{default:y(()=>t[4]||(t[4]=[P(" + ")])),_:1},8,["color","size","disabled","onClick"]),d(e.$slots,"append")])],2)}]]),we="ITextarea",ga=c({name:we,extends:te,inheritAttrs:!1,props:{color:{type:String,default:u(we,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},id:{type:String,default:""},modelValue:{type:String,default:""},name:{type:[String,Number],default:()=>S("textarea")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(we,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"]}),ka={key:0,class:"input-prepend"},$a={class:"input"},wa={key:0,class:"input-prefix"},Ia=["value","name","id","tabindex","disabled","aria-disabled","readonly","aria-readonly"],xa={key:1,class:"input-suffix"},Ca={key:1,class:"input-append"},Sa=p(ga,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["input-wrapper",e.classes])},[e.$slots.prepend?(l(),o("div",ka,[d(e.$slots,"prepend")])):f("",!0),m("div",$a,[e.$slots.prefix?(l(),o("span",wa,[d(e.$slots,"prefix")])):f("",!0),m("textarea",h(e.$attrs,{value:e.value,ref:"input",role:"textbox",name:e.name,id:e.id,tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly,"aria-readonly":!!e.isReadonly&&"true","aria-multiline":"true",onInput:t[0]||(t[0]=(...n)=>e.onInput&&e.onInput(...n)),onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n))}),null,16,Ia),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",xa,[d(e.$slots,"clearable",{clear:e.onClear},()=>[$(m("i",{class:"input-clear","aria-label":"Clear",onClick:t[2]||(t[2]=(...n)=>e.onClear&&e.onClear(...n))},null,512),[[x,e.isClearable]])]),d(e.$slots,"suffix")])):f("",!0)]),e.$slots.append?(l(),o("div",Ca,[d(e.$slots,"append")])):f("",!0)],2)}]]),Ba=p(c({name:"ILayout",inheritAttrs:!1,props:{vertical:{type:Boolean,default:!1}},computed:{classes(){return{"-vertical":this.vertical}}}}),[["render",function(e,t,i,a,s,r){return l(),o("main",h(e.$attrs,{class:["layout",e.classes]}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-9a9f03c1"]]),za=c({name:"ILayoutAside",inheritAttrs:!1}),Aa={class:"layout-aside-children"},Va=p(za,[["render",function(e,t,i,a,s,r){return l(),o("aside",h(e.$attrs,{class:"layout-aside"}),[m("div",Aa,[d(e.$slots,"default",{},void 0,!0)])],16)}],["__scopeId","data-v-6e13c28d"]]),Oa=p(c({name:"ILayoutContent",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("section",h(e.$attrs,{class:"layout-content"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-5b197a5d"]]),La=p(c({name:"ILayoutFooter",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("footer",h(e.$attrs,{class:"layout-footer"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-31bd8dbc"]]),Da=p(c({name:"ILayoutHeader",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("header",h(e.$attrs,{class:"layout-header"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-ece9ef9b"]]),Ie="IListGroup",Na=p(c({name:Ie,inheritAttrs:!1,props:{border:{type:Boolean,default:!0},color:{type:String,default:u(Ie,"color")},size:{type:String,default:u(Ie,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-border":this.border}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["list-group",e.classes],role:"list"}),[d(e.$slots,"default")],16)}]]),Ta=p(c({name:"IListGroupItem",mixins:[Z],inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{ariaDisabled(){return this.role==="link"?null:this.disabled?"true":"false"},classes(){return{"-active":this.active,"-disabled":this.disabled}},role(){return this.$attrs.to||this.$attrs.href?"link":"listitem"},tabIndex(){return this.disabled?-1:this.tabindex}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["list-group-item",e.classes],tag:e.tag,role:e.role,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.ariaDisabled}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["tag","role","tabindex","class","disabled","aria-disabled"])}]]),Pa=function(e){const t={};return(...i)=>{const a=JSON.stringify(i);return a in t||(t[a]=e(...i)),t[a]}}(function(e,t){if(!t)return[{text:e}];const i=[],a=e.toLowerCase(),s=t.toLowerCase();let r=0,n=0;for(;n=0;n=B?b:e.length,n&&(i.push({text:e.substring(r,n)}),r=n),B&&(n+=t.length,i.push({text:e.substring(r,n),marked:!0}),r=n)}return i}),Ra=c({name:"IMark",inheritAttrs:!1,props:{text:{type:String,default:""},query:{type:String,default:""}},computed:{parts(){return Pa(this.text,this.query)}}}),Ea={key:0},wt=p(Ra,[["render",function(e,t,i,a,s,r){return l(),o("span",Lt(Dt(e.$attrs)),[(l(!0),o(j,null,X(e.parts,({text:n,marked:b})=>(l(),o(j,null,[b?(l(),o("mark",Ea,_(n),1)):(l(),o(j,{key:1},[P(_(n),1)],64))],64))),256))],16)}]]),Ma=c({name:"IMedia",inheritAttrs:!1}),_a={class:"media-body"},Fa=p(Ma,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:"media"}),[d(e.$slots,"image"),m("div",_a,[d(e.$slots,"default")])],16)}]]),C={instances:{},stack:[],zIndex:1050,register(e){e&&e.name&&(C.instances[e.name]=e)},unregister(e){e&&e.name&&(C.instances[e.name]=null,delete C.instances[e.name])},open(e){typeof window<"u"&&(C.stack.push(e),C.instances[e].$el.style.zIndex=C.zIndex++)},close(e){typeof window<"u"&&C.stack.splice(C.stack.indexOf(e),1)},getTopOverlay(){const e=C.stack.slice(-1)[0]||"";return C.instances[e]},onPressEscape(){const e=C.getTopOverlay();e&&e.closeOnPressEscape&&e.hide()}};typeof window<"u"&&window.addEventListener("keydown",e=>{k("esc",e)&&C.onPressEscape()});const xe="IModal",ja=c({name:xe,directives:{ClickOutside:K},inheritAttrs:!1,props:{closeOnPressEscape:{type:Boolean,default:!0},closeAriaLabel:{type:String,default:"Close"},color:{type:String,default:u(xe,"color")},disabled:{type:Boolean,default:!1},hideOnClickOutside:{type:Boolean,default:!0},name:{type:String,default:()=>S("modal")},showClose:{type:Boolean,default:!0},size:{type:String,default:u(xe,"size"),validator:v},modelValue:{type:Boolean,default:!1},transition:{type:String,default:"zoom-in-center-transition"}},emits:["update:modelValue"],data(){return{visible:this.modelValue}},computed:{classes(){return{"-disabled":this.disabled,...g(this),[`-${this.size}`]:!!this.size}}},watch:{modelValue(e){e?this.show():this.hide()}},mounted(){C.register(this)},unmounted(){C.unregister(this)},methods:{show(){this.disabled||(this.visible=!0,this.$emit("update:modelValue",!0),C.open(this.name),typeof window<"u"&&_e(window.document.body,"-modal"))},hide(){this.disabled||(this.visible=!1,this.$emit("update:modelValue",!1),C.close(this.name),typeof window<"u"&&pt(window.document.body,"-modal"))},onClickOutside(){this.hideOnClickOutside&&this.hide()}}}),Ga=["aria-hidden","id","name","aria-labelledby"],Za={class:"modal"},Ka=["id"],qa=["aria-label"],Wa={key:1,class:"modal-body"},Ua={key:2,class:"modal-footer"},Ha=p(ja,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return l(),V(L,{name:"fade-in-transition"},{default:y(()=>[$(m("div",h(e.$attrs,{class:["modal-wrapper",e.classes],role:"dialog","aria-modal":"true","aria-hidden":e.visible?"false":"true",id:e.name,name:e.name,"aria-labelledby":`${e.name}-header`}),[I(L,{name:e.transition},{default:y(()=>[$((l(),o("div",Za,[e.$slots.header?(l(),o("div",{key:0,class:"modal-header",id:`${e.name}-header`},[d(e.$slots,"header"),e.showClose?(l(),o("button",{key:0,class:"close","aria-hidden":"true","aria-label":e.closeAriaLabel,onClick:t[0]||(t[0]=(...b)=>e.hide&&e.hide(...b))},[d(e.$slots,"close",{},()=>[t[1]||(t[1]=m("i",{class:"icon"},null,-1))])],8,qa)):f("",!0)],8,Ka)):f("",!0),e.$slots.default?(l(),o("div",Wa,[d(e.$slots,"default")])):f("",!0),e.$slots.footer?(l(),o("div",Ua,[d(e.$slots,"footer")])):f("",!0)])),[[n,e.onClickOutside],[x,e.visible]])]),_:3},8,["name"])],16,Ga),[[x,e.visible]])]),_:3})}]]),Ce="INav",Qa=p(c({name:Ce,provide(){return{nav:this}},inject:{navbar:{default:()=>({onItemClick:()=>{}})},sidebar:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{color:{type:String,default:u(Ce,"color")},size:{type:String,default:u(Ce,"size"),validator:v},vertical:{type:Boolean,default:!1}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-vertical":this.vertical}}},methods:{onItemClick(){[this.navbar,this.sidebar].forEach(e=>{e.onItemClick()})}}}),[["render",function(e,t,i,a,s,r){return l(),o("nav",h(e.$attrs,{class:["nav",e.classes],role:"menubar"}),[d(e.$slots,"default")],16)}]]),Ya=p(c({name:"INavItem",mixins:[Z],inject:{nav:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},stopPropagation:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{ariaDisabled(){return this.role==="link"?null:this.disabled?"true":"false"},classes(){return{"-active":this.active,"-disabled":this.disabled}},role(){return this.$attrs.to||this.$attrs.href?"link":"menuitem"},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(e){this.stopPropagation||this.nav.onItemClick(this,e)}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["nav-item",e.classes],role:e.role,tag:e.tag,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.ariaDisabled,onClick:e.onClick}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["role","tag","tabindex","class","disabled","aria-disabled","onClick"])}]]),Se="INavbar",Ja=p(c({name:Se,components:{IContainer:Ke,IRow:qe,IColumn:Ze,IHamburgerMenu:kt},directives:{ClickOutside:K},mixins:[ft],provide(){return{navbar:this}},inheritAttrs:!1,props:{collapseOnItemClick:{type:Boolean,default:!0},collapseOnClickOutside:{type:Boolean,default:!0},color:{type:String,default:u(Se,"color")},fluid:{type:Boolean,default:!1},size:{type:String,default:u(Se,"size"),validator:v},menuAnimation:{type:String,default:"close"}},emits:["update:modelValue"],computed:{classes(){return{...this.collapsibleClasses,...g(this),[`-${this.size}`]:!!this.size}}},methods:{onItemClick(){this.collapseOnItemClick&&this.open&&this.setOpen(!1)},onClickOutside(){this.collapseOnClickOutside&&this.open&&this.setOpen(!1)}}}),[["render",function(e,t,i,a,s,r){const n=A("i-hamburger-menu"),b=A("i-column"),B=A("i-row"),F=A("i-container"),w=G("click-outside");return $((l(),o("nav",h(e.$attrs,{class:["navbar",e.classes]}),[I(F,{fluid:e.fluid},{default:y(()=>[I(B,null,{default:y(()=>[I(b,null,{default:y(()=>[I(n,{class:"collapse-toggle",animation:e.menuAnimation,color:e.color,modelValue:e.open,"onUpdate:modelValue":e.toggleOpen},null,8,["animation","color","modelValue","onUpdate:modelValue"]),d(e.$slots,"default")]),_:3})]),_:3})]),_:3},8,["fluid"])],16)),[[w,e.onClickOutside]])}]]),Xa=p(c({name:"INavbarBrand",mixins:[Z],inheritAttrs:!1,props:{tag:{type:String,default:"div"}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:"navbar-brand",tag:e.tag,translate:"no"}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["tag"])}]]),es=c({name:"INavbarCollapsible",components:{IExpandTransition:gt},inject:{navbar:{default:()=>({})}},inheritAttrs:!1,computed:{visible(){const e=typeof window>"u";return this.navbar.open||!this.navbar.collapsible||e}}}),ts=["aria-hidden","aria-expanded"],is=p(es,[["render",function(e,t,i,a,s,r){const n=A("i-expand-transition");return l(),V(n,null,{default:y(()=>[$(m("div",h(e.$attrs,{class:"navbar-collapsible","aria-hidden":e.visible?"false":"true","aria-expanded":e.visible?"true":"false"}),[d(e.$slots,"default")],16,ts),[[x,e.visible]])]),_:3})}]]),Be="IPagination",as=c({name:Be,inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Pagination"},color:{type:String,default:u(Be,"color")},itemsPerPage:{type:Number,default:20},itemsTotal:{type:Number,default:0},limit:{type:[Number,Object],default:()=>({xs:3,sm:5})},quickLink:{type:Boolean,default:!1},modelValue:{type:Number,default:1},size:{type:String,default:u(Be,"size"),validator:v}},emits:["update:modelValue"],data:()=>({pageLimit:5}),computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}},pageCount(){return Math.ceil(this.itemsTotal/this.itemsPerPage)},showQuickPrevious(){return this.pageCount>this.pageLimit&&this.modelValue>this.pageLimit-(this.pageLimit-1)/2},showQuickNext(){return this.pageCount>this.pageLimit&&this.modelValuee.apply(s,a),t)}}(this.onWindowResize,250),typeof window<"u"&&(window.addEventListener("resize",this.debouncedOnWindowResize),this.onWindowResize())},unmounted(){typeof window<"u"&&window.removeEventListener("resize",this.debouncedOnWindowResize)},methods:{next(){this.modelValue!==this.pageCount&&this.onClick(this.modelValue+1)},quickNext(){if(!this.quickLink)return;const e=this.modelValue+(this.pageLimit-2);this.onClick(e>this.pageCount?this.pageCount:e)},previous(){this.modelValue!==1&&this.onClick(this.modelValue-1)},quickPrevious(){if(!this.quickLink)return;const e=this.modelValue-(this.pageLimit-2);this.onClick(e<1?1:e)},onClick(e){this.$emit("update:modelValue",e)},onWindowResize(){if(typeof this.limit=="number")return this.pageLimit=this.limit,this.pageLimit;for(const e of je.slice().reverse())if(this.limit.hasOwnProperty(e)&&typeof window<"u"&&window.innerWidth>=J[e][0])return this.pageLimit=this.limit[e],this.pageLimit}}}),ss=["aria-label"],ns={class:"pagination-items"},ls={"aria-hidden":"true"},rs=["aria-current","onClick"],os={"aria-hidden":"true"},ds=p(as,[["render",function(e,t,i,a,s,r){return l(),o("nav",h(e.$attrs,{class:["pagination",e.classes],role:"navigation","aria-label":e.ariaLabel}),[m("ul",ns,[e.pageCount>0?(l(),o("li",{key:0,class:z(["pagination-item -previous",{"-disabled":e.modelValue===1}]),onClick:t[0]||(t[0]=(...n)=>e.previous&&e.previous(...n))},[m("span",ls,[d(e.$slots,"previous",{},()=>[t[6]||(t[6]=P("<"))])])],2)):f("",!0),e.pageCount>0?(l(),o("li",{key:1,class:z(["pagination-item -first",{"-active":e.modelValue===1}]),onClick:t[1]||(t[1]=n=>e.onClick(1))}," 1 ",2)):f("",!0),e.showQuickPrevious?(l(),o("li",{key:2,class:z(["pagination-item -quick-previous",{"-disabled":!e.quickLink}]),onClick:t[2]||(t[2]=(...n)=>e.quickPrevious&&e.quickPrevious(...n))}," … ",2)):f("",!0),(l(!0),o(j,null,X(e.pages,n=>(l(),o("li",{class:z(["pagination-item",{"-active":e.modelValue===n}]),"aria-current":e.modelValue===n&&"page",onClick:b=>e.onClick(n)},_(n),11,rs))),256)),e.showQuickNext?(l(),o("li",{key:3,class:z(["pagination-item -quick-next",{"-disabled":!e.quickLink}]),onClick:t[3]||(t[3]=(...n)=>e.quickNext&&e.quickNext(...n))}," … ",2)):f("",!0),e.pageCount>1?(l(),o("li",{key:4,class:z(["pagination-item -last",{"-active":e.modelValue===e.pageCount}]),onClick:t[4]||(t[4]=n=>e.onClick(e.pageCount))},_(e.pageCount),3)):f("",!0),e.pageCount>0?(l(),o("li",{key:5,class:z(["pagination-item -next",{"-disabled":e.modelValue===e.pageCount}]),onClick:t[5]||(t[5]=(...n)=>e.next&&e.next(...n))},[m("span",os,[d(e.$slots,"next",{},()=>[t[7]||(t[7]=P(">"))])])],2)):f("",!0)])],16,ss)}]]),ze="IPopover",us=c({name:ze,directives:{ClickOutside:K},mixins:[ee,Ge],inheritAttrs:!1,props:{color:{type:String,default:u(ze,"color")},disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:!1},name:{type:String,default:()=>S("popover")},arrow:{type:Boolean,default:!0},placement:{type:String,default:"top"},trigger:{type:[String,Array],default:()=>["click"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!1},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(ze,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)}}}),cs=["id"],ps=["aria-describedby","aria-disabled","aria-expanded"],hs=["id","aria-hidden"],ms={key:0,"data-popper-arrow":""},fs={key:1,class:"popover-header"},bs={key:2,class:"popover-body"},ys={key:3,class:"popover-footer"},vs=p(us,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["popover-wrapper",e.classes],ref:"wrapper",id:e.name,onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))}),[m("div",{class:"popover-trigger",ref:"trigger","aria-describedby":`${e.name}-popup`,"aria-disabled":e.disabled?"true":"false","aria-expanded":e.visible?"true":"false"},[d(e.$slots,"default")],8,ps),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:"popover",ref:"popup",role:"tooltip","aria-live":"polite",id:`${e.name}-popup`,"aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",ms)):f("",!0),e.$slots.header?(l(),o("div",fs,[d(e.$slots,"header")])):f("",!0),e.$slots.body?(l(),o("div",bs,[d(e.$slots,"body")])):f("",!0),e.$slots.footer?(l(),o("div",ys,[d(e.$slots,"footer")])):f("",!0)],8,hs),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,cs)),[[n,e.onClickOutside]])}]]),Ae="IProgress",gs=p(c({name:Ae,provide(){return{progress:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Ae,"color")},min:{type:[String,Number],default:0},max:{type:[String,Number],default:100},size:{type:String,default:u(Ae,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["progress",e.classes]}),[d(e.$slots,"default")],16)}]]),at="IProgressBar",ks=c({name:at,inject:{progress:{default:()=>({min:0,max:100})}},inheritAttrs:!1,props:{color:{type:String,default:u(at,"color","primary")},value:{type:[String,Number],default:0}},computed:{computedValue(){const e=typeof this.min=="string"?parseFloat(this.min):this.min;return 100*((typeof this.value=="string"?parseFloat(this.value.replace("%","")):this.value)-e)/((typeof this.max=="string"?parseFloat(this.max):this.max)-e)},min(){return this.progress.min},max(){return this.progress.max},style(){return{width:`${this.computedValue}%`}},classes(){return{...g(this)}}}}),$s=["aria-valuemin","aria-valuemax","aria-valuenow"],ws=p(ks,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["progress-bar",e.classes],style:e.style,role:"progressbar","aria-valuemin":e.min,"aria-valuemax":e.max,"aria-valuenow":e.computedValue}),[d(e.$slots,"default")],16,$s)}]]),Ve="IRadio",Is=c({name:Ve,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(Ve,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:""},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("radio")},native:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Ve,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-native":this.native}},checked(){return this.formGroup.checked===this.value},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i,a,s;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),(s=(a=this.formGroup).onChange)==null||s.call(a,this.value),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),xs=["checked","name","disabled","readonly",".indeterminate"],Cs=["aria-checked","aria-disabled","aria-readonly","tabindex"],Ss=p(Is,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["radio",e.classes],role:"radio"}),[m("input",{checked:e.checked,ref:"input",type:"radio",tabindex:"-1",name:e.name,disabled:e.isDisabled,readonly:e.isReadonly,".indeterminate":e.indeterminate,onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,xs),m("label",{class:"radio-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n)),onClick:t[2]||(t[2]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,Cs)],16)}]]),Oe="IRadioGroup",Bs=c({name:Oe,mixins:[R],provide(){return{formGroup:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Oe,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},modelValue:{default:""},name:{type:[String,Number],default:()=>S("radio-group")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Oe,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},checked(){return this.schema?this.schema.value:this.modelValue}},methods:{onChange(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e),this.$emit("update:modelValue",e)}}}),zs=["name"],As=p(Bs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["form-group radio-group",e.classes],name:e.name,role:"radiogroup"}),[d(e.$slots,"default",{},void 0,!0)],16,zs)}],["__scopeId","data-v-fa4fe77a"]]),Vs=c({name:"ISelectOption",inject:{select:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},label:{type:String,default:""},tabindex:{type:[Number,String],default:0},value:{type:[Object,String,Number],default:()=>({})}},computed:{ariaDisabled(){return this.disabled?"true":"false"},ariaSelected(){return this.active?"true":"false"},isActive(){return this.active||this.value===this.select.modelValue},classes(){return{"-active":this.isActive,"-disabled":this.disabled}},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(){this.disabled||this.select.onInput(this.value,this.label)}}}),Os=["tabindex","aria-disabled","aria-selected"],It=p(Vs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["select-option",e.classes],role:"option",tabindex:e.tabIndex,"aria-disabled":e.ariaDisabled,"aria-selected":e.ariaSelected,onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),[d(e.$slots,"default",{},()=>[P(_(e.label),1)])],16,Os)}]]),Le="ISelect",Ls=c({name:Le,directives:{ClickOutside:K},components:{IInput:te,IIcon:$t,ISelectOption:It,IMark:wt},mixins:[R,ee],provide(){return{select:this}},props:{animationDuration:{type:Number,default:300},autocomplete:{type:Boolean,default:!1},arrow:{type:Boolean,default:!0},color:{type:String,default:u(Le,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},idField:{type:String,default:"id"},keydownTrigger:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},keydownItem:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},label:{type:[String,Function],default:"label"},loading:{type:Boolean,default:!1},modelValue:{type:[Object,String,Number],default:null},minLength:{type:Number,default:0},name:{type:[String,Number],default:()=>S("select")},options:{type:Array,default:()=>[]},placeholder:{type:String,default:""},offset:{type:Number,default:6},placement:{type:String,default:"bottom"},popperOptions:{type:Object,default:()=>({modifiers:[...bt({offset:8}),{name:"sameWidth",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:({state:e})=>{e.styles.popper.width=`${e.rects.reference.width}px`},effect({state:e}){e.elements.popper.style.width=`${e.elements.reference.offsetWidth}px`}}]})},readonly:{type:Boolean,default:!1},scrollTolerance:{type:Number,default:160},selectFirstOptionOnEnter:{type:Boolean,default:!0},size:{type:String,default:u(Le,"size"),validator:v},tabindex:{type:[Number,String],default:0},type:{type:String,default:"text"},total:{type:Number,default:void 0}},emits:["update:modelValue","search","pagination"],data(){return{animating:!1,visible:!1,inputValue:this.computeLabel(this.modelValue)||""}},computed:{wrapperClasses(){return{...g(this),[`-${this.size}`]:!!this.size}},popupClasses(){return{"-disabled":this.isDisabled,"-readonly":this.isReadonly}},tabIndex(){return this.isDisabled?-1:this.tabindex},isClearable(){return this.value&&this.clearable&&!this.isDisabled&&!this.isReadonly},value(){return this.schema?this.schema.value:this.modelValue},inputPlaceholder(){return this.value?this.computeLabel(this.value):this.placeholder}},watch:{value(e){this.inputValue=this.computeLabel(e)},inputValue(e){const t=this.inputMatchesLength(e),i=this.inputMatchesLabel(e);!t||i||this.animating||this.show(),this.$emit("search",this.inputValue)},options(){this.visible&&this.createPopper()}},methods:{onInput(e,t){var i,a;e.disabled||(this.hide(),t&&(this.inputValue=t),(a=(i=this.parent).onInput)==null||a.call(i,this.name,e),this.$emit("update:modelValue",e))},onClear(){this.animating=!0,this.$emit("update:modelValue",null),this.$nextTick(()=>{this.animating=!1})},onFocus(e){!this.value&&this.options.length===0||(this.autocomplete&&(this.inputValue=""),(!e.relatedTarget||!this.$refs.wrapper.contains(e.relatedTarget))&&this.inputShouldShowSelect(this.inputValue)&&this.show())},onBlur(e){var t,i;(!e.relatedTarget||!this.$refs.wrapper.contains(e.relatedTarget))&&(this.hide(),this.inputValue=this.computeLabel(this.value)),(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)},onClick(){this.autocomplete&&(this.inputValue=""),this.inputShouldShowSelect(this.inputValue)&&this.show()},onClickOutside(){this.hide()},onClickCaret(e){this.visible?this.onBlur(e):(this.focus(),this.onFocus(e)),e.preventDefault(),e.stopPropagation()},onScroll(){if(isNaN(this.total))return;const e=this.$refs.body.scrollTop+parseInt(getComputedStyle(this.$refs.body).height,10)>parseInt(getComputedStyle(this.$refs.options).height,10)-this.scrollTolerance,t=this.options.length>=this.total;e&&!t&&this.options.length>0&&!this.loading&&this.$emit("pagination")},onWindowResize(){this.onScroll(),this.visible&&this.$nextTick().then(()=>this.createPopper())},onTriggerKeyDown(e){if(this.keydownTrigger.length===0)return;const t=this.getFocusableItems(),i=t.findIndex(s=>s.active),a=t[i>-1?i:0];switch(!0){case(k("up",e)&&this.keydownTrigger.includes("up")):case(k("down",e)&&this.keydownTrigger.includes("down")):this.show(),setTimeout(()=>{a.focus()},this.visible?0:this.animationDuration),e.preventDefault(),e.stopPropagation();break;case(k("enter",e)&&this.keydownTrigger.includes("enter")):if(!this.selectFirstOptionOnEnter||this.value&&this.inputMatchesLabel(this.inputValue))this.onClick();else{const s=this.options.find(r=>!r.disabled);s&&(this.onInput(s),this.focus())}this.visible||setTimeout(()=>{a.focus()},this.animationDuration),e.preventDefault();break;case(k("tab",e)&&this.keydownTrigger.includes("tab")):case(k("esc",e)&&this.keydownTrigger.includes("esc")):this.hide()}},onItemKeyDown(e){if(this.keydownItem.length!==0)switch(!0){case(k("up",e)&&this.keydownItem.includes("up")):case(k("down",e)&&this.keydownItem.includes("down")):const t=this.getFocusableItems(),i=t.findIndex(r=>r===e.target),a=t.length-1;let s;s=k("up",e)?i>0?i-1:0:i{this.animating=!1},this.animationDuration))},focus(){this.$refs.trigger.focus()},getFocusableItems(){const e=[];for(const t of this.$refs.options.children)Fe(t)&&e.push(t);return e},getElementHeight(e){const t=getComputedStyle(e);return t.height?Math.ceil(parseFloat(t.height)):NaN},inputMatchesLabel(e){return this.value&&e===this.computeLabel(this.value)},inputMatchesLength(e){return this.minLength===0||e&&e.length>=this.minLength},inputShouldShowSelect(e){return!this.autocomplete||this.inputMatchesLength(e)&&!this.inputMatchesLabel(e)},computeLabel(e){return typeof e!="object"?this.inputValue:dt(this.label)?this.label(e):M(e,this.label)}}}),Ds=["id","name","aria-owns","aria-expanded"],Ns=["id","aria-hidden"],Ts={key:0,"data-popper-arrow":""},Ps={key:1,class:"select-header"},Rs={key:0,class:"select-no-results"},Es={class:"select-options",ref:"options"},Ms={key:2,class:"select-footer"},_s=p(Ls,[["render",function(e,t,i,a,s,r){const n=A("i-input"),b=A("i-mark"),B=A("i-select-option"),F=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["select-wrapper",e.wrapperClasses],id:e.name,name:e.name,ref:"wrapper",role:"combobox","aria-haspopup":"listbox","aria-owns":`${e.name}-options`,"aria-expanded":e.visible?"true":"false",onKeyup:t[3]||(t[3]=E((...w)=>e.onEscape&&e.onEscape(...w),["esc"]))}),[I(n,{modelValue:e.inputValue,"onUpdate:modelValue":t[1]||(t[1]=w=>e.inputValue=w),ref:"trigger",autocomplete:"off","aria-autocomplete":"both","aria-controls":`${e.name}-options`,disabled:e.isDisabled,readonly:e.isReadonly,tabindex:e.tabIndex,plaintext:!e.autocomplete,placeholder:e.inputPlaceholder,clearable:e.isClearable,color:e.color,size:e.size,name:`${e.name}-input`,onClick:e.onClick,onFocus:e.onFocus,onBlur:e.onBlur,onClear:e.onClear,onKeydown:e.onTriggerKeyDown},Nt({suffix:y(()=>[d(e.$slots,"suffix"),m("button",{class:"select-caret","aria-hidden":"true",role:"button",onClick:t[0]||(t[0]=(...w)=>e.onClickCaret&&e.onClickCaret(...w))})]),_:2},[e.$slots.prepend?{name:"prepend",fn:y(()=>[d(e.$slots,"prepend")]),key:"0"}:void 0,e.$slots.prefix?{name:"prefix",fn:y(()=>[d(e.$slots,"prefix")]),key:"1"}:void 0,e.$slots.append?{name:"append",fn:y(()=>[d(e.$slots,"append")]),key:"2"}:void 0]),1032,["modelValue","aria-controls","disabled","readonly","tabindex","plaintext","placeholder","clearable","color","size","name","onClick","onFocus","onBlur","onClear","onKeydown"]),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:z(["select",e.popupClasses]),id:`${e.name}-options`,role:"listbox",ref:"popup","aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",Ts)):f("",!0),e.$slots.header?(l(),o("div",Ps,[d(e.$slots,"header")])):f("",!0),m("div",{class:"select-body",ref:"body",onScroll:t[2]||(t[2]=(...w)=>e.onScroll&&e.onScroll(...w))},[e.$slots.default||e.options.length!==0?f("",!0):(l(),o("div",Rs,[d(e.$slots,"no-results",{},()=>[t[4]||(t[4]=P(" There are no results for your query. "))])])),m("div",Es,[d(e.$slots,"default"),(l(!0),o(j,null,X(e.options,w=>(l(),V(B,{key:w[e.idField],active:e.value&&e.value[e.idField]===w[e.idField],disabled:w.disabled,value:w,onKeydown:e.onItemKeyDown},{default:y(()=>[d(e.$slots,"option",{option:w},()=>[e.autocomplete&&e.inputValue!==e.computeLabel(w)?(l(),V(b,{key:0,text:e.computeLabel(w),query:e.inputValue},null,8,["text","query"])):(l(),o(j,{key:1},[P(_(e.computeLabel(w)),1)],64))])]),_:2},1032,["active","disabled","value","onKeydown"]))),128))],512)],544),e.$slots.footer?(l(),o("div",Ms,[d(e.$slots,"footer")])):f("",!0)],10,Ns),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,Ds)),[[F,e.onClickOutside]])}]]),De="ISidebar",Fs=c({name:De,mixins:[ft],provide(){return{sidebar:this}},inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Sidebar"},collapseOnItemClick:{type:Boolean,default:!0},collapseOnClickOutside:{type:Boolean,default:!0},collapsePosition:{type:String,default:"absolute"},color:{type:String,default:u(De,"color")},placement:{type:String,default:"left"},size:{type:String,default:u(De,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...this.collapsibleClasses,...g(this),[`-${this.size}`]:!!this.size,[`-collapse-${this.collapsePosition}`]:!0,[`-placement-${this.placement}`]:!0}},sidebarWrapperTransition(){return this.collapsePosition!=="relative"?"sidebar-wrapper-none-transition":"sidebar-wrapper-transition"},sidebarTransition(){return this.collapsePosition!=="relative"?"sidebar-transition":"sidebar-none-transition"}},methods:{onItemClick(){this.collapseOnItemClick&&this.open&&this.setOpen(!1)},onOverlayClick(){this.collapseOnClickOutside&&this.open&&this.setOpen(!1)}}}),js=["aria-label"],Gs={class:"sidebar"},Zs={class:"sidebar-content"},Ks=p(Fs,[["render",function(e,t,i,a,s,r){return l(),V(L,{name:e.sidebarWrapperTransition},{default:y(()=>[$(m("aside",h(e.$attrs,{role:"complementary",class:["sidebar-wrapper",e.classes],"aria-label":e.ariaLabel,ref:"wrapper"}),[I(L,{name:e.sidebarTransition},{default:y(()=>[$(m("div",Gs,[m("div",Zs,[d(e.$slots,"default")])],512),[[x,e.collapsePosition==="relative"||e.open||!e.collapsible]])]),_:3},8,["name"]),I(L,{name:"sidebar-overlay-transition"},{default:y(()=>[e.collapsePosition!=="relative"?$((l(),o("div",{key:0,class:"sidebar-overlay",onClick:t[0]||(t[0]=(...n)=>e.onOverlayClick&&e.onOverlayClick(...n))},null,512)),[[x,e.open]]):f("",!0)]),_:1})],16,js),[[x,e.open||!e.collapsible]])]),_:3},8,["name"])}]]),st="ITable",qs=c({name:st,inheritAttrs:!1,props:{border:{type:Boolean,default:!1},condensed:{type:Boolean,default:!1},striped:{type:Boolean,default:!1},hover:{type:Boolean,default:!1},responsive:{type:[Boolean,String],default:!0},nowrap:{type:Boolean,default:!1},color:{type:String,default:u(st,"color")}},computed:{classes(){return{...g(this),"-border":this.border,"-condensed":this.condensed,"-striped":this.striped,"-hover":this.hover,"-nowrap":this.nowrap,["-responsive"+(typeof this.responsive=="boolean"?"":`-${this.responsive}`)]:!!this.responsive}}}}),Ws={class:"table"},Us=p(qs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["table-wrapper",e.classes]}),[m("table",Ws,[d(e.$slots,"default")])],16)}]]),Ne="ITabs",Hs=c({name:Ne,provide(){return{tabs:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Ne,"color")},modelValue:{type:String,default:""},size:{type:String,default:u(Ne,"size"),validator:v},stretch:{type:Boolean,default:!1}},emits:["update:modelValue"],data(){return{active:this.modelValue,tabs:[]}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-stretch":this.stretch}}},watch:{modelValue(e){this.active=e}},methods:{setActive(e){this.active=e,this.$emit("update:modelValue",this.active)}}}),Qs={class:"tabs-header"},Ys=p(Hs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["tabs",e.classes],role:"tablist","aria-multiselectable":"true"}),[m("div",Qs,[d(e.$slots,"header")]),d(e.$slots,"default")],16)}]]),Js=c({name:"ITab",inject:{tabs:{default:()=>({})}},props:{title:{type:String,default:""},name:{type:String,default:()=>S("tab")}},computed:{active(){return this.tabs.active===this.name},classes(){return{"-active":this.active}}}}),Xs=["name","aria-hidden","aria-labelledby"],en={class:"tab-body"},tn=p(Js,[["render",function(e,t,i,a,s,r){return $((l(),o("div",{class:z(["tab",e.classes]),role:"tabpanel",name:e.name,"aria-hidden":!e.active,"aria-labelledby":`tab-heading-${e.name}`},[m("div",en,[d(e.$slots,"default")])],10,Xs)),[[x,e.active]])}]]),an=c({name:"ITabTitle",inject:{tabs:{default:()=>({})}},props:{for:{type:String,default:()=>S("tab")}},computed:{active(){return this.tabs.active===this.for},classes(){return{"-active":this.active}},name(){return this.for}},methods:{onClick(){this.tabs.setActive(this.for)}}}),sn=["for","active","aria-expanded","aria-controls","aria-describedby"],nn=p(an,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["tab-title",e.classes]),role:"tab",for:e.name,active:e.active,"aria-expanded":e.active,"aria-controls":`tab-content-${e.name}`,"aria-describedby":`tab-content-${e.name}`,tabindex:"0",onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))},[d(e.$slots,"default")],10,sn)}]]),Te="IToggle",ln=c({name:Te,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(Te,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:!1},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("toggle")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Te,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly}},checked(){return this.schema?this.schema.value:this.modelValue},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),rn=["checked","disabled","readonly","aria-checked","aria-disabled","aria-readonly","name"],on=["aria-checked","aria-disabled","aria-readonly","tabindex"],dn=p(ln,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["toggle",e.classes]}),[m("input",{ref:"input",type:"checkbox",checked:e.checked,disabled:e.isDisabled,readonly:e.isReadonly,"aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,name:e.name,onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,rn),m("label",{class:"toggle-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onClick:t[1]||(t[1]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onBlur:t[2]||(t[2]=(...n)=>e.onBlur&&e.onBlur(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,on)],16)}]]),Pe="ITooltip",un=c({name:Pe,directives:{ClickOutside:K},mixins:[ee,Ge],inheritAttrs:!1,props:{color:{type:String,default:u(Pe,"color")},disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:!1},name:{type:String,default:()=>S("tooltip")},arrow:{type:Boolean,default:!0},placement:{type:String,default:"top"},trigger:{type:[String,Array],default:()=>["hover","focus"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!1},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(Pe,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)}}}),cn=["id"],pn=["aria-describedby","aria-disabled","aria-expanded"],hn=["id","aria-hidden"],mn={key:0,"data-popper-arrow":""},fn=p(un,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["tooltip-wrapper",e.classes],ref:"wrapper",id:e.name,onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))}),[m("div",{class:"tooltip-trigger",ref:"trigger","aria-describedby":`${e.name}-popup`,"aria-disabled":e.disabled?"true":"false","aria-expanded":e.visible?"true":"false"},[d(e.$slots,"default")],8,pn),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:"tooltip",ref:"popup",role:"tooltip","aria-live":"polite",id:`${e.name}-popup`,"aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",mn)):f("",!0),d(e.$slots,"body")],8,hn),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,cn)),[[n,e.onClickOutside]])}]]),vn=Object.freeze(Object.defineProperty({__proto__:null,IAlert:Jt,IBadge:Xt,IBreadcrumb:ii,IBreadcrumbItem:ni,IButton:vt,IButtonGroup:ui,ICard:fi,ICheckbox:ki,ICheckboxGroup:Ii,ICollapsible:Ci,ICollapsibleItem:Oi,IColumn:Ze,IContainer:Ke,IDropdown:_i,IDropdownDivider:Fi,IDropdownItem:ji,IForm:Ki,IFormError:Yi,IFormGroup:Ui,IFormLabel:ea,IHamburgerMenu:kt,IHeader:ta,IIcon:$t,IInput:te,ILayout:Ba,ILayoutAside:Va,ILayoutContent:Oa,ILayoutFooter:La,ILayoutHeader:Da,IListGroup:Na,IListGroupItem:Ta,ILoader:yt,IMark:wt,IMedia:Fa,IModal:Ha,INav:Qa,INavItem:Ya,INavbar:Ja,INavbarBrand:Xa,INavbarCollapsible:is,INumberInput:va,IPagination:ds,IPopover:vs,IProgress:gs,IProgressBar:ws,IRadio:Ss,IRadioGroup:As,IRow:qe,ISelect:_s,ISelectOption:It,ISidebar:Ks,ITab:tn,ITabTitle:nn,ITable:Us,ITabs:Ys,ITextarea:Sa,IToggle:dn,ITooltip:fn},Symbol.toStringTag,{value:"Module"}));export{yn as I,p as _,vn as c}; diff --git a/packages/modules/display_themes/cards/web/assets/vendor-inkline-S9CBmrTS.js b/packages/modules/display_themes/cards/web/assets/vendor-inkline-S9CBmrTS.js new file mode 100644 index 0000000000..529d470787 --- /dev/null +++ b/packages/modules/display_themes/cards/web/assets/vendor-inkline-S9CBmrTS.js @@ -0,0 +1 @@ +import{h as nt,w as Bt,r as zt,d as c,a as At,b as $,v as x,e as o,f,i as m,j as d,m as h,o as l,k as P,l as V,n as Y,p as A,q as y,s as I,t as E,u as Q,T as L,x as M,y as G,z,F as j,A as X,c as se,B as Vt,C as Ot,D as Lt,E as Dt,G as Nt}from"./vendor-Bzn5cd2Y.js";const p=(e,t)=>{const i=e.__vccOpts||e;for(const[a,s]of t)i[a]=s;return i};function g(e){let t=e.color;return t||(t=e.$inkline.options.colorMode==="system"?typeof window<"u"&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e.$inkline.options.colorMode),{[`-${t}`]:!0}}function lt(e,t){if(!e||!t)return!1;if(t.indexOf(" ")!==-1)throw new Error("Class name should not contain spaces.");return e.classList?e.classList.contains(t):(" "+e.className+" ").indexOf(" "+t+" ")>-1}function Me(e,t){if(!e)return;let i=e.className;const a=(t||"").split(" ");for(let s=0,r=a.length;s"-"+s.toLowerCase())}function rt(e,t){return["string","number"].indexOf(typeof t)>-1&&t!==""?`${He(e)}-${t}`:He(e)}function Re(e){return e.charAt(0).toUpperCase()+e.slice(1)}function U(e){if(Array.isArray(e)){const t=e.slice().map(U),i=Object.keys(t);return Object.keys(e).filter(a=>!i.includes(a)).forEach(a=>{t[a]=e[a]}),t}return typeof e=="object"?Object.keys(e).reduce((t,i)=>(t[i]=U(e[i]),t),{}):e}function Fe(e){if(e.tabIndex>0||e.tabIndex===0&&e.getAttribute("tabIndex")!==null)return!0;if(e.disabled)return!1;switch(e.nodeName){case"A":return!!e.href&&e.rel!=="ignore";case"INPUT":return e.type!=="hidden"&&e.type!=="file";case"BUTTON":case"SELECT":case"TEXTAREA":return!0;default:return!1}}function Tt(e){if(!Fe(e))return!1;try{e.focus()}catch{}return typeof window<"u"&&document.activeElement===e}function ot(e){for(let t=0;t"u")return;if(e.currentStyle)return e.currentStyle[t];const i=window.getComputedStyle(e,null);return i.getPropertyValue?i.getPropertyValue(t):i[t]}const dt=e=>e instanceof Function,J={xs:[0,575],sm:[576,767],md:[768,991],lg:[992,1199],xl:[1200,1399],xxl:[1400,1/0]},je=["","xs","sm","md","lg","xl","xxl"],Pt={tab:["Tab",9],enter:["Enter",13],esc:["Escape",27],space:[" ","Space",32],left:["ArrowLeft","Left",37],up:["ArrowUp","Up",38],right:["ArrowRight","Right",39],down:["ArrowDown","Down",40]},Qe={pristine:!0,dirty:!1,untouched:!0,touched:!1,valid:!0,invalid:!1,errors:[]},Rt={value:"",validators:[]},ut=["value","validators","pristine","dirty","untouched","touched","valid","invalid","errors"],k=(e,t)=>{const i=t.key||t.keyIdentifier||t.keyCode;return Pt[e].indexOf(i)!==-1};function Et(e,t,i){e&&t&&e.removeEventListener(t,i,!1)}function _t(e,t,i){e&&t&&e.detachEvent("on"+t,i)}const T=typeof window>"u"?()=>{}:window.document.removeEventListener?Et:_t;function Mt(e,t,i){e&&t&&i&&e.addEventListener(t,i,!1)}function Ft(e,t,i){e&&t&&i&&e.attachEvent("on"+t,i)}const O=typeof window>"u"?()=>{}:window.document.addEventListener?Mt:Ft,ct=e=>e.map(t=>t.type==="element"?nt(t.name,t.attributes,ct(t.children)):t.value);function pt(e,t){if(!e||!t)return;const i=t.split(" ");let a=" "+e.className+" ";for(let s=0,r=i.length;si&&i[a],e)}function ne(e,t,i){return t&&t.split(".").reduce((a,s)=>(Object.keys(i).forEach(r=>{a[s][r]=i[r]}),a&&a[s]),e),Object.keys(i).forEach(a=>{e[a]=i[a]}),e}function S(e){return`${e?`${e}-`:""}${Math.random().toString(36).substr(2,9)}`}const D={"en-US":/^[A-Z]+$/i,"bg-BG":/^[А-Я]+$/i,"cs-CZ":/^[A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[A-ZÆØÅ]+$/i,"de-DE":/^[A-ZÄÖÜß]+$/i,"el-GR":/^[Α-ω]+$/i,"es-ES":/^[A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[A-ZÀÉÈÌÎÓÒÙ]+$/i,"nb-NO":/^[A-ZÆØÅ]+$/i,"nl-NL":/^[A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[A-ZÆØÅ]+$/i,"hu-HU":/^[A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"pl-PL":/^[A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[А-ЯЁ]+$/i,"sl-SI":/^[A-ZČĆĐŠŽ]+$/i,"sk-SK":/^[A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,"sr-RS@latin":/^[A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[A-ZÅÄÖ]+$/i,"tr-TR":/^[A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[А-ЩЬЮЯЄIЇҐі]+$/i,"ku-IQ":/^[ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,ar:/^[ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/},N={"en-US":/^[0-9A-Z]+$/i,"bg-BG":/^[0-9А-Я]+$/i,"cs-CZ":/^[0-9A-ZÁČĎÉĚÍŇÓŘŠŤÚŮÝŽ]+$/i,"da-DK":/^[0-9A-ZÆØÅ]+$/i,"de-DE":/^[0-9A-ZÄÖÜß]+$/i,"el-GR":/^[0-9Α-ω]+$/i,"es-ES":/^[0-9A-ZÁÉÍÑÓÚÜ]+$/i,"fr-FR":/^[0-9A-ZÀÂÆÇÉÈÊËÏÎÔŒÙÛÜŸ]+$/i,"it-IT":/^[0-9A-ZÀÉÈÌÎÓÒÙ]+$/i,"hu-HU":/^[0-9A-ZÁÉÍÓÖŐÚÜŰ]+$/i,"nb-NO":/^[0-9A-ZÆØÅ]+$/i,"nl-NL":/^[0-9A-ZÁÉËÏÓÖÜÚ]+$/i,"nn-NO":/^[0-9A-ZÆØÅ]+$/i,"pl-PL":/^[0-9A-ZĄĆĘŚŁŃÓŻŹ]+$/i,"pt-PT":/^[0-9A-ZÃÁÀÂÇÉÊÍÕÓÔÚÜ]+$/i,"ru-RU":/^[0-9А-ЯЁ]+$/i,"sl-SI":/^[0-9A-ZČĆĐŠŽ]+$/i,"sk-SK":/^[0-9A-ZÁČĎÉÍŇÓŠŤÚÝŽĹŔĽÄÔ]+$/i,"sr-RS@latin":/^[0-9A-ZČĆŽŠĐ]+$/i,"sr-RS":/^[0-9А-ЯЂЈЉЊЋЏ]+$/i,"sv-SE":/^[0-9A-ZÅÄÖ]+$/i,"tr-TR":/^[0-9A-ZÇĞİıÖŞÜ]+$/i,"uk-UA":/^[0-9А-ЩЬЮЯЄIЇҐі]+$/i,"ku-IQ":/^[٠١٢٣٤٥٦٧٨٩0-9ئابپتجچحخدرڕزژسشعغفڤقکگلڵمنوۆھەیێيطؤثآإأكضصةظذ]+$/i,ar:/^[٠١٢٣٤٥٦٧٨٩0-9ءآأؤإئابةتثجحخدذرزسشصضطظعغفقكلمنهوىيًٌٍَُِّْٰ]+$/};["AU","GB","HK","IN","NZ","ZA","ZM"].forEach(e=>{D[`en-${e}`]=D["en-US"],N[`en-${e}`]=N["en-US"]}),["AE","BH","DZ","EG","IQ","JO","KW","LB","LY","MA","QM","QA","SA","SD","SY","TN","YE"].forEach(e=>{D[`ar-${e}`]=D.ar,N[`ar-${e}`]=N.ar}),D["pt-BR"]=D["pt-PT"],N["pt-BR"]=N["pt-PT"],D["pl-Pl"]=D["pl-PL"],N["pl-Pl"]=N["pl-PL"];const Ye=/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,jt={alpha:function(e,t={}){const i=t.locale||"en-US",a=s=>{let r=String(s);return t.allowDashes&&(r=r.replace(/-/g,"")),t.allowSpaces&&(r=r.replace(/ /g,"")),r};return e.constructor===Array?e.every(s=>D[i].test(a(s))):D[i].test(a(e))},alphanumeric:function(e,t={}){const i=t.locale||"en-US",a=s=>{let r=String(s);return t.allowDashes&&(r=r.replace(/-/g,"")),t.allowSpaces&&(r=r.replace(/ /g,"")),r};return e.constructor===Array?e.every(s=>N[i].test(a(s))):N[i].test(a(e))},custom:function(e,t={validator:()=>!0}){return e.constructor===Array?e.every(i=>t.validator(i)):t.validator(e)},number:function(e,t={allowNegative:!1,allowDecimal:!1}){let i="\\d+";t.allowNegative&&(i="[-]?"+i),t.allowDecimal&&(i+="([\\.\\,]\\d+)?");const a=new RegExp(`^${i}$`);return e.constructor===Array?e.every(s=>a.test(s)):a.test(e)},email:function(e){return e.constructor===Array?e.every(t=>!t||Ye.test(String(t))):!e||Ye.test(String(e))},max:function(e,t={value:0}){if(e==null)return!1;const i=a=>Number(a);return Array.isArray(e)?e.every(a=>i(a)<=t.value):i(e)<=t.value},maxLength:function(e,t={value:0}){return e!=null&&(e.constructor===Array?e.length<=t.value:typeof e=="object"?Object.keys(e).length<=t.value:String(e).length<=t.value)},min:function(e,t={value:0}){if(e==null)return!1;const i=a=>Number(a);return Array.isArray(e)?e.every(a=>i(a)>=t.value):i(e)>=t.value},minLength:function(e,t={value:0}){return e!=null&&(e.constructor===Array?e.length>=t.value:typeof e=="object"?Object.keys(e).length>=t.value:String(e).length>=t.value)},required:function(e,t={invalidateFalse:!1}){return e!=null&&(e.constructor===Array?!!e.length:typeof e=="boolean"?!t.invalidateFalse||e:!!String(e).trim().length)},sameAs:function(e,t={}){if(!t.target)return!1;const i=_(t.schema(),t.target);if(!i)throw new Error(`Could not find target with name '${t.target}' in 'sameAs' validator.`);return e===i.value}};function ht(e){const t=Object.keys(e).length===0||Array.isArray(e.validators)||e.hasOwnProperty("value")?{...Qe,...Rt}:Qe;return Object.entries(t).forEach(([i,a])=>{e.hasOwnProperty(i)||(e[i]=a)}),Object.keys(e).filter(i=>!ut.includes(i)).forEach(i=>{(typeof e[i]=="object"||Array.isArray(e[i]))&&(e[i]=ht(e[i]))}),e}const Ee={locale:"en",messages:{en:{validation:{alpha:e=>{let t;switch(!0){case(e.allowSpaces&&e.allowDashes):t="letters, spaces, and dashes";break;case e.allowSpaces:t="letters and spaces";break;case e.allowDashes:t="letters and dashes";break;default:t="letters"}return`Please enter ${t} only.`},alphanumeric:e=>{let t;switch(!0){case(e.allowSpaces&&e.allowDashes):t="letters, numbers, spaces, and dashes";break;case e.allowSpaces:t="letters, numbers, and spaces";break;case e.allowDashes:t="letters, numbers, and dashes";break;default:t="letters and numbers"}return`Please enter ${t} only.`},number:e=>{let t;switch(!0){case(e.allowNegative&&e.allowDecimal):t="positive or negative decimal numbers";break;case e.allowNegative:t="positive or negative numbers";break;case e.allowDecimal:t="decimal numbers";break;default:t="numbers"}return`Please enter ${t} only.`},email:()=>"Please enter a valid email address.",max:()=>"Please enter a maximum value of {value}.",maxLength:()=>"Please enter up to {value} characters.",min:()=>"Please enter a minimum value of {value}.",minLength:()=>"Please enter at least {value} characters.",required:()=>"Please enter a value for this field.",sameAs:()=>"Please make sure that the two values match.",custom:()=>"Please enter a correct value for this field."}}}};function Gt(e,t=""){const i=[];return e.valid=(e.validators||[]).reduce((a,s)=>{const r=typeof s=="string"?{name:s}:s,n=jt[r.name](e.value,r);if(!n){const{name:b,message:B,...F}=r,w={name:t.split(".").pop(),value:e.value,...F},xt=(B instanceof Function?B():B)||function(We,ie={}){const ae=_(Ee.messages[Ee.locale],We),Ct=dt(ae)?ae(ie):ae||We;return Object.keys(ie).reduce((St,Ue)=>St.replace(new RegExp(`{${Ue}}`,"g"),`${ie[Ue]}`),Ct)}(`validation.${b}`,w);i.push({name:b,message:xt,path:t})}return a&&n},!0),e.invalid=!e.valid,e.errors=i,e}function mt(e,t=""){return e.valid=Object.keys(e).filter(i=>!ut.includes(i)).reduce((i,a)=>(Object.keys(e[a]).length===0||e[a].validators||e[a].value?e[a]=Gt(e[a],`${t}`?`${t}.${a}`:a):e[a]=mt(e[a],`${t}`?`${t}.${a}`:a),i&&e[a].valid),!0),e.invalid=!e.valid,e}function le(e){return mt(e,"")}const Zt=Object.freeze(Object.defineProperty({__proto__:null,inkCaretDown:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"caret-down",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 11c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1z"},children:[]}]},inkCheck:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"check",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M23.625 3.5l-13.125 13.125-6.125-6.125-4.375 4.375 10.5 10.5 17.5-17.5z"},children:[]}]},inkChevronDown:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"chevron-down",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M26.297 12.625l-11.594 11.578c-0.391 0.391-1.016 0.391-1.406 0l-11.594-11.578c-0.391-0.391-0.391-1.031 0-1.422l2.594-2.578c0.391-0.391 1.016-0.391 1.406 0l8.297 8.297 8.297-8.297c0.391-0.391 1.016-0.391 1.406 0l2.594 2.578c0.391 0.391 0.391 1.031 0 1.422z"},children:[]}]},inkCircle:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"24",height:"28",viewBox:"0 0 24 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"circle",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z"},children:[]}]},inkDanger:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"danger",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M14 2.625c-3.038 0-5.895 1.183-8.043 3.332s-3.332 5.005-3.332 8.043c0 3.038 1.183 5.895 3.332 8.043s5.005 3.332 8.043 3.332c3.038 0 5.895-1.183 8.043-3.332s3.332-5.005 3.332-8.043c0-3.038-1.183-5.895-3.332-8.043s-5.005-3.332-8.043-3.332zM14 0v0c7.732 0 14 6.268 14 14s-6.268 14-14 14c-7.732 0-14-6.268-14-14s6.268-14 14-14zM12.25 19.25h3.5v3.5h-3.5zM12.25 5.25h3.5v10.5h-3.5z"},children:[]}]},inkInfo:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"info",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M12.25 8.313c0-0.722 0.591-1.313 1.313-1.313h0.875c0.722 0 1.313 0.591 1.313 1.313v0.875c0 0.722-0.591 1.313-1.313 1.313h-0.875c-0.722 0-1.313-0.591-1.313-1.313v-0.875z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M17.5 21h-7v-1.75h1.75v-5.25h-1.75v-1.75h5.25v7h1.75z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M14 0c-7.732 0-14 6.268-14 14s6.268 14 14 14 14-6.268 14-14-6.268-14-14-14zM14 25.375c-6.282 0-11.375-5.093-11.375-11.375s5.093-11.375 11.375-11.375 11.375 5.093 11.375 11.375-5.093 11.375-11.375 11.375z"},children:[]}]},inkMinus:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"minus",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M0 11.375v5.25c0 0.483 0.392 0.875 0.875 0.875h26.25c0.483 0 0.875-0.392 0.875-0.875v-5.25c0-0.483-0.392-0.875-0.875-0.875h-26.25c-0.483 0-0.875 0.392-0.875 0.875z"},children:[]}]},inkPlus:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"plus",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.125 10.5h-9.625v-9.625c0-0.483-0.392-0.875-0.875-0.875h-5.25c-0.483 0-0.875 0.392-0.875 0.875v9.625h-9.625c-0.483 0-0.875 0.392-0.875 0.875v5.25c0 0.483 0.392 0.875 0.875 0.875h9.625v9.625c0 0.483 0.392 0.875 0.875 0.875h5.25c0.483 0 0.875-0.392 0.875-0.875v-9.625h9.625c0.483 0 0.875-0.392 0.875-0.875v-5.25c0-0.483-0.392-0.875-0.875-0.875z"},children:[]}]},inkSearch:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"search",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.132 23.827l-6.632-5.641c-0.686-0.617-1.419-0.9-2.011-0.873 1.566-1.834 2.511-4.213 2.511-6.813 0-5.799-4.701-10.5-10.5-10.5s-10.5 4.701-10.5 10.5 4.701 10.5 10.5 10.5c2.6 0 4.98-0.946 6.813-2.511-0.027 0.592 0.256 1.326 0.873 2.011l5.641 6.632c0.966 1.073 2.544 1.164 3.506 0.201s0.872-2.54-0.201-3.506zM10.5 17.5c-3.866 0-7-3.134-7-7s3.134-7 7-7 7 3.134 7 7-3.134 7-7 7z"},children:[]}]},inkSort:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 17c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1zM16 11c0 0.547-0.453 1-1 1h-14c-0.547 0-1-0.453-1-1 0-0.266 0.109-0.516 0.297-0.703l7-7c0.187-0.187 0.438-0.297 0.703-0.297s0.516 0.109 0.703 0.297l7 7c0.187 0.187 0.297 0.438 0.297 0.703z"},children:[]}]},inkSortAsc:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort-asc",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 11c0 0.547-0.453 1-1 1h-14c-0.547 0-1-0.453-1-1 0-0.266 0.109-0.516 0.297-0.703l7-7c0.187-0.187 0.438-0.297 0.703-0.297s0.516 0.109 0.703 0.297l7 7c0.187 0.187 0.297 0.438 0.297 0.703z"},children:[]}]},inkSortDesc:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"16",height:"28",viewBox:"0 0 16 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"sort-desc",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M16 17c0 0.266-0.109 0.516-0.297 0.703l-7 7c-0.187 0.187-0.438 0.297-0.703 0.297s-0.516-0.109-0.703-0.297l-7-7c-0.187-0.187-0.297-0.438-0.297-0.703 0-0.547 0.453-1 1-1h14c0.547 0 1 0.453 1 1z"},children:[]}]},inkTimes:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"times",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M27.745 22.495c-0-0-0-0-0-0l-8.494-8.494 8.494-8.494c0-0 0-0 0-0 0.091-0.091 0.158-0.198 0.2-0.312 0.116-0.311 0.050-0.675-0.2-0.925l-4.013-4.013c-0.25-0.25-0.614-0.316-0.925-0.2-0.114 0.042-0.221 0.109-0.312 0.2 0 0-0 0-0 0l-8.494 8.494-8.494-8.494c-0-0-0-0-0-0-0.091-0.091-0.198-0.158-0.312-0.2-0.311-0.116-0.675-0.050-0.925 0.2l-4.013 4.013c-0.25 0.25-0.316 0.614-0.2 0.925 0.042 0.114 0.109 0.221 0.2 0.312 0 0 0 0 0 0l8.494 8.494-8.494 8.494c-0 0-0 0-0 0-0.091 0.091-0.157 0.198-0.2 0.312-0.116 0.311-0.050 0.675 0.2 0.925l4.013 4.013c0.25 0.25 0.614 0.316 0.925 0.2 0.114-0.042 0.221-0.109 0.312-0.2 0-0 0-0 0-0l8.494-8.494 8.494 8.494c0 0 0 0 0 0 0.092 0.091 0.198 0.158 0.312 0.2 0.311 0.116 0.675 0.050 0.925-0.2l4.013-4.013c0.25-0.25 0.316-0.614 0.2-0.925-0.042-0.114-0.109-0.221-0.2-0.312z"},children:[]}]},inkWarning:{name:"svg",type:"element",value:"",attributes:{version:"1.1",xmlns:"http://www.w3.org/2000/svg",width:"28",height:"28",viewBox:"0 0 28 28",fill:"currentColor"},children:[{name:"title",type:"element",value:"",attributes:{},children:[{name:"",type:"text",value:"warning",attributes:{},children:[]}]},{name:"path",type:"element",value:"",attributes:{d:"M14 2.537l11.733 23.385h-23.467l11.733-23.385zM14 0c-0.603 0-1.207 0.407-1.665 1.221l-11.951 23.819c-0.916 1.628-0.137 2.96 1.731 2.96h23.77c1.868 0 2.647-1.332 1.731-2.96h0l-11.951-23.819c-0.458-0.814-1.061-1.221-1.665-1.221v0z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M15.75 22.75c0 0.966-0.784 1.75-1.75 1.75s-1.75-0.784-1.75-1.75c0-0.966 0.784-1.75 1.75-1.75s1.75 0.784 1.75 1.75z"},children:[]},{name:"path",type:"element",value:"",attributes:{d:"M14 19.25c-0.966 0-1.75-0.784-1.75-1.75v-5.25c0-0.966 0.784-1.75 1.75-1.75s1.75 0.784 1.75 1.75v5.25c0 0.966-0.784 1.75-1.75 1.75z"},children:[]}]}},Symbol.toStringTag,{value:"Module"})),Je="inkline-color-mode",re=e=>{let t;t=e==="system"?matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light":e,pt(document.body,"-light -dark"),Me(document.body,`-${t}`)},Kt={components:{},icons:{},colorMode:"system",locale:"en",validateOn:["input","blur"],color:"",size:"",routerComponent:"router-link",componentOptions:{}};function qt({icons:e,components:t,...i}){return{form:a=>ht(a),setLocale(a){(function(s){Ee.locale=s})(a)},options:zt(i)}}const W={prototype:void 0},yn={install(e,t={}){const i={...Kt,...t};for(const r in i.components)e.component(i.components[r].name,i.components[r]);if(typeof window<"u"){const r=localStorage.getItem(Je);r&&(i.colorMode=r)}const a=qt(i);W.prototype=a,e.config.globalProperties.$inkline=a,e.provide("inkline",a);const s={...Zt,...i.icons};if(e.provide("inklineIcons",s),typeof window<"u"){Bt(()=>a.options.colorMode,b=>{re(b),localStorage.setItem(Je,b)});const r=()=>{a.options.colorMode==="system"&&re(a.options.colorMode)},n=matchMedia("(prefers-color-scheme: dark)");n.addEventListener?n.addEventListener("change",r):n.addListener(r),Me(document.body,"inkline"),re(i.colorMode)}}};function u(e,t,i=""){return()=>{var a;return W.prototype?(a=W.prototype.options.componentOptions[e])!=null&&a[t]?W.prototype.options.componentOptions[e][t]:W.prototype.options[t]:i}}function v(e){return["","xs","sm","md","lg","xl","xxl"].includes(e)}const ft=c({props:{collapse:{type:[String,Boolean],default:"md"},modelValue:{type:Boolean,default:!1}},emits:["update:modelValue"],data(){return{open:this.modelValue,windowWidth:typeof window<"u"?window.innerWidth:0}},computed:{collapsibleClasses(){return{"-open":this.open,"-collapsible":this.collapsible,[`-collapse-${this.collapse}`]:!!this.collapse}},collapsible(){return this.collapse===!0||this.collapse===!1?this.collapse:this.windowWidth<=J[this.collapse][1]}},watch:{modelValue(e){this.open=e}},created(){typeof window<"u"&&(O(window,"resize",this.onWindowResize),this.onWindowResize())},beforeUnmount(){typeof window<"u"&&T(window,"resize",this.onWindowResize)},methods:{setOpen(e){this.open=e,this.$emit("update:modelValue",this.open)},toggleOpen(){this.open=!this.open,this.$emit("update:modelValue",this.open)},onWindowResize(){if(this.collapse===!0||this.collapse===!1||typeof window>"u")return;const e=window.innerWidth;this.windowWidth<=J[this.collapse][1]&&e>J[this.collapse][1]&&this.setOpen(!1),this.windowWidth=window.innerWidth}}}),R=c({inject:{formGroup:{default:()=>({})},form:{default:()=>({})}},computed:{isDisabled(){return this.disabled||this.form.isDisabled||this.formGroup.isDisabled},isReadonly(){return this.readonly||this.form.isReadonly||this.formGroup.isReadonly},parent(){return this.formGroup.$?this.formGroup:this.form},schema(){const e=this.parent.schema||{};return this.name!==""?_(e,`${this.name}`):e}}}),Z=c({props:{tag:{type:String,default:"a"}},computed:{isTag(){return this.$attrs.to?this.routerComponent:this.$attrs.href?"a":this.tag},isComponent(){return this.isTag===this.routerComponent},routerComponent(){return this.$inkline.options.routerComponent}}}),Wt=e=>({name:"offset",options:{offset:[0,e]}}),bt=({offset:e})=>[Wt(e),{name:"arrow",options:{padding:6}},{name:"preventOverflow",options:{padding:8}},{name:"computeStyles",options:{gpuAcceleration:!1,adaptive:!1}}],ee=c({props:{placement:{type:String,default:"auto"},offset:{type:Number,default:6},popperOptions:{type:Object,default:()=>({})}},data:()=>({popperInstance:void 0}),watch:{placement(e){this.popperInstance&&this.popperInstance.setOptions({placement:e})}},beforeUnmount(){this.destroyPopper()},methods:{createPopper(){if(typeof window>"u")return;const e=bt({offset:this.offset});this.popperInstance=At(this.$refs.wrapper,this.$refs.popup,{strategy:"fixed",placement:this.placement,modifiers:e,...this.popperOptions})},destroyPopper(){this.popperInstance&&(this.popperInstance.destroy(),this.popperInstance=void 0)}}}),Ge=c({props:{disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:void 0},trigger:{type:Array,default:()=>["hover","click","focus"]},interactable:{type:Boolean,default:!0},hoverHideDelay:{type:Number,default:300}},emits:["update:modelValue","click-outside"],data(){return{visible:this.modelValue,triggerStack:0,hoverHideTransition:!1}},watch:{modelValue(e){e?this.show():this.hide()}},mounted(){if(!this.$slots.default)throw new Error("Popup components require one child element to be used as trigger.");this.addEventListeners()},beforeUnmount(){this.removeEventListeners()},methods:{show(){this.disabled||this.visible||(this.triggerStack+=1,this.visible=!0,this.createPopper(),this.$emit("update:modelValue",!0))},hide(){!this.disabled&&this.visible&&(this.triggerStack-=1,this.triggerStack<=0&&(this.triggerStack=0,this.visible=!1,this.$emit("update:modelValue",!1)))},hoverShow(){this.hoverHideTransition=!1,this.show()},hoverHide(){this.hoverHideTransition=!0,setTimeout(()=>{this.hoverHideTransition&&this.hide()},this.hoverHideDelay)},onClick(){this.visible?this.hide():this.show()},onClickOutside(e){this.visible&&this.$emit("click-outside",e),this.modelValue||this.hide()},addEventListeners(){[].concat(this.trigger).forEach(e=>{switch(e){case"hover":O(this.$refs.trigger,"mouseenter",this.interactable?this.hoverShow:this.show),O(this.$refs.trigger,"mouseleave",this.interactable?this.hoverHide:this.hide),this.interactable&&(O(this.$refs.popup,"mouseenter",this.hoverShow),O(this.$refs.popup,"mouseleave",this.hoverHide));break;case"click":O(this.$refs.trigger,"click",this.onClick);break;case"focus":for(const t of this.$refs.trigger.children)O(t,"focus",this.show),O(t,"blur",this.hide)}})},removeEventListeners(){[].concat(this.trigger).forEach(e=>{switch(e){case"hover":T(this.$refs.trigger,"mouseenter",this.interactable?this.hoverShow:this.show),T(this.$refs.trigger,"mouseleave",this.interactable?this.hoverHide:this.hide),this.interactable&&(T(this.$refs.popup,"mouseenter",this.hoverShow),T(this.$refs.popup,"mouseleave",this.hoverHide));break;case"click":T(this.$refs.trigger,"click",this.onClick);break;case"focus":for(const t of this.$refs.trigger.children)T(t,"focus",this.show),T(t,"blur",this.hide)}})},focusTrigger(){for(const e of this.$refs.trigger.children)if(ot(e)){e.focus();break}}}}),oe="IAlert",Ut=c({name:oe,inheritAttrs:!1,props:{size:{type:String,default:u(oe,"size"),validator:v},color:{type:String,default:u(oe,"color")},modelValue:{type:Boolean,default:!0},dismissible:{type:Boolean,default:!1},dismissAriaLabel:{type:String,default:"Dismiss"}},emits:["update:modelValue"],data:()=>({dismissed:!1}),computed:{classes(){return{[`-${this.color}`]:!!this.color,[`-${this.size}`]:!!this.size,"-dismissible":this.dismissible,"-with-icon":!!this.$slots.icon}}},watch:{modelValue(e){this.dismissed=!e}},methods:{dismiss(){this.dismissed=!0,this.$emit("update:modelValue",!1)}}}),Ht={key:0,class:"icon",role:"img","aria-hidden":"true"},Qt={class:"content"},Yt=["aria-label"],Jt=p(Ut,[["render",function(e,t,i,a,s,r){return $((l(),o("div",h({class:["alert",e.classes],role:"alert"},e.$attrs),[e.$slots.icon?(l(),o("span",Ht,[d(e.$slots,"icon")])):f("",!0),m("div",Qt,[d(e.$slots,"default")]),e.dismissible?(l(),o("span",{key:1,class:"dismiss",role:"button","aria-label":e.dismissAriaLabel,onClick:t[0]||(t[0]=(...n)=>e.dismiss&&e.dismiss(...n))},[d(e.$slots,"dismiss",{},()=>[t[1]||(t[1]=P("×",-1))])],8,Yt)):f("",!0)],16)),[[x,!e.dismissed]])}]]),de="IBadge",Xt=p(c({name:de,inheritAttrs:!1,props:{color:{type:String,default:u(de,"color")},size:{type:String,default:u(de,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["badge",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),ue="IBreadcrumb",ei=c({name:ue,inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Breadcrumbs"},color:{type:String,default:u(ue,"color")},size:{type:String,default:u(ue,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),ti=["aria-label"],ii=p(ei,[["render",function(e,t,i,a,s,r){return l(),o("nav",h({class:["breadcrumb",e.classes],"aria-label":e.ariaLabel},e.$attrs),[m("ol",null,[d(e.$slots,"default")])],16,ti)}]]),ai=c({name:"IBreadcrumbItem",mixins:[Z],inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},href:{type:String,default:""},to:{type:[String,Object],default:""},tabindex:{type:[Number,String],default:0}},computed:{classes(){return{"-active":this.active,"-disabled":this.disabled}},tabIndex(){return this.disabled||this.active?-1:this.tabindex}}}),si=["is","href","to","tabindex","aria-current"],ni=p(ai,[["render",function(e,t,i,a,s,r){return l(),o("li",h({class:["breadcrumb-item",e.classes]},e.$attrs),[m("a",{is:e.isTag,href:e.href,to:e.to,tabindex:e.tabIndex,"aria-current":e.active?"location":null},[d(e.$slots,"default",{},void 0,!0)],8,si)],16)}],["__scopeId","data-v-28fa6b16"]]),ce="ILoader",li=c({name:ce,inheritAttrs:!1,props:{color:{type:String,default:u(ce,"color")},size:{type:String,default:u(ce,"size")}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),ri={key:0,class:"loader-text"},yt=p(li,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["loader",e.classes],role:"img","aria-hidden":"true"}),[e.$slots.default?(l(),o("span",ri,[d(e.$slots,"default")])):f("",!0),t[0]||(t[0]=m("svg",{viewBox:"25 25 50 50"},[m("circle",{cx:"50",cy:"50",r:"20",fill:"none","stroke-width":"4","stroke-miterlimit":"10"})],-1))],16)}]]),pe="IButton",vt=p(c({name:pe,components:{ILoader:yt},mixins:[Z],inject:{buttonGroup:{default:()=>({})},form:{default:()=>({})},formGroup:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},block:{type:Boolean,default:!1},circle:{type:Boolean,default:!1},color:{type:String,default:u(pe,"color")},disabled:{type:Boolean,default:!1},link:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},outline:{type:Boolean,default:!1},tag:{type:String,default:"button"},tabindex:{type:[Number,String],default:0},size:{type:String,default:u(pe,"size"),validator:v}},computed:{ariaBusy(){return this.role!=="button"?null:this.loading?"true":"false"},ariaDisabled(){return this.role!=="button"?null:this.disabled?"true":"false"},ariaPressed(){return this.role!=="button"?null:this.active?"true":"false"},classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-active":this.active,"-block":this.block,"-circle":this.circle,"-disabled":this.isDisabled,"-link":this.link,"-outline":this.outline}},isDisabled(){return this.disabled||this.buttonGroup.disabled||this.form.disabled||this.formGroup.disabled},role(){return this.$attrs.to||this.$attrs.href?"link":"button"},tabIndex(){return this.isDisabled?-1:this.tabindex}}}),[["render",function(e,t,i,a,s,r){const n=A("i-loader");return l(),V(Y(e.isTag),h(e.$attrs,{class:["button",e.classes],tag:e.tag,role:e.role,tabindex:e.tabIndex,disabled:e.isDisabled||e.loading,"aria-disabled":e.ariaDisabled,"aria-pressed":e.ariaPressed,"aria-busy":e.ariaBusy,"aria-live":"polite"}),{default:y(()=>[e.loading?d(e.$slots,"loading",{key:0},()=>[I(n)]):f("",!0),e.loading?f("",!0):d(e.$slots,"default",{key:1})]),_:3},16,["tag","role","tabindex","class","disabled","aria-disabled","aria-pressed","aria-busy"])}]]),oi=c({name:"IButtonGroup",inject:{form:{default:()=>({})},buttonGroup:{default:()=>({})},formGroup:{default:()=>({})}},provide(){return{buttonGroup:this}},inheritAttrs:!1,props:{vertical:{type:Boolean,default:!1},block:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1}},computed:{classes(){return{"-vertical":this.vertical,"-block":this.block,"-disabled":this.isDisabled}},isDisabled(){return this.disabled||this.buttonGroup.disabled||this.form.disabled||this.formGroup.disabled}}}),di=["aria-disabled"],ui=p(oi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["button-group",e.classes],role:"group","aria-disabled":e.isDisabled},e.$attrs),[d(e.$slots,"default")],16,di)}]]),H={};for(const e of je){e!==""&&(H[e]={type:[String,Boolean,Number],default:!1});for(const t of["first","last"])H[`${t}${Re(e)}`]={type:Boolean,default:!1};for(const t of["offset","push","pull"])H[`${t}${Re(e)}`]={type:[String,Number],default:""}}const Ze=p(c({name:"IColumn",inheritAttrs:!1,props:H,computed:{classes(){return Object.keys(H).reduce((e,t)=>(this[t]&&(e[rt(`-${t}`,this[t])]=!0),e),{})}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["column",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),Ke=p(c({name:"IContainer",inheritAttrs:!1,props:{fluid:{type:Boolean,default:!1}},computed:{classes(){return{"-fluid":this.fluid}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["container",e.classes]},e.$attrs),[d(e.$slots,"default")],16)}]]),he="ICard",ci=c({name:he,inheritAttrs:!1,props:{color:{type:String,default:u(he,"color")},size:{type:String,default:u(he,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),pi={key:0,class:"card-header"},hi={key:1,class:"card-body"},mi={key:2,class:"card-footer"},fi=p(ci,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["card",e.classes]},e.$attrs),[e.$slots.header?(l(),o("header",pi,[d(e.$slots,"header")])):f("",!0),d(e.$slots,"image"),e.$slots.default?(l(),o("div",hi,[d(e.$slots,"default")])):f("",!0),e.$slots.footer?(l(),o("footer",mi,[d(e.$slots,"footer")])):f("",!0)],16)}]]),me="ICheckbox",bi=c({name:me,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(me,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:!1},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("checkbox")},native:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(me,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-native":this.native}},checked(){return this.formGroup.checked?this.formGroup.checked.includes(this.value):this.schema?this.schema.value:this.modelValue},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i,a,s;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),(s=(a=this.formGroup).onChange)==null||s.call(a,this.value),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),yi=["aria-checked"],vi=["checked","name","disabled","readonly",".indeterminate"],gi=["aria-checked","aria-disabled","aria-readonly","tabindex"],ki=p(bi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["checkbox",e.classes],"aria-checked":e.checked?"true":"false",role:"checkbox"},e.$attrs),[m("input",{ref:"input",type:"checkbox",checked:e.checked,tabindex:"-1",name:e.name,disabled:e.isDisabled,readonly:e.isReadonly,".indeterminate":e.indeterminate,"aria-hidden":"true",onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,vi),m("label",{class:"checkbox-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n)),onClick:t[2]||(t[2]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,gi)],16,yi)}]]),fe="ICheckboxGroup",$i=c({name:fe,mixins:[R],provide(){return{formGroup:this}},inheritAttrs:!1,props:{color:{type:String,default:u(fe,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},modelValue:{default:()=>[]},name:{type:[String,Number],default:()=>S("checkbox-group")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(fe,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},checked(){return this.schema?this.schema.value:this.modelValue}},methods:{onChange(e){var a,s;const t=[...this.modelValue],i=t.findIndex(r=>r===e);i!==-1?t.splice(i,1):t.push(e),(s=(a=this.parent).onInput)==null||s.call(a,this.name,t),this.$emit("update:modelValue",t)}}}),wi=["name"],Ii=p($i,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["form-group checkbox-group",e.classes],name:e.name,role:"checkboxgroup"},e.$attrs),[d(e.$slots,"default",{},void 0,!0)],16,wi)}],["__scopeId","data-v-4043b2ce"]]),be="ICollapsible",xi=c({name:be,provide(){return{collapsible:this}},inheritAttrs:!1,props:{accordion:{type:Boolean,default:!1},color:{type:String,default:u(be,"color")},size:{type:String,default:u(be,"size"),validator:v},modelValue:{type:Array,default:()=>[]}},emits:["update:modelValue"],data(){return{activeItems:[].concat(this.modelValue)}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},watch:{modelValue(e){this.activeItems=[].concat(e)}},methods:{onItemClick(e){if(this.accordion)return this.activeItems=this.activeItems.indexOf(e.name)>-1?[]:[e.name],this.activeItems;const t=this.activeItems.indexOf(e.name);t>-1?this.activeItems.splice(t,1):this.activeItems.push(e.name),this.$emit("update:modelValue",this.activeItems)}}}),Ci=p(xi,[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:["collapsible",e.classes],role:"tablist","aria-multiselectable":"true"},e.$attrs),[d(e.$slots,"default")],16)}]]),gt=p(c({name:"IExpandTransition",methods:{onEnter(e){const t=q(e,"width");e.style.width=t,e.style.position="absolute",e.style.visibility="hidden",e.style.height="auto";const i=q(e,"height");e.style.width=null,e.style.position=null,e.style.visibility=null,e.style.height=0,q(e,"height"),setTimeout(()=>{e.style.height=i})},onAfterEnter(e){e.style.height="auto"},onLeave(e){e.style.height=q(e,"height"),q(e,"height"),setTimeout(()=>{e.style.height=0})}}}),[["render",function(e,t,i,a,s,r){return l(),V(L,{name:"expand",onEnter:e.onEnter,onAfterEnter:e.onAfterEnter,onLeave:e.onLeave},{default:y(()=>[d(e.$slots,"default",{},void 0,!0)]),_:3},8,["onEnter","onAfterEnter","onLeave"])}],["__scopeId","data-v-1e395af5"]]),Si=c({name:"ICollapsibleItem",components:{IExpandTransition:gt},inject:{collapsible:{default:()=>({activeItems:[]})}},inheritAttrs:!1,props:{name:{type:String,default:()=>S("collapsible-item")},title:{type:String,default:""}},computed:{active(){return this.collapsible.activeItems.indexOf(this.name)>-1},classes(){return{"-active":this.active}}},methods:{onClick(){this.collapsible.onItemClick(this)}}}),Bi=["name"],zi=["id","aria-expanded","aria-controls","aria-describedby"],Ai=["id","aria-hidden","aria-labelledby"],Vi={class:"content"},Oi=p(Si,[["render",function(e,t,i,a,s,r){const n=A("i-expand-transition");return l(),o("div",h({class:["collapsible-item",e.classes],name:e.name},e.$attrs),[m("a",{class:"collapsible-header",role:"tab",id:`collapsible-item-heading-${e.name}`,"aria-expanded":e.active?"true":"false","aria-controls":`collapsible-item-content-${e.name}`,"aria-describedby":`collapsible-item-content-${e.name}`,tabindex:"0",onClick:t[0]||(t[0]=(...b)=>e.onClick&&e.onClick(...b)),onKeydown:[t[1]||(t[1]=E(Q((...b)=>e.onClick&&e.onClick(...b),["prevent"]),["space"])),t[2]||(t[2]=E(Q((...b)=>e.onClick&&e.onClick(...b),["prevent"]),["enter"]))]},[d(e.$slots,"header",{},()=>[P(M(e.title),1)]),t[3]||(t[3]=m("i",{class:"icon"},null,-1))],40,zi),I(n,null,{default:y(()=>[$(m("div",{class:"collapsible-body",role:"tabpanel",id:`collapsible-item-content-${e.name}`,"aria-hidden":e.active?"false":"true","aria-labelledby":`collapsible-item-heading-${e.name}`},[m("div",Vi,[d(e.$slots,"default")])],8,Ai),[[x,e.active]])]),_:3})],16,Bi)}]]),Li=(e,t)=>i=>{(function(a){return!!a&&!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)})(e)&&i.target&&(e===i.target||e.contains(i.target)||t.value(i))},K={beforeMount(e,t){typeof window<"u"&&O(window.document,"mousedown",Li(e,t))}},ye="IDropdown",Di=c({name:ye,directives:{ClickOutside:K},mixins:[ee,Ge],provide(){return{dropdown:this}},inject:{navbar:{default:()=>({onItemClick:()=>{}})},sidebar:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{animationDuration:{type:Number,default:300},color:{type:String,default:u(ye,"color")},disabled:{type:Boolean,default:!1},hideOnItemClick:{type:Boolean,default:!0},keydownTrigger:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},keydownItem:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},modelValue:{type:Boolean,default:!1},arrow:{type:Boolean,default:!0},placement:{type:String,default:"bottom"},trigger:{type:[String,Array],default:()=>["click"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!0},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(ye,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},mounted(){for(const e of this.$refs.trigger.children)O(e,"keydown",this.onTriggerKeyDown);O(this.$refs.popup,"keydown",this.onItemKeyDown)},beforeUnmount(){for(const e of this.$refs.trigger.children)T(e,"keydown",this.onTriggerKeyDown);T(this.$refs.popup,"keydown",this.onItemKeyDown)},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)},getFocusableItems(){const e=[];for(const t of this.$refs.body.children)Fe(t)&&e.push(t);return e},onTriggerKeyDown(e){if(this.keydownTrigger.length===0)return;const t=this.getFocusableItems(),i=t.findIndex(s=>s.active),a=t[i>-1?i:0];switch(!0){case(k("up",e)&&this.keydownTrigger.includes("up")):case(k("down",e)&&this.keydownTrigger.includes("down")):this.show(),setTimeout(()=>{a.focus()},this.visible?0:this.animationDuration),e.preventDefault(),e.stopPropagation();break;case(k("enter",e)&&this.keydownTrigger.includes("enter")):case(k("space",e)&&this.keydownTrigger.includes("space")):this.onClick(),this.visible||setTimeout(()=>{a.focus()},this.animationDuration),e.preventDefault();break;case(k("tab",e)&&this.keydownTrigger.includes("tab")):case(k("esc",e)&&this.keydownTrigger.includes("esc")):this.hide()}},onItemKeyDown(e){if(this.keydownItem.length!==0)switch(!0){case(k("up",e)&&this.keydownItem.includes("up")):case(k("down",e)&&this.keydownItem.includes("down")):const t=this.getFocusableItems(),i=t.findIndex(r=>r===e.target),a=t.length-1;let s;s=k("up",e)?i>0?i-1:0:i{e.onItemClick()})}}}),Ni={class:"dropdown-trigger",ref:"trigger"},Ti=["aria-hidden"],Pi={key:0,"data-popper-arrow":""},Ri={key:1,class:"dropdown-header"},Ei={key:2,class:"dropdown-body",ref:"body"},_i={key:3,class:"dropdown-footer"},Mi=p(Di,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h({class:"dropdown-wrapper",ref:"wrapper","aria-haspopup":"true",onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))},e.$attrs),[m("div",Ni,[d(e.$slots,"default")],512),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:z(["dropdown",e.classes]),role:"menu",ref:"popup","aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",Pi)):f("",!0),e.$slots.header?(l(),o("div",Ri,[d(e.$slots,"header")])):f("",!0),e.$slots.body?(l(),o("div",Ei,[d(e.$slots,"body")],512)):f("",!0),e.$slots.footer?(l(),o("div",_i,[d(e.$slots,"footer")])):f("",!0)],10,Ti),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16)),[[n,e.onClickOutside]])}]]),Fi=p(c({name:"IDropdownDivider",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("div",h({class:"dropdown-divider",role:"separator"},e.$attrs),null,16)}]]),ji=p(c({name:"IDropdownItem",mixins:[Z],inject:{dropdown:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},plaintext:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{classes(){return{"-active":this.active,"-disabled":this.disabled,"-plaintext":this.plaintext}},role(){return this.$attrs.to||this.$attrs.href?"link":"menuitem"},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(e){var t,i;(i=(t=this.dropdown).onItemClick)==null||i.call(t,this,e)}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["dropdown-item",e.classes],role:e.role,tag:e.tag,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.disabled,"aria-pressed":e.active,onClick:e.onClick}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["class","role","tag","tabindex","disabled","aria-disabled","aria-pressed","onClick"])}]]),Xe="IForm",Gi=c({name:Xe,mixins:[R],provide(){return{form:this}},inheritAttrs:!1,props:{color:{type:String,default:""},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},loading:{type:Boolean,default:!1},name:{type:String,default:()=>S("form")},modelValue:{type:Object,default:()=>null},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Xe,"size"),validator:v}},emits:["update:modelValue","submit"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},schema(){return this.modelValue?this.modelValue:_(this.formGroup.schema||this.form.schema||{},this.name)}},methods:{onBlur(e,t){var i,a;if((a=(i=this.parent).onBlur)==null||a.call(i,this.name?`${this.name}.${e}`:e,t),this.modelValue){let s=U(this.modelValue);s=ne(s,e,{untouched:!1,touched:!0}),this.shouldValidate(e,"blur")&&(s=le(s)),this.$emit("update:modelValue",s)}},onInput(e,t){var i,a;if((a=(i=this.parent).onInput)==null||a.call(i,this.name?`${this.name}.${e}`:e,t),this.modelValue){let s=U(this.modelValue);s=function(r,n,b,B){return _(r,n)[b]=B,r}(s,e,"value",t),s=ne(s,e,{pristine:!1,dirty:!0}),this.shouldValidate(e,"input")&&(s=le(s)),this.$emit("update:modelValue",s)}},onSubmit(e){if(e.preventDefault(),this.modelValue){let t=U(this.modelValue);if(t=ne(le(t),"",{untouched:!1,touched:!0}),this.$emit("update:modelValue",t),t.invalid)return}this.$emit("submit",e)},shouldValidate(e,t){const i=_(this.modelValue,e);return(i.validateOn?[].concat(i.validateOn):this.$inkline.options.validateOn).includes(t)}}}),Zi=["name","readonly","disabled"],Ki=p(Gi,[["render",function(e,t,i,a,s,r){return l(),o("form",h(e.$attrs,{class:["form",e.classes],role:"form",name:e.name,readonly:e.isReadonly,disabled:e.isDisabled,onSubmit:t[0]||(t[0]=(...n)=>e.onSubmit&&e.onSubmit(...n))}),[d(e.$slots,"default",{},void 0,!0)],16,Zi)}],["__scopeId","data-v-575ccba7"]]),ve="IFormGroup",qi=c({name:ve,mixins:[R],provide(){return{formGroup:this}},props:{color:{type:String,default:u(ve,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},name:{type:String,default:""},readonly:{type:Boolean,default:!1},required:{type:Boolean,default:!1},size:{type:String,default:u(ve,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline,"-required":this.required}}},methods:{onBlur(e,t){var i,a;(a=(i=this.parent)==null?void 0:i.onBlur)==null||a.call(i,this.name?`${this.name}.${e}`:e,t)},onInput(e,t){var i,a;(a=(i=this.parent)==null?void 0:i.onInput)==null||a.call(i,this.name?`${this.name}.${e}`:e,t)}}}),Wi=["name"],Ui=p(qi,[["render",function(e,t,i,a,s,r){return l(),o("fieldset",{class:z(["form-group",e.classes]),name:e.name,role:"group"},[d(e.$slots,"default")],10,Wi)}]]),Hi=c({name:"IFormError",inject:{formGroup:{default:()=>({})},form:{default:()=>({})}},props:{for:{type:String,default:""},visible:{type:[Array,String],default:()=>["touched","dirty","invalid"]}},computed:{parent(){return this.formGroup.$?this.formGroup:this.form},schema(){return this.for!==""?_(this.parent.schema||{},`${this.for}`):this.parent.schema||{}},errors(){return this.schema.errors||[]},isVisible(){let e=!0;return this.schema&&this.visible&&[].concat(this.visible).forEach(t=>{e=e&&this.schema[t]}),e}}}),Qi={key:0,class:"form-error","aria-live":"polite"},Yi=p(Hi,[["render",function(e,t,i,a,s,r){return e.schema?$((l(),V(L,{key:0,name:"fade-in-transition"},{default:y(()=>[e.errors.length>0?(l(),o("ul",Qi,[(l(!0),o(j,null,X(e.errors,n=>(l(),o("li",null,M(n.message),1))),256))])):f("",!0)]),_:1},512)),[[x,e.isVisible]]):f("",!0)}]]),et="IFormLabel",Ji=c({name:et,mixins:[R],props:{for:{type:String,default:""},placement:{type:String,default:""},size:{type:String,default:u(et,"size"),validator:v}},computed:{classes(){return{[`-${this.size}`]:!!this.size,[`-${this.placement}`]:!!this.placement}},forAttr(){return this.for}},methods:{getNextSibling(){return this.$el.nextSibling.querySelector("input, textarea")},onClick(){var e;this.for||((e=this.getNextSibling())==null||e.focus())}}}),Xi=["for"],ea=p(Ji,[["render",function(e,t,i,a,s,r){return l(),o("label",h(e.$attrs,{class:["form-label",e.classes],for:e.forAttr,onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),[d(e.$slots,"default")],16,Xi)}]]),tt="IHamburgerMenu",kt=p(c({name:tt,inheritAttrs:!1,props:{animation:{type:String,default:"close"},color:{type:String,default:u(tt,"color")},modelValue:{type:Boolean,default:!1}},emits:["update:modelValue"],computed:{classes(){return{...g(this),"-active":this.modelValue,[`-${this.animation}`]:!0}}},methods:{onClick(){this.$emit("update:modelValue",!this.modelValue)}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["hamburger-menu",e.classes],onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),t[1]||(t[1]=[m("span",{class:"hamburger-menu-bars"},null,-1)]),16)}]]),_e={};for(const e of je)for(const t of["start","center","end","top","middle","bottom","around","between","reverse"])_e[`${t}${Re(e)}`]={type:Boolean,default:!1};const qe=p(c({name:"IRow",inheritAttrs:!1,props:{noGutter:{type:Boolean,default:!1},noCollapse:{type:Boolean,default:!1},..._e},computed:{classes(){const e=Object.keys(_e).reduce((t,i)=>(this[i]&&(t[rt(`-${i}`,this[i])]=!0),t),{});return{"-no-gutter":this.noGutter,"-no-collapse":this.noCollapse,...e}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["row",e.classes]}),[d(e.$slots,"default")],16)}]]),ge="IHeader",ta=p(c({name:ge,components:{IContainer:Ke,IRow:qe,IColumn:Ze},inheritAttrs:!1,props:{color:{type:String,default:u(ge,"color")},cover:{type:Boolean,default:!1},fluid:{type:Boolean,default:!1},fullscreen:{type:Boolean,default:!1},size:{type:String,default:u(ge,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-cover":this.cover,"-fullscreen":this.fullscreen}}}}),[["render",function(e,t,i,a,s,r){const n=A("i-column"),b=A("i-row"),B=A("i-container");return l(),o("header",h(e.$attrs,{class:["header",e.classes]}),[I(B,{fluid:e.fluid},{default:y(()=>[I(b,null,{default:y(()=>[I(n,null,{default:y(()=>[d(e.$slots,"default")]),_:3})]),_:3})]),_:3},8,["fluid"])],16)}]]),it="IIcon",$t=p(c({name:it,inheritAttrs:!1,props:{name:{type:String,default:""},size:{type:String,default:u(it,"size"),validator:v}},setup(e){const t=Vt("inklineIcons"),i=se(()=>function(r,n="dash"){const b=n==="dash"?/-([a-z0-9])/g:/_([a-z0-9])/g;return r.replace(b,(B,F)=>F.toUpperCase())}(e.name)),a=se(()=>t[i.value]),s=se(()=>({"inkline-icon":!0,[`-${e.size}`]:!!e.size}));return Ot(()=>{i.value&&t[i.value]}),()=>{var r,n;return nt("svg",{class:s.value,...(r=a.value)==null?void 0:r.attributes},ct(((n=a.value)==null?void 0:n.children)||[]))}}}),[["render",function(e,t,i,a,s,r){const n=A("icon");return l(),V(n,h(e.$attrs,{size:e.size}),null,16,["size"])}]]),ke="IInput",ia=c({name:ke,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(ke,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},error:{type:[Array,Boolean],default:()=>["touched","dirty","invalid"]},id:{type:String,default:void 0},modelValue:{type:[String,Number],default:""},name:{type:[String,Number],default:()=>S("input")},plaintext:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(ke,"size"),validator:v},tabindex:{type:[Number,String],default:0},type:{type:String,default:"text"},clearAriaLabel:{type:String,default:"Clear"}},emits:["update:modelValue","clear"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-error":this.hasError,"-readonly":this.isReadonly,"-prefixed":!!this.$slots.prefix,"-suffixed":!!this.$slots.suffix,"-prepended":!!this.$slots.prepend,"-appended":!!this.$slots.append}},hasError(){if(typeof this.error=="boolean")return this.error;if(this.schema&&this.error){let e=!0;return[].concat(this.error).forEach(t=>{e=e&&this.schema[t]}),e}return!1},tabIndex(){return this.isDisabled?-1:this.tabindex},isClearable(){return this.clearable&&!this.isDisabled&&!this.isReadonly&&this.value!==""},value(){return this.schema?this.schema.value:this.modelValue}},methods:{onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)},onInput(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.value),this.$emit("update:modelValue",e.target.value)},onClear(e){this.$emit("update:modelValue",""),this.$emit("clear",e)},focus(){this.$refs.input.focus()}}}),aa={key:0,class:"input-prepend"},sa={class:"input"},na={key:0,class:"input-prefix"},la=["value","name","id","type","tabindex","disabled","aria-disabled","readonly","aria-readonly"],ra={key:1,class:"input-suffix"},oa=["aria-label","aria-hidden"],da={key:1,class:"input-append"},te=p(ia,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["input-wrapper",e.classes])},[e.$slots.prepend?(l(),o("div",aa,[d(e.$slots,"prepend")])):f("",!0),m("div",sa,[e.$slots.prefix?(l(),o("span",na,[d(e.$slots,"prefix")])):f("",!0),m("input",h(e.$attrs,{value:e.value,ref:"input",name:e.name,id:e.id,type:e.type,tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly||e.plaintext,"aria-readonly":!(!e.isReadonly&&!e.plaintext)&&"true",onInput:t[0]||(t[0]=(...n)=>e.onInput&&e.onInput(...n)),onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n))}),null,16,la),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",ra,[d(e.$slots,"clearable",{clear:e.onClear},()=>[e.clearable?$((l(),o("i",{key:0,class:"input-clear",role:"button","aria-label":e.clearAriaLabel,"aria-hidden":e.isClearable?"false":"true",onClick:t[2]||(t[2]=(...n)=>e.onClear&&e.onClear(...n))},null,8,oa)),[[x,e.isClearable]]):f("",!0)]),d(e.$slots,"suffix")])):f("",!0)]),e.$slots.append?(l(),o("div",da,[d(e.$slots,"append")])):f("",!0)],2)}]]),$e="INumberInput",ua=c({name:$e,components:{IButton:vt},extends:te,inheritAttrs:!1,props:{color:{type:String,default:u($e,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},id:{type:String,default:""},modelValue:{type:[String,Number],default:""},name:{type:[String,Number],default:()=>S("input")},readonly:{type:Boolean,default:!1},size:{type:String,default:u($e,"size"),validator:v},tabindex:{type:[Number,String],default:0},min:{type:[Number,String],default:-1/0},max:{type:[Number,String],default:1/0},precision:{type:Number,default:0},step:{type:Number,default:1}},emits:["update:modelValue"],watch:{modelValue:{immediate:!0,handler(e){var i,a;let t=(e||"").toString().replace(/^[^0-9-]/,"").replace(/^(-)[^0-9]/,"$1").replace(new RegExp(`^(-?[0-9]+)[^0-9${this.precision>0?".":""}]`),"$1");this.precision>0&&(t=t.replace(/^(-?[0-9]+\.)[^0-9]/,"$1").replace(new RegExp(`^(-?[0-9]+\\.[0-9]{0,${this.precision}}).*`),"$1")),parseFloat(t)>=parseFloat(this.max)&&(t=this.max.toString()),parseFloat(t)<=parseFloat(this.min)&&(t=this.min.toString()),(a=(i=this.parent).onInput)==null||a.call(i,this.name,t),this.$emit("update:modelValue",t)}}},methods:{decrease(){this.$emit("update:modelValue",this.formatPrecision((Number(this.modelValue)-this.step).toString()))},increase(){this.$emit("update:modelValue",this.formatPrecision((Number(this.modelValue)+this.step).toString()))},formatPrecision(e){const t=e.split(".");let i=t[1]||"";for(let a=i.length;a0?`${t[0]}.${i}`:t[0]},onBlurFormatPrecision(e){var t,i;this.$emit("update:modelValue",this.formatPrecision(Number(this.modelValue).toString())),(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),ca={class:"input-prepend"},pa={class:"input"},ha={key:0,class:"input-prefix"},ma=["value","name","id","tabindex","disabled","aria-disabled","readonly","aria-readonly"],fa={key:1,class:"input-suffix"},ba=["aria-label","aria-hidden"],ya={class:"input-append"},va=p(ua,[["render",function(e,t,i,a,s,r){const n=A("i-button");return l(),o("div",{class:z(["input-wrapper -prepended -appended",e.classes])},[m("div",ca,[d(e.$slots,"prepend"),I(n,{type:"button",color:e.color,size:e.size,disabled:e.disabled,class:"input-button-decrease",onClick:e.decrease},{default:y(()=>t[3]||(t[3]=[P(" - ",-1)])),_:1,__:[3]},8,["color","size","disabled","onClick"])]),m("div",pa,[e.$slots.prefix?(l(),o("span",ha,[d(e.$slots,"prefix")])):f("",!0),m("input",h(e.$attrs,{value:e.value,ref:"input",name:e.name,id:e.id,type:"text",tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly,"aria-readonly":!!e.isReadonly&&"true",onInput:t[0]||(t[0]=(...b)=>e.onInput&&e.onInput(...b)),onBlur:t[1]||(t[1]=(...b)=>e.onBlur&&e.onBlur(...b))}),null,16,ma),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",fa,[d(e.$slots,"clearable",{clear:e.onClear},()=>[e.clearable?$((l(),o("i",{key:0,class:"input-clear",role:"button","aria-label":e.clearAriaLabel,"aria-hidden":e.isClearable?"false":"true",onClick:t[2]||(t[2]=(...b)=>e.onClear&&e.onClear(...b))},null,8,ba)),[[x,e.isClearable]]):f("",!0)]),d(e.$slots,"suffix")])):f("",!0)]),m("div",ya,[I(n,{type:"button",color:e.color,size:e.size,disabled:e.disabled,class:"input-button-increase",onClick:e.increase},{default:y(()=>t[4]||(t[4]=[P(" + ",-1)])),_:1,__:[4]},8,["color","size","disabled","onClick"]),d(e.$slots,"append")])],2)}]]),we="ITextarea",ga=c({name:we,extends:te,inheritAttrs:!1,props:{color:{type:String,default:u(we,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},id:{type:String,default:""},modelValue:{type:String,default:""},name:{type:[String,Number],default:()=>S("textarea")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(we,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"]}),ka={key:0,class:"input-prepend"},$a={class:"input"},wa={key:0,class:"input-prefix"},Ia=["value","name","id","tabindex","disabled","aria-disabled","readonly","aria-readonly"],xa={key:1,class:"input-suffix"},Ca={key:1,class:"input-append"},Sa=p(ga,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["input-wrapper",e.classes])},[e.$slots.prepend?(l(),o("div",ka,[d(e.$slots,"prepend")])):f("",!0),m("div",$a,[e.$slots.prefix?(l(),o("span",wa,[d(e.$slots,"prefix")])):f("",!0),m("textarea",h(e.$attrs,{value:e.value,ref:"input",role:"textbox",name:e.name,id:e.id,tabindex:e.tabIndex,disabled:e.isDisabled,"aria-disabled":!!e.isDisabled&&"true",readonly:e.isReadonly,"aria-readonly":!!e.isReadonly&&"true","aria-multiline":"true",onInput:t[0]||(t[0]=(...n)=>e.onInput&&e.onInput(...n)),onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n))}),null,16,Ia),e.$slots.suffix||e.clearable&&e.isClearable?(l(),o("span",xa,[d(e.$slots,"clearable",{clear:e.onClear},()=>[$(m("i",{class:"input-clear","aria-label":"Clear",onClick:t[2]||(t[2]=(...n)=>e.onClear&&e.onClear(...n))},null,512),[[x,e.isClearable]])]),d(e.$slots,"suffix")])):f("",!0)]),e.$slots.append?(l(),o("div",Ca,[d(e.$slots,"append")])):f("",!0)],2)}]]),Ba=p(c({name:"ILayout",inheritAttrs:!1,props:{vertical:{type:Boolean,default:!1}},computed:{classes(){return{"-vertical":this.vertical}}}}),[["render",function(e,t,i,a,s,r){return l(),o("main",h(e.$attrs,{class:["layout",e.classes]}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-9a9f03c1"]]),za=c({name:"ILayoutAside",inheritAttrs:!1}),Aa={class:"layout-aside-children"},Va=p(za,[["render",function(e,t,i,a,s,r){return l(),o("aside",h(e.$attrs,{class:"layout-aside"}),[m("div",Aa,[d(e.$slots,"default",{},void 0,!0)])],16)}],["__scopeId","data-v-6e13c28d"]]),Oa=p(c({name:"ILayoutContent",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("section",h(e.$attrs,{class:"layout-content"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-5b197a5d"]]),La=p(c({name:"ILayoutFooter",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("footer",h(e.$attrs,{class:"layout-footer"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-31bd8dbc"]]),Da=p(c({name:"ILayoutHeader",inheritAttrs:!1}),[["render",function(e,t,i,a,s,r){return l(),o("header",h(e.$attrs,{class:"layout-header"}),[d(e.$slots,"default",{},void 0,!0)],16)}],["__scopeId","data-v-ece9ef9b"]]),Ie="IListGroup",Na=p(c({name:Ie,inheritAttrs:!1,props:{border:{type:Boolean,default:!0},color:{type:String,default:u(Ie,"color")},size:{type:String,default:u(Ie,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-border":this.border}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["list-group",e.classes],role:"list"}),[d(e.$slots,"default")],16)}]]),Ta=p(c({name:"IListGroupItem",mixins:[Z],inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{ariaDisabled(){return this.role==="link"?null:this.disabled?"true":"false"},classes(){return{"-active":this.active,"-disabled":this.disabled}},role(){return this.$attrs.to||this.$attrs.href?"link":"listitem"},tabIndex(){return this.disabled?-1:this.tabindex}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["list-group-item",e.classes],tag:e.tag,role:e.role,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.ariaDisabled}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["tag","role","tabindex","class","disabled","aria-disabled"])}]]),Pa=function(e){const t={};return(...i)=>{const a=JSON.stringify(i);return a in t||(t[a]=e(...i)),t[a]}}(function(e,t){if(!t)return[{text:e}];const i=[],a=e.toLowerCase(),s=t.toLowerCase();let r=0,n=0;for(;n=0;n=B?b:e.length,n&&(i.push({text:e.substring(r,n)}),r=n),B&&(n+=t.length,i.push({text:e.substring(r,n),marked:!0}),r=n)}return i}),Ra=c({name:"IMark",inheritAttrs:!1,props:{text:{type:String,default:""},query:{type:String,default:""}},computed:{parts(){return Pa(this.text,this.query)}}}),Ea={key:0},wt=p(Ra,[["render",function(e,t,i,a,s,r){return l(),o("span",Lt(Dt(e.$attrs)),[(l(!0),o(j,null,X(e.parts,({text:n,marked:b})=>(l(),o(j,null,[b?(l(),o("mark",Ea,M(n),1)):(l(),o(j,{key:1},[P(M(n),1)],64))],64))),256))],16)}]]),_a=c({name:"IMedia",inheritAttrs:!1}),Ma={class:"media-body"},Fa=p(_a,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:"media"}),[d(e.$slots,"image"),m("div",Ma,[d(e.$slots,"default")])],16)}]]),C={instances:{},stack:[],zIndex:1050,register(e){e&&e.name&&(C.instances[e.name]=e)},unregister(e){e&&e.name&&(C.instances[e.name]=null,delete C.instances[e.name])},open(e){typeof window<"u"&&(C.stack.push(e),C.instances[e].$el.style.zIndex=C.zIndex++)},close(e){typeof window<"u"&&C.stack.splice(C.stack.indexOf(e),1)},getTopOverlay(){const e=C.stack.slice(-1)[0]||"";return C.instances[e]},onPressEscape(){const e=C.getTopOverlay();e&&e.closeOnPressEscape&&e.hide()}};typeof window<"u"&&window.addEventListener("keydown",e=>{k("esc",e)&&C.onPressEscape()});const xe="IModal",ja=c({name:xe,directives:{ClickOutside:K},inheritAttrs:!1,props:{closeOnPressEscape:{type:Boolean,default:!0},closeAriaLabel:{type:String,default:"Close"},color:{type:String,default:u(xe,"color")},disabled:{type:Boolean,default:!1},hideOnClickOutside:{type:Boolean,default:!0},name:{type:String,default:()=>S("modal")},showClose:{type:Boolean,default:!0},size:{type:String,default:u(xe,"size"),validator:v},modelValue:{type:Boolean,default:!1},transition:{type:String,default:"zoom-in-center-transition"}},emits:["update:modelValue"],data(){return{visible:this.modelValue}},computed:{classes(){return{"-disabled":this.disabled,...g(this),[`-${this.size}`]:!!this.size}}},watch:{modelValue(e){e?this.show():this.hide()}},mounted(){C.register(this)},unmounted(){C.unregister(this)},methods:{show(){this.disabled||(this.visible=!0,this.$emit("update:modelValue",!0),C.open(this.name),typeof window<"u"&&Me(window.document.body,"-modal"))},hide(){this.disabled||(this.visible=!1,this.$emit("update:modelValue",!1),C.close(this.name),typeof window<"u"&&pt(window.document.body,"-modal"))},onClickOutside(){this.hideOnClickOutside&&this.hide()}}}),Ga=["aria-hidden","id","name","aria-labelledby"],Za={class:"modal"},Ka=["id"],qa=["aria-label"],Wa={key:1,class:"modal-body"},Ua={key:2,class:"modal-footer"},Ha=p(ja,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return l(),V(L,{name:"fade-in-transition"},{default:y(()=>[$(m("div",h(e.$attrs,{class:["modal-wrapper",e.classes],role:"dialog","aria-modal":"true","aria-hidden":e.visible?"false":"true",id:e.name,name:e.name,"aria-labelledby":`${e.name}-header`}),[I(L,{name:e.transition},{default:y(()=>[$((l(),o("div",Za,[e.$slots.header?(l(),o("div",{key:0,class:"modal-header",id:`${e.name}-header`},[d(e.$slots,"header"),e.showClose?(l(),o("button",{key:0,class:"close","aria-hidden":"true","aria-label":e.closeAriaLabel,onClick:t[0]||(t[0]=(...b)=>e.hide&&e.hide(...b))},[d(e.$slots,"close",{},()=>[t[1]||(t[1]=m("i",{class:"icon"},null,-1))])],8,qa)):f("",!0)],8,Ka)):f("",!0),e.$slots.default?(l(),o("div",Wa,[d(e.$slots,"default")])):f("",!0),e.$slots.footer?(l(),o("div",Ua,[d(e.$slots,"footer")])):f("",!0)])),[[n,e.onClickOutside],[x,e.visible]])]),_:3},8,["name"])],16,Ga),[[x,e.visible]])]),_:3})}]]),Ce="INav",Qa=p(c({name:Ce,provide(){return{nav:this}},inject:{navbar:{default:()=>({onItemClick:()=>{}})},sidebar:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{color:{type:String,default:u(Ce,"color")},size:{type:String,default:u(Ce,"size"),validator:v},vertical:{type:Boolean,default:!1}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-vertical":this.vertical}}},methods:{onItemClick(){[this.navbar,this.sidebar].forEach(e=>{e.onItemClick()})}}}),[["render",function(e,t,i,a,s,r){return l(),o("nav",h(e.$attrs,{class:["nav",e.classes],role:"menubar"}),[d(e.$slots,"default")],16)}]]),Ya=p(c({name:"INavItem",mixins:[Z],inject:{nav:{default:()=>({onItemClick:()=>{}})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},stopPropagation:{type:Boolean,default:!1},tag:{type:String,default:"div"},tabindex:{type:[Number,String],default:0}},computed:{ariaDisabled(){return this.role==="link"?null:this.disabled?"true":"false"},classes(){return{"-active":this.active,"-disabled":this.disabled}},role(){return this.$attrs.to||this.$attrs.href?"link":"menuitem"},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(e){this.stopPropagation||this.nav.onItemClick(this,e)}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:["nav-item",e.classes],role:e.role,tag:e.tag,tabindex:e.tabIndex,disabled:e.disabled,"aria-disabled":e.ariaDisabled,onClick:e.onClick}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["role","tag","tabindex","class","disabled","aria-disabled","onClick"])}]]),Se="INavbar",Ja=p(c({name:Se,components:{IContainer:Ke,IRow:qe,IColumn:Ze,IHamburgerMenu:kt},directives:{ClickOutside:K},mixins:[ft],provide(){return{navbar:this}},inheritAttrs:!1,props:{collapseOnItemClick:{type:Boolean,default:!0},collapseOnClickOutside:{type:Boolean,default:!0},color:{type:String,default:u(Se,"color")},fluid:{type:Boolean,default:!1},size:{type:String,default:u(Se,"size"),validator:v},menuAnimation:{type:String,default:"close"}},emits:["update:modelValue"],computed:{classes(){return{...this.collapsibleClasses,...g(this),[`-${this.size}`]:!!this.size}}},methods:{onItemClick(){this.collapseOnItemClick&&this.open&&this.setOpen(!1)},onClickOutside(){this.collapseOnClickOutside&&this.open&&this.setOpen(!1)}}}),[["render",function(e,t,i,a,s,r){const n=A("i-hamburger-menu"),b=A("i-column"),B=A("i-row"),F=A("i-container"),w=G("click-outside");return $((l(),o("nav",h(e.$attrs,{class:["navbar",e.classes]}),[I(F,{fluid:e.fluid},{default:y(()=>[I(B,null,{default:y(()=>[I(b,null,{default:y(()=>[I(n,{class:"collapse-toggle",animation:e.menuAnimation,color:e.color,modelValue:e.open,"onUpdate:modelValue":e.toggleOpen},null,8,["animation","color","modelValue","onUpdate:modelValue"]),d(e.$slots,"default")]),_:3})]),_:3})]),_:3},8,["fluid"])],16)),[[w,e.onClickOutside]])}]]),Xa=p(c({name:"INavbarBrand",mixins:[Z],inheritAttrs:!1,props:{tag:{type:String,default:"div"}}}),[["render",function(e,t,i,a,s,r){return l(),V(Y(e.isTag),h(e.$attrs,{class:"navbar-brand",tag:e.tag,translate:"no"}),{default:y(()=>[d(e.$slots,"default")]),_:3},16,["tag"])}]]),es=c({name:"INavbarCollapsible",components:{IExpandTransition:gt},inject:{navbar:{default:()=>({})}},inheritAttrs:!1,computed:{visible(){const e=typeof window>"u";return this.navbar.open||!this.navbar.collapsible||e}}}),ts=["aria-hidden","aria-expanded"],is=p(es,[["render",function(e,t,i,a,s,r){const n=A("i-expand-transition");return l(),V(n,null,{default:y(()=>[$(m("div",h(e.$attrs,{class:"navbar-collapsible","aria-hidden":e.visible?"false":"true","aria-expanded":e.visible?"true":"false"}),[d(e.$slots,"default")],16,ts),[[x,e.visible]])]),_:3})}]]),Be="IPagination",as=c({name:Be,inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Pagination"},color:{type:String,default:u(Be,"color")},itemsPerPage:{type:Number,default:20},itemsTotal:{type:Number,default:0},limit:{type:[Number,Object],default:()=>({xs:3,sm:5})},quickLink:{type:Boolean,default:!1},modelValue:{type:Number,default:1},size:{type:String,default:u(Be,"size"),validator:v}},emits:["update:modelValue"],data:()=>({pageLimit:5}),computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}},pageCount(){return Math.ceil(this.itemsTotal/this.itemsPerPage)},showQuickPrevious(){return this.pageCount>this.pageLimit&&this.modelValue>this.pageLimit-(this.pageLimit-1)/2},showQuickNext(){return this.pageCount>this.pageLimit&&this.modelValuee.apply(s,a),t)}}(this.onWindowResize,250),typeof window<"u"&&(window.addEventListener("resize",this.debouncedOnWindowResize),this.onWindowResize())},unmounted(){typeof window<"u"&&window.removeEventListener("resize",this.debouncedOnWindowResize)},methods:{next(){this.modelValue!==this.pageCount&&this.onClick(this.modelValue+1)},quickNext(){if(!this.quickLink)return;const e=this.modelValue+(this.pageLimit-2);this.onClick(e>this.pageCount?this.pageCount:e)},previous(){this.modelValue!==1&&this.onClick(this.modelValue-1)},quickPrevious(){if(!this.quickLink)return;const e=this.modelValue-(this.pageLimit-2);this.onClick(e<1?1:e)},onClick(e){this.$emit("update:modelValue",e)},onWindowResize(){if(typeof this.limit=="number")return this.pageLimit=this.limit,this.pageLimit;for(const e of je.slice().reverse())if(this.limit.hasOwnProperty(e)&&typeof window<"u"&&window.innerWidth>=J[e][0])return this.pageLimit=this.limit[e],this.pageLimit}}}),ss=["aria-label"],ns={class:"pagination-items"},ls={"aria-hidden":"true"},rs=["aria-current","onClick"],os={"aria-hidden":"true"},ds=p(as,[["render",function(e,t,i,a,s,r){return l(),o("nav",h(e.$attrs,{class:["pagination",e.classes],role:"navigation","aria-label":e.ariaLabel}),[m("ul",ns,[e.pageCount>0?(l(),o("li",{key:0,class:z(["pagination-item -previous",{"-disabled":e.modelValue===1}]),onClick:t[0]||(t[0]=(...n)=>e.previous&&e.previous(...n))},[m("span",ls,[d(e.$slots,"previous",{},()=>[t[6]||(t[6]=P("<",-1))])])],2)):f("",!0),e.pageCount>0?(l(),o("li",{key:1,class:z(["pagination-item -first",{"-active":e.modelValue===1}]),onClick:t[1]||(t[1]=n=>e.onClick(1))}," 1 ",2)):f("",!0),e.showQuickPrevious?(l(),o("li",{key:2,class:z(["pagination-item -quick-previous",{"-disabled":!e.quickLink}]),onClick:t[2]||(t[2]=(...n)=>e.quickPrevious&&e.quickPrevious(...n))}," … ",2)):f("",!0),(l(!0),o(j,null,X(e.pages,n=>(l(),o("li",{class:z(["pagination-item",{"-active":e.modelValue===n}]),"aria-current":e.modelValue===n&&"page",onClick:b=>e.onClick(n)},M(n),11,rs))),256)),e.showQuickNext?(l(),o("li",{key:3,class:z(["pagination-item -quick-next",{"-disabled":!e.quickLink}]),onClick:t[3]||(t[3]=(...n)=>e.quickNext&&e.quickNext(...n))}," … ",2)):f("",!0),e.pageCount>1?(l(),o("li",{key:4,class:z(["pagination-item -last",{"-active":e.modelValue===e.pageCount}]),onClick:t[4]||(t[4]=n=>e.onClick(e.pageCount))},M(e.pageCount),3)):f("",!0),e.pageCount>0?(l(),o("li",{key:5,class:z(["pagination-item -next",{"-disabled":e.modelValue===e.pageCount}]),onClick:t[5]||(t[5]=(...n)=>e.next&&e.next(...n))},[m("span",os,[d(e.$slots,"next",{},()=>[t[7]||(t[7]=P(">",-1))])])],2)):f("",!0)])],16,ss)}]]),ze="IPopover",us=c({name:ze,directives:{ClickOutside:K},mixins:[ee,Ge],inheritAttrs:!1,props:{color:{type:String,default:u(ze,"color")},disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:!1},name:{type:String,default:()=>S("popover")},arrow:{type:Boolean,default:!0},placement:{type:String,default:"top"},trigger:{type:[String,Array],default:()=>["click"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!1},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(ze,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)}}}),cs=["id"],ps=["aria-describedby","aria-disabled","aria-expanded"],hs=["id","aria-hidden"],ms={key:0,"data-popper-arrow":""},fs={key:1,class:"popover-header"},bs={key:2,class:"popover-body"},ys={key:3,class:"popover-footer"},vs=p(us,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["popover-wrapper",e.classes],ref:"wrapper",id:e.name,onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))}),[m("div",{class:"popover-trigger",ref:"trigger","aria-describedby":`${e.name}-popup`,"aria-disabled":e.disabled?"true":"false","aria-expanded":e.visible?"true":"false"},[d(e.$slots,"default")],8,ps),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:"popover",ref:"popup",role:"tooltip","aria-live":"polite",id:`${e.name}-popup`,"aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",ms)):f("",!0),e.$slots.header?(l(),o("div",fs,[d(e.$slots,"header")])):f("",!0),e.$slots.body?(l(),o("div",bs,[d(e.$slots,"body")])):f("",!0),e.$slots.footer?(l(),o("div",ys,[d(e.$slots,"footer")])):f("",!0)],8,hs),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,cs)),[[n,e.onClickOutside]])}]]),Ae="IProgress",gs=p(c({name:Ae,provide(){return{progress:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Ae,"color")},min:{type:[String,Number],default:0},max:{type:[String,Number],default:100},size:{type:String,default:u(Ae,"size"),validator:v}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}}}),[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["progress",e.classes]}),[d(e.$slots,"default")],16)}]]),at="IProgressBar",ks=c({name:at,inject:{progress:{default:()=>({min:0,max:100})}},inheritAttrs:!1,props:{color:{type:String,default:u(at,"color","primary")},value:{type:[String,Number],default:0}},computed:{computedValue(){const e=typeof this.min=="string"?parseFloat(this.min):this.min;return 100*((typeof this.value=="string"?parseFloat(this.value.replace("%","")):this.value)-e)/((typeof this.max=="string"?parseFloat(this.max):this.max)-e)},min(){return this.progress.min},max(){return this.progress.max},style(){return{width:`${this.computedValue}%`}},classes(){return{...g(this)}}}}),$s=["aria-valuemin","aria-valuemax","aria-valuenow"],ws=p(ks,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["progress-bar",e.classes],style:e.style,role:"progressbar","aria-valuemin":e.min,"aria-valuemax":e.max,"aria-valuenow":e.computedValue}),[d(e.$slots,"default")],16,$s)}]]),Ve="IRadio",Is=c({name:Ve,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(Ve,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:""},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("radio")},native:{type:Boolean,default:!1},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Ve,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-native":this.native}},checked(){return this.formGroup.checked===this.value},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i,a,s;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),(s=(a=this.formGroup).onChange)==null||s.call(a,this.value),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),xs=["checked","name","disabled","readonly",".indeterminate"],Cs=["aria-checked","aria-disabled","aria-readonly","tabindex"],Ss=p(Is,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["radio",e.classes],role:"radio"}),[m("input",{checked:e.checked,ref:"input",type:"radio",tabindex:"-1",name:e.name,disabled:e.isDisabled,readonly:e.isReadonly,".indeterminate":e.indeterminate,onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,xs),m("label",{class:"radio-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onBlur:t[1]||(t[1]=(...n)=>e.onBlur&&e.onBlur(...n)),onClick:t[2]||(t[2]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,Cs)],16)}]]),Oe="IRadioGroup",Bs=c({name:Oe,mixins:[R],provide(){return{formGroup:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Oe,"color")},disabled:{type:Boolean,default:!1},inline:{type:Boolean,default:!1},modelValue:{default:""},name:{type:[String,Number],default:()=>S("radio-group")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Oe,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly,"-inline":this.inline}},checked(){return this.schema?this.schema.value:this.modelValue}},methods:{onChange(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e),this.$emit("update:modelValue",e)}}}),zs=["name"],As=p(Bs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["form-group radio-group",e.classes],name:e.name,role:"radiogroup"}),[d(e.$slots,"default",{},void 0,!0)],16,zs)}],["__scopeId","data-v-fa4fe77a"]]),Vs=c({name:"ISelectOption",inject:{select:{default:()=>({})}},inheritAttrs:!1,props:{active:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},label:{type:String,default:""},tabindex:{type:[Number,String],default:0},value:{type:[Object,String,Number],default:()=>({})}},computed:{ariaDisabled(){return this.disabled?"true":"false"},ariaSelected(){return this.active?"true":"false"},isActive(){return this.active||this.value===this.select.modelValue},classes(){return{"-active":this.isActive,"-disabled":this.disabled}},tabIndex(){return this.disabled?-1:this.tabindex}},methods:{onClick(){this.disabled||this.select.onInput(this.value,this.label)}}}),Os=["tabindex","aria-disabled","aria-selected"],It=p(Vs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["select-option",e.classes],role:"option",tabindex:e.tabIndex,"aria-disabled":e.ariaDisabled,"aria-selected":e.ariaSelected,onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))}),[d(e.$slots,"default",{},()=>[P(M(e.label),1)])],16,Os)}]]),Le="ISelect",Ls=c({name:Le,directives:{ClickOutside:K},components:{IInput:te,IIcon:$t,ISelectOption:It,IMark:wt},mixins:[R,ee],provide(){return{select:this}},props:{animationDuration:{type:Number,default:300},autocomplete:{type:Boolean,default:!1},arrow:{type:Boolean,default:!0},color:{type:String,default:u(Le,"color")},clearable:{type:Boolean,default:!1},disabled:{type:Boolean,default:!1},idField:{type:String,default:"id"},keydownTrigger:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},keydownItem:{type:Array,default:()=>["up","down","enter","space","tab","esc"]},label:{type:[String,Function],default:"label"},loading:{type:Boolean,default:!1},modelValue:{type:[Object,String,Number],default:null},minLength:{type:Number,default:0},name:{type:[String,Number],default:()=>S("select")},options:{type:Array,default:()=>[]},placeholder:{type:String,default:""},offset:{type:Number,default:6},placement:{type:String,default:"bottom"},popperOptions:{type:Object,default:()=>({modifiers:[...bt({offset:8}),{name:"sameWidth",enabled:!0,phase:"beforeWrite",requires:["computeStyles"],fn:({state:e})=>{e.styles.popper.width=`${e.rects.reference.width}px`},effect({state:e}){e.elements.popper.style.width=`${e.elements.reference.offsetWidth}px`}}]})},readonly:{type:Boolean,default:!1},scrollTolerance:{type:Number,default:160},selectFirstOptionOnEnter:{type:Boolean,default:!0},size:{type:String,default:u(Le,"size"),validator:v},tabindex:{type:[Number,String],default:0},type:{type:String,default:"text"},total:{type:Number,default:void 0}},emits:["update:modelValue","search","pagination"],data(){return{animating:!1,visible:!1,inputValue:this.computeLabel(this.modelValue)||""}},computed:{wrapperClasses(){return{...g(this),[`-${this.size}`]:!!this.size}},popupClasses(){return{"-disabled":this.isDisabled,"-readonly":this.isReadonly}},tabIndex(){return this.isDisabled?-1:this.tabindex},isClearable(){return this.value&&this.clearable&&!this.isDisabled&&!this.isReadonly},value(){return this.schema?this.schema.value:this.modelValue},inputPlaceholder(){return this.value?this.computeLabel(this.value):this.placeholder}},watch:{value(e){this.inputValue=this.computeLabel(e)},inputValue(e){const t=this.inputMatchesLength(e),i=this.inputMatchesLabel(e);!t||i||this.animating||this.show(),this.$emit("search",this.inputValue)},options(){this.visible&&this.createPopper()}},methods:{onInput(e,t){var i,a;e.disabled||(this.hide(),t&&(this.inputValue=t),(a=(i=this.parent).onInput)==null||a.call(i,this.name,e),this.$emit("update:modelValue",e))},onClear(){this.animating=!0,this.$emit("update:modelValue",null),this.$nextTick(()=>{this.animating=!1})},onFocus(e){!this.value&&this.options.length===0||(this.autocomplete&&(this.inputValue=""),(!e.relatedTarget||!this.$refs.wrapper.contains(e.relatedTarget))&&this.inputShouldShowSelect(this.inputValue)&&this.show())},onBlur(e){var t,i;(!e.relatedTarget||!this.$refs.wrapper.contains(e.relatedTarget))&&(this.hide(),this.inputValue=this.computeLabel(this.value)),(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)},onClick(){this.autocomplete&&(this.inputValue=""),this.inputShouldShowSelect(this.inputValue)&&this.show()},onClickOutside(){this.hide()},onClickCaret(e){this.visible?this.onBlur(e):(this.focus(),this.onFocus(e)),e.preventDefault(),e.stopPropagation()},onScroll(){if(isNaN(this.total))return;const e=this.$refs.body.scrollTop+parseInt(getComputedStyle(this.$refs.body).height,10)>parseInt(getComputedStyle(this.$refs.options).height,10)-this.scrollTolerance,t=this.options.length>=this.total;e&&!t&&this.options.length>0&&!this.loading&&this.$emit("pagination")},onWindowResize(){this.onScroll(),this.visible&&this.$nextTick().then(()=>this.createPopper())},onTriggerKeyDown(e){if(this.keydownTrigger.length===0)return;const t=this.getFocusableItems(),i=t.findIndex(s=>s.active),a=t[i>-1?i:0];switch(!0){case(k("up",e)&&this.keydownTrigger.includes("up")):case(k("down",e)&&this.keydownTrigger.includes("down")):this.show(),setTimeout(()=>{a.focus()},this.visible?0:this.animationDuration),e.preventDefault(),e.stopPropagation();break;case(k("enter",e)&&this.keydownTrigger.includes("enter")):if(!this.selectFirstOptionOnEnter||this.value&&this.inputMatchesLabel(this.inputValue))this.onClick();else{const s=this.options.find(r=>!r.disabled);s&&(this.onInput(s),this.focus())}this.visible||setTimeout(()=>{a.focus()},this.animationDuration),e.preventDefault();break;case(k("tab",e)&&this.keydownTrigger.includes("tab")):case(k("esc",e)&&this.keydownTrigger.includes("esc")):this.hide()}},onItemKeyDown(e){if(this.keydownItem.length!==0)switch(!0){case(k("up",e)&&this.keydownItem.includes("up")):case(k("down",e)&&this.keydownItem.includes("down")):const t=this.getFocusableItems(),i=t.findIndex(r=>r===e.target),a=t.length-1;let s;s=k("up",e)?i>0?i-1:0:i{this.animating=!1},this.animationDuration))},focus(){this.$refs.trigger.focus()},getFocusableItems(){const e=[];for(const t of this.$refs.options.children)Fe(t)&&e.push(t);return e},getElementHeight(e){const t=getComputedStyle(e);return t.height?Math.ceil(parseFloat(t.height)):NaN},inputMatchesLabel(e){return this.value&&e===this.computeLabel(this.value)},inputMatchesLength(e){return this.minLength===0||e&&e.length>=this.minLength},inputShouldShowSelect(e){return!this.autocomplete||this.inputMatchesLength(e)&&!this.inputMatchesLabel(e)},computeLabel(e){return typeof e!="object"?this.inputValue:dt(this.label)?this.label(e):_(e,this.label)}}}),Ds=["id","name","aria-owns","aria-expanded"],Ns=["id","aria-hidden"],Ts={key:0,"data-popper-arrow":""},Ps={key:1,class:"select-header"},Rs={key:0,class:"select-no-results"},Es={class:"select-options",ref:"options"},_s={key:2,class:"select-footer"},Ms=p(Ls,[["render",function(e,t,i,a,s,r){const n=A("i-input"),b=A("i-mark"),B=A("i-select-option"),F=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["select-wrapper",e.wrapperClasses],id:e.name,name:e.name,ref:"wrapper",role:"combobox","aria-haspopup":"listbox","aria-owns":`${e.name}-options`,"aria-expanded":e.visible?"true":"false",onKeyup:t[3]||(t[3]=E((...w)=>e.onEscape&&e.onEscape(...w),["esc"]))}),[I(n,{modelValue:e.inputValue,"onUpdate:modelValue":t[1]||(t[1]=w=>e.inputValue=w),ref:"trigger",autocomplete:"off","aria-autocomplete":"both","aria-controls":`${e.name}-options`,disabled:e.isDisabled,readonly:e.isReadonly,tabindex:e.tabIndex,plaintext:!e.autocomplete,placeholder:e.inputPlaceholder,clearable:e.isClearable,color:e.color,size:e.size,name:`${e.name}-input`,onClick:e.onClick,onFocus:e.onFocus,onBlur:e.onBlur,onClear:e.onClear,onKeydown:e.onTriggerKeyDown},Nt({suffix:y(()=>[d(e.$slots,"suffix"),m("button",{class:"select-caret","aria-hidden":"true",role:"button",onClick:t[0]||(t[0]=(...w)=>e.onClickCaret&&e.onClickCaret(...w))})]),_:2},[e.$slots.prepend?{name:"prepend",fn:y(()=>[d(e.$slots,"prepend")]),key:"0"}:void 0,e.$slots.prefix?{name:"prefix",fn:y(()=>[d(e.$slots,"prefix")]),key:"1"}:void 0,e.$slots.append?{name:"append",fn:y(()=>[d(e.$slots,"append")]),key:"2"}:void 0]),1032,["modelValue","aria-controls","disabled","readonly","tabindex","plaintext","placeholder","clearable","color","size","name","onClick","onFocus","onBlur","onClear","onKeydown"]),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:z(["select",e.popupClasses]),id:`${e.name}-options`,role:"listbox",ref:"popup","aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",Ts)):f("",!0),e.$slots.header?(l(),o("div",Ps,[d(e.$slots,"header")])):f("",!0),m("div",{class:"select-body",ref:"body",onScroll:t[2]||(t[2]=(...w)=>e.onScroll&&e.onScroll(...w))},[e.$slots.default||e.options.length!==0?f("",!0):(l(),o("div",Rs,[d(e.$slots,"no-results",{},()=>[t[4]||(t[4]=P(" There are no results for your query. ",-1))])])),m("div",Es,[d(e.$slots,"default"),(l(!0),o(j,null,X(e.options,w=>(l(),V(B,{key:w[e.idField],active:e.value&&e.value[e.idField]===w[e.idField],disabled:w.disabled,value:w,onKeydown:e.onItemKeyDown},{default:y(()=>[d(e.$slots,"option",{option:w},()=>[e.autocomplete&&e.inputValue!==e.computeLabel(w)?(l(),V(b,{key:0,text:e.computeLabel(w),query:e.inputValue},null,8,["text","query"])):(l(),o(j,{key:1},[P(M(e.computeLabel(w)),1)],64))])]),_:2},1032,["active","disabled","value","onKeydown"]))),128))],512)],544),e.$slots.footer?(l(),o("div",_s,[d(e.$slots,"footer")])):f("",!0)],10,Ns),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,Ds)),[[F,e.onClickOutside]])}]]),De="ISidebar",Fs=c({name:De,mixins:[ft],provide(){return{sidebar:this}},inheritAttrs:!1,props:{ariaLabel:{type:String,default:"Sidebar"},collapseOnItemClick:{type:Boolean,default:!0},collapseOnClickOutside:{type:Boolean,default:!0},collapsePosition:{type:String,default:"absolute"},color:{type:String,default:u(De,"color")},placement:{type:String,default:"left"},size:{type:String,default:u(De,"size"),validator:v}},emits:["update:modelValue"],computed:{classes(){return{...this.collapsibleClasses,...g(this),[`-${this.size}`]:!!this.size,[`-collapse-${this.collapsePosition}`]:!0,[`-placement-${this.placement}`]:!0}},sidebarWrapperTransition(){return this.collapsePosition!=="relative"?"sidebar-wrapper-none-transition":"sidebar-wrapper-transition"},sidebarTransition(){return this.collapsePosition!=="relative"?"sidebar-transition":"sidebar-none-transition"}},methods:{onItemClick(){this.collapseOnItemClick&&this.open&&this.setOpen(!1)},onOverlayClick(){this.collapseOnClickOutside&&this.open&&this.setOpen(!1)}}}),js=["aria-label"],Gs={class:"sidebar"},Zs={class:"sidebar-content"},Ks=p(Fs,[["render",function(e,t,i,a,s,r){return l(),V(L,{name:e.sidebarWrapperTransition},{default:y(()=>[$(m("aside",h(e.$attrs,{role:"complementary",class:["sidebar-wrapper",e.classes],"aria-label":e.ariaLabel,ref:"wrapper"}),[I(L,{name:e.sidebarTransition},{default:y(()=>[$(m("div",Gs,[m("div",Zs,[d(e.$slots,"default")])],512),[[x,e.collapsePosition==="relative"||e.open||!e.collapsible]])]),_:3},8,["name"]),I(L,{name:"sidebar-overlay-transition"},{default:y(()=>[e.collapsePosition!=="relative"?$((l(),o("div",{key:0,class:"sidebar-overlay",onClick:t[0]||(t[0]=(...n)=>e.onOverlayClick&&e.onOverlayClick(...n))},null,512)),[[x,e.open]]):f("",!0)]),_:1})],16,js),[[x,e.open||!e.collapsible]])]),_:3},8,["name"])}]]),st="ITable",qs=c({name:st,inheritAttrs:!1,props:{border:{type:Boolean,default:!1},condensed:{type:Boolean,default:!1},striped:{type:Boolean,default:!1},hover:{type:Boolean,default:!1},responsive:{type:[Boolean,String],default:!0},nowrap:{type:Boolean,default:!1},color:{type:String,default:u(st,"color")}},computed:{classes(){return{...g(this),"-border":this.border,"-condensed":this.condensed,"-striped":this.striped,"-hover":this.hover,"-nowrap":this.nowrap,["-responsive"+(typeof this.responsive=="boolean"?"":`-${this.responsive}`)]:!!this.responsive}}}}),Ws={class:"table"},Us=p(qs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["table-wrapper",e.classes]}),[m("table",Ws,[d(e.$slots,"default")])],16)}]]),Ne="ITabs",Hs=c({name:Ne,provide(){return{tabs:this}},inheritAttrs:!1,props:{color:{type:String,default:u(Ne,"color")},modelValue:{type:String,default:""},size:{type:String,default:u(Ne,"size"),validator:v},stretch:{type:Boolean,default:!1}},emits:["update:modelValue"],data(){return{active:this.modelValue,tabs:[]}},computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-stretch":this.stretch}}},watch:{modelValue(e){this.active=e}},methods:{setActive(e){this.active=e,this.$emit("update:modelValue",this.active)}}}),Qs={class:"tabs-header"},Ys=p(Hs,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["tabs",e.classes],role:"tablist","aria-multiselectable":"true"}),[m("div",Qs,[d(e.$slots,"header")]),d(e.$slots,"default")],16)}]]),Js=c({name:"ITab",inject:{tabs:{default:()=>({})}},props:{title:{type:String,default:""},name:{type:String,default:()=>S("tab")}},computed:{active(){return this.tabs.active===this.name},classes(){return{"-active":this.active}}}}),Xs=["name","aria-hidden","aria-labelledby"],en={class:"tab-body"},tn=p(Js,[["render",function(e,t,i,a,s,r){return $((l(),o("div",{class:z(["tab",e.classes]),role:"tabpanel",name:e.name,"aria-hidden":!e.active,"aria-labelledby":`tab-heading-${e.name}`},[m("div",en,[d(e.$slots,"default")])],10,Xs)),[[x,e.active]])}]]),an=c({name:"ITabTitle",inject:{tabs:{default:()=>({})}},props:{for:{type:String,default:()=>S("tab")}},computed:{active(){return this.tabs.active===this.for},classes(){return{"-active":this.active}},name(){return this.for}},methods:{onClick(){this.tabs.setActive(this.for)}}}),sn=["for","active","aria-expanded","aria-controls","aria-describedby"],nn=p(an,[["render",function(e,t,i,a,s,r){return l(),o("div",{class:z(["tab-title",e.classes]),role:"tab",for:e.name,active:e.active,"aria-expanded":e.active,"aria-controls":`tab-content-${e.name}`,"aria-describedby":`tab-content-${e.name}`,tabindex:"0",onClick:t[0]||(t[0]=(...n)=>e.onClick&&e.onClick(...n))},[d(e.$slots,"default")],10,sn)}]]),Te="IToggle",ln=c({name:Te,mixins:[R],inheritAttrs:!1,props:{color:{type:String,default:u(Te,"color")},disabled:{type:Boolean,default:!1},indeterminate:{type:Boolean,default:!1},value:{default:!1},modelValue:{default:!1},name:{type:[String,Number],default:()=>S("toggle")},readonly:{type:Boolean,default:!1},size:{type:String,default:u(Te,"size"),validator:v},tabindex:{type:[Number,String],default:0}},emits:["update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size,"-disabled":this.isDisabled,"-readonly":this.isReadonly}},checked(){return this.schema?this.schema.value:this.modelValue},tabIndex(){return this.isDisabled?-1:this.tabindex}},methods:{clickInputRef(){this.isReadonly||this.$refs.input.click()},onChange(e){var t,i;(i=(t=this.parent).onInput)==null||i.call(t,this.name,e.target.checked),this.$emit("update:modelValue",e.target.checked)},onBlur(e){var t,i;(i=(t=this.parent).onBlur)==null||i.call(t,this.name,e)}}}),rn=["checked","disabled","readonly","aria-checked","aria-disabled","aria-readonly","name"],on=["aria-checked","aria-disabled","aria-readonly","tabindex"],dn=p(ln,[["render",function(e,t,i,a,s,r){return l(),o("div",h(e.$attrs,{class:["toggle",e.classes]}),[m("input",{ref:"input",type:"checkbox",checked:e.checked,disabled:e.isDisabled,readonly:e.isReadonly,"aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,name:e.name,onChange:t[0]||(t[0]=(...n)=>e.onChange&&e.onChange(...n))},null,40,rn),m("label",{class:"toggle-label","aria-checked":e.checked,"aria-disabled":e.isDisabled,"aria-readonly":e.isReadonly,tabindex:e.tabIndex,onClick:t[1]||(t[1]=(...n)=>e.clickInputRef&&e.clickInputRef(...n)),onBlur:t[2]||(t[2]=(...n)=>e.onBlur&&e.onBlur(...n)),onKeydown:t[3]||(t[3]=E(Q((...n)=>e.clickInputRef&&e.clickInputRef(...n),["stop","prevent"]),["space"]))},[d(e.$slots,"default")],40,on)],16)}]]),Pe="ITooltip",un=c({name:Pe,directives:{ClickOutside:K},mixins:[ee,Ge],inheritAttrs:!1,props:{color:{type:String,default:u(Pe,"color")},disabled:{type:Boolean,default:!1},modelValue:{type:Boolean,default:!1},name:{type:String,default:()=>S("tooltip")},arrow:{type:Boolean,default:!0},placement:{type:String,default:"top"},trigger:{type:[String,Array],default:()=>["hover","focus"]},offset:{type:Number,default:6},interactable:{type:Boolean,default:!1},popperOptions:{type:Object,default:()=>({})},size:{type:String,default:u(Pe,"size"),validator:v}},emits:["click-outside","update:modelValue"],computed:{classes(){return{...g(this),[`-${this.size}`]:!!this.size}}},methods:{onEscape(){this.visible=!1,this.$emit("update:modelValue",!1)},handleClickOutside(e){this.visible=!1,this.$emit("update:modelValue",!1),this.onClickOutside(e)}}}),cn=["id"],pn=["aria-describedby","aria-disabled","aria-expanded"],hn=["id","aria-hidden"],mn={key:0,"data-popper-arrow":""},fn=p(un,[["render",function(e,t,i,a,s,r){const n=G("click-outside");return $((l(),o("div",h(e.$attrs,{class:["tooltip-wrapper",e.classes],ref:"wrapper",id:e.name,onKeyup:t[0]||(t[0]=E((...b)=>e.onEscape&&e.onEscape(...b),["esc"]))}),[m("div",{class:"tooltip-trigger",ref:"trigger","aria-describedby":`${e.name}-popup`,"aria-disabled":e.disabled?"true":"false","aria-expanded":e.visible?"true":"false"},[d(e.$slots,"default")],8,pn),I(L,{name:"zoom-in-top-transition",onAfterLeave:e.destroyPopper},{default:y(()=>[$(m("div",{class:"tooltip",ref:"popup",role:"tooltip","aria-live":"polite",id:`${e.name}-popup`,"aria-hidden":e.visible?"false":"true"},[e.arrow?(l(),o("span",mn)):f("",!0),d(e.$slots,"body")],8,hn),[[x,e.visible]])]),_:3},8,["onAfterLeave"])],16,cn)),[[n,e.onClickOutside]])}]]),vn=Object.freeze(Object.defineProperty({__proto__:null,IAlert:Jt,IBadge:Xt,IBreadcrumb:ii,IBreadcrumbItem:ni,IButton:vt,IButtonGroup:ui,ICard:fi,ICheckbox:ki,ICheckboxGroup:Ii,ICollapsible:Ci,ICollapsibleItem:Oi,IColumn:Ze,IContainer:Ke,IDropdown:Mi,IDropdownDivider:Fi,IDropdownItem:ji,IForm:Ki,IFormError:Yi,IFormGroup:Ui,IFormLabel:ea,IHamburgerMenu:kt,IHeader:ta,IIcon:$t,IInput:te,ILayout:Ba,ILayoutAside:Va,ILayoutContent:Oa,ILayoutFooter:La,ILayoutHeader:Da,IListGroup:Na,IListGroupItem:Ta,ILoader:yt,IMark:wt,IMedia:Fa,IModal:Ha,INav:Qa,INavItem:Ya,INavbar:Ja,INavbarBrand:Xa,INavbarCollapsible:is,INumberInput:va,IPagination:ds,IPopover:vs,IProgress:gs,IProgressBar:ws,IRadio:Ss,IRadioGroup:As,IRow:qe,ISelect:Ms,ISelectOption:It,ISidebar:Ks,ITab:tn,ITabTitle:nn,ITable:Us,ITabs:Ys,ITextarea:Sa,IToggle:dn,ITooltip:fn},Symbol.toStringTag,{value:"Module"}));export{yn as I,p as _,vn as c}; diff --git a/packages/modules/display_themes/cards/web/index.html b/packages/modules/display_themes/cards/web/index.html index 8815664696..03b716a1be 100644 --- a/packages/modules/display_themes/cards/web/index.html +++ b/packages/modules/display_themes/cards/web/index.html @@ -6,12 +6,12 @@ openWB Display - Cards - - - - + + + + - + diff --git a/packages/modules/display_themes/colors/source/src/App.vue b/packages/modules/display_themes/colors/source/src/App.vue index 598bac0d6d..9cbc6abeb6 100644 --- a/packages/modules/display_themes/colors/source/src/App.vue +++ b/packages/modules/display_themes/colors/source/src/App.vue @@ -12,7 +12,6 @@ import DisplayTheme from './views/DisplayTheme.vue' import { wbSettings } from './assets/js/themeConfig' onMounted(() => { - console.log('on mounted') let uri = window.location.search if (uri != '') { console.debug('search', uri) diff --git a/packages/modules/display_themes/colors/source/src/assets/js/sendMessages.ts b/packages/modules/display_themes/colors/source/src/assets/js/sendMessages.ts index c3225fbd94..dc6396f6a3 100755 --- a/packages/modules/display_themes/colors/source/src/assets/js/sendMessages.ts +++ b/packages/modules/display_themes/colors/source/src/assets/js/sendMessages.ts @@ -49,6 +49,7 @@ const topics: { [topic: string]: string } = { socUpdate: 'openWB/set/vehicle/%/get/force_soc_update', setSoc: 'openWB/set/vehicle/%/soc_module/calculated_soc_state/manual_soc', priceCharging: 'openWB/set/vehicle/template/charge_template/%/et/active', + chargeTemplate: 'openWB/set/chargepoint/%/set/charge_template', } export function updateServer( item: string, @@ -66,7 +67,7 @@ export function updateServer( return } switch (item) { - case 'chargeMode': + /* case 'chargeMode': case 'cpPriority': case 'cpScheduledCharging': case 'cpInstantTargetCurrent': @@ -81,7 +82,7 @@ export function updateServer( case 'cpPvMinSocCurrent': // these values are set in the charge template topic = topic.replace('%', chargePoints[index].chargeTemplate.toString()) - break + break */ default: topic = topic.replace('%', String(index)) if (index2 != undefined) { @@ -105,3 +106,10 @@ export function sendCommand(command: string, data: object = {}) { JSON.stringify({ command: command, data: data }), ) } + +export function updateChargeTemplate(cp: number) { + mqttPublish( + topics.chargeTemplate.replace('%', String(cp)), + JSON.stringify(chargePoints[cp].chargeTemplate), + ) +} diff --git a/packages/modules/display_themes/colors/source/src/assets/js/themeConfig.ts b/packages/modules/display_themes/colors/source/src/assets/js/themeConfig.ts index c9395cadd0..69ac2157f9 100755 --- a/packages/modules/display_themes/colors/source/src/assets/js/themeConfig.ts +++ b/packages/modules/display_themes/colors/source/src/assets/js/themeConfig.ts @@ -293,29 +293,29 @@ export const widescreen = computed(() => { return screensize.x >= breakpoint }) export const chargemodes: { [key: string]: ChargeModeInfo } = { - pv_charging: { - mode: ChargeMode.pv_charging, - name: 'PV', - color: 'var(--color-pv', - icon: 'fa-solar-panel', - }, instant_charging: { mode: ChargeMode.instant_charging, name: 'Sofort', color: 'var(--color-charging)', icon: 'fa-bolt', }, + pv_charging: { + mode: ChargeMode.pv_charging, + name: 'PV', + color: 'var(--color-pv', + icon: 'fa-solar-panel', + }, scheduled_charging: { mode: ChargeMode.scheduled_charging, name: 'Zielladen', color: 'var(--color-battery)', icon: 'fa-bullseye', }, - standby: { - mode: ChargeMode.standby, - name: 'Standby', - color: 'var(--color-axis)', - icon: 'fa-pause', + eco_charging: { + mode: ChargeMode.eco_charging, + name: 'Eco', + color: 'var(--color-devices)', + icon: 'fa-coins', }, stop: { mode: ChargeMode.stop, diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargeConfig.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargeConfig.vue deleted file mode 100755 index 8e3373bdb1..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargeConfig.vue +++ /dev/null @@ -1,167 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargePoint.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargePoint.vue deleted file mode 100755 index 953868838a..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPChargePoint.vue +++ /dev/null @@ -1,221 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigInstant.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigInstant.vue deleted file mode 100755 index 27d8417ec7..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigInstant.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigPv.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigPv.vue deleted file mode 100755 index 4d30372135..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigPv.vue +++ /dev/null @@ -1,157 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigScheduled.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigScheduled.vue deleted file mode 100755 index 5b592d5c7a..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigScheduled.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigTimed.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigTimed.vue deleted file mode 100755 index 875457bb02..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPConfigTimed.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPVehicle.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/CPVehicle.vue deleted file mode 100644 index b3419b1404..0000000000 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/CPVehicle.vue +++ /dev/null @@ -1,275 +0,0 @@ - - - diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/ChargePoint.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/ChargePoint.vue new file mode 100755 index 0000000000..626891d61d --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/ChargePoint.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/VehicleData.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/VehicleData.vue new file mode 100644 index 0000000000..727a1a5529 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/VehicleData.vue @@ -0,0 +1,275 @@ + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ChargeConfig.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ChargeConfig.vue new file mode 100755 index 0000000000..bf5b4edc58 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ChargeConfig.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigEco.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigEco.vue new file mode 100644 index 0000000000..880c70eabe --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigEco.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigInstant.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigInstant.vue new file mode 100755 index 0000000000..fa08a2d536 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigInstant.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigPv.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigPv.vue new file mode 100755 index 0000000000..a4838cb768 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigPv.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigScheduled.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigScheduled.vue new file mode 100755 index 0000000000..037a9eb850 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigScheduled.vue @@ -0,0 +1,97 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigTimed.vue b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigTimed.vue new file mode 100755 index 0000000000..fefc1f0e26 --- /dev/null +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/configPanels/ConfigTimed.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/model.ts b/packages/modules/display_themes/colors/source/src/components/chargePointList/model.ts index f62e04385e..710d180a37 100755 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/model.ts +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/model.ts @@ -1,5 +1,5 @@ import { reactive } from 'vue' -import { updateServer } from '@/assets/js/sendMessages' +import { updateChargeTemplate, updateServer } from '@/assets/js/sendMessages' import type { PowerItem } from '@/assets/js/types' export class ChargePoint { id: number @@ -17,10 +17,9 @@ export class ChargePoint { isCharging = false private _isLocked = false private _connectedVehicle = 0 - chargeTemplate = 0 + chargeTemplate: ChargeTemplate | null = null + chargeTemplateId = 0 evTemplate = 0 - private _chargeMode = ChargeMode.pv_charging - private _hasPriority = false currentPlan = '' averageConsumption = 0 vehicleName = '' @@ -45,18 +44,6 @@ export class ChargePoint { isSocManual = false waitingForSoc = false color = 'white' - private _timedCharging = false - private _instantChargeLimitMode = '' - private _instantTargetCurrent = 0 - private _instantTargetSoc = 0 - private _instantMaxEnergy = 0 - private _pvFeedInLimit = false - private _pvMinCurrent = 0 - private _pvMaxSoc = 101 - private _pvMinSoc = 0 - private _pvMinSocCurrent = 0 - private _etActive = false - private _etMaxPrice = 20 constructor(index: number) { this.id = index @@ -94,126 +81,226 @@ export class ChargePoint { } } get chargeMode() { - return this._chargeMode + return this.chargeTemplate?.chargemode.selected ?? ChargeMode.stop } set chargeMode(cm: ChargeMode) { - this._chargeMode = cm - updateServer('chargeMode', cm, this.id) - } - updateChargeMode(cm: ChargeMode) { - this._chargeMode = cm + console.log('set mode') + if (this.chargeTemplate) { + console.log('active') + this.chargeTemplate.chargemode.selected = cm + updateChargeTemplate(this.id) + } } get hasPriority() { - return this._hasPriority + return this.chargeTemplate?.prio ?? false } set hasPriority(prio: boolean) { - this._hasPriority = prio - updateServer('cpPriority', prio, this.id) - } - updateCpPriority(prio: boolean) { - this._hasPriority = prio + this.chargeTemplate!.prio = prio + updateChargeTemplate(this.id) } get timedCharging() { - if (chargeTemplates[this.chargeTemplate]) { - return chargeTemplates[this.chargeTemplate].time_charging.active + if (this.chargeTemplate) { + return this.chargeTemplate.time_charging.active } else { return false } } set timedCharging(setting: boolean) { - // chargeTemplates[this.chargeTemplate].time_charging.active = false - chargeTemplates[this.chargeTemplate].time_charging.active = setting - updateServer('cpTimedCharging', setting, this.chargeTemplate) + this.chargeTemplate!.time_charging.active = setting + updateChargeTemplate(this.id) } get instantTargetCurrent() { - return this._instantTargetCurrent + return this.chargeTemplate?.chargemode.instant_charging.current ?? 0 } set instantTargetCurrent(current: number) { - this._instantTargetCurrent = current - updateServer('cpInstantTargetCurrent', current, this.id) - } - updateInstantTargetCurrent(current: number) { - this._instantTargetCurrent = current + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.current = current + updateChargeTemplate(this.id) + } } get instantChargeLimitMode() { - return this._instantChargeLimitMode + return ( + this.chargeTemplate?.chargemode.instant_charging.limit.selected ?? 'none' + ) } set instantChargeLimitMode(mode: string) { - this._instantChargeLimitMode = mode - updateServer('cpInstantChargeLimitMode', mode, this.id) - } - updateInstantChargeLimitMode(mode: string) { - this._instantChargeLimitMode = mode + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.selected = mode + updateChargeTemplate(this.id) + } } get instantTargetSoc() { - return this._instantTargetSoc + return this.chargeTemplate?.chargemode.instant_charging.limit.soc ?? 0 } set instantTargetSoc(soc: number) { - this._instantTargetSoc = soc - updateServer('cpInstantTargetSoc', soc, this.id) - } - updateInstantTargetSoc(soc: number) { - this._instantTargetSoc = soc + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.soc = soc + updateChargeTemplate(this.id) + } } get instantMaxEnergy() { - return this._instantMaxEnergy + return this.chargeTemplate?.chargemode.instant_charging.limit.amount ?? 0 } set instantMaxEnergy(max: number) { - this._instantMaxEnergy = max - updateServer('cpInstantMaxEnergy', max, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.amount = max + updateChargeTemplate(this.id) + } + } + get instantTargetPhases() { + return this.chargeTemplate?.chargemode.instant_charging.phases_to_use ?? 0 } - updateInstantMaxEnergy(max: number) { - this._instantMaxEnergy = max + set instantTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } } get pvFeedInLimit() { - return this._pvFeedInLimit + return this.chargeTemplate?.chargemode.pv_charging.feed_in_limit ?? false } set pvFeedInLimit(setting: boolean) { - this._pvFeedInLimit = setting - updateServer('cpPvFeedInLimit', setting, this.id) - } - updatePvFeedInLimit(setting: boolean) { - this._pvFeedInLimit = setting + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.feed_in_limit = setting + updateChargeTemplate(this.id) + } } get pvMinCurrent() { - return this._pvMinCurrent + return this.chargeTemplate?.chargemode.pv_charging.min_current ?? 0 } set pvMinCurrent(min: number) { - this._pvMinCurrent = min - updateServer('cpPvMinCurrent', min, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_current = min + updateChargeTemplate(this.id) + } } - updatePvMinCurrent(min: number) { - this._pvMinCurrent = min + get pvMinSoc() { + return this.chargeTemplate?.chargemode.pv_charging.min_soc ?? 0 } - get pvMaxSoc() { - return this._pvMaxSoc + set pvMinSoc(min: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_soc = min + updateChargeTemplate(this.id) + } } - set pvMaxSoc(max: number) { - this._pvMaxSoc = max - updateServer('cpPvMaxSoc', max, this.id) + get pvMinSocCurrent() { + return this.chargeTemplate?.chargemode.pv_charging.min_soc_current ?? 0 } - updatePvMaxSoc(max: number) { - this._pvMaxSoc = max + set pvMinSocCurrent(a: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_soc_current = a + updateChargeTemplate(this.id) + } } - get pvMinSoc() { - return this._pvMinSoc + set pvMinSocPhases(n: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.phases_to_use_min_soc = n + updateChargeTemplate(this.id) + } } - set pvMinSoc(min: number) { - this._pvMinSoc = min - updateServer('cpPvMinSoc', min, this.id) + get pvMinSocPhases() { + return ( + this.chargeTemplate?.chargemode.pv_charging.phases_to_use_min_soc ?? 0 + ) } - updatePvMinSoc(min: number) { - this._pvMinSoc = min + get pvChargeLimitMode() { + return this.chargeTemplate?.chargemode.pv_charging.limit.selected ?? 'none' } - get pvMinSocCurrent() { - return this._pvMinSocCurrent + set pvChargeLimitMode(mode: string) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.selected = mode + updateChargeTemplate(this.id) + } } - set pvMinSocCurrent(a: number) { - this._pvMinSocCurrent = a - updateServer('cpPvMinSocCurrent', a, this.id) + get pvTargetSoc() { + return this.chargeTemplate?.chargemode.pv_charging.limit.soc ?? 0 + } + set pvTargetSoc(soc: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.soc = soc + updateChargeTemplate(this.id) + } + } + get pvMaxEnergy() { + return this.chargeTemplate?.chargemode.pv_charging.limit.amount ?? 0 + } + set pvMaxEnergy(max: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.amount = max + updateChargeTemplate(this.id) + } + } + get pvTargetPhases() { + return this.chargeTemplate?.chargemode.pv_charging.phases_to_use ?? 0 } - updatePvMinSocCurrent(a: number) { - this._pvMinSocCurrent = a + set pvTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } + } + get ecoMinCurrent() { + return this.chargeTemplate?.chargemode.eco_charging.current ?? 0 + } + set ecoMinCurrent(min: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.current = min + updateChargeTemplate(this.id) + } + } + get ecoTargetPhases() { + return this.chargeTemplate?.chargemode.eco_charging.phases_to_use ?? 0 + } + set ecoTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } + } + get ecoChargeLimitMode() { + return this.chargeTemplate?.chargemode.eco_charging.limit.selected ?? 'none' + } + set ecoChargeLimitMode(mode: string) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.selected = mode + updateChargeTemplate(this.id) + } + } + get ecoTargetSoc() { + return this.chargeTemplate?.chargemode.eco_charging.limit.soc ?? 0 + } + set ecoTargetSoc(soc: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.soc = soc + updateChargeTemplate(this.id) + } + } + get ecoMaxEnergy() { + return this.chargeTemplate?.chargemode.eco_charging.limit.amount ?? 0 + } + set ecoMaxEnergy(max: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.amount = max + updateChargeTemplate(this.id) + } + } + get etMaxPrice() { + return ( + (this.chargeTemplate?.chargemode.eco_charging.max_price ?? 0) * 100000 + ) + } + set etMaxPrice(newPrice: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.max_price = + Math.ceil(newPrice * 1000) / 100000000 + updateChargeTemplate(this.id) + } + } + get etActive() { + return ( + this.chargeTemplate && + this.chargeTemplate.chargemode.selected == ChargeMode.eco_charging + ) } get realCurrent() { switch (this.phasesInUse) { @@ -229,24 +316,6 @@ export class ChargePoint { return 0 } } - get etActive() { - if (vehicles[this.connectedVehicle]) { - return vehicles[this.connectedVehicle].etActive - } else { - return false - } - } - set etActive(val) { - if (vehicles[this.connectedVehicle]) { - vehicles[this.connectedVehicle].etActive = val - } - } - get etMaxPrice() { - return vehicles[this.connectedVehicle].etMaxPrice ?? 0 - } - set etMaxPrice(newPrice: number) { - updateServer('cpEtMaxPrice', Math.round(newPrice * 10) / 1000000, this.id) - } toPowerItem(): PowerItem { return { name: this.name, @@ -294,27 +363,6 @@ export class Vehicle { updateEvTemplateId(id: number) { this._evTemplateId = id } - get etActive() { - if (chargeTemplates[this.chargeTemplateId]) { - return chargeTemplates[this.chargeTemplateId].et.active - } else { - return false - } - } - set etActive(val) { - if (chargeTemplates[this.chargeTemplateId]) { - updateServer('priceCharging', val, this.chargeTemplateId) - - // openWB/set/vehicle/template/charge_template/2/et/active -> false - } - } - get etMaxPrice() { - if (chargeTemplates[this.chargeTemplateId]) { - if (chargeTemplates[this.chargeTemplateId].et.active) { - return chargeTemplates[this.chargeTemplateId].et.max_price * 100000 - } - } - } get chargepoint(): ChargePoint | undefined { for (const cp of Object.values(chargePoints)) { if (cp.connectedVehicle == this.id) { @@ -336,7 +384,7 @@ export enum ChargeMode { instant_charging = 'instant_charging', pv_charging = 'pv_charging', scheduled_charging = 'scheduled_charging', - standby = 'standby', + eco_charging = 'eco_charging', stop = 'stop', } export interface ChargeTimePlan { @@ -376,31 +424,57 @@ export class ChargeSchedule { } } export interface ChargeTemplate { + id: number name: string prio: boolean + load_default: boolean + time_charging: { + active: boolean + plans: [ChargeTimePlan] + } chargemode: { selected: ChargeMode - instant_charging: { + eco_charging: { current: number + dc_current: number limit: { selected: string soc: number amount: number } + max_price: number + phases_to_use: number } pv_charging: { + dc_min_current: number + dc_min_soc_current: number feed_in_limit: boolean + limit: { + selected: string + amount: number + soc: number + } min_current: number - max_soc: number - min_soc: number min_soc_current: number + min_soc: number + phases_to_use: number + phases_to_use_min_soc: number + } + scheduled_charging: { + plans: ChargeSchedule[] + } + instant_charging: { + current: number + dc_current: number + limit: { + selected: string + soc: number + amount: number + } + phases_to_use: number } - } - time_charging: { - active: boolean } disable_after_unplug: boolean - load_default: boolean et: { active: boolean max_price: number diff --git a/packages/modules/display_themes/colors/source/src/components/chargePointList/processMessages.ts b/packages/modules/display_themes/colors/source/src/components/chargePointList/processMessages.ts index b51170d0f0..816285961b 100755 --- a/packages/modules/display_themes/colors/source/src/components/chargePointList/processMessages.ts +++ b/packages/modules/display_themes/colors/source/src/components/chargePointList/processMessages.ts @@ -5,16 +5,11 @@ import { chargeTemplates, evTemplates, Vehicle, - ChargeMode, - type ChargeTimePlan, - scheduledChargingPlans, - timeChargingPlans, } from './model' import type { ConnectedVehicleConfig, ChargeTemplate, EvTemplate, - ChargeSchedule, } from './model' export function processChargepointMessages(topic: string, message: string) { @@ -110,25 +105,12 @@ export function processChargepointMessages(topic: string, message: string) { ) ) { const config: ConnectedVehicleConfig = JSON.parse(message) - switch (config.chargemode) { - case 'instant_charging': - chargePoints[index].updateChargeMode(ChargeMode.instant_charging) - break - case 'pv_charging': - chargePoints[index].updateChargeMode(ChargeMode.pv_charging) - break - case 'scheduled_charging': - chargePoints[index].updateChargeMode(ChargeMode.scheduled_charging) - break - case 'standby': - chargePoints[index].updateChargeMode(ChargeMode.standby) - break - case 'stop': - chargePoints[index].updateChargeMode(ChargeMode.stop) - break - } - chargePoints[index].chargeTemplate = config.charge_template chargePoints[index].averageConsumption = config.average_consumption + chargePoints[index].chargeTemplateId = config.charge_template + } else if ( + topic.match(/^openwb\/chargepoint\/[0-9]+\/set\/charge_template$/i) + ) { + chargePoints[index].chargeTemplate = JSON.parse(message) } else { // console.warn('Ignored chargepoint message: ' + topic) } @@ -183,41 +165,7 @@ export function processVehicleTemplateMessages(topic: string, message: string) { const match = topic.match(/[0-9]+$/i) if (match) { const index = +match[0] - const template: ChargeTemplate = JSON.parse(message) as ChargeTemplate - chargeTemplates[index] = template - updateCpFromChargeTemplate(index, template) - } - } else if ( - topic.match( - /^openwb\/vehicle\/template\/charge_template\/[0-9]+\/time_charging\/plans\/[0-9]+$/i, - ) - ) { - const tidMatch = topic.match(/(?:\/)([0-9]+)(?:\/)/g) - const pidMatch = topic.match(/[0-9]+$/i) - if (tidMatch && pidMatch) { - const tId = +tidMatch[0].replace(/[^0-9]+/g, '') - const pId = +pidMatch[0] - const plan: ChargeTimePlan = JSON.parse(message) - if (!(tId in timeChargingPlans)) { - timeChargingPlans[tId] = [] - } - timeChargingPlans[tId][pId] = plan - } - } else if ( - topic.match( - /^openwb\/vehicle\/template\/charge_template\/[0-9]+\/chargemode\/scheduled_charging\/plans\/[0-9]+$/i, - ) - ) { - const tidMatch = topic.match(/(?:\/)([0-9]+)(?:\/)/g) - const pidMatch = topic.match(/[0-9]+$/i) - if (tidMatch && pidMatch) { - const tId = +tidMatch[0].replace(/[^0-9]+/g, '') - const pId = +pidMatch[0] - const plan: ChargeSchedule = JSON.parse(message) - if (!(tId in scheduledChargingPlans)) { - scheduledChargingPlans[tId] = [] - } - scheduledChargingPlans[tId][pId] = plan + chargeTemplates[index] = JSON.parse(message) as ChargeTemplate } } else if (topic.match(/^openwb\/vehicle\/template\/ev_template\/[0-9]+$/i)) { const match = topic.match(/[0-9]+$/i) @@ -225,35 +173,11 @@ export function processVehicleTemplateMessages(topic: string, message: string) { const index = +match[0] const template: EvTemplate = JSON.parse(message) as EvTemplate evTemplates[index] = template - // updateCpFromChargeTemplate(index, template) } } else { // console.warn('Ignored VEHICLE TEMPLATE message [' + topic + ']=' + message) } } -function updateCpFromChargeTemplate(index: number, template: ChargeTemplate) { - Object.values(chargePoints).forEach((cp) => { - if (cp.chargeTemplate == index) { - cp.updateCpPriority(template.prio) - // cp.updateChargeMode(template.chargemode.selected) - cp.updateInstantChargeLimitMode( - template.chargemode.instant_charging.limit.selected, - ) - cp.updateInstantTargetCurrent( - template.chargemode.instant_charging.current, - ) - cp.updateInstantTargetSoc(template.chargemode.instant_charging.limit.soc) - cp.updateInstantMaxEnergy( - template.chargemode.instant_charging.limit.amount, - ) - cp.updatePvFeedInLimit(template.chargemode.pv_charging.feed_in_limit) - cp.updatePvMinCurrent(template.chargemode.pv_charging.min_current) - cp.updatePvMaxSoc(template.chargemode.pv_charging.max_soc) - cp.updatePvMinSoc(template.chargemode.pv_charging.min_soc) - cp.updatePvMinSocCurrent(template.chargemode.pv_charging.min_soc_current) - } - }) -} function getIndex(topic: string): number | undefined { let index = 0 diff --git a/packages/modules/display_themes/colors/source/src/components/powerGraph/PgSoc.vue b/packages/modules/display_themes/colors/source/src/components/powerGraph/PgSoc.vue index d43453d16b..dfa14c32d5 100755 --- a/packages/modules/display_themes/colors/source/src/components/powerGraph/PgSoc.vue +++ b/packages/modules/display_themes/colors/source/src/components/powerGraph/PgSoc.vue @@ -118,7 +118,7 @@ const nameY = computed(() => { return yScale.value(graphData.data[index]['soc' + vID.value] + 2) case 2: index = Math.round(graphData.data.length / 2) - return yScale.value(graphData.data[index].batSoc + 2) + return yScale.value((graphData.data[index]?.batSoc ?? 0) + 2) default: return 0 } diff --git a/packages/modules/display_themes/colors/source/src/components/priceChart/PriceChart.vue b/packages/modules/display_themes/colors/source/src/components/priceChart/PriceChart.vue index 88e9f2197c..62e29ef3dd 100755 --- a/packages/modules/display_themes/colors/source/src/components/priceChart/PriceChart.vue +++ b/packages/modules/display_themes/colors/source/src/components/priceChart/PriceChart.vue @@ -1,9 +1,9 @@ -
+
- +
+ +
diff --git a/packages/modules/web_themes/colors/source/src/components/batteryList/model.ts b/packages/modules/web_themes/colors/source/src/components/batteryList/model.ts index f6aa31cadb..a876c5bab0 100755 --- a/packages/modules/web_themes/colors/source/src/components/batteryList/model.ts +++ b/packages/modules/web_themes/colors/source/src/components/batteryList/model.ts @@ -5,10 +5,12 @@ */ import { masterData } from '@/assets/js/model' +import { PowerItemType, type PowerItem } from '@/assets/js/types' import { reactive, ref } from 'vue' -export class Battery { +export class Battery implements PowerItem { id: number name = 'Speicher' + type = PowerItemType.battery color = 'var(--color-battery)' dailyYieldExport = 0 dailyYieldImport = 0 @@ -22,6 +24,12 @@ export class Battery { imported = 0 power = 0 soc = 0 + energy = 0 + energyPv = 0 + energyBat = 0 + pvPercentage = 0 + showInGraph = true + icon = 'Speicher' constructor(index: number) { this.id = index } diff --git a/packages/modules/web_themes/colors/source/src/components/buttonBar/BBSelect.vue b/packages/modules/web_themes/colors/source/src/components/buttonBar/BBSelect.vue index 9e1aafd653..59b3c3d648 100755 --- a/packages/modules/web_themes/colors/source/src/components/buttonBar/BBSelect.vue +++ b/packages/modules/web_themes/colors/source/src/components/buttonBar/BBSelect.vue @@ -122,7 +122,7 @@ const buttons = [ name: 'Zielladen', color: 'var(--color-battery)', }, - { mode: 'standby', name: 'Standby', color: 'var(--color-axis)' }, + { mode: 'eco_charging', name: 'Eco', color: 'var(--color-devices)' }, { mode: 'stop', name: 'Stop', color: 'var(--color-axis)' }, ] const cp = computed(() => { diff --git a/packages/modules/web_themes/colors/source/src/components/buttonBar/BbChargeButton.vue b/packages/modules/web_themes/colors/source/src/components/buttonBar/BbChargeButton.vue index fcfa6dcf5a..d275e00c64 100644 --- a/packages/modules/web_themes/colors/source/src/components/buttonBar/BbChargeButton.vue +++ b/packages/modules/web_themes/colors/source/src/components/buttonBar/BbChargeButton.vue @@ -100,7 +100,6 @@ const modePillStyle = computed(() => { style = swapcolors(style) } break - case ChargeMode.standby: case ChargeMode.stop: style.background = 'darkgrey' style.color = 'black' diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/CPChargePoint.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/CPChargePoint.vue old mode 100755 new mode 100644 index 6a2648ce41..4ae1bc2696 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/CPChargePoint.vue +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/CPChargePoint.vue @@ -22,7 +22,6 @@ @@ -47,7 +46,7 @@
- @@ -55,225 +54,14 @@
- + - + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/ChargingState.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/ChargingState.vue new file mode 100644 index 0000000000..68afb5c3e6 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/ChargingState.vue @@ -0,0 +1,65 @@ + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/VehicleData.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/VehicleData.vue new file mode 100644 index 0000000000..24744fb9d2 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/VehicleData.vue @@ -0,0 +1,308 @@ + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfig.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfig.vue deleted file mode 100755 index 0998e3b47a..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfig.vue +++ /dev/null @@ -1,150 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue deleted file mode 100755 index b895ec125c..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPChargeConfigPanel.vue +++ /dev/null @@ -1,225 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigInstant.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigInstant.vue deleted file mode 100755 index f210a901a2..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigInstant.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigPv.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigPv.vue deleted file mode 100755 index 12240501fc..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigPv.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigScheduled.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigScheduled.vue deleted file mode 100755 index b2d08ffbb6..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigScheduled.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigTimed.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigTimed.vue deleted file mode 100755 index 293228118d..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigTimed.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigVehicle.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigVehicle.vue deleted file mode 100755 index 29d65975a4..0000000000 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/CPConfigVehicle.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - - - diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ChargeConfigPanel.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ChargeConfigPanel.vue new file mode 100755 index 0000000000..9f797d8ac3 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ChargeConfigPanel.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigEco.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigEco.vue new file mode 100644 index 0000000000..0ce0f36e8b --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigEco.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigGeneral.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigGeneral.vue new file mode 100755 index 0000000000..29cd262901 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigGeneral.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigInstant.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigInstant.vue new file mode 100755 index 0000000000..17e80c94e8 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigInstant.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigPv.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigPv.vue new file mode 100755 index 0000000000..61229674ed --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigPv.vue @@ -0,0 +1,219 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigScheduled.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigScheduled.vue new file mode 100755 index 0000000000..fc9ee2b6d7 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigScheduled.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigTimed.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigTimed.vue new file mode 100755 index 0000000000..e8db4bdf8f --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ConfigTimed.vue @@ -0,0 +1,143 @@ + + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ScheduleDetails.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ScheduleDetails.vue new file mode 100644 index 0000000000..fd89722d44 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/ScheduleDetails.vue @@ -0,0 +1,82 @@ + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/TimePlanDetails.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/TimePlanDetails.vue new file mode 100644 index 0000000000..721e0bf9f3 --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpConfig/TimePlanDetails.vue @@ -0,0 +1,85 @@ + + + diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpSimpleList/CpsListItem2.vue b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpSimpleList/CpsListItem2.vue index 623dbc65de..c8b5453e0a 100755 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/cpSimpleList/CpsListItem2.vue +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/cpSimpleList/CpsListItem2.vue @@ -118,7 +118,7 @@ :modal-id="'cpsconfig-' + chargepoint.id" > - @@ -131,7 +131,7 @@ import { ref, computed } from 'vue' import { chargePoints, type ChargePoint } from '../model' import { chargemodes, globalConfig } from '@/assets/js/themeConfig' import { formatWatt, formatWattH } from '@/assets/js/helpers' -import CPChargeConfigPanel from '../cpConfig/CPChargeConfigPanel.vue' +import ChargeConfigPanel from '../cpConfig/ChargeConfigPanel.vue' import BatterySymbol from '../../shared/BatterySymbol.vue' import RangeInput from '@/components/shared/RangeInput.vue' import { updateServer } from '@/assets/js/sendMessages' diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/model.ts b/packages/modules/web_themes/colors/source/src/components/chargePointList/model.ts index 8ea0944683..3be83da59b 100755 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/model.ts +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/model.ts @@ -1,13 +1,13 @@ import { computed, reactive } from 'vue' -import { updateServer } from '@/assets/js/sendMessages' -import { ChargeMode, type PowerItem } from '@/assets/js/types' +import { updateChargeTemplate, updateServer } from '@/assets/js/sendMessages' +import { ChargeMode, PowerItemType, type PowerItem } from '@/assets/js/types' import { globalConfig } from '@/assets/js/themeConfig' import { masterData } from '@/assets/js/model' -export class ChargePoint { +export class ChargePoint implements PowerItem { id: number name = 'Ladepunkt' icon = 'Ladepunkt' - type = '' + type = PowerItemType.chargepoint ev = 0 template = 0 connectedPhases = 0 @@ -19,7 +19,7 @@ export class ChargePoint { isCharging = false private _isLocked = false private _connectedVehicle = 0 - chargeTemplate = 0 + chargeTemplate: ChargeTemplate | null = null evTemplate = 0 private _chargeMode = ChargeMode.pv_charging private _hasPriority = false @@ -47,16 +47,29 @@ export class ChargePoint { isSocManual = false waitingForSoc = false color = 'white' + energy = 0 + showInGraph = true private _timedCharging = false private _instantChargeLimitMode = '' private _instantTargetCurrent = 0 private _instantTargetSoc = 0 private _instantMaxEnergy = 0 + private _instantTargetPhases = 0 private _pvFeedInLimit = false private _pvMinCurrent = 0 private _pvMaxSoc = 0 private _pvMinSoc = 0 private _pvMinSocCurrent = 0 + private _pvMinSocPhases = 1 + private _pvChargeLimitMode = '' + private _pvTargetSoc = 0 + private _pvMaxEnergy = 0 + private _pvTargetPhases = 0 + private _ecoMinCurrent = 0 + private _ecoTargetPhases = 0 + private _ecoChargeLimitMode = '' + private _ecoTargetSoc = 0 + private _ecoMaxEnergy = 0 private _etActive = false private _etMaxPrice = 20 @@ -96,97 +109,128 @@ export class ChargePoint { } } get chargeMode() { - return this._chargeMode + return this.chargeTemplate?.chargemode.selected ?? ChargeMode.stop } set chargeMode(cm: ChargeMode) { - this._chargeMode = cm - updateServer('chargeMode', cm, this.id) + console.log('set mode') + if (this.chargeTemplate) { + console.log('active') + this.chargeTemplate.chargemode.selected = cm + updateChargeTemplate(this.id) + } } - updateChargeMode(cm: ChargeMode) { + /* updateChargeMode(cm: ChargeMode) { this._chargeMode = cm - } + } */ get hasPriority() { - return this._hasPriority + return this.chargeTemplate?.prio ?? false } set hasPriority(prio: boolean) { - this._hasPriority = prio - updateServer('cpPriority', prio, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.prio = prio + updateServer('cpPriority', prio, this.id) + } } - updateCpPriority(prio: boolean) { + /* updateCpPriority(prio: boolean) { this._hasPriority = prio - } + } */ get timedCharging() { - if (chargeTemplates[this.chargeTemplate]) { - return chargeTemplates[this.chargeTemplate].time_charging.active + if (this.chargeTemplate) { + return this.chargeTemplate.time_charging.active } else { return false } } set timedCharging(setting: boolean) { // chargeTemplates[this.chargeTemplate].time_charging.active = false - chargeTemplates[this.chargeTemplate].time_charging.active = setting - updateServer('cpTimedCharging', setting, this.chargeTemplate) + this.chargeTemplate!.time_charging.active = setting + updateServer('cpTimedCharging', setting, this.id) } get instantTargetCurrent() { - return this._instantTargetCurrent + return this.chargeTemplate?.chargemode.instant_charging.current ?? 0 } set instantTargetCurrent(current: number) { - this._instantTargetCurrent = current - updateServer('cpInstantTargetCurrent', current, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.current = current + updateChargeTemplate(this.id) + } } - updateInstantTargetCurrent(current: number) { + /* updateInstantTargetCurrent(current: number) { this._instantTargetCurrent = current } + */ get instantChargeLimitMode() { - return this._instantChargeLimitMode + return ( + this.chargeTemplate?.chargemode.instant_charging.limit.selected ?? 'none' + ) } set instantChargeLimitMode(mode: string) { - this._instantChargeLimitMode = mode - updateServer('cpInstantChargeLimitMode', mode, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.selected = mode + updateChargeTemplate(this.id) + } } - updateInstantChargeLimitMode(mode: string) { + /* updateInstantChargeLimitMode(mode: string) { this._instantChargeLimitMode = mode - } + } */ get instantTargetSoc() { - return this._instantTargetSoc + return this.chargeTemplate?.chargemode.instant_charging.limit.soc ?? 0 } set instantTargetSoc(soc: number) { - this._instantTargetSoc = soc - updateServer('cpInstantTargetSoc', soc, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.soc = soc + updateChargeTemplate(this.id) + } } - updateInstantTargetSoc(soc: number) { + /* updateInstantTargetSoc(soc: number) { this._instantTargetSoc = soc - } + } */ get instantMaxEnergy() { - return this._instantMaxEnergy + return this.chargeTemplate?.chargemode.instant_charging.limit.amount ?? 0 } set instantMaxEnergy(max: number) { - this._instantMaxEnergy = max - updateServer('cpInstantMaxEnergy', max, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.limit.amount = max + updateChargeTemplate(this.id) + } } - updateInstantMaxEnergy(max: number) { + /* updateInstantMaxEnergy(max: number) { this._instantMaxEnergy = max + } */ + get instantTargetPhases() { + return this.chargeTemplate?.chargemode.instant_charging.phases_to_use ?? 0 + } + set instantTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.instant_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } } + get pvFeedInLimit() { - return this._pvFeedInLimit + return this.chargeTemplate?.chargemode.pv_charging.feed_in_limit ?? false } set pvFeedInLimit(setting: boolean) { - this._pvFeedInLimit = setting - updateServer('cpPvFeedInLimit', setting, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.feed_in_limit = setting + updateChargeTemplate(this.id) + } } - updatePvFeedInLimit(setting: boolean) { + /* updatePvFeedInLimit(setting: boolean) { this._pvFeedInLimit = setting - } + } */ get pvMinCurrent() { - return this._pvMinCurrent + return this.chargeTemplate?.chargemode.pv_charging.min_current ?? 0 } set pvMinCurrent(min: number) { - this._pvMinCurrent = min - updateServer('cpPvMinCurrent', min, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_current = min + updateChargeTemplate(this.id) + } } - updatePvMinCurrent(min: number) { + /* updatePvMinCurrent(min: number) { this._pvMinCurrent = min - } + } */ get pvMaxSoc() { return this._pvMaxSoc } @@ -198,24 +242,135 @@ export class ChargePoint { this._pvMaxSoc = max } get pvMinSoc() { - return this._pvMinSoc + return this.chargeTemplate?.chargemode.pv_charging.min_soc ?? 0 } set pvMinSoc(min: number) { - this._pvMinSoc = min - updateServer('cpPvMinSoc', min, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_soc = min + updateChargeTemplate(this.id) + } } - updatePvMinSoc(min: number) { + /* updatePvMinSoc(min: number) { this._pvMinSoc = min - } + } */ get pvMinSocCurrent() { - return this._pvMinSocCurrent + return this.chargeTemplate?.chargemode.pv_charging.min_soc_current ?? 0 } set pvMinSocCurrent(a: number) { - this._pvMinSocCurrent = a - updateServer('cpPvMinSocCurrent', a, this.id) + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.min_soc_current = a + updateChargeTemplate(this.id) + } + } + set pvMinSocPhases(n: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.phases_to_use_min_soc = n + updateChargeTemplate(this.id) + } + } + get pvMinSocPhases() { + return ( + this.chargeTemplate?.chargemode.pv_charging.phases_to_use_min_soc ?? 0 + ) + } + get pvChargeLimitMode() { + return this.chargeTemplate?.chargemode.pv_charging.limit.selected ?? 'none' + } + set pvChargeLimitMode(mode: string) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.selected = mode + updateChargeTemplate(this.id) + } + } + get pvTargetSoc() { + return this.chargeTemplate?.chargemode.pv_charging.limit.soc ?? 0 + } + set pvTargetSoc(soc: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.soc = soc + updateChargeTemplate(this.id) + } + } + get pvMaxEnergy() { + return this.chargeTemplate?.chargemode.pv_charging.limit.amount ?? 0 + } + set pvMaxEnergy(max: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.limit.amount = max + updateChargeTemplate(this.id) + } + } + get pvTargetPhases() { + return this.chargeTemplate?.chargemode.pv_charging.phases_to_use ?? 0 + } + set pvTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.pv_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } + } + get ecoMinCurrent() { + return this.chargeTemplate?.chargemode.eco_charging.current ?? 0 + } + set ecoMinCurrent(min: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.current = min + updateChargeTemplate(this.id) + } + } + get ecoTargetPhases() { + return this.chargeTemplate?.chargemode.eco_charging.phases_to_use ?? 0 } - updatePvMinSocCurrent(a: number) { - this._pvMinSocCurrent = a + set ecoTargetPhases(phases: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.phases_to_use = phases + updateChargeTemplate(this.id) + } + } + get ecoChargeLimitMode() { + return this.chargeTemplate?.chargemode.eco_charging.limit.selected ?? 'none' + } + set ecoChargeLimitMode(mode: string) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.selected = mode + updateChargeTemplate(this.id) + } + } + get ecoTargetSoc() { + return this.chargeTemplate?.chargemode.eco_charging.limit.soc ?? 0 + } + set ecoTargetSoc(soc: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.soc = soc + updateChargeTemplate(this.id) + } + } + get ecoMaxEnergy() { + return this.chargeTemplate?.chargemode.eco_charging.limit.amount ?? 0 + } + set ecoMaxEnergy(max: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.limit.amount = max + updateChargeTemplate(this.id) + } + } + get etMaxPrice() { + return ( + (this.chargeTemplate?.chargemode.eco_charging.max_price ?? 0) * 100000 + ) + } + set etMaxPrice(newPrice: number) { + if (this.chargeTemplate) { + this.chargeTemplate.chargemode.eco_charging.max_price = + Math.ceil(newPrice * 1000) / 100000000 + updateChargeTemplate(this.id) + } + } + get etActive() { + return ( + this.chargeTemplate && + this.chargeTemplate.chargemode.selected == ChargeMode.eco_charging + ) } get realCurrent() { switch (this.phasesInUse) { @@ -231,27 +386,10 @@ export class ChargePoint { return 0 } } - get etActive() { - if (vehicles[this.connectedVehicle]) { - return vehicles[this.connectedVehicle].etActive - } else { - return false - } - } - set etActive(val) { - if (vehicles[this.connectedVehicle]) { - vehicles[this.connectedVehicle].etActive = val - } - } - get etMaxPrice() { - return vehicles[this.connectedVehicle].etMaxPrice ?? 0 - } - set etMaxPrice(newPrice: number) { - updateServer('cpEtMaxPrice', Math.round(newPrice * 10) / 1000000, this.id) - } toPowerItem(): PowerItem { return { name: this.name, + type: PowerItemType.chargepoint, power: this.power, energy: this.dailyYield, energyPv: this.energyPv, @@ -297,25 +435,6 @@ export class Vehicle { updateEvTemplateId(id: number) { this._evTemplateId = id } - get etActive() { - if (chargeTemplates[this.chargeTemplateId]) { - return chargeTemplates[this.chargeTemplateId].et.active - } else { - return false - } - } - set etActive(val) { - if (chargeTemplates[this.chargeTemplateId]) { - updateServer('priceCharging', val, this.chargeTemplateId) - } - } - get etMaxPrice() { - if (chargeTemplates[this.chargeTemplateId]) { - if (chargeTemplates[this.chargeTemplateId].et.active) { - return chargeTemplates[this.chargeTemplateId].et.max_price * 100000 - } - } - } get chargepoint(): ChargePoint | undefined { for (const cp of Object.values(chargePoints)) { if (cp.connectedVehicle == this.id) { @@ -340,22 +459,34 @@ export interface ConnectedVehicleConfig { priority: boolean } export interface ChargeTimePlan { + id: number + name: string active: boolean + time: string[] + current: number + dc_current: number + phases_to_use: number + limit: { + selected: string + amount: number + soc: number + } frequency: { - once: Array + once: string[] selected: string weekly: boolean[] } - name: string - time: Array - current: number } export interface ChargeSchedule { + id: number name: string active: boolean - timed: boolean time: string current: number + dc_current: number + phases_to_use: number + phases_to_use_pv: number + et_active: boolean limit: { selected: string amount: number @@ -363,40 +494,61 @@ export interface ChargeSchedule { soc_scheduled: number } frequency: { - once: Array + once: string selected: string weekly: boolean[] } } export interface ChargeTemplate { + id: number name: string prio: boolean + load_default: boolean + time_charging: { + active: boolean + plans: [ChargeTimePlan] + } chargemode: { selected: ChargeMode - instant_charging: { + eco_charging: { current: number + dc_current: number limit: { selected: string soc: number amount: number } + max_price: number + phases_to_use: number } pv_charging: { + dc_min_current: number + dc_min_soc_current: number feed_in_limit: boolean + limit: { + selected: string + amount: number + soc: number + } min_current: number - max_soc: number - min_soc: number min_soc_current: number + min_soc: number + phases_to_use: number + phases_to_use_min_soc: number + } + scheduled_charging: { + plans: [ChargeSchedule] + } + instant_charging: { + current: number + dc_current: number + limit: { + selected: string + soc: number + amount: number + } + phases_to_use: number } - } - time_charging: { - active: boolean - } - disable_after_unplug: boolean - load_default: boolean - et: { - active: boolean - max_price: number } } export interface EvTemplate { @@ -419,12 +571,6 @@ export interface EvTemplate { export const chargePoints: { [key: number]: ChargePoint } = reactive({}) export const vehicles: { [key: number]: Vehicle } = reactive({}) // the list of vehicles, key is the vehicle ID export const chargeTemplates: { [key: number]: ChargeTemplate } = reactive({}) -export const scheduledChargingPlans: { [key: number]: ChargeSchedule[] } = - reactive({}) -export const timeChargingPlans: { [key: number]: ChargeTimePlan[] } = reactive( - {}, -) - export const evTemplates: { [key: number]: EvTemplate } = reactive({}) export function addChargePoint(chargePointIndex: number) { @@ -489,3 +635,8 @@ export const topVehicles = computed(() => { } return result }) +export const chargeLimitModes = [ + { name: 'keine', id: 'none' }, + { name: 'Ladestand', id: 'soc' }, + { name: 'Energie', id: 'amount' }, +] diff --git a/packages/modules/web_themes/colors/source/src/components/chargePointList/processMessages.ts b/packages/modules/web_themes/colors/source/src/components/chargePointList/processMessages.ts index fd35958b08..dc689abc51 100755 --- a/packages/modules/web_themes/colors/source/src/components/chargePointList/processMessages.ts +++ b/packages/modules/web_themes/colors/source/src/components/chargePointList/processMessages.ts @@ -2,20 +2,15 @@ import { usageSummary, globalData, masterData } from '@/assets/js/model' import { chargePoints, vehicles, - chargeTemplates, evTemplates, Vehicle, - type ChargeTimePlan, - scheduledChargingPlans, - timeChargingPlans, + chargeTemplates, } from './model' import type { ConnectedVehicleConfig, - ChargeTemplate, EvTemplate, - ChargeSchedule, + ChargeTemplate, } from './model' -import { ChargeMode } from '@/assets/js/types' export function processChargepointMessages(topic: string, message: string) { const index = getIndex(topic) @@ -110,25 +105,11 @@ export function processChargepointMessages(topic: string, message: string) { ) ) { const config: ConnectedVehicleConfig = JSON.parse(message) - switch (config.chargemode) { - case 'instant_charging': - chargePoints[index].updateChargeMode(ChargeMode.instant_charging) - break - case 'pv_charging': - chargePoints[index].updateChargeMode(ChargeMode.pv_charging) - break - case 'scheduled_charging': - chargePoints[index].updateChargeMode(ChargeMode.scheduled_charging) - break - case 'standby': - chargePoints[index].updateChargeMode(ChargeMode.standby) - break - case 'stop': - chargePoints[index].updateChargeMode(ChargeMode.stop) - break - } - chargePoints[index].chargeTemplate = config.charge_template chargePoints[index].averageConsumption = config.average_consumption + } else if ( + topic.match(/^openwb\/chargepoint\/[0-9]+\/set\/charge_template$/i) + ) { + chargePoints[index].chargeTemplate = JSON.parse(message) } else { // console.warn('Ignored chargepoint message: ' + topic) } @@ -185,41 +166,7 @@ export function processVehicleTemplateMessages(topic: string, message: string) { const match = topic.match(/[0-9]+$/i) if (match) { const index = +match[0] - const template: ChargeTemplate = JSON.parse(message) as ChargeTemplate - chargeTemplates[index] = template - updateCpFromChargeTemplate(index, template) - } - } else if ( - topic.match( - /^openwb\/vehicle\/template\/charge_template\/[0-9]+\/time_charging\/plans\/[0-9]+$/i, - ) - ) { - const tidMatch = topic.match(/(?:\/)([0-9]+)(?:\/)/g) - const pidMatch = topic.match(/[0-9]+$/i) - if (tidMatch && pidMatch) { - const tId = +tidMatch[0].replace(/[^0-9]+/g, '') - const pId = +pidMatch[0] - const plan: ChargeTimePlan = JSON.parse(message) - if (!(tId in timeChargingPlans)) { - timeChargingPlans[tId] = [] - } - timeChargingPlans[tId][pId] = plan - } - } else if ( - topic.match( - /^openwb\/vehicle\/template\/charge_template\/[0-9]+\/chargemode\/scheduled_charging\/plans\/[0-9]+$/i, - ) - ) { - const tidMatch = topic.match(/(?:\/)([0-9]+)(?:\/)/g) - const pidMatch = topic.match(/[0-9]+$/i) - if (tidMatch && pidMatch) { - const tId = +tidMatch[0].replace(/[^0-9]+/g, '') - const pId = +pidMatch[0] - const plan: ChargeSchedule = JSON.parse(message) - if (!(tId in scheduledChargingPlans)) { - scheduledChargingPlans[tId] = [] - } - scheduledChargingPlans[tId][pId] = plan + chargeTemplates[index] = JSON.parse(message) as ChargeTemplate } } else if (topic.match(/^openwb\/vehicle\/template\/ev_template\/[0-9]+$/i)) { const match = topic.match(/[0-9]+$/i) @@ -227,35 +174,11 @@ export function processVehicleTemplateMessages(topic: string, message: string) { const index = +match[0] const template: EvTemplate = JSON.parse(message) as EvTemplate evTemplates[index] = template - // updateCpFromChargeTemplate(index, template) } } else { // console.warn('Ignored VEHICLE TEMPLATE message [' + topic + ']=' + message) } } -function updateCpFromChargeTemplate(index: number, template: ChargeTemplate) { - Object.values(chargePoints).forEach((cp) => { - if (cp.chargeTemplate == index) { - cp.updateCpPriority(template.prio) - // cp.updateChargeMode(template.chargemode.selected) - cp.updateInstantChargeLimitMode( - template.chargemode.instant_charging.limit.selected, - ) - cp.updateInstantTargetCurrent( - template.chargemode.instant_charging.current, - ) - cp.updateInstantTargetSoc(template.chargemode.instant_charging.limit.soc) - cp.updateInstantMaxEnergy( - template.chargemode.instant_charging.limit.amount, - ) - cp.updatePvFeedInLimit(template.chargemode.pv_charging.feed_in_limit) - cp.updatePvMinCurrent(template.chargemode.pv_charging.min_current) - cp.updatePvMaxSoc(template.chargemode.pv_charging.max_soc) - cp.updatePvMinSoc(template.chargemode.pv_charging.min_soc) - cp.updatePvMinSocCurrent(template.chargemode.pv_charging.min_soc_current) - } - }) -} function getIndex(topic: string): number | undefined { let index = 0 diff --git a/packages/modules/web_themes/colors/source/src/components/counterList/model.ts b/packages/modules/web_themes/colors/source/src/components/counterList/model.ts index 47d0046087..189b033494 100755 --- a/packages/modules/web_themes/colors/source/src/components/counterList/model.ts +++ b/packages/modules/web_themes/colors/source/src/components/counterList/model.ts @@ -1,17 +1,21 @@ +import { PowerItemType, type PowerItem } from '@/assets/js/types' import { reactive } from 'vue' -export class Counter { +export class Counter implements PowerItem { id: number name = 'Zähler' power = 0 energy_imported = 0 energy_exported = 0 grid = false - type = 'counter' + counterType = 'counter' + type = PowerItemType.counter color = 'var(--color-evu)' + energy = 0 energyPv = 0 energyBat = 0 pvPercentage = 0 icon = '' + showInGraph = true constructor(index: number) { this.id = index } @@ -19,11 +23,11 @@ export class Counter { export const counters: { [key: number]: Counter } = reactive({}) -export function addCounter(index: number, type: string) { +export function addCounter(index: number, counterType: string) { if (!(index in counters)) { counters[index] = new Counter(index) - counters[index].type = type - switch (type) { + counters[index].counterType = counterType + switch (counterType) { case 'counter': counters[index].color = 'var(--color-evu)' break diff --git a/packages/modules/web_themes/colors/source/src/components/energyMeter2/EnergyMeter2.vue b/packages/modules/web_themes/colors/source/src/components/energyMeter2/EnergyMeter2.vue index 05e225f54b..e8c640fae4 100755 --- a/packages/modules/web_themes/colors/source/src/components/energyMeter2/EnergyMeter2.vue +++ b/packages/modules/web_themes/colors/source/src/components/energyMeter2/EnergyMeter2.vue @@ -66,7 +66,7 @@ diff --git a/packages/modules/web_themes/colors/source/src/components/powerGraph/model.ts b/packages/modules/web_themes/colors/source/src/components/powerGraph/model.ts index 9336d6835c..37aba013fa 100755 --- a/packages/modules/web_themes/colors/source/src/components/powerGraph/model.ts +++ b/packages/modules/web_themes/colors/source/src/components/powerGraph/model.ts @@ -172,7 +172,7 @@ export const dayGraph = reactive({ graphData.waitForData = true sendCommand({ command: 'getDailyLog', - data: { day: dateString }, + data: { date: dateString }, }) } }, @@ -208,7 +208,7 @@ export const monthGraph = reactive({ graphData.waitForData = true sendCommand({ command: 'getMonthlyLog', - data: { month: dateString }, + data: { date: dateString }, }) }, deactivate() { @@ -256,7 +256,7 @@ export const yearGraph = reactive({ graphData.waitForData = true sendCommand({ command: 'getYearlyLog', - data: { year: dateString }, + data: { date: dateString }, }) }, deactivate() { diff --git a/packages/modules/web_themes/colors/source/src/components/powerMeter/PMArc.vue b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMArc.vue new file mode 100644 index 0000000000..0e53cf73af --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMArc.vue @@ -0,0 +1,93 @@ + + + diff --git a/packages/modules/web_themes/colors/source/src/components/powerMeter/PMPopup.vue b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMPopup.vue new file mode 100644 index 0000000000..03f40918af --- /dev/null +++ b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMPopup.vue @@ -0,0 +1,63 @@ + + + + diff --git a/packages/modules/web_themes/colors/source/src/components/powerMeter/PMSourceArc.vue b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMSourceArc.vue index cecbe1de83..e1a540c719 100755 --- a/packages/modules/web_themes/colors/source/src/components/powerMeter/PMSourceArc.vue +++ b/packages/modules/web_themes/colors/source/src/components/powerMeter/PMSourceArc.vue @@ -1,25 +1,36 @@ - + diff --git a/packages/modules/web_themes/colors/source/src/components/powerMeter/PowerMeter.vue b/packages/modules/web_themes/colors/source/src/components/powerMeter/PowerMeter.vue index f1ddb70d3f..3d67f74e36 100755 --- a/packages/modules/web_themes/colors/source/src/components/powerMeter/PowerMeter.vue +++ b/packages/modules/web_themes/colors/source/src/components/powerMeter/PowerMeter.vue @@ -10,14 +10,15 @@ :corner-radius="cornerRadius" :circle-gap-size="circleGapSize" :empty-power="emptyPower" + :show-labels="globalConfig.showPmLabels" /> @@ -147,6 +148,18 @@ > Peak: {{ maxPowerString }} + + {{ '\uf05a' }} + @@ -166,7 +179,6 @@ import { usageSummary, masterData, } from '@/assets/js/model' -import { shDevices } from '../smartHome/model' import { chargePoints, vehicles, @@ -183,7 +195,7 @@ import { etData } from '../priceChart/model' const width = 500 const height = width const margin = 20 -const cornerRadius = 1 +const cornerRadius = 20 const circleGapSize = Math.PI / 40 const schemes = [[4], [4, 6], [1, 4, 6], [0, 2, 4, 6], [0, 2, 3, 5, 6]] @@ -199,7 +211,7 @@ const labelPositions = [ { x: 0, y: ((height / 2) * 3) / 5 }, ] const radius = computed(() => { - return width / 2 - margin + return width / 2.0 - margin }) const currentConsumptionString = computed(() => { let consumptionLabel = '' @@ -268,6 +280,10 @@ const currentPrice = computed(() => { const [p] = etData.etPriceList.values() return Math.round(p * 10) / 10 }) +function toggleInfo() { + //showLabels.value = !showLabels.value + globalConfig.showPmLabels = !globalConfig.showPmLabels +} diff --git a/packages/modules/web_themes/colors/source/src/components/priceChart/GlobalPriceChart.vue b/packages/modules/web_themes/colors/source/src/components/priceChart/GlobalPriceChart.vue index 347f68018c..53fe483039 100755 --- a/packages/modules/web_themes/colors/source/src/components/priceChart/GlobalPriceChart.vue +++ b/packages/modules/web_themes/colors/source/src/components/priceChart/GlobalPriceChart.vue @@ -136,7 +136,8 @@ const yAxisGenerator = computed(() => { .ticks(yDomain.value[1] - yDomain.value[0]) .tickSize(0) .tickSizeInner(-(width - margin.right - margin.left)) - .tickFormat((d) => d.toString()) + //.tickFormat((d) => d.toString()) + .tickFormat((d: number) => (d % 5 != 0 ? '' : d.toString())) ) }) // Draw the diagram diff --git a/packages/modules/web_themes/colors/source/src/components/priceChart/PriceChart.vue b/packages/modules/web_themes/colors/source/src/components/priceChart/PriceChart.vue index be844e3893..7399c297cb 100755 --- a/packages/modules/web_themes/colors/source/src/components/priceChart/PriceChart.vue +++ b/packages/modules/web_themes/colors/source/src/components/priceChart/PriceChart.vue @@ -1,5 +1,4 @@