diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8edd61db..5e688d9d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,6 +45,9 @@ jobs: - name: 🔄 Configure Git to use SSH run: git config --global url."git@github.com:".insteadOf "https://github.com/" + - name: 📋 Setup Environment File + run: cp env.template assets/.env + - name: Install Dependencies run: flutter pub get @@ -97,6 +100,9 @@ jobs: - name: 🔄 Configure Git to use SSH run: git config --global url."git@github.com:".insteadOf "https://github.com/" + - name: 📋 Setup Environment File + run: cp env.template assets/.env + - name: Install Dependencies run: flutter pub get diff --git a/.github/workflows/deploy-demo.yml b/.github/workflows/deploy-demo.yml new file mode 100644 index 000000000..b92431e61 --- /dev/null +++ b/.github/workflows/deploy-demo.yml @@ -0,0 +1,84 @@ +name: 🚀 Deploy Demo to GitHub Pages + +# 手動觸發 +on: + workflow_dispatch: + inputs: + environment: + description: 'Deployment environment' + required: false + default: 'demo' + type: choice + options: + - demo + - staging + +# 設定 GitHub Pages 所需的權限 +permissions: + contents: read + pages: write + id-token: write + +# 避免同時部署衝突 +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + name: 🏗️ Build Demo Web + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: 📥 Checkout + uses: actions/checkout@v4 + + - name: 🐦 Setup Flutter + uses: subosito/flutter-action@v2 + with: + channel: 'stable' + cache: true + + # Setup SSH for private dependencies (same as ci.yml) + - name: 🔑 Setup SSH for Private Deps + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.UI_KIT_DEPLOY_KEY }} + + - name: 🔄 Configure Git to use SSH + run: git config --global url."git@github.com:".insteadOf "https://github.com/" + + - name: 📦 Install Dependencies + run: flutter pub get + + - name: 🌐 Build Web (Demo Mode) + run: | + flutter build web \ + --release \ + --target=lib/main_demo.dart \ + --base-href "/PrivacyGUI/demo/" + + - name: � Prepare Deploy Structure + run: | + mkdir -p deploy/demo + mv build/web/* deploy/demo/ + + - name: �📤 Upload Build Artifact + uses: actions/upload-pages-artifact@v3 + with: + path: deploy + + deploy: + name: 🚀 Deploy to GitHub Pages + needs: build + runs-on: ubuntu-latest + + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + + steps: + - name: 🌍 Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index 763298e31..dc39a1b46 100644 --- a/.gitignore +++ b/.gitignore @@ -81,7 +81,7 @@ plugins/ios_push_notification_plugin/example/pubspec.lock plugins/sms_receiver_plugin/example/pubspec.lock plugins/universal_link_plugin/example/pubspec.lock build/app/outputs/* -packages + plugins/connecting_wifi_plugin/example/pubspec.lock plugins/ios_push_notification_plugin/example/pubspec.lock plugins/sms_receiver_plugin/example/pubspec.lock @@ -97,7 +97,7 @@ test/**/*.mocks.dart # untranslated.txt -packages + snapshots/* reports/* lib/l10n/gen/app_localizations_ar.dart diff --git a/assets/resources/demo_cache_data.json b/assets/resources/demo_cache_data.json new file mode 100644 index 000000000..69cb8b57c --- /dev/null +++ b/assets/resources/demo_cache_data.json @@ -0,0 +1,2818 @@ +{ + "http://linksys.com/jnap/core/IsAdminPasswordDefault": { + "target": "http://linksys.com/jnap/core/IsAdminPasswordDefault", + "cachedAt": 1766560099456, + "data": { + "result": "OK", + "output": { + "isAdminPasswordDefault": false + } + } + }, + "http://linksys.com/jnap/nodes/setup/IsAdminPasswordSetByUser": { + "target": "http://linksys.com/jnap/nodes/setup/IsAdminPasswordSetByUser", + "cachedAt": 1766560099456, + "data": { + "result": "OK", + "output": { + "isAdminPasswordSetByUser": true + } + } + }, + "http://linksys.com/jnap/core/GetDeviceInfo": { + "target": "http://linksys.com/jnap/core/GetDeviceInfo", + "cachedAt": 1766560099659, + "data": { + "result": "OK", + "output": { + "manufacturer": "Linksys", + "modelNumber": "LN16", + "hardwareVersion": "1", + "description": "Linksys Velop Micro Mesh 7", + "serialNumber": "65G10M2CE00107", + "firmwareVersion": "1.0.10.216621", + "firmwareDate": "2025-05-19T01:22:58Z", + "services": [ + "http://linksys.com/jnap/bornon/BornOn", + "http://linksys.com/jnap/core/Core", + "http://linksys.com/jnap/core/Core2", + "http://linksys.com/jnap/core/Core3", + "http://linksys.com/jnap/core/Core4", + "http://linksys.com/jnap/core/Core5", + "http://linksys.com/jnap/core/Core6", + "http://linksys.com/jnap/core/Core7", + "http://linksys.com/jnap/core/Core8", + "http://linksys.com/jnap/core/Core9", + "http://linksys.com/jnap/ddns/DDNS", + "http://linksys.com/jnap/ddns/DDNS2", + "http://linksys.com/jnap/ddns/DDNS3", + "http://linksys.com/jnap/ddns/DDNS4", + "http://linksys.com/jnap/devicelist/DeviceList", + "http://linksys.com/jnap/devicelist/DeviceList2", + "http://linksys.com/jnap/devicelist/DeviceList4", + "http://linksys.com/jnap/devicelist/DeviceList5", + "http://linksys.com/jnap/devicelist/DeviceList6", + "http://linksys.com/jnap/devicelist/DeviceList7", + "http://linksys.com/jnap/devicepreauthorization/DevicePreauthorization", + "http://linksys.com/jnap/diagnostics/Diagnostics", + "http://linksys.com/jnap/diagnostics/Diagnostics10", + "http://linksys.com/jnap/diagnostics/Diagnostics2", + "http://linksys.com/jnap/diagnostics/Diagnostics3", + "http://linksys.com/jnap/diagnostics/Diagnostics6", + "http://linksys.com/jnap/diagnostics/Diagnostics7", + "http://linksys.com/jnap/diagnostics/Diagnostics8", + "http://linksys.com/jnap/diagnostics/Diagnostics9", + "http://linksys.com/jnap/diagnostics/Reliability", + "http://linksys.com/jnap/dynamicportforwarding/DynamicPortForwarding", + "http://linksys.com/jnap/dynamicportforwarding/DynamicPortForwarding2", + "http://linksys.com/jnap/dynamicsession/DynamicSession", + "http://linksys.com/jnap/dynamicsession/DynamicSession2", + "http://linksys.com/jnap/firewall/Firewall", + "http://linksys.com/jnap/firewall/Firewall2", + "http://linksys.com/jnap/firmwareupdate/FirmwareUpdate", + "http://linksys.com/jnap/firmwareupdate/FirmwareUpdate2", + "http://linksys.com/jnap/guestnetwork/GuestNetwork", + "http://linksys.com/jnap/guestnetwork/GuestNetwork2", + "http://linksys.com/jnap/guestnetwork/GuestNetwork3", + "http://linksys.com/jnap/guestnetwork/GuestNetwork4", + "http://linksys.com/jnap/guestnetwork/GuestNetwork5", + "http://linksys.com/jnap/guestnetwork/GuestNetworkAuthentication", + "http://linksys.com/jnap/httpproxy/HttpProxy", + "http://linksys.com/jnap/httpproxy/HttpProxy2", + "http://linksys.com/jnap/locale/Locale", + "http://linksys.com/jnap/locale/Locale2", + "http://linksys.com/jnap/locale/Locale3", + "http://linksys.com/jnap/macfilter/MACFilter", + "http://linksys.com/jnap/macfilter/MACFilter2", + "http://linksys.com/jnap/networkconnections/NetworkConnections", + "http://linksys.com/jnap/networkconnections/NetworkConnections2", + "http://linksys.com/jnap/networkconnections/NetworkConnections3", + "http://linksys.com/jnap/nodes/autoonboarding/AutoOnboarding", + "http://linksys.com/jnap/nodes/autoonboarding/AutoOnboarding2", + "http://linksys.com/jnap/nodes/autoonboarding/AutoOnboarding3", + "http://linksys.com/jnap/nodes/bluetooth/Bluetooth", + "http://linksys.com/jnap/nodes/bluetooth/Bluetooth2", + "http://linksys.com/jnap/nodes/bluetooth/Bluetooth3", + "http://linksys.com/jnap/nodes/btsmartconnect/BTSmartConnect", + "http://linksys.com/jnap/nodes/btsmartconnect/BTSmartConnect2", + "http://linksys.com/jnap/nodes/btsmartconnect/BTSmartConnect3", + "http://linksys.com/jnap/nodes/diagnostics/Diagnostics", + "http://linksys.com/jnap/nodes/diagnostics/Diagnostics2", + "http://linksys.com/jnap/nodes/diagnostics/Diagnostics3", + "http://linksys.com/jnap/nodes/diagnostics/Diagnostics5", + "http://linksys.com/jnap/nodes/diagnostics/Diagnostics6", + "http://linksys.com/jnap/nodes/firmwareupdate/FirmwareUpdate", + "http://linksys.com/jnap/nodes/networkconnections/NodesNetworkConnections", + "http://linksys.com/jnap/nodes/networkconnections/NodesNetworkConnections2", + "http://linksys.com/jnap/nodes/notification/Notification", + "http://linksys.com/jnap/nodes/setup/Setup", + "http://linksys.com/jnap/nodes/setup/Setup10", + "http://linksys.com/jnap/nodes/setup/Setup11", + "http://linksys.com/jnap/nodes/setup/Setup2", + "http://linksys.com/jnap/nodes/setup/Setup3", + "http://linksys.com/jnap/nodes/setup/Setup4", + "http://linksys.com/jnap/nodes/setup/Setup5", + "http://linksys.com/jnap/nodes/setup/Setup6", + "http://linksys.com/jnap/nodes/setup/Setup7", + "http://linksys.com/jnap/nodes/setup/Setup8", + "http://linksys.com/jnap/nodes/setup/Setup9", + "http://linksys.com/jnap/nodes/smartconnect/SmartConnect", + "http://linksys.com/jnap/nodes/smartconnect/SmartConnect2", + "http://linksys.com/jnap/nodes/smartconnect/SmartConnect3", + "http://linksys.com/jnap/nodes/smartconnect/SmartConnect4", + "http://linksys.com/jnap/nodes/smartmode/SmartMode", + "http://linksys.com/jnap/nodes/smartmode/SmartMode2", + "http://linksys.com/jnap/nodes/topologyoptimization/TopologyOptimization", + "http://linksys.com/jnap/nodes/topologyoptimization/TopologyOptimization2", + "http://linksys.com/jnap/ownednetwork/OwnedNetwork", + "http://linksys.com/jnap/ownednetwork/OwnedNetwork2", + "http://linksys.com/jnap/ownednetwork/OwnedNetwork3", + "http://linksys.com/jnap/parentalcontrol/ParentalControl", + "http://linksys.com/jnap/parentalcontrol/ParentalControl2", + "http://linksys.com/jnap/pgui/PGUI", + "http://linksys.com/jnap/powertable/PowerTable", + "http://linksys.com/jnap/product/Product", + "http://linksys.com/jnap/qos/QoS", + "http://linksys.com/jnap/qos/QoS2", + "http://linksys.com/jnap/qos/QoS3", + "http://linksys.com/jnap/qos/calibration/Calibration", + "http://linksys.com/jnap/router/Router", + "http://linksys.com/jnap/router/Router10", + "http://linksys.com/jnap/router/Router11", + "http://linksys.com/jnap/router/Router12", + "http://linksys.com/jnap/router/Router13", + "http://linksys.com/jnap/router/Router3", + "http://linksys.com/jnap/router/Router4", + "http://linksys.com/jnap/router/Router5", + "http://linksys.com/jnap/router/Router6", + "http://linksys.com/jnap/router/Router7", + "http://linksys.com/jnap/router/Router8", + "http://linksys.com/jnap/router/Router9", + "http://linksys.com/jnap/routerleds/RouterLEDs", + "http://linksys.com/jnap/routerleds/RouterLEDs2", + "http://linksys.com/jnap/routerleds/RouterLEDs3", + "http://linksys.com/jnap/routerleds/RouterLEDs4", + "http://linksys.com/jnap/routerlog/RouterLog", + "http://linksys.com/jnap/routerlog/RouterLog2", + "http://linksys.com/jnap/routermanagement/RouterManagement", + "http://linksys.com/jnap/routermanagement/RouterManagement2", + "http://linksys.com/jnap/routermanagement/RouterManagement3", + "http://linksys.com/jnap/routerstatus/RouterStatus", + "http://linksys.com/jnap/routerstatus/RouterStatus2", + "http://linksys.com/jnap/routerupnp/RouterUPnP", + "http://linksys.com/jnap/routerupnp/RouterUPnP2", + "http://linksys.com/jnap/smartconnect/SmartConnectClient", + "http://linksys.com/jnap/smartconnect/SmartConnectClient2", + "http://linksys.com/jnap/ui/Settings", + "http://linksys.com/jnap/ui/Settings2", + "http://linksys.com/jnap/ui/Settings3", + "http://linksys.com/jnap/wirelessap/AdvancedWirelessAP", + "http://linksys.com/jnap/wirelessap/AdvancedWirelessAP2", + "http://linksys.com/jnap/wirelessap/AirtimeFairness", + "http://linksys.com/jnap/wirelessap/DynamicFrequencySelection", + "http://linksys.com/jnap/wirelessap/MultiLinkOperation", + "http://linksys.com/jnap/wirelessap/WPSServer", + "http://linksys.com/jnap/wirelessap/WPSServer2", + "http://linksys.com/jnap/wirelessap/WPSServer3", + "http://linksys.com/jnap/wirelessap/WPSServer4", + "http://linksys.com/jnap/wirelessap/WPSServer5", + "http://linksys.com/jnap/wirelessap/WirelessAP", + "http://linksys.com/jnap/wirelessap/WirelessAP2", + "http://linksys.com/jnap/wirelessap/WirelessAP4", + "http://linksys.com/jnap/wirelessap/WirelessAP5", + "http://linksys.com/jnap/wirelessap/qualcomm/AdvancedQualcomm", + "http://linksys.com/jnap/wirelessap/ssidprioritization/SSIDPrioritization", + "http://linksys.com/jnap/wirelessscheduler/WirelessScheduler", + "http://linksys.com/jnap/wirelessscheduler/WirelessScheduler2", + "http://linksys.com/jnap/xconnect/XConnect" + ] + } + } + }, + "http://linksys.com/jnap/nodes/smartmode/GetDeviceMode": { + "target": "http://linksys.com/jnap/nodes/smartmode/GetDeviceMode", + "cachedAt": 1766559864757, + "data": { + "result": "OK", + "output": { + "mode": "Master" + } + } + }, + "http://linksys.com/jnap/macfilter/GetMACFilterSettings": { + "target": "http://linksys.com/jnap/macfilter/GetMACFilterSettings", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "macFilterMode": "Disabled", + "macAddresses": [], + "maxMACAddresses": 56 + } + } + }, + "http://linksys.com/jnap/macfilter/GetSTABSSIDS": { + "target": "http://linksys.com/jnap/macfilter/GetSTABSSIDS", + "cachedAt": 1766559865023, + "data": { + "result": "OK", + "output": { + "staBSSIDS": [] + } + } + }, + "http://linksys.com/jnap/devicelist/GetLocalDevice": { + "target": "http://linksys.com/jnap/devicelist/GetLocalDevice", + "cachedAt": 1766559865468, + "data": { + "result": "OK", + "output": { + "deviceID": "b9b753cc-71ae-44a5-b196-6e6db758b3f3" + } + } + }, + "http://linksys.com/jnap/nodes/networkconnections/GetNodesWirelessNetworkConnections2": { + "target": "http://linksys.com/jnap/nodes/networkconnections/GetNodesWirelessNetworkConnections2", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "nodeWirelessConnections": [ + { + "deviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98", + "connections": [ + { + "macAddress": "1A:C0:2D:04:D7:AA", + "negotiatedMbps": 286800, + "timestamp": "2025-12-24T05:47:03Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -53, + "txRate": 152738, + "rxRate": 100914, + "isMLOCapable": false + } + }, + { + "macAddress": "B8:F8:62:EB:50:8C", + "negotiatedMbps": 200000, + "timestamp": "2025-12-14T06:23:03Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -36, + "txRate": 1000, + "rxRate": 52990, + "isMLOCapable": false + } + }, + { + "macAddress": "20:1F:3B:74:0B:F0", + "negotiatedMbps": 433300, + "timestamp": "2025-12-20T05:13:28Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -50, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "38:B4:D3:AF:0C:7E", + "negotiatedMbps": 433300, + "timestamp": "2025-12-19T01:50:16Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -74, + "txRate": 264063, + "rxRate": 261786, + "isMLOCapable": false + } + }, + { + "macAddress": "A4:36:C7:64:49:80", + "negotiatedMbps": 86500, + "timestamp": "2025-12-04T21:54:19Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -41, + "txRate": 1000, + "rxRate": 16000, + "isMLOCapable": false + } + }, + { + "macAddress": "E2:AF:EC:F6:51:5F", + "negotiatedMbps": 866700, + "timestamp": "2025-12-20T14:37:45Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -63, + "txRate": 520000, + "rxRate": 141862, + "isMLOCapable": false + } + }, + { + "macAddress": "B8:16:5F:F7:9F:D9", + "negotiatedMbps": 86500, + "timestamp": "2025-12-16T05:25:47Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -33, + "txRate": 43300, + "rxRate": 1000, + "isMLOCapable": false + } + }, + { + "macAddress": "D4:43:8A:A8:DA:FC", + "negotiatedMbps": 200000, + "timestamp": "2025-12-22T04:42:33Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -55, + "txRate": 90000, + "rxRate": 2117, + "isMLOCapable": false + } + }, + { + "macAddress": "A8:16:9D:D6:1A:3A", + "negotiatedMbps": 866700, + "timestamp": "2025-12-19T13:14:48Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -49, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "6A:2C:C1:AC:14:9E", + "negotiatedMbps": 1201000, + "timestamp": "2025-12-21T19:38:52Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -61, + "txRate": 835788, + "rxRate": 626571, + "isMLOCapable": false + } + }, + { + "macAddress": "FE:23:B0:AE:E9:7D", + "negotiatedMbps": 1201000, + "timestamp": "2025-12-24T01:50:40Z", + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -51, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + } + ] + }, + { + "deviceID": "78705b46-ca02-8be2-af38-80691a202892", + "connections": [ + { + "macAddress": "2C:1B:3A:94:F9:A2", + "negotiatedMbps": 1, + "timestamp": "2025-12-22T17:31:52Z", + "wireless": { + "bssid": "80:69:1A:20:28:93", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -64, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "3C:A6:F6:51:87:B2", + "negotiatedMbps": 144, + "timestamp": "2025-12-05T11:50:08Z", + "wireless": { + "bssid": "80:69:1A:20:28:94", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -59, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "B8:88:80:68:CD:2E", + "negotiatedMbps": 1, + "timestamp": "2025-12-23T19:30:13Z", + "wireless": { + "bssid": "80:69:1A:20:28:93", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -71, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "E0:EF:BF:A4:F5:87", + "negotiatedMbps": 216, + "timestamp": "2025-12-22T17:40:01Z", + "wireless": { + "bssid": "80:69:1A:20:28:94", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -65, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + } + ] + }, + { + "deviceID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50", + "connections": [ + { + "macAddress": "C2:EE:5A:F5:D4:02", + "negotiatedMbps": 680, + "timestamp": "2025-12-24T05:37:25Z", + "wireless": { + "bssid": "E8:9F:80:E1:0F:53", + "isGuest": false, + "radioID": "RADIO_5GHz_2", + "band": "5GHz", + "signalDecibels": -60, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + }, + { + "macAddress": "EE:13:9A:FF:31:3D", + "negotiatedMbps": 34, + "timestamp": "2025-12-22T17:57:02Z", + "wireless": { + "bssid": "E8:9F:80:E1:0F:53", + "isGuest": false, + "radioID": "RADIO_5GHz_2", + "band": "5GHz", + "signalDecibels": -77, + "txRate": 0, + "rxRate": 0, + "isMLOCapable": false + } + } + ] + } + ] + } + } + }, + "http://linksys.com/jnap/networkconnections/GetNetworkConnections2": { + "target": "http://linksys.com/jnap/networkconnections/GetNetworkConnections2", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "connections": [ + { + "macAddress": "1A:C0:2D:04:D7:AA", + "negotiatedMbps": 172, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -54 + } + }, + { + "macAddress": "20:1F:3B:74:0B:F0", + "negotiatedMbps": 292, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -46 + } + }, + { + "macAddress": "38:B4:D3:AF:0C:7E", + "negotiatedMbps": 260, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -72 + } + }, + { + "macAddress": "6A:2C:C1:AC:14:9E", + "negotiatedMbps": 6, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -58 + } + }, + { + "macAddress": "86:69:1A:20:28:95", + "negotiatedMbps": 1201, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -47 + } + }, + { + "macAddress": "A4:36:C7:64:49:80", + "negotiatedMbps": 1, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -46 + } + }, + { + "macAddress": "A8:16:9D:D6:1A:3A", + "negotiatedMbps": 6, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -47 + } + }, + { + "macAddress": "B8:16:5F:F7:9F:D9", + "negotiatedMbps": 1, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -43 + } + }, + { + "macAddress": "B8:88:80:68:CD:2E", + "negotiatedMbps": 0, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -84 + } + }, + { + "macAddress": "B8:F8:62:EB:50:8C", + "negotiatedMbps": 72, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -34 + } + }, + { + "macAddress": "D4:43:8A:A8:DA:FC", + "negotiatedMbps": 1, + "wireless": { + "bssid": "80:69:1A:BB:7E:99", + "isGuest": false, + "radioID": "RADIO_2.4GHz", + "band": "2.4GHz", + "signalDecibels": -53 + } + }, + { + "macAddress": "E2:AF:EC:F6:51:5F", + "negotiatedMbps": 585, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -46 + } + }, + { + "macAddress": "FE:23:B0:AE:E9:7D", + "negotiatedMbps": 1201, + "wireless": { + "bssid": "80:69:1A:BB:7E:9A", + "isGuest": false, + "radioID": "RADIO_5GHz", + "band": "5GHz", + "signalDecibels": -57 + } + } + ] + } + } + }, + "http://linksys.com/jnap/wirelessap/GetRadioInfo3": { + "target": "http://linksys.com/jnap/wirelessap/GetRadioInfo3", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "isBandSteeringSupported": false, + "radios": [ + { + "radioID": "RADIO_2.4GHz", + "physicalRadioID": "ath0", + "bssid": "80:69:1A:BB:7E:99", + "band": "2.4GHz", + "supportedModes": [ + "802.11bg", + "802.11bgn", + "802.11bgnax", + "802.11mixed" + ], + "defaultMixedMode": "802.11mixed", + "supportedChannelsForChannelWidths": [ + { + "channelWidth": "Auto", + "channels": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ] + }, + { + "channelWidth": "Standard", + "channels": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13 + ] + } + ], + "supportedSecurityTypes": [ + "Enhanced-Open+None", + "Enhanced-Open-Only", + "None", + "WPA2-Personal", + "WPA2/WPA3-Mixed-Personal", + "WPA3-Personal" + ], + "maxRADIUSSharedKeyLength": 64, + "settings": { + "isEnabled": true, + "mode": "802.11mixed", + "ssid": "HAO-9F-2.4G", + "broadcastSSID": true, + "channelWidth": "Auto", + "channel": 7, + "security": "WPA2/WPA3-Mixed-Personal", + "wpaPersonalSettings": { + "passphrase": "0975280250" + } + } + }, + { + "radioID": "RADIO_5GHz", + "physicalRadioID": "ath10", + "bssid": "80:69:1A:BB:7E:9A", + "band": "5GHz", + "supportedModes": [ + "802.11a", + "802.11an", + "802.11anac", + "802.11anacax", + "802.11anacaxbe" + ], + "defaultMixedMode": "802.11anacaxbe", + "supportedChannelsForChannelWidths": [ + { + "channelWidth": "Auto", + "channels": [ + 0, + 36, + 40, + 44, + 48, + 149, + 153, + 157, + 161, + 165 + ] + }, + { + "channelWidth": "Standard", + "channels": [ + 0, + 36, + 40, + 44, + 48, + 149, + 153, + 157, + 161, + 165 + ] + }, + { + "channelWidth": "Wide", + "channels": [ + 0, + 36, + 40, + 44, + 48, + 149, + 153, + 157, + 161 + ] + } + ], + "supportedSecurityTypes": [ + "Enhanced-Open+None", + "Enhanced-Open-Only", + "None", + "WPA2-Personal", + "WPA2/WPA3-Mixed-Personal", + "WPA3-Personal" + ], + "maxRADIUSSharedKeyLength": 64, + "settings": { + "isEnabled": true, + "mode": "802.11anacaxbe", + "ssid": "HAO-9F", + "broadcastSSID": true, + "channelWidth": "Auto", + "channel": 0, + "security": "WPA2/WPA3-Mixed-Personal", + "wpaPersonalSettings": { + "passphrase": "0975280250" + } + } + } + ] + } + } + }, + "http://linksys.com/jnap/guestnetwork/GetGuestRadioSettings2": { + "target": "http://linksys.com/jnap/guestnetwork/GetGuestRadioSettings2", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "isGuestNetworkACaptivePortal": false, + "isGuestNetworkEnabled": true, + "radios": [ + { + "radioID": "RADIO_2.4GHz", + "isEnabled": true, + "broadcastGuestSSID": true, + "guestSSID": "Linksys00107-guest", + "guestWPAPassphrase": "BeMyGuest", + "canEnableRadio": true + }, + { + "radioID": "RADIO_5GHz", + "isEnabled": true, + "broadcastGuestSSID": true, + "guestSSID": "Linksys00107-guest", + "guestWPAPassphrase": "BeMyGuest", + "canEnableRadio": true + } + ] + } + } + }, + "http://linksys.com/jnap/devicelist/GetDevices3": { + "target": "http://linksys.com/jnap/devicelist/GetDevices3", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "revision": 38818, + "devices": [ + { + "deviceID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50", + "lastChangeRevision": 38817, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "MX42", + "hardwareVersion": "1", + "description": "Velop AX4200 WiFi 6 System" + }, + "unit": { + "serialNumber": "38U10M33B16420", + "firmwareVersion": "1.0.13.216602", + "firmwareDate": "2022-04-13T23:49:52Z" + }, + "isAuthority": false, + "nodeType": "Slave", + "isHomeKitSupported": false, + "friendlyName": "Linksys16420", + "knownInterfaces": [ + { + "macAddress": "F6:9F:80:E1:0F:51", + "interfaceType": "Unknown" + }, + { + "macAddress": "EE:9F:80:E1:0F:53", + "interfaceType": "Unknown" + }, + { + "macAddress": "E8:9F:80:E1:0F:50", + "interfaceType": "Unknown" + }, + { + "macAddress": "EE:9F:80:E1:0F:52", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "E8:9F:80:E1:0F:50", + "ipAddress": "192.168.1.20", + "ipv6Address": "fe80:0000:0000:0000:ea9f:80ff:fee1:0f50" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "85613401-2e4f-4138-aa47-bef57c315763", + "lastChangeRevision": 38647, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "LGE_DHUM2_open", + "knownInterfaces": [ + { + "macAddress": "B8:16:5F:F7:9F:D9", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "B8:16:5F:F7:9F:D9", + "ipAddress": "192.168.1.226", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "3a006806-5576-45b1-ac35-047f929531ca", + "lastChangeRevision": 38639, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "B8F862EB508C", + "knownInterfaces": [ + { + "macAddress": "B8:F8:62:EB:50:8C", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "B8:F8:62:EB:50:8C", + "ipAddress": "192.168.1.243", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "654243c9-7ab5-48ff-aed3-730e648c2e17", + "lastChangeRevision": 38818, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "Living Room display", + "knownInterfaces": [ + { + "macAddress": "20:1F:3B:74:0B:F0", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "20:1F:3B:74:0B:F0", + "ipAddress": "192.168.1.145", + "ipv6Address": "fe80:0000:0000:0000:603f:011b:f5b1:7580", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "ba51729f-62cc-4b31-8164-153ed25d7284", + "lastChangeRevision": 38757, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_22fc8ae11cd34a4e941a9cfcf927bbc7", + "knownInterfaces": [ + { + "macAddress": "6A:2C:C1:AC:14:9E", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "6A:2C:C1:AC:14:9E", + "ipAddress": "192.168.1.131", + "ipv6Address": "fe80:0000:0000:0000:682c:c1ff:feac:149e", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "17633e54-8065-4ace-a48b-676ec26a50e8", + "lastChangeRevision": 38638, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "2C:1B:3A:94:F9:A2", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "2C:1B:3A:94:F9:A2", + "ipAddress": "192.168.1.151", + "parentDeviceID": "78705b46-ca02-8be2-af38-80691a202892" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "031221ee-8a06-407b-9237-9ac79400aafc", + "lastChangeRevision": 37539, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "LN16", + "hardwareVersion": "1", + "description": "Linksys Velop Micro Mesh 7" + }, + "unit": { + "serialNumber": "65G10M2CE00107", + "firmwareVersion": "1.0.10.216621", + "firmwareDate": "2025-05-19T01:22:58Z", + "operatingSystem": "macOS" + }, + "isAuthority": false, + "friendlyName": "ASTWP-028312", + "knownInterfaces": [ + { + "macAddress": "E2:AF:EC:F6:51:5F", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "E2:AF:EC:F6:51:5F", + "ipAddress": "192.168.1.227", + "ipv6Address": "fe80:0000:0000:0000:1060:f832:5cac:70f5", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "1da86bc0-f9f8-43a8-9456-5ba5cbe94ef3", + "lastChangeRevision": 38646, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "LGE_AIR2_open", + "knownInterfaces": [ + { + "macAddress": "A4:36:C7:64:49:80", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "A4:36:C7:64:49:80", + "ipAddress": "192.168.1.137", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "942b8df9-57bf-4ea3-bd98-657fa4fe65d6", + "lastChangeRevision": 38814, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "xiaomi_vacuum", + "knownInterfaces": [ + { + "macAddress": "D4:43:8A:A8:DA:FC", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "D4:43:8A:A8:DA:FC", + "ipAddress": "192.168.1.22", + "ipv6Address": "fe80:0000:0000:0000:d643:8aff:fea8:dafc", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "b9b753cc-71ae-44a5-b196-6e6db758b3f3", + "lastChangeRevision": 38813, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "LN16", + "hardwareVersion": "1", + "description": "Linksys Velop Micro Mesh 7" + }, + "unit": { + "serialNumber": "65G10M2CE00107", + "firmwareVersion": "1.0.10.216621", + "firmwareDate": "2025-05-19T01:22:58Z" + }, + "isAuthority": false, + "friendlyName": "MacBook Pro", + "knownInterfaces": [ + { + "macAddress": "FE:23:B0:AE:E9:7D", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "FE:23:B0:AE:E9:7D", + "ipAddress": "192.168.1.163", + "ipv6Address": "fe80:0000:0000:0000:1045:1b9a:65d3:105f", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "78705b46-ca02-8be2-af38-80691a202892", + "lastChangeRevision": 38815, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "MX42", + "hardwareVersion": "1", + "description": "Velop AX4200 WiFi 6 System" + }, + "unit": { + "serialNumber": "38U10M5AC03045", + "firmwareVersion": "1.0.13.216602", + "firmwareDate": "2022-04-13T23:49:52Z" + }, + "isAuthority": false, + "nodeType": "Slave", + "isHomeKitSupported": false, + "friendlyName": "Linksys03045", + "knownInterfaces": [ + { + "macAddress": "86:69:1A:20:28:95", + "interfaceType": "Wireless", + "band": "5GHz" + }, + { + "macAddress": "80:69:1A:20:28:92", + "interfaceType": "Unknown" + } + ], + "connections": [ + { + "macAddress": "86:69:1A:20:28:95", + "ipAddress": "192.168.1.115", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + }, + { + "macAddress": "80:69:1A:20:28:92", + "ipAddress": "192.168.1.115", + "ipv6Address": "fe80:0000:0000:0000:8269:1aff:fe20:2892" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "04281dda-4859-4b7a-9146-5f0be5a01549", + "lastChangeRevision": 38714, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "chuangmi_camera_077ac1", + "knownInterfaces": [ + { + "macAddress": "B8:88:80:68:CD:2E", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "B8:88:80:68:CD:2E", + "ipAddress": "192.168.1.86", + "parentDeviceID": "78705b46-ca02-8be2-af38-80691a202892" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "9a6f8d51-2b2d-4be3-8c7a-79cf5e8e5e28", + "lastChangeRevision": 38812, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_C2BZAQXQ", + "knownInterfaces": [ + { + "macAddress": "1A:C0:2D:04:D7:AA", + "interfaceType": "Wireless", + "band": "2.4GHz" + } + ], + "connections": [ + { + "macAddress": "1A:C0:2D:04:D7:AA", + "ipAddress": "192.168.1.234", + "ipv6Address": "fe80:0000:0000:0000:18c0:2dff:fe04:d7aa", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "6b5d78d0-28d5-46fc-b32e-3d0b25b07846", + "lastChangeRevision": 38499, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "HomeApplianceDiscovery", + "knownInterfaces": [ + { + "macAddress": "EE:13:9A:FF:31:3D", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "EE:13:9A:FF:31:3D", + "ipAddress": "192.168.1.143", + "ipv6Address": "fe80:0000:0000:0000:ec13:9aff:feff:313d", + "parentDeviceID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "883f60df-73ea-4e37-b609-bd8989436c4f", + "lastChangeRevision": 38816, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_HBBCLCZD", + "knownInterfaces": [ + { + "macAddress": "C2:EE:5A:F5:D4:02", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "C2:EE:5A:F5:D4:02", + "ipAddress": "192.168.1.190", + "ipv6Address": "fe80:0000:0000:0000:c0ee:5aff:fef5:d402", + "parentDeviceID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "858ecca6-29cd-479c-8189-bc64d9b4977f", + "lastChangeRevision": 38476, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "E0:EF:BF:A4:F5:87", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "E0:EF:BF:A4:F5:87", + "ipAddress": "192.168.1.72", + "ipv6Address": "fe80:0000:0000:0000:e2ef:bfff:fea4:f587", + "parentDeviceID": "78705b46-ca02-8be2-af38-80691a202892" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "72a690ef-831c-479c-a2ff-364b4fcee297", + "lastChangeRevision": 38514, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "bosch-dishwasher-012100522899000500", + "knownInterfaces": [ + { + "macAddress": "38:B4:D3:AF:0C:7E", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "38:B4:D3:AF:0C:7E", + "ipAddress": "192.168.1.28", + "ipv6Address": "fe80:0000:0000:0000:3ab4:d3ff:feaf:0c7e", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "618425a1-43d9-4d16-8589-76635afe8bc5", + "lastChangeRevision": 38789, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "unknown", + "knownInterfaces": [ + { + "macAddress": "C2:4B:A3:5E:CB:9D", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [ + { + "name": "userDeviceOS", + "value": "Android 15" + }, + { + "name": "userDeviceManufacturer", + "value": "Samsung" + }, + { + "name": "userDeviceModelNumber", + "value": "SM-S9260" + } + ], + "maxAllowedProperties": 16 + }, + { + "deviceID": "cdd1025d-d009-41d3-98bf-8f65f11592fd", + "lastChangeRevision": 38707, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "10F TV", + "knownInterfaces": [ + { + "macAddress": "48:9E:9D:1C:AD:39", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "f02b4bbf-dee4-488b-b324-129732bf3744", + "lastChangeRevision": 38397, + "model": { + "deviceType": "Mobile", + "manufacturer": "Microsoft Corporation", + "modelNumber": "Windows Media Player", + "description": "Media Renderer" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "GoogleTV1713", + "knownInterfaces": [ + { + "macAddress": "A8:16:9D:D6:1A:3A", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [ + { + "macAddress": "A8:16:9D:D6:1A:3A", + "ipAddress": "192.168.1.68", + "ipv6Address": "fe80:0000:0000:0000:3769:2d23:c5b2:f68d", + "parentDeviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98", + "lastChangeRevision": 38630, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "LN16", + "hardwareVersion": "1", + "description": "Linksys Velop Micro Mesh 7" + }, + "unit": { + "serialNumber": "65G10M2CE00107", + "firmwareVersion": "1.0.10.216621", + "firmwareDate": "2025-05-19T01:22:58Z" + }, + "isAuthority": true, + "nodeType": "Master", + "isHomeKitSupported": false, + "friendlyName": "Linksys00107", + "knownInterfaces": [ + { + "macAddress": "80:69:1A:BB:7E:98", + "interfaceType": "Wired" + } + ], + "connections": [ + { + "macAddress": "80:69:1A:BB:7E:98", + "ipAddress": "192.168.1.1" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "ceaa3004-db28-490c-8fad-6bfb740b0e95", + "lastChangeRevision": 38261, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "dreame_vacuum_p2114a", + "knownInterfaces": [ + { + "macAddress": "70:C9:32:2D:71:CD", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "0b2d50fc-94b9-4841-ac46-f6b290af5cc2", + "lastChangeRevision": 37421, + "model": { + "deviceType": "Infrastructure", + "manufacturer": "Linksys", + "modelNumber": "LN16", + "hardwareVersion": "1", + "description": "Linksys Velop Micro Mesh 7" + }, + "unit": { + "serialNumber": "65G10M2CE00107", + "firmwareVersion": "1.0.7.216520", + "firmwareDate": "2025-02-17T22:24:18Z", + "operatingSystem": "macOS" + }, + "isAuthority": false, + "friendlyName": "Natalie's MacBook Air", + "knownInterfaces": [ + { + "macAddress": "3C:22:FB:E4:4F:18", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "0bc43db8-ad3c-4d9b-9866-c95279fe07f4", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "D8:1F:12:38:9B:F6", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "52383afc-da16-4cc4-829c-dad9efaad57f", + "lastChangeRevision": 0, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_EJZXUT7G", + "knownInterfaces": [ + { + "macAddress": "EE:0F:F6:EE:AE:29", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "104fc891-b465-4ff9-a352-b2340a58dea0", + "lastChangeRevision": 37789, + "model": { + "deviceType": "Computer", + "manufacturer": "Apple Inc.", + "modelNumber": "MacBook Air" + }, + "unit": { + "operatingSystem": "macOS" + }, + "isAuthority": false, + "friendlyName": "Natalie's MacBook Air", + "knownInterfaces": [ + { + "macAddress": "3C:A6:F6:51:87:B2", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "e9165410-04f8-4b4c-9725-29d7c859161d", + "lastChangeRevision": 37911, + "model": { + "deviceType": "Computer" + }, + "unit": { + "operatingSystem": "Windows" + }, + "isAuthority": false, + "friendlyName": "DESKTOP-MOB04KQ", + "knownInterfaces": [ + { + "macAddress": "44:E5:17:06:9D:5C", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "9c08e232-2a00-4f93-8b45-3689e05b3f61", + "lastChangeRevision": 38377, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "SM-R960", + "knownInterfaces": [ + { + "macAddress": "42:2F:54:84:A1:9C", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "b546cdfa-3ba3-459f-8b49-7cd06c869248", + "lastChangeRevision": 0, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_f23e96c804c74801b1988ba2bad85779", + "knownInterfaces": [ + { + "macAddress": "66:D1:08:EA:C8:F3", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "655300fd-017d-461d-9d7b-491d046ea45a", + "lastChangeRevision": 3940, + "model": { + "deviceType": "", + "manufacturer": "Nintendo Co., Ltd." + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "5C:52:1E:5C:20:66", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "811394a4-a141-4293-a25d-20289682bb7e", + "lastChangeRevision": 0, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "jia-hao-de-S24", + "knownInterfaces": [ + { + "macAddress": "D2:D4:5E:69:AA:46", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "b1873b4f-a0e7-4f04-99d1-ace7f84d954c", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "PS5-A5A6D9", + "knownInterfaces": [ + { + "macAddress": "1C:98:C1:8D:F4:C8", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "14c19eee-a92f-49d6-977e-31c606630df2", + "lastChangeRevision": 0, + "model": { + "deviceType": "Phone", + "manufacturer": "Apple Inc.", + "modelNumber": "iPhone" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "iPhone", + "knownInterfaces": [ + { + "macAddress": "02:EF:50:55:29:1C", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "5292131c-9cb5-4e06-928d-050ab11bd827", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "bosch-dishwasher-012120522899010831", + "knownInterfaces": [ + { + "macAddress": "C8:D7:78:59:C9:82", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "7d4bc396-ea11-4b67-bc1c-e1c50c34eaa1", + "lastChangeRevision": 0, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android-2", + "knownInterfaces": [ + { + "macAddress": "3E:95:A1:A0:E8:A5", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "86b26a56-b186-4d9c-9ec1-8841aeda5357", + "lastChangeRevision": 37790, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Android_b568a8e42977415f91c203e7ee9a11f4", + "knownInterfaces": [ + { + "macAddress": "9E:04:16:86:FF:0F", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "4e16ec7e-2007-43f1-b1f3-716127be4cde", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "C2:B3:8D:83:41:31", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "b9b8e67e-d45b-40ae-95a3-53f9d6901f20", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "B6:A3:67:07:10:49", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "28e67058-15b9-4ed3-a73b-8a538c3350ed", + "lastChangeRevision": 0, + "model": { + "deviceType": "Computer", + "manufacturer": "Apple Inc.", + "modelNumber": "MacBook Pro" + }, + "unit": { + "operatingSystem": "macOS" + }, + "isAuthority": false, + "friendlyName": "ASTWP-29134", + "knownInterfaces": [ + { + "macAddress": "8E:21:38:0E:BA:3C", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "7129768d-bc57-4095-801b-da5311f9d237", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "C2:40:2A:3B:F0:30", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "94eb8907-65a4-43b0-979c-4b960292bb85", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "F6:82:53:8B:2C:08", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "7e8b1456-49c9-49c4-9482-7add0d5175f1", + "lastChangeRevision": 0, + "model": { + "deviceType": "Phone", + "manufacturer": "Apple Inc.", + "modelNumber": "iPhone" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "0bed0da9-7b5a-464b-aa67-ca56f30fca37", + "knownInterfaces": [ + { + "macAddress": "62:89:01:CD:E4:C2", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "c480e5bc-b0f9-495d-808b-748436a20dda", + "lastChangeRevision": 37909, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "F6:80:A0:45:50:F1", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "b4ff1c44-bf0d-494f-a007-78d843904227", + "lastChangeRevision": 0, + "model": { + "deviceType": "Phone", + "manufacturer": "Apple Inc.", + "modelNumber": "iPhone" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "Austins-iPhone", + "knownInterfaces": [ + { + "macAddress": "4A:C4:6C:49:40:D8", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "67eca8cf-b07d-4c51-a72b-07d30be9ab9d", + "lastChangeRevision": 0, + "model": { + "deviceType": "Phone", + "manufacturer": "Apple Inc.", + "modelNumber": "iPhone" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "iPhone", + "knownInterfaces": [ + { + "macAddress": "92:29:61:F5:D3:00", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "7d0ccb66-d505-48e6-b7c5-895f64151353", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "Watch", + "knownInterfaces": [ + { + "macAddress": "AE:6F:F9:6B:38:24", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "d57e9396-8854-469f-b95d-986cb076d002", + "lastChangeRevision": 0, + "model": { + "deviceType": "Phone", + "manufacturer": "Apple Inc.", + "modelNumber": "iPhone" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "iPhone", + "knownInterfaces": [ + { + "macAddress": "22:81:93:86:9B:38", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "eba918bc-d96f-4f85-9bcb-41ab54422f08", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "D2:52:A0:92:06:88", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "adce14af-5431-451a-a13a-8c765d4305b8", + "lastChangeRevision": 0, + "model": { + "deviceType": "Tablet", + "manufacturer": "Apple Inc.", + "modelNumber": "iPad" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "Emily's iPad", + "knownInterfaces": [ + { + "macAddress": "FA:53:57:E7:6E:39", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "fdb41e4f-d8a0-4736-bc93-f16acae227e8", + "lastChangeRevision": 38612, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "Pixel-7", + "knownInterfaces": [ + { + "macAddress": "5E:DB:E6:51:9B:97", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "3b499d56-dfe2-4d59-82d1-ff02deeb3d57", + "lastChangeRevision": 0, + "model": { + "deviceType": "Tablet", + "manufacturer": "Apple Inc.", + "modelNumber": "iPad" + }, + "unit": { + "operatingSystem": "iOS" + }, + "isAuthority": false, + "friendlyName": "Emily's iPad", + "knownInterfaces": [ + { + "macAddress": "56:E1:FD:30:2F:07", + "interfaceType": "Wireless" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "9a7b66a1-b800-464b-ac8e-59a8f098106e", + "lastChangeRevision": 37600, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "WING-Austin", + "knownInterfaces": [ + { + "macAddress": "48:90:2F:CD:33:FB", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "545bcc93-7789-4db6-9c93-95945b4593a2", + "lastChangeRevision": 0, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "knownInterfaces": [ + { + "macAddress": "12:5F:40:8C:1F:D4", + "interfaceType": "Unknown" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "be92a241-8fd9-46a2-bbe8-c15684e06ac1", + "lastChangeRevision": 38636, + "model": { + "deviceType": "Mobile" + }, + "unit": { + "operatingSystem": "Android" + }, + "isAuthority": false, + "friendlyName": "SM-R960", + "knownInterfaces": [ + { + "macAddress": "62:92:65:B0:E5:C9", + "interfaceType": "Unknown" + } + ], + "connections": [ + { + "macAddress": "62:92:65:B0:E5:C9", + "ipv6Address": "fe80:0000:0000:0000:6092:65ff:feb0:e5c9", + "parentDeviceID": "78705b46-ca02-8be2-af38-80691a202892" + } + ], + "properties": [], + "maxAllowedProperties": 16 + }, + { + "deviceID": "ccfb46a0-de12-46d8-b015-5f6b85bf3e06", + "lastChangeRevision": 37442, + "model": { + "deviceType": "" + }, + "unit": {}, + "isAuthority": false, + "friendlyName": "MACBOOK-PRO", + "knownInterfaces": [ + { + "macAddress": "5C:9B:A6:69:9F:F4", + "interfaceType": "Wireless", + "band": "5GHz" + } + ], + "connections": [], + "properties": [], + "maxAllowedProperties": 16 + } + ] + } + } + }, + "http://linksys.com/jnap/firmwareupdate/GetFirmwareUpdateSettings": { + "target": "http://linksys.com/jnap/firmwareupdate/GetFirmwareUpdateSettings", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "updatePolicy": "AutomaticallyCheckAndInstall", + "autoUpdateWindow": { + "startMinute": 0, + "durationMinutes": 240 + } + } + } + }, + "http://linksys.com/jnap/nodes/diagnostics/GetBackhaulInfo2": { + "target": "http://linksys.com/jnap/nodes/diagnostics/GetBackhaulInfo2", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "backhaulDevices": [ + { + "deviceUUID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50", + "ipAddress": "192.168.1.20", + "parentIPAddress": "192.168.1.115", + "connectionType": "Wireless", + "wirelessConnectionInfo": { + "radioID": "5GL", + "channel": 48, + "apRSSI": -86, + "stationRSSI": -77, + "apBSSID": "80:69:1A:20:28:94", + "stationBSSID": "EE:9F:80:E1:0F:52", + "isMultiLinkOperation": false + }, + "speedMbps": "28.813", + "timestamp": "2025-12-24T06:55:13Z" + }, + { + "deviceUUID": "78705b46-ca02-8be2-af38-80691a202892", + "ipAddress": "192.168.1.115", + "parentIPAddress": "192.168.1.1", + "connectionType": "Wireless", + "wirelessConnectionInfo": { + "radioID": "5GH", + "channel": 149, + "apRSSI": -37, + "stationRSSI": -38, + "apBSSID": "80:69:1A:BB:7E:9A", + "stationBSSID": "86:69:1A:20:28:95", + "txRate": 1200990, + "rxRate": 1201000, + "isMultiLinkOperation": false + }, + "speedMbps": "211.866", + "timestamp": "2025-12-24T06:55:06Z" + } + ] + } + } + }, + "http://linksys.com/jnap/router/GetWANStatus3": { + "target": "http://linksys.com/jnap/router/GetWANStatus3", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "supportedWANTypes": [ + "DHCP", + "Static", + "PPPoE", + "PPTP", + "L2TP", + "Bridge" + ], + "supportedIPv6WANTypes": [ + "Automatic", + "PPPoE", + "Pass-through" + ], + "supportedWANCombinations": [ + { + "wanType": "DHCP", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "Static", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "PPPoE", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "L2TP", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "PPTP", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "Bridge", + "wanIPv6Type": "Automatic" + }, + { + "wanType": "DHCP", + "wanIPv6Type": "Pass-through" + }, + { + "wanType": "PPPoE", + "wanIPv6Type": "PPPoE" + } + ], + "isDetectingWANType": false, + "detectedWANType": "DHCP", + "wanStatus": "Connected", + "wanConnection": { + "wanType": "DHCP", + "ipAddress": "192.168.15.2", + "networkPrefixLength": 24, + "gateway": "192.168.15.1", + "mtu": 0, + "dhcpLeaseMinutes": 4320, + "dnsServer1": "192.168.15.1" + }, + "wanIPv6Status": "Connecting", + "linkLocalIPv6Address": "fe80:0000:0000:0000:8269:1aff:febb:7e98", + "macAddress": "80:69:1A:BB:7E:98" + } + } + }, + "http://linksys.com/jnap/router/GetEthernetPortConnections": { + "target": "http://linksys.com/jnap/router/GetEthernetPortConnections", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "wanPortConnection": "1Gbps", + "lanPortConnections": [] + } + } + }, + "http://linksys.com/jnap/diagnostics/GetSystemStats2": { + "target": "http://linksys.com/jnap/diagnostics/GetSystemStats2", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "uptimeSeconds": 2870497, + "CPULoad": "", + "MemoryLoad": "0.79" + } + } + }, + "http://linksys.com/jnap/powertable/GetPowerTableSettings": { + "target": "http://linksys.com/jnap/powertable/GetPowerTableSettings", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "isPowerTableSelectable": true, + "country": "SGP", + "supportedCountries": [ + "USA", + "CAN", + "EEE", + "HKG", + "SGP", + "TWN", + "AUS", + "LAM" + ] + } + } + }, + "http://linksys.com/jnap/locale/GetLocalTime": { + "target": "http://linksys.com/jnap/locale/GetLocalTime", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "currentTime": "2025-12-24T15:04:27+0800" + } + } + }, + "http://linksys.com/jnap/nodes/setup/GetInternetConnectionStatus": { + "target": "http://linksys.com/jnap/nodes/setup/GetInternetConnectionStatus", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "connectionStatus": "InternetConnected" + } + } + }, + "http://linksys.com/jnap/nodes/firmwareupdate/GetFirmwareUpdateStatus": { + "target": "http://linksys.com/jnap/nodes/firmwareupdate/GetFirmwareUpdateStatus", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "firmwareUpdateStatus": [ + { + "deviceUUID": "4a8bfcbf-6fa5-4383-9a26-80691abb7e98", + "lastSuccessfulCheckTime": "2025-12-24T07:01:55Z", + "pendingOperation": { + "operation": "Checking", + "progressPercent": 0 + } + }, + { + "deviceUUID": "78705b46-ca02-8be2-af38-80691a202892", + "lastSuccessfulCheckTime": "2025-12-24T07:01:54Z", + "pendingOperation": { + "operation": "Checking", + "progressPercent": 50 + } + }, + { + "deviceUUID": "8cf4c9d0-3cda-88a0-cda4-e89f80e10f50", + "lastSuccessfulCheckTime": "2025-12-24T07:04:27Z" + } + ] + } + } + }, + "http://linksys.com/jnap/product/GetSoftSKUSettings": { + "target": "http://linksys.com/jnap/product/GetSoftSKUSettings", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "modelNumber": "LN16-AH" + } + } + }, + "http://linksys.com/jnap/routerleds/GetLedNightModeSetting": { + "target": "http://linksys.com/jnap/routerleds/GetLedNightModeSetting", + "cachedAt": 1766559868105, + "data": { + "result": "OK", + "output": { + "Enable": false + } + } + }, + "http://linksys.com/jnap/nodes/topologyoptimization/GetTopologyOptimizationSettings2": { + "target": "http://linksys.com/jnap/nodes/topologyoptimization/GetTopologyOptimizationSettings2", + "cachedAt": 1766559752535, + "data": { + "result": "OK", + "output": { + "isClientSteeringEnabled": true, + "isNodeSteeringEnabled": true + } + } + }, + "http://linksys.com/jnap/wirelessap/GetMLOSettings": { + "target": "http://linksys.com/jnap/wirelessap/GetMLOSettings", + "cachedAt": 1766559752535, + "data": { + "result": "OK", + "output": { + "isMLOSupported": true, + "isMLOEnabled": false + } + } + }, + "http://linksys.com/jnap/wirelessap/GetDFSSettings": { + "target": "http://linksys.com/jnap/wirelessap/GetDFSSettings", + "cachedAt": 1766559752535, + "data": { + "result": "OK", + "output": { + "isDFSSupported": true, + "isDFSEnabled": false + } + } + }, + "http://linksys.com/jnap/wirelessap/GetAirtimeFairnessSettings": { + "target": "http://linksys.com/jnap/wirelessap/GetAirtimeFairnessSettings", + "cachedAt": 1766559752535, + "data": { + "result": "OK", + "output": { + "isAirtimeFairnessSupported": false + } + } + }, + "http://linksys.com/jnap/router/GetLANSettings": { + "target": "http://linksys.com/jnap/router/GetLANSettings", + "cachedAt": 1766559770320, + "data": { + "result": "OK", + "output": { + "ipAddress": "192.168.1.1", + "networkPrefixLength": 24, + "minNetworkPrefixLength": 16, + "maxNetworkPrefixLength": 30, + "hostName": "Linksys00107", + "minAllowedDHCPLeaseMinutes": 1, + "maxAllowedDHCPLeaseMinutes": 525600, + "maxDHCPReservationDescriptionLength": 63, + "isDHCPEnabled": true, + "dhcpSettings": { + "leaseMinutes": 1440, + "firstClientIPAddress": "192.168.1.10", + "lastClientIPAddress": "192.168.1.254", + "reservations": [] + } + } + } + }, + "http://linksys.com/jnap/locale/GetTimeSettings": { + "target": "http://linksys.com/jnap/locale/GetTimeSettings", + "cachedAt": 1766559772184, + "data": { + "result": "OK", + "output": { + "timeZoneID": "SGT-8-NO-DST", + "autoAdjustForDST": false, + "supportedTimeZones": [ + { + "timeZoneID": "GMT0-NO-DST", + "utcOffsetMinutes": 0, + "observesDST": false, + "description": "(GMT) Gambia, Liberia, Morocco" + }, + { + "timeZoneID": "GST-4-NO-DST", + "utcOffsetMinutes": 240, + "observesDST": false, + "description": "(GMT+04:00) Armenia" + }, + { + "timeZoneID": "EST5-NO-DST", + "utcOffsetMinutes": -300, + "observesDST": false, + "description": "(GMT-05:00) Indiana East, Colombia, Panama" + }, + { + "timeZoneID": "AEST-10", + "utcOffsetMinutes": 600, + "observesDST": true, + "description": "(GMT+10:00) Australia" + }, + { + "timeZoneID": "MST7-NO-DST", + "utcOffsetMinutes": -420, + "observesDST": false, + "description": "(GMT-07:00) Arizona" + }, + { + "timeZoneID": "PKT-5-NO-DST", + "utcOffsetMinutes": 300, + "observesDST": false, + "description": "(GMT+05:00) Pakistan, Russia" + }, + { + "timeZoneID": "ALMT-6-NO-DST", + "utcOffsetMinutes": 360, + "observesDST": false, + "description": "(GMT+06:00) Bangladesh, Russia" + }, + { + "timeZoneID": "NST03:30", + "utcOffsetMinutes": -210, + "observesDST": true, + "description": "(GMT-03:30) Newfoundland" + }, + { + "timeZoneID": "BRT3", + "utcOffsetMinutes": -180, + "observesDST": true, + "description": "(GMT-03:00) Brazil East, Greenland" + }, + { + "timeZoneID": "HST10-NO-DST", + "utcOffsetMinutes": -600, + "observesDST": false, + "description": "(GMT-10:00) Hawaii" + }, + { + "timeZoneID": "CST6", + "utcOffsetMinutes": -360, + "observesDST": true, + "description": "(GMT-06:00) Central Time (USA & Canada)" + }, + { + "timeZoneID": "PST8", + "utcOffsetMinutes": -480, + "observesDST": true, + "description": "(GMT-08:00) Pacific Time (USA & Canada)" + }, + { + "timeZoneID": "JST-9-NO-DST", + "utcOffsetMinutes": 540, + "observesDST": false, + "description": "(GMT+09:00) Japan, Korea" + }, + { + "timeZoneID": "EST5", + "utcOffsetMinutes": -300, + "observesDST": true, + "description": "(GMT-05:00) Eastern Time (USA & Canada)" + }, + { + "timeZoneID": "ICT-7-NO-DST", + "utcOffsetMinutes": 420, + "observesDST": false, + "description": "(GMT+07:00) Thailand, Russia" + }, + { + "timeZoneID": "HKT-8-NO-DST", + "utcOffsetMinutes": 480, + "observesDST": false, + "description": "(GMT+08:00) China, Hong Kong, Australia Western" + }, + { + "timeZoneID": "WST11-NO-DST", + "utcOffsetMinutes": -660, + "observesDST": false, + "description": "(GMT-11:00) Midway Island, Samoa" + }, + { + "timeZoneID": "SBT-11-NO-DST", + "utcOffsetMinutes": 660, + "observesDST": false, + "description": "(GMT+11:00) Solomon Islands" + }, + { + "timeZoneID": "FJT-12-NO-DST", + "utcOffsetMinutes": 720, + "observesDST": false, + "description": "(GMT+12:00) Fiji" + }, + { + "timeZoneID": "NZST-12", + "utcOffsetMinutes": 720, + "observesDST": true, + "description": "(GMT+12:00) New Zealand" + }, + { + "timeZoneID": "GST-10-NO-DST", + "utcOffsetMinutes": 600, + "observesDST": false, + "description": "(GMT+10:00) Guam, Russia" + }, + { + "timeZoneID": "CET-1-NO-DST", + "utcOffsetMinutes": 60, + "observesDST": false, + "description": "(GMT+01:00) Tunisia" + }, + { + "timeZoneID": "MAT2-NO-DST", + "utcOffsetMinutes": -120, + "observesDST": false, + "description": "(GMT-02:00) Mid-Atlantic" + }, + { + "timeZoneID": "MHT12-NO-DST", + "utcOffsetMinutes": -720, + "observesDST": false, + "description": "(GMT-12:00) Kwajalein" + }, + { + "timeZoneID": "AZOT1", + "utcOffsetMinutes": -60, + "observesDST": true, + "description": "(GMT-01:00) Azores" + }, + { + "timeZoneID": "CET-1", + "utcOffsetMinutes": 60, + "observesDST": true, + "description": "(GMT+01:00) France, Germany, Italy" + }, + { + "timeZoneID": "ART3-NO-DST", + "utcOffsetMinutes": -180, + "observesDST": false, + "description": "(GMT-03:00) Guyana" + }, + { + "timeZoneID": "IST-05:30-NO-DST", + "utcOffsetMinutes": 330, + "observesDST": false, + "description": "(GMT+05:30) Bombay, Kalkutta, Madras, Neu Delhi" + }, + { + "timeZoneID": "VET4-NO-DST", + "utcOffsetMinutes": -240, + "observesDST": false, + "description": "(GMT-04:00) Bolivia, Venezuela" + }, + { + "timeZoneID": "GMT0", + "utcOffsetMinutes": 0, + "observesDST": true, + "description": "(GMT) England" + }, + { + "timeZoneID": "AST-3-NO-DST", + "utcOffsetMinutes": 180, + "observesDST": false, + "description": "(GMT+03:00) Iraq, Jordan, Kuwait" + }, + { + "timeZoneID": "AKST9", + "utcOffsetMinutes": -540, + "observesDST": true, + "description": "(GMT-09:00) Alaska" + }, + { + "timeZoneID": "MST7", + "utcOffsetMinutes": -420, + "observesDST": true, + "description": "(GMT-07:00) Mountain Time (USA & Canada)" + }, + { + "timeZoneID": "CLT4", + "utcOffsetMinutes": -240, + "observesDST": true, + "description": "(GMT-04:00) Chile Time (Chile, Antarctica)" + }, + { + "timeZoneID": "SAST-2-NO-DST", + "utcOffsetMinutes": 120, + "observesDST": false, + "description": "(GMT+02:00) South Africa" + }, + { + "timeZoneID": "EET-2", + "utcOffsetMinutes": 120, + "observesDST": true, + "description": "(GMT+02:00) Greece, Ukraine, Romania, Turkey" + }, + { + "timeZoneID": "SGT-8-NO-DST", + "utcOffsetMinutes": 480, + "observesDST": false, + "description": "(GMT+08:00) Singapore, Taiwan, Russia" + }, + { + "timeZoneID": "CST6-NO-DST", + "utcOffsetMinutes": -360, + "observesDST": false, + "description": "(GMT-06:00) Mexico" + }, + { + "timeZoneID": "AST4", + "utcOffsetMinutes": -240, + "observesDST": true, + "description": "(GMT-04:00) Atlantic Time (Canada, Greenland, Atlantic Islands)" + } + ], + "currentTime": "2025-12-24T07:02:52Z" + } + } + }, + "http://linksys.com/jnap/core/GetAdminPasswordHint": { + "target": "http://linksys.com/jnap/core/GetAdminPasswordHint", + "cachedAt": 1766559772281, + "data": { + "result": "OK", + "output": { + "passwordHint": "ve" + } + } + }, + "http://linksys.com/jnap/router/GetWANExternal": { + "target": "http://linksys.com/jnap/router/GetWANExternal", + "cachedAt": 1766559803838, + "data": { + "result": "OK", + "output": { + "PublicWanIPv4": "111.240.93.175", + "PrivateWanIPv4": "192.168.15.2" + } + } + } +} \ No newline at end of file diff --git a/doc/ai_assistant/FAQ_AGENT.md b/doc/ai_assistant/FAQ_AGENT.md new file mode 100644 index 000000000..f9d685de2 --- /dev/null +++ b/doc/ai_assistant/FAQ_AGENT.md @@ -0,0 +1,279 @@ +# FAQ Agent Design Document + +> Technical design and implementation details for the FAQ Assistant feature in PrivacyGUI. + +--- + +## 1. Overview + +The **FAQ Agent** is a floating action button (FAB) component that provides AI-powered FAQ search and analysis capabilities. It combines: + +- **Linksys Support API** — Real-time FAQ article search +- **AWS Bedrock Claude 3** — Intelligent analysis and recommendations +- **Generative UI Framework** — Dynamic component rendering + +### Key Features + +| Feature | Description | +|---------|-------------| +| **Dual Mode** | AI mode (Bedrock) with Search mode fallback | +| **Smart Analysis** | LLM analyzes search results and provides conclusions | +| **Clickable Results** | Direct links to Linksys Support articles | +| **Graceful Degradation** | Falls back to direct search when AI unavailable | + +--- + +## 2. Architecture + +```mermaid +flowchart TB + subgraph UI ["UI Layer"] + FAB[FloatingActionButton] + Panel[Chat Panel] + Input[AppTextField] + end + + subgraph GenUI ["Generative UI"] + Container[GenUiContainer] + Registry[ComponentRegistry] + Orchestrator[OrchestrateUIFlowUseCase] + end + + subgraph Generators ["Content Generators"] + Bedrock[BedrockFAQGenerator] + Direct[DirectSearchGenerator] + end + + subgraph External ["External APIs"] + LinksysAPI["Linksys Support API"] + AWS["AWS Bedrock Claude 3"] + end + + FAB --> Panel + Panel --> Container + Input --> Container + Container --> Orchestrator + Orchestrator --> Registry + + Orchestrator --> Bedrock + Orchestrator --> Direct + + Bedrock --> LinksysAPI + Bedrock --> AWS + Direct --> LinksysAPI + + style Bedrock fill:#e8f5e9 + style Direct fill:#fff3e0 +``` + +--- + +## 3. Data Flow + +```mermaid +sequenceDiagram + participant User + participant FAB as FAQAgentFab + participant Container as GenUiContainer + participant Orch as Orchestrator + participant Gen as BedrockFAQGenerator + participant Linksys as Linksys API + participant AWS as AWS Bedrock + + User->>FAB: Type question + Submit + FAB->>Container: sendMessage(text) + Container->>Orch: generate(prompt) + Orch->>Gen: generate(prompt) + + Gen->>Linksys: GET /get_related_kb_forums/?text=... + Linksys-->>Gen: JSON results (articles list) + + Gen->>AWS: generateWithHistory(question + results) + AWS-->>Gen: LLMResponse (TextBlock + ToolUseBlocks) + + Gen-->>Orch: LLMResponse + Orch-->>Container: LLMResponse + Container->>Container: Render UI components + Container-->>User: Display analysis + clickable links +``` + +--- + +## 4. Component Structure + +### 4.1 Main Widget: FAQAgentFab + +```dart +class _FAQAgentFabState extends State { + // State + bool _isExpanded = false; // Dialog expansion state + bool _useMock = true; // AI mode or Search mode + + // Core Components + late final OrchestrateUIFlowUseCase _orchestrator; + late final IComponentRegistry _registry; + + // UI Controllers + final _containerKey = GlobalKey(); + final _inputController = TextEditingController(); + late final AnimationController _animController; +} +``` + +### 4.2 Custom UI Components + +| Component | Purpose | Properties | +|-----------|---------|------------| +| `FAQResult` | Clickable article link | `id`, `title`, `type` | +| `NoResults` | Empty state message | `message` | + +```dart +registry.register('FAQResult', (context, props, {onAction}) { + return AppListTile( + leading: Icon(props['type'] == 'article' + ? Icons.article_outlined + : Icons.forum_outlined), + title: AppText.bodyMedium(props['title'] ?? ''), + trailing: const Icon(Icons.open_in_new), + onTap: () => gotoOfficialWebUrl( + 'https://support.linksys.com/${props['type']}/article/${props['id']}-en/' + ), + ); +}); +``` + +--- + +## 5. Content Generators + +### 5.1 BedrockFAQGenerator (AI Mode) + +**Flow**: User Question → Linksys API Search → Results + Question → AWS Bedrock → Analysis + Recommendations + +```dart +class BedrockFAQGenerator implements IContentGenerator { + @override + Future generate(String prompt) async { + // 1. Search Linksys API + final searchResults = await _fetchSearchResults(prompt); + + // 2. Build context for LLM + final userContent = ''' +User question: $prompt + +Search results from Linksys Support: +${searchResults.map((r) => '- [ID: ${r['id']}] ${r['title']}').join('\n')} +'''; + + // 3. Call AWS Bedrock with tools + return await _awsGenerator.generateWithHistory( + [ChatMessage.user(userContent)], + tools: _tools, + systemPrompt: _systemPrompt, + forceToolUse: false, + ); + } +} +``` + +**System Prompt Guidelines**: +1. First provide conclusion/analysis +2. Then list source articles using FAQResult tool +3. If no search results, suggest 3 possible article topics +4. Limit to 5 most relevant articles +5. Respond in user's language + +### 5.2 DirectSearchGenerator (Fallback Mode) + +**Flow**: User Question → Linksys API Search → Format as ToolUseBlocks + +Used when AWS credentials are not configured or API fails. + +--- + +## 6. External APIs + +### Linksys Support API + +``` +GET https://support.linksys.com/get_related_kb_forums/?text={keyword} +``` + +**Response**: +```json +[ + { "id": 123, "title": "How to reset router", "type": "article" }, + { "id": 456, "title": "Factory reset discussion", "type": "forum" } +] +``` + +--- + +## 7. Protocol Choice: Tool Use + +This implementation uses **Tool Use** instead of A2UI for the following reasons: + +| Requirement | FAQ Agent Needs | Protocol Choice | +|-------------|-----------------|-----------------| +| UI Structure | Flat list | Tool Use ✓ | +| Data Binding | None needed | Tool Use ✓ | +| User Interaction | Click only (Client-handled) | Tool Use ✓ | +| State Management | None needed | Tool Use ✓ | +| Dynamic Updates | None needed | Tool Use ✓ | + +> [!NOTE] +> For detailed protocol comparison, see [PROTOCOL_COMPARISON.md](../../ui_kit/generative_ui/docs/PROTOCOL_COMPARISON.md) + +--- + +## 8. UI Layout + +``` +┌─────────────────────────────────────────┐ +│ [🤖] FAQ Assistant [AI] [✕] │ ← Header with mode indicator +├─────────────────────────────────────────┤ +│ │ +│ User: How to reset my router? │ ← GenUiContainer +│ │ +│ Assistant: │ +│ To reset your router, you can... │ ← TextBlock +│ │ +│ 📄 Factory reset Linksys router 🔗 │ ← FAQResult components +│ 📄 How to perform hard reset 🔗 │ +│ 📄 Router not responding 🔗 │ +│ │ +├─────────────────────────────────────────┤ +│ [Search for help topics... ] [🔍] │ ← Input bar +└─────────────────────────────────────────┘ + + ┌────┐ + │ 🤖 │ ← FAB (collapsed) + └────┘ +``` + +--- + +## 9. Configuration + +### AWS Bedrock Configuration + +The FAQ Agent requires AWS credentials in `.env`: + +```env +AWS_REGION=us-west-2 +AWS_ACCESS_KEY_ID=your_access_key +AWS_SECRET_ACCESS_KEY=your_secret_key +``` + +If credentials are missing, the agent automatically falls back to **Search mode**. + +--- + +## 10. Future Enhancements + +| Enhancement | Description | Protocol Impact | +|-------------|-------------|-----------------| +| Follow-up questions | Allow users to ask clarifying questions | May require A2UI | +| Article ratings | Let users rate article relevance | Requires action handling | +| Conversation history | Maintain context across questions | Requires state management | +| Multi-language support | Detect and respond in user's language | No protocol change | diff --git a/doc/ai_assistant/router_ai_assistant.md b/doc/ai_assistant/router_ai_assistant.md new file mode 100644 index 000000000..a07bad4af --- /dev/null +++ b/doc/ai_assistant/router_ai_assistant.md @@ -0,0 +1,142 @@ +# Router AI Assistant Architecture & Workflow + +This document outlines the internal architecture and workflow of the **Router AI Assistant**, a generative UI feature that allows users to interact with their Linksys router using natural language. + +--- + +## 1. System Architecture + +The AI Assistant is built on a **Modular Agentic Architecture**, separating UI, Logic, and Data Execution. + +### Core Components + +| Component | Responsibility | +|-----------|----------------| +| **RouterAssistantView** | The main Chat UI. Handles user input, displays message history, and renders A2UI responses (Widgets). | +| **RouterAgentOrchestrator** | The "Brain". Manages the conversation loop, builds System Prompts, handles LLM calls, and parses Tool calls. | +| **RouterContextProvider** | Provides "Environmental Awareness". Injects a real-time summary of the router status (connected devices count, WAN status) into the System Prompt. | +| **JnapCommandProvider** | The execution layer. Translates abstract AI tools (e.g., `router://devices`) into actual JNAP API calls (`GetDevices`). | +| **RouterComponentRegistry** | A dictionary of custom UI components (`NetworkStatusCard`, `RouterSettingsCard`) that the AI can "summon" via JSON. | +| **A2UIResponseRenderer** | The engine that turns raw JSONL from the LLM into Flutter Widgets using the Registry. | + +### Visual Architecture + +```mermaid +graph TD + User([User]) <--> UI[RouterAssistantView] + + subgraph "AI Core" + UI <--> Orch[RouterAgentOrchestrator] + Orch <--> LLM[LLM / Bedrock] + Orch --> Context[RouterContextProvider] + end + + subgraph "Execution Layer" + Orch --> Cmd[JnapCommandProvider] + Cmd --> JNAP[Router Repository] + JNAP <--> Router[(Physical Router)] + end + + subgraph "Rendering Layer" + UI --> Renderer[A2UIResponseRenderer] + Renderer --> Reg[RouterComponentRegistry] + end +``` + +--- + +## 2. Conversation Workflow (The Loop) + +The conversation follows a strict **Thought-Action-Observation** loop, enhanced with Generative UI capabilities. + +### Step-by-Step Flow + +1. **User Input**: User types "Why is my internet slow?" +2. **Context Construction**: + * `RouterContextProvider` fetches a **summary** (not full data) of the current state. + * *Example*: "WAN: Connected, Devices: 15". + * This is injected into the System Prompt. +3. **LLM Processing (Thought)**: + * The LLM analyzes the request against the context. + * *Decision*: "I need to check the actual bandwidth usage." +4. **Tool Execution (Action)**: + * LLM calls function `router_status`. + * `RouterAgentOrchestrator` intercepts this, calls `JnapCommandProvider`. + * JNAP API is executed. +5. **Observation**: + * The API returns raw JSON data. + * This data is fed back to the LLM as a "Tool Result". +6. **Response Generation (Answer)**: + * The LLM formulates a final response. + * **Crucial Step**: Instead of just text, it generates **A2UI JSONL**. + * *Example*: It decides to show a `NetworkStatusCard`. +7. **Rendering**: + * `RouterAssistantView` receives the stream. + * It detects `application/vnd.a2ui` content. + * `A2UIResponseRenderer` looks up `NetworkStatusCard` in the Registry. + * The generic JSON data is mapped to the concrete Flutter Widget. + +--- + +## 3. Data Strategy: Summary vs. On-Demand + +To optimize performance and token usage, we use a hybrid data strategy: + +* **Always-On Context (Summary)**: + * A lightweight summary is sent with *every* message. + * Includes: WAN connection status, *Count* of connected devices (e.g., "55"), Firmware version. + * **Purpose**: Allows the AI to answer basic questions ("Are we online?") without calling tools. +* **On-Demand Data (Tooling)**: + * Heavy data (e.g., the full list of 55 devices with IP/MAC addresses) is **NOT** sent by default. + * The AI must explicitly call `router://devices` to fetch this. + * **Purpose**: Prevents context window overflow and saves tokens. + +> **Data Consistency Rule**: +> We explicitly instruct the LLM in the System Prompt to always use the *Context Summary* for counts (e.g., "55 devices") even if it hasn't fetched the full list, ensuring UI consistency (avoiding "0 devices" bugs). + +--- + +## 4. Sequence Diagram + +This diagram illustrates the flow of a complex request: "List my devices". + +```mermaid +sequenceDiagram + participant User + participant View as RouterAssistantView + participant Orch as Orchestrator + participant Context as ContextProvider + participant LLM + participant Tools as CommandProvider + + User->>View: "Show me my devices" + View->>Orch: Send Message + + rect rgb(240, 240, 240) + Note over Orch, Context: Phase 1: Context Building + Orch->>Context: buildContextPrompt() + Context-->>Orch: "Status: Online, Count: 5" + end + + Orch->>LLM: Prompt + Context + User Msg + + rect rgb(255, 240, 240) + Note over LLM, Tools: Phase 2: Tool Execution + LLM-->>Orch: Call Tool: "get_devices" + Orch->>Tools: execute("get_devices") + Tools-->>Orch: JSON List [iPhone, Macbook...] + end + + Orch->>LLM: Tool Result JSON + + rect rgb(240, 255, 240) + Note over LLM, View: Phase 3: UI Generation + LLM-->>Orch: A2UI Response (JSONL) + Note right of LLM: {"type": "DeviceListView", ...} + Orch-->>View: Stream Token / Block + end + + View->>View: Parse JSONL + View->>View: Render DeviceListView Widget + View-->>User: Display Interactive List +``` diff --git a/doc/ai_assistant/router_assistant_architecture.md b/doc/ai_assistant/router_assistant_architecture.md new file mode 100644 index 000000000..2b349dabd --- /dev/null +++ b/doc/ai_assistant/router_assistant_architecture.md @@ -0,0 +1,155 @@ +# Router AI Assistant: Architecture Design + +This document describes the structural design and software patterns used in the implementation of the Router AI Assistant module within PrivacyGUI. + +--- + +## 1. High-Level Design + +The system implements a **Modular Agentic Architecture** integrated into a Clean Architecture application. It bridges the Generative UI framework (`generative_ui`) with the router's domain logic (`jnap`). + +### Core Design Principles +* **Separation of Concerns**: UI rendering (View), AI Logic (Orchestrator), and Data Execution (Provider) are strictly decoupled. +* **Dependency Injection**: All major components (`RouterRepository`, `CommandProvider`) are injected via Riverpod. +* **Protocol-Oriented**: Interactions are defined by abstract interfaces (`IConversationGenerator`, `IRouterCommandProvider`, `IComponentRegistry`) to allow easy mocking and swapping. + +--- + +## 2. Structural Class Diagram + +This diagram illustrates the static relationships between classes and interfaces. + +```mermaid +classDiagram + %% Core View + class RouterAssistantView { + +build() + -sendMessage() + -handleA2UI() + } + + %% AI Orchestration Layer + class RouterAgentOrchestrator { + +generateWithHistory() + +executeConfirmedCommand() + -buildSystemPrompt() + -executeCommand() + } + + class RouterContextProvider { + +buildContextPrompt() + -fetchStatus() + -fetchDeviceCount() + } + + class RouterSystemPrompt { + +build() + -basePrompt + -componentSchema + } + + %% Abstractions + class IConversationGenerator { + <> + +generateWithHistory() + } + + class IRouterCommandProvider { + <> + +execute() + +listCommands() + } + + %% Execution Layer + class JnapCommandProvider { + +execute() + +listCommands() + -mapJnapAction() + } + + class RouterCommand { + +name + +description + +inputSchema + +requiresConfirmation + } + + %% UI Rendering Layer + class RouterComponentRegistry { + +create() + +registerComponents() + } + + class EthernetPortsCard { + +ports: List + } + + class NetworkStatusCard { + +wanStatus + +connectedDevices + } + + %% Relationships + RouterAssistantView --> IConversationGenerator + RouterAssistantView --> RouterComponentRegistry + + RouterAgentOrchestrator ..|> IConversationGenerator + RouterAgentOrchestrator --> IRouterCommandProvider + RouterAgentOrchestrator --> RouterContextProvider + + RouterContextProvider ..> RouterSystemPrompt : uses + RouterContextProvider --> IRouterCommandProvider : reads status + + JnapCommandProvider ..|> IRouterCommandProvider + JnapCommandProvider ..> RouterCommand : provides + + RouterComponentRegistry ..> EthernetPortsCard : creates + RouterComponentRegistry ..> NetworkStatusCard : creates +``` + +--- + +## 3. Design Patterns Applied + +### 1. Orchestrator Pattern (`RouterAgentOrchestrator`) +**Purpose**: Centralizes the complex logic of managing the AI's "thought process". +* It acts as a mediator between the LLM (brain), the User (chat history), and the System (tools). +* It manages the internal "loop" (Thought -> Tool Call -> Result -> Answer) transparently to the View. + +### 2. Adapter Pattern (`JnapCommandProvider`) +**Purpose**: Adapts the router's internal JNAP API to a format understandable by the AI. +* **Adaptee**: `RouterRepository` (Raw JNAP methods like `send(JNAPAction.getDevices)`). +* **Target Interface**: `IRouterCommandProvider` (AI-friendly `RouterCommand` with JSON schemas). +* This generic interface allows the AI to "see" router capabilities as standard tools without knowing JNAP details. + +### 3. Registry Pattern (`RouterComponentRegistry`) +**Purpose**: Decouples the A2UI parsing engine from concrete Flutter widgets. +* The `A2UIResponseRenderer` (engine) doesn't know about `EthernetPortsCard` at compile time. +* The Registry provides a lookup mechanism (`String` -> `WidgetBuilder`), allowing us to dynamically extend the AI's UI capabilities without modifying the core engine. + +### 4. Builder Pattern (`RouterSystemPrompt`) +**Purpose**: Constructs the complex System Prompt string. +* Assembles various distinct parts: Format Constraints, Role Definition, Component Schemas, and Dynamic Context. +* Ensures the prompt is always well-formed with critical instructions (like the A2UI JSON requirement) placement. + +--- + +## 4. Directory Structure + +The architecture maps directly to the project folder structure in `lib/ai/`. + +``` +lib/ai/ +├── abstraction/ # Core Interfaces +│ ├── _abstraction.dart # IConversationGenerator, etc. +│ └── models/ # ChatMessage, LLMResponse +├── orchestrator/ # The "Brain" +│ ├── router_agent_orchestrator.dart # Main Logic +│ └── router_context_provider.dart # Context Builder +├── providers/ # Data Adapters +│ └── jnap_command_provider.dart # JNAP Implementation +├── registry/ # UI Registries +│ └── router_component_registry.dart # Component definitions +└── prompts/ # Prompt Engineering + └── router_system_prompt.dart # Prompt Templates +``` diff --git a/env.template b/env.template new file mode 100644 index 000000000..bf8447a91 --- /dev/null +++ b/env.template @@ -0,0 +1,5 @@ +# AWS Bedrock Configuration +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_REGION=us-east-1 +BEDROCK_MODEL_ID=anthropic.claude-3-5-sonnet-20241022-v2:0 diff --git a/lib/ai/_ai.dart b/lib/ai/_ai.dart new file mode 100644 index 000000000..b9a910c1f --- /dev/null +++ b/lib/ai/_ai.dart @@ -0,0 +1,15 @@ +/// AI module for Router AI Assistant. +/// +/// This module provides: +/// - [IRouterCommandProvider] - Abstract interface for router command providers +/// - [JnapCommandProvider] - JNAP-based implementation +/// - [RouterAgentOrchestrator] - Orchestrates AI conversation with router commands +/// - [RouterSystemPrompt] - System prompt templates +/// - [RouterComponentRegistry] - Component registry with router-specific widgets +library ai; + +export 'abstraction/_abstraction.dart'; +export 'providers/_providers.dart'; +export 'orchestrator/_orchestrator.dart'; +export 'prompts/_prompts.dart'; +export 'registry/_registry.dart'; diff --git a/lib/ai/abstraction/_abstraction.dart b/lib/ai/abstraction/_abstraction.dart new file mode 100644 index 000000000..4780c50f9 --- /dev/null +++ b/lib/ai/abstraction/_abstraction.dart @@ -0,0 +1,6 @@ +// AI module abstraction layer exports + +export 'access_level.dart'; +export 'router_command.dart'; +export 'router_resource.dart'; +export 'i_router_command_provider.dart'; diff --git a/lib/ai/abstraction/access_level.dart b/lib/ai/abstraction/access_level.dart new file mode 100644 index 000000000..4d5a23665 --- /dev/null +++ b/lib/ai/abstraction/access_level.dart @@ -0,0 +1,11 @@ +/// Access level for router commands. +enum AccessLevel { + /// Read-only operations, no confirmation needed + read, + + /// Write operations, requires user confirmation + write, + + /// Administrative operations, requires double confirmation + admin, +} diff --git a/lib/ai/abstraction/i_router_command_provider.dart b/lib/ai/abstraction/i_router_command_provider.dart new file mode 100644 index 000000000..4f942e51d --- /dev/null +++ b/lib/ai/abstraction/i_router_command_provider.dart @@ -0,0 +1,73 @@ +import 'router_command.dart'; +import 'router_resource.dart'; + +/// Core abstraction for router command providers. +/// +/// This interface follows MCP (Model Context Protocol) patterns: +/// - `listCommands()` ≈ MCP `tools/list` +/// - `execute()` ≈ MCP `tools/call` +/// - `listResources()` ≈ MCP `resources/list` +/// - `readResource()` ≈ MCP `resources/read` +/// +/// Implementations: +/// - [JnapCommandProvider] - JNAP-based implementation +/// - [UspCommandProvider] - USP-based implementation (future) +abstract class IRouterCommandProvider { + /// Returns the list of available commands. + /// + /// This is used to generate the tool definitions for the AI. + Future> listCommands(); + + /// Executes a command with the given parameters. + /// + /// Throws [UnauthorizedCommandException] if the command is not in the whitelist. + /// Throws [CommandExecutionException] if the command fails to execute. + Future execute( + String commandName, + Map params, + ); + + /// Returns the list of available resources. + /// + /// Resources are read-only data that the AI can query. + List listResources(); + + /// Reads a resource by its URI. + /// + /// Throws [ResourceNotFoundException] if the resource does not exist. + Future readResource(String resourceUri); +} + +/// Exception thrown when attempting to execute an unauthorized command. +class UnauthorizedCommandException implements Exception { + final String commandName; + + const UnauthorizedCommandException(this.commandName); + + @override + String toString() => + 'UnauthorizedCommandException: Command "$commandName" is not allowed'; +} + +/// Exception thrown when command execution fails. +class CommandExecutionException implements Exception { + final String commandName; + final String message; + + const CommandExecutionException(this.commandName, this.message); + + @override + String toString() => + 'CommandExecutionException: Command "$commandName" failed: $message'; +} + +/// Exception thrown when a resource is not found. +class ResourceNotFoundException implements Exception { + final String resourceUri; + + const ResourceNotFoundException(this.resourceUri); + + @override + String toString() => + 'ResourceNotFoundException: Resource "$resourceUri" not found'; +} diff --git a/lib/ai/abstraction/router_command.dart b/lib/ai/abstraction/router_command.dart new file mode 100644 index 000000000..9c550582b --- /dev/null +++ b/lib/ai/abstraction/router_command.dart @@ -0,0 +1,69 @@ +import 'package:equatable/equatable.dart'; + +import 'access_level.dart'; + +/// Describes a router command that can be executed via AI. +/// +/// This maps to MCP's Tool concept. +class RouterCommand extends Equatable { + /// Unique command name (e.g., 'getDevices', 'setRadioSettings') + final String name; + + /// Human-readable description for AI understanding + final String description; + + /// JSON Schema describing the input parameters + final Map inputSchema; + + /// Whether this command requires user confirmation before execution + final bool requiresConfirmation; + + /// Access level required to execute this command + final AccessLevel accessLevel; + + const RouterCommand({ + required this.name, + required this.description, + this.inputSchema = const {}, + this.requiresConfirmation = false, + this.accessLevel = AccessLevel.read, + }); + + @override + List get props => [ + name, + description, + inputSchema, + requiresConfirmation, + accessLevel, + ]; +} + +/// Result of executing a router command. +class RouterCommandResult extends Equatable { + /// Whether the command executed successfully + final bool success; + + /// Result data from the command + final Map data; + + /// Error message if the command failed + final String? error; + + const RouterCommandResult({ + required this.success, + this.data = const {}, + this.error, + }); + + factory RouterCommandResult.success(Map data) { + return RouterCommandResult(success: true, data: data); + } + + factory RouterCommandResult.failure(String error) { + return RouterCommandResult(success: false, error: error); + } + + @override + List get props => [success, data, error]; +} diff --git a/lib/ai/abstraction/router_resource.dart b/lib/ai/abstraction/router_resource.dart new file mode 100644 index 000000000..635c94c8f --- /dev/null +++ b/lib/ai/abstraction/router_resource.dart @@ -0,0 +1,49 @@ +import 'package:equatable/equatable.dart'; + +/// Describes a router resource that can be queried via AI. +/// +/// This maps to MCP's Resource concept. +class RouterResourceDescriptor extends Equatable { + /// Unique resource URI (e.g., 'router://devices', 'router://wifi') + final String uri; + + /// Human-readable name + final String name; + + /// MIME type of the resource content + final String mimeType; + + /// Description for AI understanding + final String description; + + const RouterResourceDescriptor({ + required this.uri, + required this.name, + this.mimeType = 'application/json', + this.description = '', + }); + + @override + List get props => [uri, name, mimeType, description]; +} + +/// Content of a router resource. +class RouterResource extends Equatable { + /// The resource URI this content belongs to + final String uri; + + /// The content as a JSON-encodable map + final Map content; + + /// Optional text representation + final String? text; + + const RouterResource({ + required this.uri, + required this.content, + this.text, + }); + + @override + List get props => [uri, content, text]; +} diff --git a/lib/ai/orchestrator/_orchestrator.dart b/lib/ai/orchestrator/_orchestrator.dart new file mode 100644 index 000000000..7b6ff7b30 --- /dev/null +++ b/lib/ai/orchestrator/_orchestrator.dart @@ -0,0 +1,4 @@ +// AI module orchestrator exports + +export 'router_context_provider.dart'; +export 'router_agent_orchestrator.dart'; diff --git a/lib/ai/orchestrator/router_agent_orchestrator.dart b/lib/ai/orchestrator/router_agent_orchestrator.dart new file mode 100644 index 000000000..aeb4344b4 --- /dev/null +++ b/lib/ai/orchestrator/router_agent_orchestrator.dart @@ -0,0 +1,203 @@ +import 'package:flutter/foundation.dart'; +import 'package:generative_ui/generative_ui.dart'; + +import 'package:privacy_gui/ai/abstraction/_abstraction.dart'; +import 'package:privacy_gui/ai/prompts/router_system_prompt.dart'; + +import 'router_context_provider.dart'; + +/// Orchestrator that bridges AI conversation with router commands. +/// +/// This class: +/// 1. Injects router context into LLM system prompts +/// 2. Handles tool execution requests from LLM +/// 3. Manages action confirmation flow for write/admin operations +class RouterAgentOrchestrator implements IConversationGenerator { + final IConversationGenerator _llmGenerator; + final IRouterCommandProvider _commandProvider; + final RouterContextProvider _contextProvider; + + /// Callback for when a command requires user confirmation. + final void Function(RouterCommand command, Map params)? + onConfirmationRequired; + + /// Cached system prompt with router context. + String? _cachedSystemPrompt; + + RouterAgentOrchestrator({ + required IConversationGenerator llmGenerator, + required IRouterCommandProvider commandProvider, + RouterContextProvider? contextProvider, + this.onConfirmationRequired, + }) : _llmGenerator = llmGenerator, + _commandProvider = commandProvider, + _contextProvider = + contextProvider ?? RouterContextProvider(commandProvider); + + @override + Future generateWithHistory( + List messages, { + List? tools, + String? systemPrompt, + bool forceToolUse = false, + }) async { + // Build enhanced system prompt with router context + final enhancedPrompt = await _buildSystemPrompt(systemPrompt); + + // Build tool definitions from command provider + final routerTools = await _buildRouterTools(); + final allTools = [...?tools, ...routerTools]; + + // Local copy of messages for the loop + var currentMessages = List.from(messages); + var loopCount = 0; + const maxLoops = 5; + + while (loopCount < maxLoops) { + loopCount++; + + // Call LLM + final response = await _llmGenerator.generateWithHistory( + currentMessages, + tools: allTools.isNotEmpty ? allTools : null, + systemPrompt: enhancedPrompt, + // Only force tool use on the FIRST turn if requested + forceToolUse: loopCount == 1 && forceToolUse, + ); + + // Check for tool use + final toolUseBlocks = response.content.whereType().toList(); + + if (toolUseBlocks.isEmpty) { + // No tools used, return final response + return response; + } + + // Add assistant response to history + currentMessages.add(ChatMessage.assistant(response)); + + // Execute tools + var confirmationRequired = false; + + for (final toolUse in toolUseBlocks) { + final executionResult = await _executeCommand(toolUse); + + if (executionResult.$1) { + // Confirmation required + confirmationRequired = true; + } + + // Add result message + currentMessages.add(ChatMessage.toolResult( + toolUseId: toolUse.id, + result: executionResult.$2, + isError: executionResult.$3, + )); + } + + if (confirmationRequired) { + // If confirmation was required, stop loop and return the response. + // The UI will handle the confirmation dialog via callback. + return response; + } + + // Continue loop with new history + } + + return LLMResponse(id: 'error', model: 'error', content: [ + TextBlock( + text: '⚠️ Error: Too many conversation turns, execution stopped.') + ]); + } + + /// Builds the system prompt with router context injected. + Future _buildSystemPrompt(String? basePrompt) async { + if (_cachedSystemPrompt == null) { + final routerContext = await _contextProvider.buildContextPrompt(); + _cachedSystemPrompt = + RouterSystemPrompt.build(routerContext: routerContext); + + // DEBUG: Log first 500 chars of system prompt + debugPrint('=== SYSTEM PROMPT FIRST 500 CHARS ==='); + debugPrint(_cachedSystemPrompt!.substring( + 0, + _cachedSystemPrompt!.length > 500 + ? 500 + : _cachedSystemPrompt!.length)); + debugPrint('=== END SYSTEM PROMPT PREVIEW ==='); + } + + if (basePrompt != null && basePrompt.isNotEmpty) { + return '$_cachedSystemPrompt\n\n$basePrompt'; + } + + return _cachedSystemPrompt!; + } + + /// Builds GenTool definitions from the command provider. + Future> _buildRouterTools() async { + final commands = await _commandProvider.listCommands(); + return commands + .map((cmd) => GenTool( + name: cmd.name, + description: cmd.description, + inputSchema: cmd.inputSchema, + )) + .toList(); + } + + /// Executes a command and returns (requiresConfirmation, output/error, isError) + Future<(bool, Map, bool)> _executeCommand( + ToolUseBlock toolCall) async { + // Check if this is a router command + final commands = await _commandProvider.listCommands(); + final command = commands.cast().firstWhere( + (c) => c?.name == toolCall.name, + orElse: () => null, + ); + + if (command == null) { + return (false, {'error': 'Unknown command: ${toolCall.name}'}, true); + } + + // Check if confirmation is required + if (command.requiresConfirmation) { + debugPrint( + 'RouterAgentOrchestrator: Confirmation required for ${command.name}'); + onConfirmationRequired?.call(command, toolCall.input); + return (true, {'status': 'pending_confirmation'}, false); + } + + // Execute read-only command immediately + try { + final result = await _commandProvider.execute( + command.name, + toolCall.input, + ); + + debugPrint( + 'RouterAgentOrchestrator: Executed ${command.name}, success=${result.success}'); + + return (false, result.data, !result.success); + } catch (e) { + debugPrint( + 'RouterAgentOrchestrator: Error executing ${command.name}: $e'); + return (false, {'error': e.toString()}, true); + } + } + + /// Clears the cached system prompt to force refresh. + void clearCache() { + _cachedSystemPrompt = null; + } + + /// Executes a confirmed command. + /// + /// Call this after user confirms a write/admin operation. + Future executeConfirmedCommand( + String commandName, + Map params, + ) async { + return _commandProvider.execute(commandName, params); + } +} diff --git a/lib/ai/orchestrator/router_context_provider.dart b/lib/ai/orchestrator/router_context_provider.dart new file mode 100644 index 000000000..f8d08d81a --- /dev/null +++ b/lib/ai/orchestrator/router_context_provider.dart @@ -0,0 +1,74 @@ +import 'dart:convert'; + +import 'package:privacy_gui/ai/abstraction/_abstraction.dart'; + +/// Provides router context information for AI system prompts. +/// +/// This class fetches current router state and formats it as context +/// that can be injected into the AI system prompt. +class RouterContextProvider { + final IRouterCommandProvider _commandProvider; + + RouterContextProvider(this._commandProvider); + + /// Builds a context string with current router state for the AI. + Future buildContextPrompt() async { + final buffer = StringBuffer(); + buffer.writeln('## Current Router State'); + buffer.writeln(); + + try { + // Get device info + final statusResource = + await _commandProvider.readResource('router://status'); + buffer.writeln('### System Status'); + buffer.writeln('```json'); + buffer.writeln( + const JsonEncoder.withIndent(' ').convert(statusResource.content)); + buffer.writeln('```'); + buffer.writeln(); + + // Get connected devices count + final devicesResource = + await _commandProvider.readResource('router://devices'); + final devices = devicesResource.content['devices'] as List? ?? []; + // Filter for online devices only (those with active connections) + final onlineDevices = devices.where((d) { + final connections = d['connections'] as List? ?? []; + return connections.isNotEmpty; + }).toList(); + + buffer.writeln('### Connected Devices'); + buffer.writeln('- Total connected devices: ${onlineDevices.length}'); + // Also provide total known devices for context if needed, but primary focus is connected + buffer.writeln('- Total known devices: ${devices.length}'); + buffer.writeln(); + } catch (e) { + buffer + .writeln('> Note: Some router information is currently unavailable.'); + } + + // List available commands + buffer.writeln('### Available Commands'); + final commands = await _commandProvider.listCommands(); + for (final cmd in commands) { + final level = cmd.accessLevel.name.toUpperCase(); + final confirm = cmd.requiresConfirmation ? ' ⚠️' : ''; + buffer.writeln('- `${cmd.name}` [$level]$confirm: ${cmd.description}'); + } + + return buffer.toString(); + } + + /// Builds a list of available tools for the AI in GenTool format. + Future>> buildToolDefinitions() async { + final commands = await _commandProvider.listCommands(); + return commands + .map((cmd) => { + 'name': cmd.name, + 'description': cmd.description, + 'input_schema': cmd.inputSchema, + }) + .toList(); + } +} diff --git a/lib/ai/prompts/_prompts.dart b/lib/ai/prompts/_prompts.dart new file mode 100644 index 000000000..bdcbc9c75 --- /dev/null +++ b/lib/ai/prompts/_prompts.dart @@ -0,0 +1,3 @@ +// AI module prompts exports + +export 'router_system_prompt.dart'; diff --git a/lib/ai/prompts/router_system_prompt.dart b/lib/ai/prompts/router_system_prompt.dart new file mode 100644 index 000000000..c4cd9ff11 --- /dev/null +++ b/lib/ai/prompts/router_system_prompt.dart @@ -0,0 +1,207 @@ +/// System prompt template for the Router AI Assistant. +/// +/// This defines how the AI should behave and what components it can generate. +class RouterSystemPrompt { + RouterSystemPrompt._(); + + /// Builds the complete system prompt with context. + static String build({String? routerContext}) { + final buffer = StringBuffer(); + + // CRITICAL: Put format constraint FIRST for highest priority + buffer.writeln(_formatConstraint); + buffer.writeln(); + buffer.writeln(_basePrompt); + buffer.writeln(); + buffer.writeln(_componentGuide); + buffer.writeln(); + buffer.writeln(_a2uiGuide); + + if (routerContext != null && routerContext.isNotEmpty) { + buffer.writeln(); + buffer.writeln(routerContext); + } + + // End with reminder + buffer.writeln(); + buffer.writeln(_formatReminder); + + return buffer.toString(); + } + + /// Critical format constraint placed at the very beginning. + static const _formatConstraint = ''' +⚠️ CRITICAL OUTPUT FORMAT REQUIREMENT ⚠️ + +You MUST output ALL UI using A2UI JSONL format. +You MUST NOT output markdown text, lists, or tables to present data. +You MUST NOT wrap JSON in code blocks. +You MUST start your response with a valid A2UI JSON object. + +Failure to comply will result in rendering errors. +'''; + + /// Reminder at the end of the prompt. + static const _formatReminder = ''' +⚠️ FINAL REMINDER: +Your response MUST be valid A2UI JSONL containing: +1. surfaceUpdate with COMPLETE component definitions +2. dataModelUpdate (if using boundPath) +3. beginRendering with rootId + +NEVER skip surfaceUpdate. NEVER output just text. NEVER use markdown. +Start IMMEDIATELY with {"surfaceUpdate":... +'''; + + static const _basePrompt = ''' +# Router AI Assistant + +You are an intelligent assistant for Linksys router management. Your role is to help users: +- View and understand their network status +- Configure WiFi and network settings +- Manage connected devices +- Troubleshoot network issues + +## Behavior Guidelines + +1. **Be Helpful**: Provide clear, actionable information. +2. **Be Safe**: Always confirm before making changes (especially for write/admin operations). +3. **Be Concise**: Use UI components to display structured data instead of long text. +4. **Language**: Respond in the same language as the user. + +## Safety Rules + +- **Read operations**: Execute freely and display results. +- **Write operations**: Always ask for confirmation before executing. +- **Admin operations**: Warn about consequences and require explicit confirmation. +- **Never** execute `factoryReset` or similar destructive operations. +'''; + + static const _componentGuide = ''' +## Available UI Components + +Use these components to display information: + +### Display Components +- `AppText` - Text display with variants: headline, body, caption +- `AppCard` - Card container for grouped content +- `DeviceListView` - List of connected devices +- `NetworkStatusCard` - Network status summary +- `WifiSettingsForm` - WiFi configuration form +- `TopologyTreeView` - Mesh network topology visualization + +### Layout Components +- `Column` - Vertical layout +- `Row` - Horizontal layout +- `AppSurface` - Elevated surface container + +### Interactive Components +- `AppButton` - Action button +- `ConfirmationSheet` - Confirmation dialog for dangerous operations +'''; + + static const _a2uiGuide = ''' +## A2UI Output Format + +CRITICAL: You MUST output all data using the A2UI JSONL format defined below. +DO NOT use markdown text, lists, or tables to present data (e.g., do not write "Network Status: Connected"). +DO NOT wrap the JSON in markdown code blocks (e.g., ```json). +Just output the raw JSONL lines. + +⚠️ EVERY RESPONSE MUST CONTAIN ALL THREE PARTS: + +1. `surfaceUpdate` - REQUIRED: Define the complete component tree with ALL components +2. `dataModelUpdate` - OPTIONAL: Provide data for bound properties +3. `beginRendering` - REQUIRED: Start rendering with rootId pointing to root component + +IMPORTANT FOR MULTI-TURN CONVERSATIONS: +- Each response is SELF-CONTAINED - you must include the COMPLETE surfaceUpdate every time +- Do NOT assume previous UI components still exist +- Do NOT just send dataModelUpdate without surfaceUpdate +- The client will REPLACE the entire UI with your new surfaceUpdate + +### Component Schemas + +#### NetworkStatusCard +Use for displaying network status overview. +```json + "type": "NetworkStatusCard", + "properties": { + "wanStatus": "Connected", // String: Connected, Disconnected + "connectedDevices": 5, // Integer: Total count. MUST use 'Total connected devices' from Context. + "uploadSpeed": "100 Mbps", // String (Optional) + "downloadSpeed": "500 Mbps" // String (Optional) + } + } +} + +⚠️ IMPORTANT: When using NetworkStatusCard, you MUST populate 'connectedDevices' with the 'Total connected devices' count provided in the 'Current Router State' context. DO NOT default to 0 unless the context explicitly says 0. + +#### DeviceListView +Use for displaying list of connected devices. +```json +{ + "type": "DeviceListView", + "properties": { + "devices": [ + { + "name": "iPhone 13", + "ip": "192.168.1.10", + "mac": "00:11:22:33:44:55", + "connectionType": "WiFi 5GHz" + } + ] + } +} +``` + +#### WifiSettingsCard +Use for displaying or editing WiFi settings. +```json +{ + "type": "WifiSettingsCard", + "properties": { + "ssid": "MyWiFi", + "password": "secretpassword", + "securityMode": "WPA3", + "band": "Dual-band" + } +} +``` + +#### EthernetPortsCard +Use for displaying ethernet port status (WAN/LAN). +```json +{ + "type": "EthernetPortsCard", + "properties": { + "ports": [ + {"label": "WAN", "status": "Connected", "speed": "1 Gbps"}, + {"label": "1", "status": "Disconnected"}, + {"label": "2", "status": "Connected", "speed": "100 Mbps"}, + {"label": "3", "status": "Disconnected"}, + {"label": "4", "status": "Disconnected"} + ] + } +} +``` + +### Example: Display Device List + +{"surfaceUpdate":{"surfaceId":"main","components":[ + {"id":"root","type":"Column","childIds":["header","devices"]}, + {"id":"header","type":"AppText","properties":{"text":"Connected Devices","variant":"headline"}}, + {"id":"devices","type":"DeviceListView","properties":{"devices":{"boundPath":"/data/devices"}}} +]}} +{"dataModelUpdate":{"surfaceId":"main","contents":[ + {"path":"/data/devices","value":[{"name":"iPhone","ip":"192.168.1.101"},{"name":"MacBook","ip":"192.168.1.102"}]} +]}} +{"beginRendering":{"surfaceId":"main","root":"root"}} + +### Data Binding + +Use `boundPath` for dynamic data: +- `{"literalString": "Static text"}` - Static value +- `{"boundPath": "/data/devices"}` - Dynamic binding from dataModelUpdate +'''; +} diff --git a/lib/ai/providers/_providers.dart b/lib/ai/providers/_providers.dart new file mode 100644 index 000000000..fabd2db99 --- /dev/null +++ b/lib/ai/providers/_providers.dart @@ -0,0 +1,3 @@ +// AI module providers exports + +export 'jnap_command_provider.dart'; diff --git a/lib/ai/providers/jnap_command_provider.dart b/lib/ai/providers/jnap_command_provider.dart new file mode 100644 index 000000000..ecb83cb08 --- /dev/null +++ b/lib/ai/providers/jnap_command_provider.dart @@ -0,0 +1,281 @@ +import 'package:privacy_gui/ai/abstraction/_abstraction.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; + +/// Metadata for allowed JNAP actions. +class _ActionMeta { + final String description; + final AccessLevel accessLevel; + final bool requiresConfirmation; + + const _ActionMeta({ + required this.description, + this.accessLevel = AccessLevel.read, + this.requiresConfirmation = false, + }); +} + +/// JNAP-based implementation of [IRouterCommandProvider]. +/// +/// This provider exposes a whitelist of JNAP actions to the AI agent. +/// Only actions in [_allowedActions] can be executed. +class JnapCommandProvider implements IRouterCommandProvider { + final RouterRepository _router; + + JnapCommandProvider(this._router); + + /// Whitelist of JNAP actions exposed to AI. + /// + /// Actions not in this map will be rejected. + static const _allowedActions = { + // === Read-only operations === + + // Device management + JNAPAction.getDevices: _ActionMeta( + description: + 'Get all connected devices with their details (name, IP, MAC, connection type)', + accessLevel: AccessLevel.read, + ), + JNAPAction.getDeviceInfo: _ActionMeta( + description: + 'Get router device information (model, firmware version, serial number)', + accessLevel: AccessLevel.read, + ), + + // WiFi settings + JNAPAction.getRadioInfo: _ActionMeta( + description: + 'Get WiFi radio settings (SSID, security mode, channel, band)', + accessLevel: AccessLevel.read, + ), + JNAPAction.getSimpleWiFiSettings: _ActionMeta( + description: 'Get simplified WiFi settings (SSID, password)', + accessLevel: AccessLevel.read, + ), + + // Network status + JNAPAction.getWANStatus: _ActionMeta( + description: + 'Get WAN connection status (IP address, connection type, uptime)', + accessLevel: AccessLevel.read, + ), + JNAPAction.getNetworkConnections: _ActionMeta( + description: 'Get all network connections with bandwidth usage', + accessLevel: AccessLevel.read, + ), + JNAPAction.getSystemStats: _ActionMeta( + description: 'Get system statistics (CPU, memory, uplink/downlink speed)', + accessLevel: AccessLevel.read, + ), + + // Guest network + JNAPAction.getGuestNetworkSettings: _ActionMeta( + description: 'Get guest network settings (enabled, SSID, password)', + accessLevel: AccessLevel.read, + ), + JNAPAction.getGuestNetworkClients: _ActionMeta( + description: 'Get clients connected to guest network', + accessLevel: AccessLevel.read, + ), + + // Mesh/Nodes + JNAPAction.getBackhaulInfo: _ActionMeta( + description: + 'Get mesh backhaul information (connection quality between nodes)', + accessLevel: AccessLevel.read, + ), + JNAPAction.getNodesWirelessNetworkConnections: _ActionMeta( + description: 'Get wireless connections for all mesh nodes', + accessLevel: AccessLevel.read, + ), + + // Security + JNAPAction.getFirewallSettings: _ActionMeta( + description: 'Get firewall settings', + accessLevel: AccessLevel.read, + ), + JNAPAction.getMACFilterSettings: _ActionMeta( + description: 'Get MAC address filter settings (blocked/allowed devices)', + accessLevel: AccessLevel.read, + ), + + // QoS + JNAPAction.getQoSSettings: _ActionMeta( + description: 'Get Quality of Service settings (bandwidth prioritization)', + accessLevel: AccessLevel.read, + ), + + // Firmware + JNAPAction.getFirmwareUpdateStatus: _ActionMeta( + description: + 'Get firmware update status (current version, available updates)', + accessLevel: AccessLevel.read, + ), + + // Health check + JNAPAction.getHealthCheckResults: _ActionMeta( + description: 'Get network health check results', + accessLevel: AccessLevel.read, + ), + + // === Write operations (require confirmation) === + + // WiFi settings + JNAPAction.setRadioSettings: _ActionMeta( + description: 'Change WiFi settings (SSID, password, channel)', + accessLevel: AccessLevel.write, + requiresConfirmation: true, + ), + JNAPAction.setSimpleWiFiSettings: _ActionMeta( + description: 'Change WiFi SSID and password', + accessLevel: AccessLevel.write, + requiresConfirmation: true, + ), + + // Guest network + JNAPAction.setGuestNetworkSettings: _ActionMeta( + description: 'Enable/disable or configure guest network', + accessLevel: AccessLevel.write, + requiresConfirmation: true, + ), + + // Device management + JNAPAction.setDeviceProperties: _ActionMeta( + description: 'Set device properties (name, icon)', + accessLevel: AccessLevel.write, + requiresConfirmation: true, + ), + + // MAC filter + JNAPAction.setMACFilterSettings: _ActionMeta( + description: 'Block or allow devices by MAC address', + accessLevel: AccessLevel.write, + requiresConfirmation: true, + ), + + // === Admin operations (require double confirmation) === + + JNAPAction.reboot: _ActionMeta( + description: + 'Restart the router (will disconnect all devices temporarily)', + accessLevel: AccessLevel.admin, + requiresConfirmation: true, + ), + }; + + /// Resource definitions for MCP resources/list. + static const _resources = [ + RouterResourceDescriptor( + uri: 'router://devices', + name: 'Connected Devices', + description: 'List of all devices connected to the router', + ), + RouterResourceDescriptor( + uri: 'router://wifi', + name: 'WiFi Settings', + description: 'Current WiFi configuration', + ), + RouterResourceDescriptor( + uri: 'router://topology', + name: 'Network Topology ', + description: 'Mesh network topology and node information', + ), + RouterResourceDescriptor( + uri: 'router://status', + name: 'System Status', + description: 'Router system status and statistics', + ), + ]; + + @override + Future> listCommands() async { + return _allowedActions.entries.map((entry) { + return RouterCommand( + name: entry.key.name, + description: entry.value.description, + inputSchema: _generateSchema(entry.key), + requiresConfirmation: entry.value.requiresConfirmation, + accessLevel: entry.value.accessLevel, + ); + }).toList(); + } + + @override + Future execute( + String commandName, + Map params, + ) async { + // Find the action by name + final action = JNAPAction.values.cast().firstWhere( + (a) => a?.name == commandName, + orElse: () => null, + ); + + if (action == null) { + throw UnauthorizedCommandException(commandName); + } + + if (!_allowedActions.containsKey(action)) { + throw UnauthorizedCommandException(commandName); + } + + try { + final result = await _router.send(action, data: params); + return RouterCommandResult.success(result.output); + } catch (e) { + return RouterCommandResult.failure(e.toString()); + } + } + + @override + List listResources() => _resources; + + @override + Future readResource(String resourceUri) async { + switch (resourceUri) { + case 'router://devices': + final result = await _router.send(JNAPAction.getDevices); + return RouterResource( + uri: resourceUri, + content: result.output, + ); + + case 'router://wifi': + final result = await _router.send(JNAPAction.getRadioInfo); + return RouterResource( + uri: resourceUri, + content: result.output, + ); + + case 'router://topology': + final result = await _router.send(JNAPAction.getBackhaulInfo); + return RouterResource( + uri: resourceUri, + content: result.output, + ); + + case 'router://status': + final result = await _router.send(JNAPAction.getSystemStats); + return RouterResource( + uri: resourceUri, + content: result.output, + ); + + default: + throw ResourceNotFoundException(resourceUri); + } + } + + /// Generates a JSON Schema for the given action. + /// + /// TODO: This could be enhanced to read from JNAP spec files + /// or use reflection to generate accurate schemas. + Map _generateSchema(JNAPAction action) { + // For now, return a basic object schema + // This can be enhanced with action-specific schemas + return { + 'type': 'object', + 'properties': {}, + }; + } +} diff --git a/lib/ai/registry/_registry.dart b/lib/ai/registry/_registry.dart new file mode 100644 index 000000000..3cf2f5a62 --- /dev/null +++ b/lib/ai/registry/_registry.dart @@ -0,0 +1,3 @@ +// AI module registry exports + +export 'router_component_registry.dart'; diff --git a/lib/ai/registry/router_component_registry.dart b/lib/ai/registry/router_component_registry.dart new file mode 100644 index 000000000..a0f89be6f --- /dev/null +++ b/lib/ai/registry/router_component_registry.dart @@ -0,0 +1,488 @@ +import 'package:flutter/material.dart'; +import 'package:generative_ui/generative_ui.dart'; +import 'package:ui_kit_library/ui_kit.dart'; + +/// Component registry with Router-specific components registered. +/// +/// This combines UI Kit standard components with router-specific ones. +class RouterComponentRegistry { + RouterComponentRegistry._(); + + /// Creates a new [ComponentRegistry] with all router components registered. + static ComponentRegistry create() { + final registry = ComponentRegistry(); + + // Register UI Kit standard components + UiKitCatalog.standardBuilders.forEach((name, builder) { + registry.register(name, builder); + }); + + // Override AppCard to provide default spacing in Chat UI + registry.register('AppCard', (context, props, {onAction, children}) { + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: AppCard( + onTap: props['onTap'] != null + ? () => onAction?.call({'action': props['onTap']}) + : null, + child: _buildFlexibleChild(children), + ), + ); + }); + + // Register Router-specific components + _registerRouterComponents(registry); + + return registry; + } + + static Widget _buildFlexibleChild(List? children) { + if (children == null || children.isEmpty) { + return const SizedBox(); + } + // If there's only one child, return it directly to allow flexible layout (Row, etc.) + if (children.length == 1) { + return children.first; + } + // If multiple children, stack them vertically by default + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: children, + ); + } + + static void _registerRouterComponents(ComponentRegistry registry) { + // DeviceListView - Display connected devices + registry.register('DeviceListView', (context, props, {onAction, children}) { + final devices = props['devices'] as List? ?? []; + return _DeviceListView( + devices: devices.cast>(), + onAction: onAction, + ); + }); + + // NetworkStatusCard - Show network status summary + registry.register('NetworkStatusCard', (context, props, + {onAction, children}) { + return _NetworkStatusCard( + wanStatus: props['wanStatus'] as String? ?? 'Unknown', + connectedDevices: props['connectedDevices'] as int? ?? 0, + uploadSpeed: props['uploadSpeed'] as String?, + downloadSpeed: props['downloadSpeed'] as String?, + ); + }); + + // WifiSettingsCard - Display WiFi information + registry.register('WifiSettingsCard', (context, props, + {onAction, children}) { + return _WifiSettingsCard( + ssid: props['ssid'] as String? ?? '', + password: props['password'] as String?, + securityMode: props['securityMode'] as String?, + band: props['band'] as String?, + ); + }); + + // EthernetPortsCard - Display ethernet port status + registry.register('EthernetPortsCard', (context, props, + {onAction, children}) { + final ports = props['ports'] as List? ?? []; + return _EthernetPortsCard( + ports: ports.cast>(), + ); + }); + + // ConfirmationSheet - Confirmation dialog for dangerous operations + registry.register('ConfirmationSheet', (context, props, + {onAction, children}) { + return _ConfirmationSheet( + title: props['title'] as String? ?? 'Confirmation', + message: props['message'] as String? ?? '', + confirmLabel: props['confirmLabel'] as String? ?? 'Confirm', + cancelLabel: props['cancelLabel'] as String? ?? 'Cancel', + confirmAction: props['confirmAction'] as String?, + onAction: onAction, + ); + }); + } +} + +/// Widget to display a list of connected devices. +class _DeviceListView extends StatelessWidget { + final List> devices; + final GenUiActionCallback? onAction; + + const _DeviceListView({ + required this.devices, + this.onAction, + }); + + @override + Widget build(BuildContext context) { + if (devices.isEmpty) { + return AppSurface( + child: Padding( + padding: const EdgeInsets.all(16), + child: Center( + child: AppText.body('No connected devices found'), + ), + ), + ); + } + + return AppSurface( + child: Column( + children: devices.map((device) { + final name = device['name'] as String? ?? 'Unknown Device'; + final ip = device['ip'] as String? ?? ''; + final mac = device['mac'] as String? ?? ''; + final connectionType = device['connectionType'] as String? ?? ''; + + return AppListTile( + leading: Icon(_getDeviceIcon(name)), + title: AppText.body(name), + subtitle: AppText.caption(ip.isNotEmpty ? ip : mac), + trailing: connectionType.isNotEmpty + ? AppBadge(label: connectionType) + : null, + onTap: () { + onAction?.call({ + 'action': 'deviceSelected', + 'device': device, + }); + }, + ); + }).toList(), + ), + ); + } + + IconData _getDeviceIcon(String name) { + final lowerName = name.toLowerCase(); + if (lowerName.contains('iphone') || lowerName.contains('android')) { + return Icons.phone_android; + } else if (lowerName.contains('mac') || + lowerName.contains('pc') || + lowerName.contains('laptop')) { + return Icons.laptop; + } else if (lowerName.contains('tv') || lowerName.contains('television')) { + return Icons.tv; + } else if (lowerName.contains('tablet') || lowerName.contains('ipad')) { + return Icons.tablet; + } + return Icons.devices; + } +} + +/// Widget to display network status summary. +class _NetworkStatusCard extends StatelessWidget { + final String wanStatus; + final int connectedDevices; + final String? uploadSpeed; + final String? downloadSpeed; + + const _NetworkStatusCard({ + required this.wanStatus, + required this.connectedDevices, + this.uploadSpeed, + this.downloadSpeed, + }); + + bool _isConnected(String status) { + final lower = status.toLowerCase(); + return lower == 'connected' || + lower == 'online' || + lower == '已連線' || + lower.contains('connect'); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + // Add padding at the bottom to ensure spacing between cards in the list + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: AppCard( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + _isConnected(wanStatus) ? Icons.check_circle : Icons.error, + color: _isConnected(wanStatus) + ? theme.colorScheme.primary + : theme.colorScheme.error, + ), + const SizedBox(width: 8), + AppText.headline('Network Status'), + ], + ), + const SizedBox(height: 16), + _buildInfoRow('WAN Status', wanStatus), + _buildInfoRow('Connected Devices', '$connectedDevices'), + if (uploadSpeed != null) + _buildInfoRow('Upload Speed', uploadSpeed!), + if (downloadSpeed != null) + _buildInfoRow('Download Speed', downloadSpeed!), + ], + ), + ), + ), + ); + } + + Widget _buildInfoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppText.body(label), + AppText.body(value), + ], + ), + ); + } +} + +/// Widget to display WiFi settings. +class _WifiSettingsCard extends StatelessWidget { + final String ssid; + final String? password; + final String? securityMode; + final String? band; + + const _WifiSettingsCard({ + required this.ssid, + this.password, + this.securityMode, + this.band, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: AppCard( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.wifi), + const SizedBox(width: 8), + AppText.headline('WiFi Settings'), + ], + ), + const SizedBox(height: 16), + _buildInfoRow('Network Name (SSID)', ssid), + if (password != null) _buildInfoRow('Password', password!), + if (securityMode != null) + _buildInfoRow('Security Mode', securityMode!), + if (band != null) _buildInfoRow('Band', band!), + ], + ), + ), + ), + ); + } + + Widget _buildInfoRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AppText.body(label), + AppText.body(value), + ], + ), + ); + } +} + +/// Widget to display ethernet port status. +class _EthernetPortsCard extends StatelessWidget { + final List> ports; + + const _EthernetPortsCard({required this.ports}); + + @override + Widget build(BuildContext context) { + // Note: AppCard override automatically adds padding and internal Column/SizedBox logic. + // However, since we are returning the content for the wrapper to use, + // we should use AppCard here and let the wrapper wrap it? + // Wait, the wrapper logic in registry wraps the *result* of this function? + // Reference registry logic: + // registry.register('AppCard', ... return Padding(child: AppCard(...))) + // Here we are returning `_EthernetPortsCard`, which is a Widget. + // The registry for 'EthernetPortsCard' was: + // returning _EthernetPortsCard(...) + // So _EthernetPortsCard itself should return an AppCard. + // And since our AppCard override is only for 'AppCard' key, using AppCard class + // directly here will NOT get the wrapper (Padding) automatically unless we replicate it + // or if the component system uses the registry recursively. + // Actually, calling `AppCard(...)` directly creates the widget. The registry override only affects when A2UI asks for "AppCard". + // So we should manually add Padding if we want consistency, OR rely on the fact that + // this component produces a Card. + // Let's manually add the Padding here to match the global style we established. + + final theme = Theme.of(context); + + return Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: AppCard( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon(Icons.settings_ethernet), + const SizedBox(width: 8), + AppText.headline('Ethernet Ports'), + ], + ), + const SizedBox(height: 16), + if (ports.isEmpty) + AppText.body('No port information available') + else + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: + ports.map((port) => _buildPortItem(theme, port)).toList(), + ), + ], + ), + ), + ), + ); + } + + Widget _buildPortItem(ThemeData theme, Map port) { + final label = port['label'] as String? ?? '?'; + final status = port['status'] as String? ?? 'Disconnected'; + final speed = port['speed'] as String?; + + // Check connection status loosely + final isConnected = status.toLowerCase() == 'connected' || + status.toLowerCase() == 'online' || + status.toLowerCase() == 'up' || + status.toLowerCase() == '已連線'; + + final color = isConnected + ? theme.colorScheme.primary + : theme.colorScheme.onSurface.withValues(alpha: 0.2); + + return Column( + children: [ + Container( + width: 48, + height: 40, + decoration: BoxDecoration( + color: color.withValues(alpha: isConnected ? 0.1 : 0.05), + border: Border.all( + color: color, + width: 2, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Icon( + Icons.lan, + color: color, + size: 20, + ), + ), + ), + const SizedBox(height: 8), + AppText.body(label), // e.g. WAN, 1, 2 + if (speed != null && isConnected) ...[ + const SizedBox(height: 4), + AppText.caption(speed, + color: theme.colorScheme.onSurface.withValues(alpha: 0.6)), + ], + ], + ); + } +} + +/// Confirmation dialog for dangerous operations. +class _ConfirmationSheet extends StatelessWidget { + final String title; + final String message; + final String confirmLabel; + final String cancelLabel; + final String? confirmAction; + final GenUiActionCallback? onAction; + + const _ConfirmationSheet({ + required this.title, + required this.message, + required this.confirmLabel, + required this.cancelLabel, + this.confirmAction, + this.onAction, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return AppCard( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.warning_amber_rounded, + size: 48, + color: theme.colorScheme.error, + ), + const SizedBox(height: 12), + AppText.headline(title), + const SizedBox(height: 8), + AppText.body(message, textAlign: TextAlign.center), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: AppButton( + label: cancelLabel, + variant: SurfaceVariant.base, + onTap: () { + onAction?.call({ + 'action': 'cancelled', + }); + }, + ), + ), + const SizedBox(width: 12), + Expanded( + child: AppButton( + label: confirmLabel, + variant: SurfaceVariant.highlight, + onTap: () { + onAction?.call({ + 'action': confirmAction ?? 'confirmed', + }); + }, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/app.dart b/lib/app.dart index 47119cda7..ac1cafefc 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -105,12 +105,14 @@ class _LinksysAppState extends ConsumerState router.routerDelegate.removeListener(_onReceiveRouteChanged); router.routerDelegate.addListener(_onReceiveRouteChanged); - final themeColor = appSettings.themeColor ?? AppPalette.brandPrimary; + // Only override theme color if user has explicitly set one + // If null, let the JSON config's seedColor be used + final userThemeColor = appSettings.themeColor; // Use ThemeJsonConfig as single source of truth final themeConfig = getIt(); - final appLightTheme = themeConfig.createLightTheme(themeColor); - final appDarkTheme = themeConfig.createDarkTheme(themeColor); + final appLightTheme = themeConfig.createLightTheme(userThemeColor); + final appDarkTheme = themeConfig.createDarkTheme(userThemeColor); return MaterialApp.router( onGenerateTitle: (context) => loc(context).appTitle, theme: appLightTheme, diff --git a/lib/constants/build_config.dart b/lib/constants/build_config.dart index 1e60984eb..36b8a1466 100644 --- a/lib/constants/build_config.dart +++ b/lib/constants/build_config.dart @@ -51,6 +51,34 @@ class BuildConfig { int.fromEnvironment('refresh_time', defaultValue: 60); static const copyRightYear = int.fromEnvironment('year', defaultValue: 2025); + /// Advanced Visual Effects Switch (Bitmask) + /// + /// Controls advanced rendering effects for UI components (e.g., AppSurface). + /// These flags are aligned with the definitions in [AppThemeConfig]. + /// + /// Bit Definitions: + /// - Bit 0 (1): Directional Shadow (Depth) + /// - Bit 1 (2): Gradient Border (Detail) + /// - Bit 2 (4): Background Blur (Filter) + /// - Bit 3 (8): Noise Texture (Texture) + /// - Bit 4 (16): Dynamic Shimmer (Motion) + /// + /// Examples: + /// - `0` = All off (Best performance) + /// - `7` = First three on (1+2+4) -> Shadow + Border + Blur + /// - `31` = All on (Max Quality) + /// + /// Enable at build time: `flutter run --dart-define=liquid_glass=31` + static const int liquidGlassEffects = + int.fromEnvironment('liquid_glass', defaultValue: 0); + + // Visual Effect Convenience Getters + static bool get enableShadows => (liquidGlassEffects & 1) != 0; + static bool get enableGradientBorder => (liquidGlassEffects & 2) != 0; + static bool get enableBlur => (liquidGlassEffects & 4) != 0; + static bool get enableNoiseTexture => (liquidGlassEffects & 8) != 0; + static bool get enableShimmer => (liquidGlassEffects & 16) != 0; + @pragma('vm:entry-point') static load() async { logger.d('load build configuration'); diff --git a/lib/core/usp/_usp.dart b/lib/core/usp/_usp.dart new file mode 100644 index 000000000..b32b18942 --- /dev/null +++ b/lib/core/usp/_usp.dart @@ -0,0 +1,14 @@ +/// USP Core Layer +/// +/// Provides USP/gRPC integration with the USP Simulator. +library; + +// Re-export from the usp_client_core package +export 'package:usp_client_core/usp_client_core.dart'; + +// PrivacyGUI-specific components (not in package) +export 'usp_connection_provider.dart'; +export 'usp_mapper_repository.dart'; + +// Legacy - to be removed once fully migrated +export 'jnap_tr181_mapper.dart'; diff --git a/lib/core/usp/capabilities/capability_registry.dart b/lib/core/usp/capabilities/capability_registry.dart new file mode 100644 index 000000000..b985f59cb --- /dev/null +++ b/lib/core/usp/capabilities/capability_registry.dart @@ -0,0 +1,56 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'models/device_feature.dart'; +import 'repositories/capability_repository.dart'; + +/// Riverpod provider for CapabilityRepository. +/// +/// This provider must be overridden at app startup with the appropriate +/// repository implementation (UspCapabilityRepository or LocalCapabilityRepository). +/// +/// Example: +/// ```dart +/// runApp( +/// ProviderScope( +/// overrides: [ +/// capabilityRepositoryProvider.overrideWithValue(myRepository), +/// ], +/// child: MyApp(), +/// ), +/// ); +/// ``` +final capabilityRepositoryProvider = Provider((ref) { + throw UnimplementedError( + 'capabilityRepositoryProvider must be overridden at app startup', + ); +}); + +/// Convenience provider for checking feature support. +/// +/// Usage: +/// ```dart +/// final hasWifi = ref.watch(hasFeatureProvider(DeviceFeature.wifi5Hz)); +/// ``` +/// +/// NOTE: When capabilityRepositoryProvider is not overridden (normal/demo mode), +/// this returns `true` for all features (unrestricted mode). +final hasFeatureProvider = Provider.family((ref, feature) { + try { + return ref.watch(capabilityRepositoryProvider).hasFeature(feature); + } catch (_) { + // Repository not overridden - assume all features are available (unrestricted mode) + return true; + } +}); + +/// Convenience provider for checking path support. +/// +/// NOTE: When capabilityRepositoryProvider is not overridden, +/// this returns `true` for all paths (unrestricted mode). +final hasPathProvider = Provider.family((ref, path) { + try { + return ref.watch(capabilityRepositoryProvider).hasPath(path); + } catch (_) { + // Repository not overridden - assume all paths are available + return true; + } +}); diff --git a/lib/core/usp/capabilities/models/device_feature.dart b/lib/core/usp/capabilities/models/device_feature.dart new file mode 100644 index 000000000..0ed4dca6f --- /dev/null +++ b/lib/core/usp/capabilities/models/device_feature.dart @@ -0,0 +1,10 @@ +enum DeviceFeature { + wifi5Hz, + guestNetwork, + parentalControl, + rebootEvent, + firewall, + mesh, + diagnostics, + vpn +} diff --git a/lib/core/usp/capabilities/repositories/capability_repository.dart b/lib/core/usp/capabilities/repositories/capability_repository.dart new file mode 100644 index 000000000..6c19e82c4 --- /dev/null +++ b/lib/core/usp/capabilities/repositories/capability_repository.dart @@ -0,0 +1,12 @@ +import '../models/device_feature.dart'; + +abstract class CapabilityRepository { + /// Initializes capabilities by querying the device or loading local config. + Future initialize(); + + /// Checks if a specific feature is supported. + bool hasFeature(DeviceFeature feature); + + /// Checks if a raw TR-181 path is supported. + bool hasPath(String path); +} diff --git a/lib/core/usp/capabilities/repositories/local_capability_repository.dart b/lib/core/usp/capabilities/repositories/local_capability_repository.dart new file mode 100644 index 000000000..f83857e10 --- /dev/null +++ b/lib/core/usp/capabilities/repositories/local_capability_repository.dart @@ -0,0 +1,41 @@ +import 'package:flutter/foundation.dart'; +import '../models/device_feature.dart'; +import 'capability_repository.dart'; + +class LocalCapabilityRepository implements CapabilityRepository { + final Set _enabledFeatures; + + LocalCapabilityRepository({ + Set? features, + }) : _enabledFeatures = features ?? + { + DeviceFeature.wifi5Hz, + DeviceFeature.guestNetwork, + DeviceFeature.parentalControl, + DeviceFeature.firewall, + DeviceFeature.diagnostics, + // Reboot event not supported in local/mock usually? Or maybe yes. + }; + + @override + Future initialize() async { + // Simulate delay + await Future.delayed(const Duration(milliseconds: 500)); + debugPrint( + '✅ LocalCapabilityRepository: Initialized with ${_enabledFeatures.length} features.'); + } + + @override + bool hasFeature(DeviceFeature feature) { + return _enabledFeatures.contains(feature); + } + + @override + bool hasPath(String path) { + // Mock implementation: return true for standard paths + if (path.startsWith('Device.WiFi.')) return true; + if (path.startsWith('Device.Firewall.')) return true; + if (path.startsWith('Device.DeviceInfo.')) return true; + return false; + } +} diff --git a/lib/core/usp/capabilities/repositories/usp_capability_repository.dart b/lib/core/usp/capabilities/repositories/usp_capability_repository.dart new file mode 100644 index 000000000..93c6a3628 --- /dev/null +++ b/lib/core/usp/capabilities/repositories/usp_capability_repository.dart @@ -0,0 +1,56 @@ +import 'package:flutter/foundation.dart'; +import 'package:usp_client_core/usp_client_core.dart'; +import '../models/device_feature.dart'; +import 'capability_repository.dart'; + +class UspCapabilityRepository implements CapabilityRepository { + // Now we rely on the Core Service from the package + final UspCapabilityService _capabilityService; + + /// Expects a UspCapabilityService instance. + /// We can construct it here or pass it in. passing UspGrpcClientService + /// allows maintaining the same DI signature for now. + UspCapabilityRepository(UspGrpcClientService grpcService) + : _capabilityService = UspCapabilityService(grpcService); + + @override + Future initialize() async { + debugPrint('🔍 CapabilityDiscovery: Delegating to Core Service...'); + await _capabilityService.initialize(); + } + + @override + bool hasFeature(DeviceFeature feature) { + switch (feature) { + case DeviceFeature.wifi5Hz: + return hasPath('Device.WiFi.'); + + case DeviceFeature.guestNetwork: + return hasPath('Device.WiFi.SSID.'); + + case DeviceFeature.parentalControl: + return hasPath('Device.X_LINKSYS_ParentalControl.') || + hasPath('Device.ParentalControl.'); + + case DeviceFeature.rebootEvent: + return hasPath('Device.Boot!'); + + case DeviceFeature.firewall: + return hasPath('Device.Firewall.'); + + case DeviceFeature.mesh: + return hasPath('Device.WiFi.DataElements.'); + + case DeviceFeature.diagnostics: + return hasPath('Device.IP.Diagnostics.'); + + case DeviceFeature.vpn: + return hasPath('Device.X_LINKSYS_VPN.') || hasPath('Device.VPN.'); + } + } + + @override + bool hasPath(String path) { + return _capabilityService.isPathSupported(path); + } +} diff --git a/lib/core/usp/capabilities/repositories/usp_wifi_repository.dart b/lib/core/usp/capabilities/repositories/usp_wifi_repository.dart new file mode 100644 index 000000000..62d05d52d --- /dev/null +++ b/lib/core/usp/capabilities/repositories/usp_wifi_repository.dart @@ -0,0 +1,93 @@ +import 'dart:async'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:usp_client_core/usp_client_core.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; +import 'package:privacy_gui/core/usp/providers/polling_manager_provider.dart'; +import '../../usp_mapper_repository.dart'; +import '../capability_registry.dart'; +import '../models/device_feature.dart'; + +// Defines the data structure for the WiFi bundle +class WifiDataBundle { + final Map radioInfo; + final Map guestRadioSettings; + final Map macFilterSettings; + + WifiDataBundle({ + required this.radioInfo, + required this.guestRadioSettings, + required this.macFilterSettings, + }); +} + +/// A generic interface allows swapping implementations (JNAP vs USP) +abstract class WifiRepository { + Stream watchWifiBundle(); +} + +class UspWifiRepository implements WifiRepository { + final UspWifiService _wifiService; + final PollingManager _pollingManager; + final bool Function(DeviceFeature) _hasFeature; + + late final ResourceWatcher _watcher; + + UspWifiRepository({ + required UspWifiService wifiService, + required PollingManager pollingManager, + required bool Function(DeviceFeature) hasFeature, + }) : _wifiService = wifiService, + _pollingManager = pollingManager, + _hasFeature = hasFeature { + _watcher = ResourceWatcher( + path: 'Device.WiFi.', + pollingManager: _pollingManager, + strategy: WatchStrategy.eventPreferred, + checkEventSupport: () => _hasFeature(DeviceFeature.rebootEvent), + fetchData: _fetchBundle, + ensureSubscription: _subscribeToWifi, + ); + } + + @override + Stream watchWifiBundle() { + return _watcher.start(); + } + + Future _fetchBundle() async { + final radioInfo = await _wifiService.getRadioInfo(); + final guestInfo = await _wifiService.getGuestRadioSettings(); + final macFilter = await _wifiService.getMACFilterSettings(); + + return WifiDataBundle( + radioInfo: radioInfo, + guestRadioSettings: guestInfo, + macFilterSettings: macFilter, + ); + } + + Future _subscribeToWifi() async { + throw UnimplementedError('Subscription not supported yet'); + } + + void dispose() { + _watcher.stop(); + } +} + +final uspWifiRepositoryProvider = Provider((ref) { + final repo = ref.watch(routerRepositoryProvider); + + if (repo is! UspMapperRepository) { + throw UnimplementedError('UspWifiRepository requires UspMapperRepository'); + } + + final pollingManager = ref.watch(pollingManagerProvider); + final capabilityRepo = ref.watch(capabilityRepositoryProvider); + + return UspWifiRepository( + wifiService: repo.wifiService, + pollingManager: pollingManager, + hasFeature: capabilityRepo.hasFeature, + ); +}); diff --git a/lib/core/usp/jnap_tr181_mapper.dart b/lib/core/usp/jnap_tr181_mapper.dart new file mode 100644 index 000000000..7ec3cf2a4 --- /dev/null +++ b/lib/core/usp/jnap_tr181_mapper.dart @@ -0,0 +1,1183 @@ +/// JNAP to TR-181 Mapper +/// +/// Maps JNAP Actions to TR-181 paths and converts responses. +library; + +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; +import 'package:privacy_gui/core/utils/logger.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// Mapper for converting between JNAP Actions and TR-181 paths. +/// +/// This mapper supports: +/// - Converting JNAP Actions to TR-181 path patterns +/// - Converting USP GetResponse to JNAP-style output maps +class JnapTr181Mapper { + /// Maps JNAP Action names to their corresponding TR-181 paths. + /// + /// Note: Only include actions here if toJnapResponse has a proper handler. + /// Actions without handlers should use fallback responses instead. + /// + /// GetDeviceInfo is NOT included here intentionally - the app needs the + /// full services list (150+ items) from demo_cache_data.json for + /// buildBetterActions() to work properly. + /// + /// Use BASE NAMES only (no version suffix). The lookup logic will strip + /// version suffixes automatically, e.g., GetRadioInfo3 → GetRadioInfo. + static const _actionPathMappings = >{ + // Device Info - services list handled by mock serviceHelper + 'GetDeviceInfo': ['Device.DeviceInfo.'], + + // WiFi - Radio + 'GetRadioInfo': [ + 'Device.WiFi.', // For RadioNumberOfEntries + 'Device.WiFi.Radio.', + 'Device.WiFi.SSID.', + 'Device.WiFi.AccessPoint.', + ], + + // Connected Devices - Clients + Mesh Nodes + 'GetDevices': [ + 'Device.Hosts.', // For HostNumberOfEntries + 'Device.Hosts.Host.', // Client devices + 'Device.WiFi.MultiAP.', // For APDeviceNumberOfEntries + 'Device.WiFi.MultiAP.APDevice.', // Mesh nodes + ], + + // Backhaul Info for mesh nodes + 'GetBackhaulInfo': [ + 'Device.WiFi.MultiAP.', + 'Device.WiFi.MultiAP.APDevice.', + ], + + // Network - WAN Status + 'GetWANStatus': [ + 'Device.IP.Interface.1.', + 'Device.Ethernet.Interface.1.', + 'Device.DHCPv4.Client.', + 'Device.Routing.Router.1.', + ], + + // Diagnostics + 'GetSystemStats': ['Device.DeviceInfo.'], + 'GetInternetConnectionStatus': ['Device.IP.Interface.1.'], + 'GetEthernetPortConnections': ['Device.Ethernet.Interface.'], + 'GetNetworkConnections': [ + 'Device.Hosts.Host.', + 'Device.WiFi.Radio.', // To map Frequency Band + 'Device.WiFi.AccessPoint.', // To map AssociatedDevice + 'Device.WiFi.SSID.', // To map BSSID (MACAddress) + ], + 'GetNodesWirelessNetworkConnections': [ + 'Device.WiFi.MultiAP.APDevice.', // To find Master Node ID + 'Device.WiFi.AccessPoint.', // To get AssociatedDevice connections + 'Device.WiFi.SSID.', // To map BSSID (MACAddress) + ], + + // LAN Settings - WORKS! + 'GetLANSettings': ['Device.IP.Interface.2.', 'Device.DHCPv4.'], + + // Time + 'GetLocalTime': ['Device.Time.'], + 'GetTimeSettings': ['Device.Time.'], + + // Guest Network + 'GetGuestRadioSettings': [ + 'Device.WiFi.', // For AccessPointNumberOfEntries + 'Device.WiFi.AccessPoint.', + 'Device.WiFi.SSID.', + 'Device.WiFi.Radio.', + ], + + // MAC Filtering + 'GetMACFilterSettings': [ + 'Device.WiFi.AccessPoint.', + ], + }; + + /// Gets the TR-181 paths for a given JNAP Action. + /// + /// Strips version suffix from action name to match base mapping. + /// e.g., GetRadioInfo3 → GetRadioInfo + List getTr181Paths(JNAPAction action) { + final actionName = _extractActionName(action.actionValue); + + // Try exact match first + var paths = _actionPathMappings[actionName]; + if (paths != null) return paths; + + // Try base name without version suffix + final baseName = _stripVersionSuffix(actionName); + if (baseName != actionName) { + paths = _actionPathMappings[baseName]; + if (paths != null) return paths; + } + + return []; + } + + /// Strips version suffix from action name. + /// e.g., "GetRadioInfo3" → "GetRadioInfo" + String _stripVersionSuffix(String actionName) { + final regex = RegExp(r'[2-9]+$'); + return actionName.replaceFirst(regex, ''); + } + + /// Extracts the action name from a JNAP action URL. + /// + /// Example: 'http://linksys.com/jnap/core/GetDeviceInfo' → 'GetDeviceInfo' + String _extractActionName(String actionUrl) { + return actionUrl.split('/').last; + } + + /// Converts a USP GetResponse to a JNAP-style output map. + /// + /// The conversion is based on the original JNAP Action type. + Map toJnapResponse( + JNAPAction action, + UspGetResponse response, + ) { + final actionName = _extractActionName(action.actionValue); + // Use base name for switch matching (strips version suffix) + final baseName = _stripVersionSuffix(actionName); + + logger.d('🔄 Mapping USP response for $actionName (base: $baseName)'); + logger.d(' Raw USP values: ${response.results.length} items'); + + // Print raw TR-181 data received + final buffer = StringBuffer(); + buffer.writeln(' 📦 Raw TR-181 Data:'); + for (final entry in response.results.entries) { + buffer.writeln(' ${entry.key.fullPath} = ${entry.value.value}'); + } + logger.d(buffer.toString()); + + Map result; + switch (baseName) { + case 'GetDeviceInfo': + result = _mapDeviceInfo(response); + break; + case 'GetRadioInfo': + result = _mapRadioInfo(response); + break; + case 'GetDevices': + result = _mapDevices(response); + break; + case 'GetBackhaulInfo': + result = _mapBackhaulInfo(response); + break; + case 'GetWANStatus': + case 'GetWANSettings': + result = _mapWANStatus(response); + break; + case 'GetLANSettings': + result = _mapLANSettings(response); + break; + case 'GetLocalTime': + case 'GetTimeSettings': + result = _mapTimeSettings(response); + break; + case 'GetGuestRadioSettings': + result = _mapGuestRadioSettings(response); + break; + case 'GetMACFilterSettings': + result = _mapMACFilterSettings(response); + break; + case 'GetSystemStats': + result = _mapSystemStats(response); + break; + case 'GetInternetConnectionStatus': + result = _mapInternetConnectionStatus(response); + break; + case 'GetEthernetPortConnections': + result = _mapEthernetPortConnections(response); + break; + case 'GetNetworkConnections': + result = _mapNetworkConnections2(response); + break; + case 'GetNodesWirelessNetworkConnections': + result = _mapNodesWirelessNetworkConnections2(response); + break; + default: + logger.w('⚠️ No mapper for action: $baseName'); + result = _mapGeneric(response); + } + + // Print mapped JNAP output + logger.d(' ✅ Mapped JNAP output: $result'); + return result; + } + + /// Maps Device.DeviceInfo.* to JNAP GetDeviceInfo response format. + /// + /// Required fields for NodeDeviceInfo: + /// - modelNumber, firmwareVersion, description, firmwareDate + /// - manufacturer, serialNumber, hardwareVersion, services (List) + Map _mapDeviceInfo(UspGetResponse response) { + final values = _flattenResults(response); + return { + 'manufacturer': + values['Device.DeviceInfo.Manufacturer']?.toString() ?? 'Unknown', + 'modelNumber': + values['Device.DeviceInfo.ModelName']?.toString() ?? 'Unknown', + 'serialNumber': + values['Device.DeviceInfo.SerialNumber']?.toString() ?? 'Unknown', + 'hardwareVersion': + values['Device.DeviceInfo.HardwareVersion']?.toString() ?? 'v1.0', + 'firmwareVersion': + values['Device.DeviceInfo.SoftwareVersion']?.toString() ?? '1.0.0', + 'firmwareDate': '', // TR-181 doesn't have this, use empty string + 'description': values['Device.DeviceInfo.Description']?.toString() ?? '', + 'services': _supportedServices, + }; + } + + /// List of JNAP services supported by this mapper. + /// This is used to populate the 'services' field in GetDeviceInfo. + /// NOTE: These must be SERVICE URLs (e.g. .../core/Core), NOT Action URLs. + static const _supportedServices = [ + 'http://linksys.com/jnap/core/Core', + 'http://linksys.com/jnap/wirelessap/WirelessAP', + 'http://linksys.com/jnap/wirelessap/WirelessAP2', + 'http://linksys.com/jnap/wirelessap/WirelessAP3', + 'http://linksys.com/jnap/devicelist/DeviceList', + 'http://linksys.com/jnap/devicelist/DeviceList2', + 'http://linksys.com/jnap/nodes/diagnostics/Diagnostics', + 'http://linksys.com/jnap/nodes/diagnostics/Diagnostics2', + 'http://linksys.com/jnap/router/Router', + 'http://linksys.com/jnap/router/Router2', + 'http://linksys.com/jnap/router/Router3', + 'http://linksys.com/jnap/locale/Locale', + 'http://linksys.com/jnap/guestnetwork/GuestNetwork', + 'http://linksys.com/jnap/guestnetwork/GuestNetwork2', + 'http://linksys.com/jnap/macfilter/MACFilter', + 'http://linksys.com/jnap/macfilter/MACFilter2', + 'http://linksys.com/jnap/networkconnections/NetworkConnections', + 'http://linksys.com/jnap/networkconnections/NetworkConnections2', + ]; + + /// Maps Device.WiFi.* to JNAP GetRadioInfo response format. + /// + /// Uses TR-181 standard fields: + /// - PossibleChannels: "1,2,3,4,5,6,7,8,9,10,11,12,13" + /// - SupportedStandards: "b,g,n,ax" + /// - OperatingChannelBandwidth: "Auto", "20MHz", "40MHz", etc. + /// - Security.ModesSupported: "None,WPA2-Personal,WPA3-Personal" + Map _mapRadioInfo(UspGetResponse response) { + final values = _flattenResults(response); + + // Group by radio instances + final radios = >[]; + final radioCount = int.tryParse( + values['Device.WiFi.RadioNumberOfEntries']?.toString() ?? '0') ?? + 0; + + for (int i = 1; i <= radioCount; i++) { + final prefix = 'Device.WiFi.Radio.$i'; + final ssidPrefix = 'Device.WiFi.SSID.$i'; + final apPrefix = 'Device.WiFi.AccessPoint.$i'; + + // Parse PossibleChannels: "1,2,3,4,5,6,7,8,9,10,11,12,13" -> [1,2,...] + final possibleChannelsStr = + values['$prefix.PossibleChannels']?.toString() ?? ''; + final possibleChannels = possibleChannelsStr.isNotEmpty + ? possibleChannelsStr + .split(',') + .map((c) => int.tryParse(c.trim()) ?? 0) + .toList() + : []; + + // Parse SupportedStandards: "b,g,n,ax" -> ["802.11bg", "802.11bgn", ...] + final supportedStandardsStr = + values['$prefix.SupportedStandards']?.toString() ?? 'n,ax'; + final supportedModes = _convertToJnapModes(supportedStandardsStr); + + // Get current operating mode + final operatingStandards = + values['$prefix.OperatingStandards']?.toString() ?? 'ax'; + final currentMode = _convertToJnapMode(operatingStandards); + + // Get channel width + final channelWidth = + values['$prefix.OperatingChannelBandwidth']?.toString() ?? 'Auto'; + + // Build supportedChannelsForChannelWidths based on SupportedStandards + final supportedChannelsForWidths = _buildSupportedChannelsForWidths( + supportedStandardsStr, possibleChannels); + + // Parse Security.ModesSupported + final securityModesStr = + values['$apPrefix.Security.ModesSupported']?.toString() ?? ''; + final supportedSecurityTypes = securityModesStr.isNotEmpty + ? securityModesStr.split(',').map((s) => s.trim()).toList() + : ['None', 'WPA2-Personal', 'WPA3-Personal']; + + // Get radio ID and band from TR-181 Name field + final radioName = values['$prefix.Name']?.toString() ?? 'RADIO_$i'; + final band = values['$prefix.OperatingFrequencyBand']?.toString() ?? ''; + + radios.add({ + 'radioID': radioName, + 'physicalRadioID': 'ath${i - 1}0', + 'bssid': values['$ssidPrefix.MACAddress']?.toString() ?? '', + 'band': band, + 'supportedModes': supportedModes, + 'defaultMixedMode': + supportedModes.isNotEmpty ? supportedModes.last : currentMode, + 'supportedChannelsForChannelWidths': supportedChannelsForWidths, + 'supportedSecurityTypes': supportedSecurityTypes, + 'maxRADIUSSharedKeyLength': 64, + 'settings': { + 'isEnabled': values['$prefix.Enable']?.toString() == 'true', + 'mode': currentMode, + 'ssid': values['$ssidPrefix.SSID']?.toString() ?? '', + 'broadcastSSID': + values['$apPrefix.SSIDAdvertisementEnabled']?.toString() == + 'true', + 'channelWidth': channelWidth, + 'channel': + int.tryParse(values['$prefix.Channel']?.toString() ?? '0') ?? 0, + 'security': values['$apPrefix.Security.ModeEnabled']?.toString() ?? + 'WPA2-Personal', + 'wpaPersonalSettings': { + 'passphrase': + values['$apPrefix.Security.KeyPassphrase']?.toString() ?? '', + }, + }, + }); + } + + return { + 'isBandSteeringSupported': false, // Demo default + 'radios': radios, + }; + } + + /// Convert TR-181 standards string to JNAP mode list. + /// e.g., "b,g,n,ax" -> ["802.11bg", "802.11bgn", "802.11bgnax"] + List _convertToJnapModes(String standardsStr) { + final standards = standardsStr.split(',').map((s) => s.trim()).toList(); + final modes = []; + + if (standards.contains('b') && standards.contains('g')) { + modes.add('802.11bg'); + } + if (standards.contains('n')) { + modes.add('802.11bgn'); + } + if (standards.contains('ax')) { + modes.add('802.11bgnax'); + } + if (standards.contains('a')) { + modes.add('802.11a'); + } + if (standards.contains('ac')) { + modes.add('802.11anac'); + } + if (standards.contains('be')) { + modes.add('802.11anacaxbe'); + } + + // Always add mixed mode as default + if (modes.isNotEmpty) { + modes.add('802.11mixed'); + } + + return modes.isEmpty ? ['802.11mixed'] : modes; + } + + /// Convert single TR-181 standard to JNAP mode. + String _convertToJnapMode(String standard) { + switch (standard) { + case 'ax': + return '802.11ax'; + case 'ac': + return '802.11ac'; + case 'n': + return '802.11n'; + case 'be': + return '802.11be'; + default: + return '802.11mixed'; + } + } + + /// Build supportedChannelsForChannelWidths from standards. + List> _buildSupportedChannelsForWidths( + String standardsStr, List channels) { + final result = >[]; + + // Auto width always included + result.add({ + 'channelWidth': 'Auto', + 'channels': [0, ...channels], + }); + + // Standard (20MHz) always available + result.add({ + 'channelWidth': 'Standard', + 'channels': [0, ...channels], + }); + + // Wide (40MHz) if n/ac/ax/be + if (standardsStr.contains('n') || + standardsStr.contains('ac') || + standardsStr.contains('ax') || + standardsStr.contains('be')) { + result.add({ + 'channelWidth': 'Wide', + 'channels': [0, ...channels], + }); + } + + return result; + } + + /// Maps Device.Hosts.Host.* + Device.WiFi.MultiAP.APDevice.* to GetDevices. + /// + /// Combines client devices from Hosts and mesh nodes from MultiAP. + Map _mapDevices(UspGetResponse response) { + final values = _flattenResults(response); + final devices = >[]; + + // 1. Add client devices from Hosts + final hostCount = int.tryParse( + values['Device.Hosts.HostNumberOfEntries']?.toString() ?? '0') ?? + 0; + + // Find Master Node ID for parentDeviceID linkage + String masterNodeId = ''; + final multiApCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + for (int i = 1; i <= multiApCount; i++) { + if (values['Device.WiFi.MultiAP.APDevice.$i.BackhaulLinkType'] + ?.toString() == + 'None') { + masterNodeId = + values['Device.WiFi.MultiAP.APDevice.$i.MACAddress']?.toString() ?? + ''; + break; + } + } + if (masterNodeId.isEmpty) masterNodeId = '00:00:00:00:00:00'; // Fallback + + for (int i = 1; i <= hostCount; i++) { + final prefix = 'Device.Hosts.Host.$i'; + final mac = values['$prefix.PhysAddress']?.toString() ?? ''; + final ifType = values['$prefix.InterfaceType']?.toString() ?? ''; + final layer1 = values['$prefix.Layer1Interface']?.toString() ?? ''; + + String? band; + if (ifType == '802.11') { + // Derive band from SSID reference: Device.WiFi.SSID.1 -> 2.4GHz + final ssidIndex = int.tryParse(layer1.split('.').lastOrNull ?? '') ?? 0; + if (ssidIndex == 1 || ssidIndex == 4) band = '2.4GHz'; + if (ssidIndex == 2) band = '5GHz'; + if (ssidIndex == 3) band = '6GHz'; + } + + devices.add({ + 'deviceID': mac.isNotEmpty ? mac : 'unknown-$i', + 'properties': >[], + 'unit': {}, + 'maxAllowedProperties': 10, + 'model': {'deviceType': 'Computer'}, + 'lastChangeRevision': 0, + 'knownMACAddresses': [mac], + 'knownInterfaces': [ + { + 'macAddress': mac, + 'interfaceType': ifType == '802.11' ? 'Wireless' : 'Wired', + if (band != null) 'band': band, + } + ], + 'friendlyName': values['$prefix.HostName']?.toString() ?? 'Device $i', + 'isAuthority': false, + 'connections': [ + { + 'macAddress': mac, + 'ipAddress': values['$prefix.IPAddress']?.toString() ?? '', + 'isOnline': values['$prefix.Active']?.toString() == 'true', + 'interfaceType': ifType == '802.11' ? 'Wireless' : 'Wired', + 'parentDeviceID': masterNodeId, + } + ], + }); + } + + // 2. Add mesh nodes from MultiAP.APDevice + final apCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apCount; i++) { + final prefix = 'Device.WiFi.MultiAP.APDevice.$i'; + // Use MACAddress as device ID since ID/ALID are not available + final mac = values['$prefix.MACAddress']?.toString() ?? ''; + final backhaulType = + values['$prefix.BackhaulLinkType']?.toString() ?? 'None'; + final isMaster = backhaulType == 'None'; // Master has no backhaul + final serial = values['$prefix.SerialNumber']?.toString() ?? ''; + final parentMac = values['$prefix.BackhaulMACAddress']?.toString() ?? ''; + + devices.add({ + 'deviceID': mac, + 'properties': >[], + 'unit': {'serialNumber': serial}, + 'maxAllowedProperties': 10, + 'model': { + 'deviceType': 'Infrastructure', + 'manufacturer': values['$prefix.Manufacturer']?.toString(), + 'modelNumber': isMaster ? 'Master' : 'Slave', + }, + 'lastChangeRevision': 0, + 'knownMACAddresses': [mac], + 'knownInterfaces': [ + { + 'macAddress': mac, + 'interfaceType': + (backhaulType == 'Wi-Fi' || isMaster) ? 'Wireless' : 'Wired', + } + ], + 'friendlyName': isMaster ? 'Master Router' : 'Mesh Node', + 'isAuthority': isMaster, + 'nodeType': isMaster ? 'Master' : 'Slave', + 'connections': [ + { + 'macAddress': mac, + 'ipAddress': + '', // Nodes might have IP but we don't have it easily here + 'isOnline': 'true', // Mesh nodes derived from APDevice are active + 'interfaceType': 'Infrastructure', + 'parentDeviceID': isMaster ? null : parentMac, + } + ], + }); + } + + return {'devices': devices}; + } + + /// Maps Device.WiFi.MultiAP.APDevice.* to GetBackhaulInfo response format. + /// Matches BackHaulInfoData model keys. + Map _mapBackhaulInfo(UspGetResponse response) { + final values = _flattenResults(response); + final backhaulDevices = >[]; + + final apCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apCount; i++) { + final prefix = 'Device.WiFi.MultiAP.APDevice.$i'; + final backhaulType = + values['$prefix.BackhaulLinkType']?.toString() ?? 'None'; + + // Skip master node (no backhaul) + if (backhaulType == 'None') continue; + + final rssi = int.tryParse( + values['$prefix.BackhaulSignalStrength']?.toString() ?? '0') ?? + 0; + + int rssiDbm; + // If RSSI is negative, it's already in dBm. If positive (0-220), it's RCPI. + if (rssi < 0) { + rssiDbm = rssi; + } else { + // Convert RCPI (0-220) to dBm: (RCPI / 2) - 110 + rssiDbm = (rssi / 2).round() - 110; + } + + final mac = values['$prefix.MACAddress']?.toString() ?? ''; + final parentMac = values['$prefix.BackhaulMACAddress']?.toString() ?? ''; + + // Mock IPs for demonstration as standard MultiAP APDevice doesn't expose IP easily + // In real scenario, we might look this up or USP provides it. + final mockIp = '192.168.1.${10 + i}'; + final mockParentIp = '192.168.1.1'; // Assume master is parent + + backhaulDevices.add({ + 'deviceUUID': mac, + 'ipAddress': mockIp, + 'parentIPAddress': mockParentIp, + 'timestamp': DateTime.now().toIso8601String(), + 'connectionType': backhaulType == 'Wi-Fi' ? 'Wireless' : 'Wired', + 'wirelessConnectionInfo': backhaulType == 'Wi-Fi' + ? { + 'radioID': 'RADIO_5GHz', // Mock band + 'channel': 0, + 'apRSSI': rssiDbm, + 'stationRSSI': rssiDbm, + 'apBSSID': parentMac, + 'stationBSSID': mac, + 'txRate': 0, + 'rxRate': 0, + 'isMultiLinkOperation': false, + } + : null, // Must be null for Wired + 'speedMbps': '0', + }); + } + + return {'backhaulDevices': backhaulDevices}; + } + + /// Maps Device.IP.Interface.1.* to JNAP GetWANStatus response format. + /// + /// Provides complete WAN status including supportedWANTypes, wanConnection, etc. + Map _mapWANStatus(UspGetResponse response) { + final values = _flattenResults(response); + + // Get WAN IP info + final ipAddress = + values['Device.IP.Interface.1.IPv4Address.1.IPAddress']?.toString() ?? + ''; + final subnetMask = + values['Device.IP.Interface.1.IPv4Address.1.SubnetMask']?.toString() ?? + ''; + final gateway = + values['Device.Routing.Router.1.IPv4Forwarding.1.GatewayIPAddress'] + ?.toString() ?? + ''; + final macAddress = + values['Device.Ethernet.Interface.1.MACAddress']?.toString() ?? ''; + + // Determine WAN type from AddressingType + final addressingType = + values['Device.IP.Interface.1.IPv4Address.1.AddressingType'] + ?.toString() ?? + 'DHCP'; + final wanType = addressingType == 'Static' ? 'Static' : 'DHCP'; + + // Determine WAN status from interface status + final ifStatus = values['Device.IP.Interface.1.Status']?.toString() ?? 'Up'; + final wanStatus = ifStatus == 'Up' ? 'Connected' : 'Disconnected'; + + return { + 'macAddress': macAddress, + 'detectedWANType': wanType, + 'wanStatus': wanStatus, + 'wanIPv6Status': 'Disconnected', // Simplified for demo + 'isDetectingWANType': false, + 'wanConnection': { + 'wanType': wanType, + 'ipAddress': ipAddress, + 'networkPrefixLength': _subnetMaskToPrefix(subnetMask), + 'gateway': gateway, + 'mtu': 1500, + 'dhcpLeaseMinutes': wanType == 'DHCP' ? 4320 : null, + 'dnsServer1': gateway.isNotEmpty ? gateway : '8.8.8.8', + }, + // Hardcoded supported types for demo + 'supportedWANTypes': [ + 'DHCP', + 'Static', + 'PPPoE', + 'PPTP', + 'L2TP', + 'Bridge' + ], + 'supportedIPv6WANTypes': ['Automatic', 'PPPoE', 'Pass-through'], + 'supportedWANCombinations': [ + {'wanType': 'DHCP', 'wanIPv6Type': 'Automatic'}, + {'wanType': 'Static', 'wanIPv6Type': 'Automatic'}, + {'wanType': 'PPPoE', 'wanIPv6Type': 'Automatic'}, + ], + }; + } + + Map _mapSystemStats(UspGetResponse response) { + final values = _flattenResults(response); + final uptime = + int.tryParse(values['Device.DeviceInfo.UpTime']?.toString() ?? '0') ?? + 0; + final cpuUsage = int.tryParse( + values['Device.DeviceInfo.ProcessStatus.CPUUsage']?.toString() ?? + '0') ?? + 0; + final memTotal = int.tryParse( + values['Device.DeviceInfo.MemoryStatus.Total']?.toString() ?? + '1') ?? + 1; + final memFree = int.tryParse( + values['Device.DeviceInfo.MemoryStatus.Free']?.toString() ?? '0') ?? + 0; + + // Calculate memory load (0.0 - 1.0) + final memLoad = (memTotal > 0) ? (memTotal - memFree) / memTotal : 0.0; + + return { + 'uptimeSeconds': uptime, + 'CPULoad': (cpuUsage / 100).toString(), + 'MemoryLoad': memLoad.toStringAsFixed(2), + }; + } + + Map _mapInternetConnectionStatus(UspGetResponse response) { + final values = _flattenResults(response); + final status = values['Device.IP.Interface.1.Status']?.toString() ?? 'Down'; + + final jnapStatus = + (status == 'Up') ? 'InternetConnected' : 'InternetDisconnected'; + + return { + 'connectionStatus': jnapStatus, + }; + } + + Map _mapEthernetPortConnections(UspGetResponse response) { + final values = _flattenResults(response); + // WAN -> Device.Ethernet.Interface.1 + final wanEnable = + values['Device.Ethernet.Interface.1.Enable']?.toString() == 'true'; + final wanBitRate = + values['Device.Ethernet.Interface.1.MaxBitRate']?.toString() ?? '0'; + + String wanConnection = 'Disconnected'; + if (wanEnable) { + if (wanBitRate == '1000') + wanConnection = '1Gbps'; + else if (wanBitRate == '100') + wanConnection = '100Mbps'; + else if (wanBitRate != '0') wanConnection = '${wanBitRate}Mbps'; + } + + // LAN Ports (Interfaces 3+ in mock data) + final lanList = []; + final interfaceCount = int.tryParse( + values['Device.Ethernet.InterfaceNumberOfEntries']?.toString() ?? + '6') ?? + 6; + + // LAN ports usually start at index 3 (1=WAN, 2=Bridge) + for (int i = 3; i <= interfaceCount; i++) { + // Check if data exists for this index (User might have deleted the entry from JSON) + final enableKey = 'Device.Ethernet.Interface.$i.Enable'; + if (!values.containsKey(enableKey)) { + continue; + } + + final enable = values[enableKey]?.toString() == 'true'; + final bitRate = + values['Device.Ethernet.Interface.$i.MaxBitRate']?.toString() ?? '0'; + + String status = 'Disconnected'; + if (enable) { + if (bitRate == '1000') + status = '1Gbps'; + else if (bitRate == '100') + status = '100Mbps'; + else if (bitRate == '10') + status = '10Mbps'; + else if (bitRate != '0') status = '${bitRate}Mbps'; + } + lanList.add(status); + } + + return { + 'wanPortConnection': wanConnection, + 'lanPortConnections': lanList, + }; + } + + /// Convert subnet mask to prefix length. + /// e.g., "255.255.255.0" -> 24 + int _subnetMaskToPrefix(String subnetMask) { + if (subnetMask.isEmpty) return 24; + try { + final parts = subnetMask.split('.'); + if (parts.length != 4) return 24; + int prefix = 0; + for (final part in parts) { + int octet = int.parse(part); + while (octet > 0) { + prefix += octet & 1; + octet >>= 1; + } + } + return prefix; + } catch (_) { + return 24; + } + } + + /// Maps Device.IP.Interface.2.* and Device.DHCPv4.* to JNAP GetLANSettings. + Map _mapLANSettings(UspGetResponse response) { + final values = _flattenResults(response); + final ipAddress = values['Device.IP.Interface.2.IPv4Address.1.IPAddress'] ?? + '192.168.1.1'; + final subnetMask = + values['Device.IP.Interface.2.IPv4Address.1.SubnetMask'] ?? + '255.255.255.0'; + final isDhcpEnabled = + (values['Device.DHCPv4.Server.Enable']?.toString() ?? 'true') == 'true'; + final hostName = values['Device.DeviceInfo.HostName'] ?? ''; + + return { + 'ipAddress': ipAddress, + // 'subnetMask': subnetMask, // Not directly in top-level model, handled by networkPrefixLength + 'minNetworkPrefixLength': 16, + 'maxNetworkPrefixLength': 30, + 'networkPrefixLength': _subnetMaskToPrefix(subnetMask), + 'minAllowedDHCPLeaseMinutes': 1, + 'hostName': hostName, + 'maxDHCPReservationDescriptionLength': 63, + 'isDHCPEnabled': isDhcpEnabled, + 'maxAllowedDHCPLeaseMinutes': 525600, + 'dhcpSettings': { + // 'isEnabled': isDhcpEnabled, // Not in DHCPSettings model, it's boolean in JNAP + 'lastClientIPAddress': + values['Device.DHCPv4.Server.Pool.1.MinAddress'] ?? + '192.168.1.2', // Usually .100+ + 'firstClientIPAddress': + values['Device.DHCPv4.Server.Pool.1.MinAddress'] ?? '192.168.1.100', + 'leaseMinutes': 1440, + 'reservations': [], + // Optional DNS + 'dnsServer1': '8.8.8.8', + 'dnsServer2': '8.8.4.4', + }, + }; + } + + /// Maps Device.Time.* to JNAP GetLocalTime/GetTimeSettings. + Map _mapTimeSettings(UspGetResponse response) { + final values = _flattenResults(response); + final currentTime = values['Device.Time.CurrentLocalTime']?.toString() ?? + DateTime.now().toIso8601String(); + + return { + 'currentTime': currentTime, + 'timeZoneID': values['Device.Time.LocalTimeZone'] ?? 'Asia/Taipei', + 'ntpServer1': values['Device.Time.NTPServer1'] ?? '', + 'ntpServer2': values['Device.Time.NTPServer2'] ?? '', + 'isDaylightSaving': + false, // No easy way to tell from TR-181 without parse + // Fields requested by various Time actions + 'dstSetting': 'Auto', + 'autoAdjustDST': true, + }; + } + + /// Maps Device.WiFi.AccessPoint.* to GetGuestRadioSettings. + /// Looks for AccessPoints where IsolationEnable is true. + Map _mapGuestRadioSettings(UspGetResponse response) { + final values = _flattenResults(response); + final guestRadios = >[]; + + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + // We assume 2.4GHz and 5GHz for guest radios if not explicitly mapped + // But here we iterate APs to find guest ones + bool isGuestEnabled = false; + + // Pre-scan to check global guest enabled status (if any guest AP is enabled) + for (int i = 1; i <= apCount; i++) { + if (values['Device.WiFi.AccessPoint.$i.IsolationEnable']?.toString() == + 'true') { + if (values['Device.WiFi.AccessPoint.$i.Enable']?.toString() == 'true') { + isGuestEnabled = true; + } + } + } + + // Now build radio info list + // In a real device, we'd map physical radios to guest APs. + // For this simulation, we'll scan APs, find the guest one, + // and assume it effectively represents the guest settings for that band. + for (int i = 1; i <= apCount; i++) { + final prefix = 'Device.WiFi.AccessPoint.$i'; + final isIsolation = + values['$prefix.IsolationEnable']?.toString() == 'true'; + + if (!isIsolation) continue; + + // Resolve SSID name/enable from reference would require complex lookups + // or we just trust the flat list has the SSID object data if we asked for it. + // Since we requested Device.WiFi.SSID., we can try to key match if we knew ID. + // But typically GetGuestRadioSettings just wants the config. + + // We'll create a GuestRadioInfo entry for this AP + guestRadios.add({ + // Guess radio ID based on index or just hardcode for demo completeness + 'radioID': (i % 2 == 0) ? 'RADIO_5GHz' : 'RADIO_2.4GHz', + 'isEnabled': values['$prefix.Enable']?.toString() == 'true', + 'broadcastGuestSSID': + values['$prefix.SSIDAdvertisementEnabled']?.toString() == 'true', + 'guestSSID': 'DartSim_Guest', // Placeholder or need cross-ref + 'guestPassword': + values['$prefix.Security.KeyPassphrase']?.toString() ?? '', + 'canEnableRadio': true, + }); + } + + return { + 'isGuestNetworkEnabled': isGuestEnabled, + 'isGuestNetworkACaptivePortal': false, + 'radios': guestRadios, + 'maxSimultaneousGuests': 50, + }; + } + + /// Maps Device.WiFi.AccessPoint.* to GetMACFilterSettings. + Map _mapMACFilterSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // Usually MAC filtering is global or per-AP. JNAP usually sets it globally or + // for the main APs. We'll look at the first AP as the "master" switch. + final prefix = 'Device.WiFi.AccessPoint.1'; + + final isEnabled = + values['$prefix.MACAddressControlEnabled']?.toString() == 'true'; + final macListStr = values['$prefix.AllowedMACAddress']?.toString() ?? ''; + + // TR-181 AllowedMACAddress is CSV + final macList = macListStr.isNotEmpty + ? macListStr.split(',').map((s) => s.trim()).toList() + : []; + + return { + 'macFilterMode': isEnabled ? 'Allow' : 'Deny', // Simple mapping + 'isEnabled': isEnabled, + 'macAddresses': macList, + 'maxMACAddresses': 32, + }; + } + + /// Maps Device.Hosts.Host.* combined with Device.WiFi.AccessPoint.{i}.AssociatedDevice.* to JNAP GetNetworkConnections2. + Map _mapNetworkConnections2(UspGetResponse response) { + final values = _flattenResults(response); + final connections = >[]; + + // 1. Parse Hosts + final hostCount = int.tryParse( + values['Device.Hosts.HostNumberOfEntries']?.toString() ?? '0') ?? + 0; + + // Helper to find Associated Device for a MAC + // Returns {downlinkRate, signalStrength, apIndex} or null + Map? findWirelessInfo(String mac) { + // Iterate APs to find this MAC in AssociatedDevice table + // Note: In real world, we'd know which AP. Here we scan due to flat structure. + // We don't have AP count easily available unless we parse AccessPointNumberOfEntries which we requested. + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '4') ?? + 4; + + for (int i = 1; i <= apCount; i++) { + final assocCount = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDeviceNumberOfEntries'] + ?.toString() ?? + '0') ?? + 0; + for (int j = 1; j <= assocCount; j++) { + final assocMac = values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.MACAddress']; + if (assocMac?.toString().toLowerCase() == mac.toLowerCase()) { + return { + 'downlinkRate': values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.LastDataDownlinkRate'], + 'signalStrength': values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.SignalStrength'], + 'apIndex': i, + 'bssid': values[ + 'Device.WiFi.AccessPoint.$i.SSIDReference'], // Usually mapped via SSIDRef -> SSID -> Radio + }; + } + } + } + return null; + } + + for (int i = 1; i <= hostCount; i++) { + final mac = values['Device.Hosts.Host.$i.PhysAddress']?.toString() ?? ''; + final ip = values['Device.Hosts.Host.$i.IPAddress']?.toString() ?? ''; + final interfaceType = + values['Device.Hosts.Host.$i.InterfaceType']?.toString() ?? ''; + + // Skip invalid entries + if (mac.isEmpty) continue; + + final connection = { + 'macAddress': mac, + 'ipAddress': ip, + }; + + if (interfaceType == '802.11' || interfaceType == 'Wireless') { + final wirelessInfo = findWirelessInfo(mac); + if (wirelessInfo != null) { + // Found detailed stats + final rateKbps = + int.tryParse(wirelessInfo['downlinkRate']?.toString() ?? '0') ?? + 0; + connection['negotiatedMbps'] = + (rateKbps / 1000).round(); // Kbps to Mbps + + final apIndex = wirelessInfo['apIndex']; + // Determine Band from Radio (Simplified: AP 1=2.4G, 2=5G, 3=6G based on mock) + // Real lookup: AP -> SSID -> LowerLayers (Radio) -> OperatingFrequencyBand + // We'll use a heuristic based on AP Index matching typical mock setup + String band = '5GHz'; + if (apIndex == 1) band = '2.4GHz'; + if (apIndex == 3) band = '6GHz'; + + connection['wireless'] = { + 'signalDecibels': int.tryParse( + wirelessInfo['signalStrength']?.toString() ?? '-50') ?? + -50, + 'band': band, + 'bssid': values['Device.WiFi.SSID.$apIndex.MACAddress'] ?? + '00:00:00:00:00:A$apIndex', // Use SSID MAC as BSSID + 'isGuest': apIndex == 4, // Guest AP is usually index 4 in mock + 'radioID': 'RADIO_$band', + 'txRate': rateKbps, + 'rxRate': rateKbps, + 'isMLOCapable': false, + }; + } else { + // Wireless host but no active association found (offline?) + // Provide basic minimal data + connection['negotiatedMbps'] = 0; + connection['wireless'] = { + 'signalDecibels': -99, + 'band': '2.4GHz', // Default + 'isGuest': false + }; + } + } else { + // Wired + connection['negotiatedMbps'] = 1000; // Assume Gigabit for wired + } + connections.add(connection); + } + + return { + 'connections': connections, + }; + } + + /// Maps Device.WiFi.AccessPoint.{i}.AssociatedDevice.* to JNAP GetNodesWirelessNetworkConnections2. + /// Aggregates all local clients under the Master Node ID. + Map _mapNodesWirelessNetworkConnections2( + UspGetResponse response) { + final values = _flattenResults(response); + final nodeWirelessConnections = >[]; + + // 1. Find Master Node ID (BackhaulLinkType == None) + String masterNodeId = ''; + final apDeviceCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apDeviceCount; i++) { + final backhaulType = + values['Device.WiFi.MultiAP.APDevice.$i.BackhaulLinkType'] + ?.toString(); + if (backhaulType == 'None') { + masterNodeId = + values['Device.WiFi.MultiAP.APDevice.$i.MACAddress']?.toString() ?? + ''; + break; + } + } + + if (masterNodeId.isEmpty) { + // Fallback if no master found in MultiAP table + masterNodeId = '00:00:00:00:00:00'; + } + + // 2. Aggregate all local Associated Devices + final connections = >[]; + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '4') ?? + 4; + + for (int i = 1; i <= apCount; i++) { + final assocCount = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDeviceNumberOfEntries'] + ?.toString() ?? + '0') ?? + 0; + + for (int j = 1; j <= assocCount; j++) { + final mac = + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.MACAddress']; + if (mac != null) { + final rateKbps = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.LastDataDownlinkRate'] + ?.toString() ?? + '0') ?? + 0; + final signal = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.SignalStrength'] + ?.toString() ?? + '-50') ?? + -50; + + // Determine Band from AP Index (Heuristic) + String band = '5GHz'; + if (i == 1) band = '2.4GHz'; + if (i == 3) band = '6GHz'; + + connections.add({ + 'macAddress': mac, + 'negotiatedMbps': (rateKbps / 1000).round(), + 'timestamp': DateTime.now().toIso8601String(), // Mock timestamp + 'wireless': { + 'bssid': values['Device.WiFi.SSID.$i.MACAddress'] ?? + '00:00:00:00:00:00', // Use SSID MAC as BSSID + 'isGuest': i == 4, + 'radioID': 'RADIO_$band', + 'band': band, + 'signalDecibels': signal, + 'txRate': rateKbps, // Raw value + 'rxRate': rateKbps, // Mock as symmetric + 'isMLOCapable': false, + } + }); + } + } + } + + // 3. Construct response structure + if (connections.isNotEmpty) { + nodeWirelessConnections.add({ + 'deviceID': masterNodeId, + 'connections': connections, + }); + } + + final result = { + 'nodeWirelessConnections': nodeWirelessConnections, + }; + logger.d('🚀 _mapNodesWirelessNetworkConnections2 result: $result'); + return result; + } + + /// Generic mapper for unmapped actions. + Map _mapGeneric(UspGetResponse response) { + return _flattenResults(response); + } + + /// Flattens USP results into a simple key-value map. + Map _flattenResults(UspGetResponse response) { + final result = {}; + + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + + return result; + } +} diff --git a/lib/core/usp/providers/polling_manager_provider.dart b/lib/core/usp/providers/polling_manager_provider.dart new file mode 100644 index 000000000..3a6ae24d3 --- /dev/null +++ b/lib/core/usp/providers/polling_manager_provider.dart @@ -0,0 +1,12 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:usp_client_core/usp_client_core.dart'; + +/// Riverpod provider for PollingManager. +/// +/// The App layer controls the lifecycle of the PollingManager. +/// This provider should be accessed to pause/resume polling globally. +final pollingManagerProvider = Provider((ref) { + final manager = PollingManager(); + ref.onDispose(() => manager.dispose()); + return manager; +}); diff --git a/lib/core/usp/usp_connection_provider.dart b/lib/core/usp/usp_connection_provider.dart new file mode 100644 index 000000000..2860ca738 --- /dev/null +++ b/lib/core/usp/usp_connection_provider.dart @@ -0,0 +1,81 @@ +/// USP Connection Providers +/// +/// Riverpod providers for managing USP/gRPC connection state. +library; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:usp_client_core/usp_client_core.dart'; +import 'package:privacy_gui/core/utils/logger.dart'; + +/// Provider for USP connection configuration. +/// +/// This is a StateProvider so that UI can update the config. +final uspConnectionConfigProvider = StateProvider((ref) { + return null; +}); + +/// Provider for the USP gRPC client service. +/// +/// This is a singleton service instance. +final uspGrpcServiceProvider = Provider((ref) { + final service = UspGrpcClientService(); + + // Cleanup on dispose + ref.onDispose(() { + service.disconnect(); + }); + + return service; +}); + +/// Provider for USP connection state. +/// +/// Manages the lifecycle of the gRPC connection. +final uspConnectionStateProvider = + StateNotifierProvider>((ref) { + return UspConnectionNotifier(ref); +}); + +/// Notifier for managing USP connection state. +class UspConnectionNotifier extends StateNotifier> { + final Ref _ref; + + UspConnectionNotifier(this._ref) : super(const AsyncValue.data(false)); + + /// Connects to the USP Agent with the given configuration. + Future connect(UspConnectionConfig config) async { + state = const AsyncValue.loading(); + + try { + final service = _ref.read(uspGrpcServiceProvider); + await service.connect(config); + + // Update the config provider + _ref.read(uspConnectionConfigProvider.notifier).state = config; + + state = const AsyncValue.data(true); + logger.i('🔌 USP: Connection state updated to connected'); + } catch (e, stack) { + state = AsyncValue.error(e, stack); + logger.e('❌ USP: Connection state error: $e'); + } + } + + /// Disconnects from the USP Agent. + Future disconnect() async { + try { + final service = _ref.read(uspGrpcServiceProvider); + await service.disconnect(); + + _ref.read(uspConnectionConfigProvider.notifier).state = null; + + state = const AsyncValue.data(false); + logger.i('🔌 USP: Connection state updated to disconnected'); + } catch (e, stack) { + state = AsyncValue.error(e, stack); + } + } + + /// Whether the client is currently connected. + bool get isConnected => state.valueOrNull ?? false; +} diff --git a/lib/core/usp/usp_mapper_repository.dart b/lib/core/usp/usp_mapper_repository.dart new file mode 100644 index 000000000..8b8f5181b --- /dev/null +++ b/lib/core/usp/usp_mapper_repository.dart @@ -0,0 +1,205 @@ +/// USP Mapper Repository +/// +/// A RouterRepository implementation that uses USP Services. +library; + +import 'dart:async'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; +import 'package:privacy_gui/core/jnap/actions/jnap_transaction.dart'; +import 'package:privacy_gui/core/jnap/command/base_command.dart'; +import 'package:privacy_gui/core/jnap/providers/side_effect_provider.dart'; +import 'package:privacy_gui/core/jnap/result/jnap_result.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; +import 'package:usp_client_core/usp_client_core.dart'; +import 'package:privacy_gui/core/utils/logger.dart'; +import 'package:privacy_gui/demo/jnap/jnap_mock_registry.dart'; + +/// RouterRepository implementation that uses USP Services. +/// +/// This repository: +/// 1. Receives JNAP-style requests +/// 2. Delegates to appropriate USP Service +/// 3. Returns JNAP-formatted responses +class UspMapperRepository extends RouterRepository { + final UspGrpcClientService _grpcService; + + // USP Services + late final UspDeviceService _deviceService; + late final UspWifiService _wifiService; + + UspWifiService get wifiService => _wifiService; + + late final UspNetworkService _networkService; + late final UspTopologyService _topologyService; + late final UspDiagnosticsService _diagnosticsService; + + /// Creates a new [UspMapperRepository]. + UspMapperRepository( + Ref ref, + this._grpcService, + ) : super(ref) { + _deviceService = UspDeviceService(_grpcService); + _wifiService = UspWifiService(_grpcService); + _networkService = UspNetworkService(_grpcService); + _topologyService = UspTopologyService(_grpcService); + _diagnosticsService = UspDiagnosticsService(_grpcService); + } + + @override + Future send( + JNAPAction action, { + Map data = const {}, + Map extraHeaders = const {}, + bool auth = false, + CommandType? type, + bool fetchRemote = false, + CacheLevel? cacheLevel, + int timeoutMs = 10000, + int retries = 1, + SideEffectPollConfig? pollConfig, + }) async { + final actionName = _extractActionName(action.actionValue); + final baseName = _stripVersionSuffix(actionName); + logger.d('🔵 UspMapperRepository.send: $actionName (base: $baseName)'); + + try { + final output = await _dispatchToService(baseName); + if (output != null) { + logger.d('✅ Service response for $actionName'); + return JNAPSuccess(result: 'OK', output: output); + } + + // Fallback to mock registry for unsupported actions + logger.w('⚠️ No Service mapping for: $actionName, using mock'); + return JNAPSuccess( + result: 'OK', + output: JnapMockRegistry.getResponse(action), + ); + } catch (e) { + logger.e('❌ USP request failed: $e'); + // Fallback to mock on error + return JNAPSuccess( + result: 'OK', + output: JnapMockRegistry.getResponse(action), + ); + } + } + + /// Dispatches to the appropriate Service based on action name. + Future?> _dispatchToService(String baseName) async { + switch (baseName) { + // Device Service + case 'GetDeviceInfo': + return _deviceService.getDeviceInfo(); + case 'GetSystemStats': + return _deviceService.getSystemStats(); + + // WiFi Service + case 'GetRadioInfo': + return _wifiService.getRadioInfo(); + case 'GetGuestRadioSettings': + return _wifiService.getGuestRadioSettings(); + case 'GetMACFilterSettings': + return _wifiService.getMACFilterSettings(); + + // Network Service + case 'GetWANStatus': + case 'GetWANSettings': + return _networkService.getWANStatus(); + case 'GetLANSettings': + return _networkService.getLANSettings(); + case 'GetLocalTime': + case 'GetTimeSettings': + return _networkService.getTimeSettings(); + + // Topology Service + case 'GetDevices': + return _topologyService.getDevices(); + case 'GetBackhaulInfo': + return _topologyService.getBackhaulInfo(); + case 'GetNetworkConnections': + return _topologyService.getNetworkConnections(); + case 'GetNodesWirelessNetworkConnections': + return _topologyService.getNodesWirelessNetworkConnections(); + + // Diagnostics Service + case 'GetInternetConnectionStatus': + return _diagnosticsService.getInternetConnectionStatus(); + case 'GetEthernetPortConnections': + return _diagnosticsService.getEthernetPortConnections(); + + default: + return null; // Not handled by services + } + } + + @override + Future transaction( + JNAPTransactionBuilder builder, { + bool fetchRemote = false, + CacheLevel cacheLevel = CacheLevel.localCached, + int timeoutMs = 10000, + int retries = 1, + SideEffectPollConfig? pollConfig, + }) async { + final actionNames = + builder.commands.map((e) => e.key.actionValue.split('/').last).toList(); + logger.d('🔵 UspMapperRepository.transaction: $actionNames'); + + // Process each action in the transaction + final results = >[]; + + for (final entry in builder.commands) { + final action = entry.key; + try { + final result = await send(action); + results.add(MapEntry(action, result)); + } catch (e) { + results.add(MapEntry( + action, + JNAPSuccess(result: 'OK', output: const {}), + )); + } + } + + return JNAPTransactionSuccessWrap(result: 'OK', data: results); + } + + @override + Stream scheduledCommand({ + required JNAPAction action, + int retryDelayInMilliSec = 5000, + int maxRetry = 10, + int firstDelayInMilliSec = 3000, + Map data = const {}, + bool Function(JNAPResult)? condition, + Function(bool exceedMaxRetry)? onCompleted, + int? requestTimeoutOverride, + bool auth = false, + }) async* { + // For now, just return a single result + await Future.delayed(Duration(milliseconds: firstDelayInMilliSec ~/ 10)); + + final result = await send(action, data: data); + yield result; + + onCompleted?.call(false); + } + + // =========================================================================== + // Helper Methods + // =========================================================================== + + /// Extracts the action name from a JNAP action URL. + String _extractActionName(String actionUrl) { + return actionUrl.split('/').last; + } + + /// Strips version suffix from action name. + /// e.g., "GetRadioInfo3" → "GetRadioInfo" + String _stripVersionSuffix(String actionName) { + final regex = RegExp(r'[2-9]+$'); + return actionName.replaceFirst(regex, ''); + } +} diff --git a/lib/demo/data/demo_cache_data.dart b/lib/demo/data/demo_cache_data.dart new file mode 100644 index 000000000..115d5cf9b --- /dev/null +++ b/lib/demo/data/demo_cache_data.dart @@ -0,0 +1,172 @@ +/// Demo Cache Data Loader +/// +/// Loads and provides access to the cached JNAP responses from demo_cache_data.json. +/// This provides real device data for the demo app. +library; + +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; + +/// Loader for demo cache data from demo_cache_data.json asset +class DemoCacheDataLoader { + DemoCacheDataLoader._(); + + static DemoCacheDataLoader? _instance; + static DemoCacheDataLoader get instance => + _instance ??= DemoCacheDataLoader._(); + + /// Cache data map: action URL -> cached entry + Map? _cacheData; + + /// Pre-built lookup map for fast access (includes version fallbacks) + Map>? _lookupMap; + + /// Whether the cache has been loaded + bool get isLoaded => _cacheData != null; + + /// Load cache data from asset + Future load() async { + if (_cacheData != null) return; + + try { + final jsonString = await rootBundle.loadString( + 'assets/resources/demo_cache_data.json', + ); + _cacheData = json.decode(jsonString) as Map; + _buildLookupMap(); + debugPrint('📦 Demo: Loaded ${_cacheData!.length} cached JNAP responses'); + } catch (e) { + debugPrint('⚠️ Demo: Failed to load cache data: $e'); + _cacheData = {}; + _lookupMap = {}; + } + } + + /// Build lookup map with version-agnostic keys + void _buildLookupMap() { + _lookupMap = {}; + + for (final entry in _cacheData!.entries) { + final actionUrl = entry.key; + final output = _extractOutput(entry.value); + if (output == null) continue; + + // Store with original key + _lookupMap![actionUrl] = output; + + // Also store with base name (without version suffix) + // e.g., "GetDevices3" -> "GetDevices" + final baseName = _stripVersionSuffix(actionUrl); + if (baseName != actionUrl) { + _lookupMap![baseName] = output; + } + + // Store by simple action name (last part of URL) + // e.g. ".../nodes/smartmode/GetDeviceMode" -> "GetDeviceMode" + // This handles path variations (e.g. nodes/ vs non-nodes/) + final simpleName = actionUrl.split('/').last; + final simpleBaseName = _stripVersionSuffix(simpleName); + + // Prioritize preserving existing mappings if collision occurs, + // but ensure at least one entry exists for the simple name. + if (!_lookupMap!.containsKey(simpleName)) { + _lookupMap![simpleName] = output; + } + if (simpleBaseName != simpleName && + !_lookupMap!.containsKey(simpleBaseName)) { + _lookupMap![simpleBaseName] = output; + } + } + + // Debug: Print keys containing "Device" + final deviceKeys = + _lookupMap!.keys.where((k) => k.contains('Device')).toList(); + debugPrint('🔍 Demo: Device-related keys: $deviceKeys'); + } + + /// Strip version suffix from action URL + /// e.g., "http://linksys.com/jnap/devicelist/GetDevices3" -> "http://linksys.com/jnap/devicelist/GetDevices" + String _stripVersionSuffix(String actionUrl) { + // Match trailing digits (1-9 only, not 0) + final regex = RegExp(r'[2-9]+$'); + return actionUrl.replaceFirst(regex, ''); + } + + /// Extract output from cache entry + Map? _extractOutput(dynamic entryValue) { + if (entryValue is! Map) return null; + + final data = entryValue['data']; + if (data is! Map) return null; + + final output = data['output']; + if (output is Map) { + return output; + } + if (output is Map) { + return Map.from(output); + } + return null; + } + + /// Get the output data for a given JNAP action URL + /// Returns null if not found + /// + /// Lookup order: + /// 1. Exact match + /// 2. Base name without version suffix + Map? getOutput(String actionUrl) { + if (_lookupMap == null) { + debugPrint('⚠️ Demo: Cache not loaded, call load() first'); + return {}; + } + + // Debug for GetDevices or GetLocalDevice + final isDeviceAction = + actionUrl.contains('GetDevices') || actionUrl.contains('GetDevice'); + if (isDeviceAction) { + debugPrint('🔍 Demo: Looking up $actionUrl'); + debugPrint( + '🔍 Demo: Base name would be: ${_stripVersionSuffix(actionUrl)}'); + } + + // 1. Try exact match + var output = _lookupMap![actionUrl]; + if (output != null) { + if (isDeviceAction) { + debugPrint('✅ Demo: Found exact match for $actionUrl'); + } + return output; + } + + // 2. Try base name (strip version suffix from request) + final baseName = _stripVersionSuffix(actionUrl); + if (baseName != actionUrl) { + output = _lookupMap![baseName]; + if (output != null) { + if (actionUrl.contains('GetDevices') || + actionUrl.contains('GetDevice')) { + debugPrint('✅ Demo: Found via base name $baseName'); + } + return output; + } + } + + if (actionUrl.contains('GetDevices') || actionUrl.contains('GetDevice')) { + debugPrint('❌ Demo: NOT FOUND for $actionUrl'); + } + return null; + } + + /// Check if an action URL exists in the cache + bool hasAction(String actionUrl) { + return _lookupMap?.containsKey(actionUrl) ?? false; + } + + /// Get all cached action URLs + List get cachedActions { + return _lookupMap?.keys.toList() ?? []; + } +} diff --git a/lib/demo/demo_app.dart b/lib/demo/demo_app.dart new file mode 100644 index 000000000..9765f49e3 --- /dev/null +++ b/lib/demo/demo_app.dart @@ -0,0 +1,228 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; +import 'package:privacy_gui/di.dart'; +import 'package:privacy_gui/localization/localization_hook.dart'; +import 'package:privacy_gui/page/components/layouts/root_container.dart'; +import 'package:privacy_gui/providers/app_settings/app_settings_provider.dart'; +import 'package:privacy_gui/route/route_model.dart'; +import 'package:privacy_gui/route/router_provider.dart'; +import 'package:privacy_gui/util/languages.dart'; +import 'package:privacy_gui/l10n/gen/app_localizations.dart'; +import 'package:ui_kit_library/ui_kit.dart'; +import 'package:privacy_gui/theme/theme_json_config.dart'; + +import 'widgets/demo_theme_settings_fab.dart'; + +/// Demo version of the Linksys application. +/// +/// This app uses mock providers instead of real network services, +/// making it suitable for: +/// - UI demonstrations +/// - Design reviews +/// - Sales presentations +/// - Training purposes +class DemoLinksysApp extends ConsumerStatefulWidget { + const DemoLinksysApp({super.key}); + + @override + ConsumerState createState() => _DemoLinksysAppState(); +} + +class _DemoLinksysAppState extends ConsumerState { + LinksysRoute? _currentRoute; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + // Use the main app's routerProvider for routing + final router = ref.watch(routerProvider); + + // Add listener for route changes + router.routerDelegate.addListener(_onReceiveRouteChanged); + + final appSettings = ref.watch(appSettingsProvider); + final systemLocaleStr = Intl.getCurrentLocale(); + final systemLocale = Locale(getLanguageData(systemLocaleStr)['value']); + + // Watch demo theme config for dynamic updates + final demoConfig = ref.watch(demoThemeConfigProvider); + + // Use demo seedColor if set, otherwise fall back to appSettings + final effectiveSeedColor = demoConfig.seedColor ?? appSettings.themeColor; + + // Build dynamic theme from demo config + final themeData = _buildThemeData( + brightness: appSettings.themeMode == ThemeMode.dark + ? Brightness.dark + : Brightness.light, + style: demoConfig.style, + globalOverlay: demoConfig.globalOverlay, + visualEffects: demoConfig.visualEffects, + userThemeColor: effectiveSeedColor, + ); + + final darkTheme = _buildThemeData( + brightness: Brightness.dark, + style: demoConfig.style, + globalOverlay: demoConfig.globalOverlay, + visualEffects: demoConfig.visualEffects, + userThemeColor: effectiveSeedColor, + ); + + // Update GetIt with the new dark theme so BottomNavigationMenu (TopBar) picks it up. + // This is safe because GetIt lookup is synchronous and we update before subtree builds. + if (getIt.isRegistered(instanceName: 'darkThemeData')) { + getIt.unregister(instanceName: 'darkThemeData'); + } + getIt.registerSingleton(darkTheme, + instanceName: 'darkThemeData'); + + return MaterialApp.router( + onGenerateTitle: (context) => '${loc(context).appTitle} (Demo)', + theme: themeData, + darkTheme: darkTheme, + themeMode: appSettings.themeMode, + locale: appSettings.locale ?? systemLocale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + builder: (context, child) => Material( + child: DesignSystem.init( + context, + Stack( + children: [ + AppRootContainer( + route: _currentRoute, + child: child, + ), + // Demo mode banner + const Positioned( + top: 0, + right: 0, + child: _DemoModeBanner(), + ), + // Theme settings FAB + const DemoThemeSettingsFab(), + ], + ), + ), + ), + routeInformationProvider: router.routeInformationProvider, + routeInformationParser: router.routeInformationParser, + routerDelegate: router.routerDelegate, + ); + } + + /// Build theme data from dynamic configuration. + ThemeData _buildThemeData({ + required Brightness brightness, + required String style, + GlobalOverlayType? globalOverlay, + int visualEffects = AppThemeConfig.effectAll, + Color? userThemeColor, + }) { + // Get base JSON config + final themeConfig = getIt(); + final baseJson = brightness == Brightness.dark + ? themeConfig.darkJson + : themeConfig.lightJson; + + // Create dynamic config by merging base with overrides + final dynamicJson = Map.from(baseJson); + dynamicJson['style'] = style; + if (globalOverlay != null) { + dynamicJson['globalOverlay'] = globalOverlay.name; + } else { + dynamicJson.remove('globalOverlay'); + } + dynamicJson['visualEffects'] = visualEffects; + + // Override seed color if user set one + if (userThemeColor != null) { + dynamicJson['seedColor'] = + '#${userThemeColor.toARGB32().toRadixString(16).substring(2)}'; + } + + // Build design theme + final designTheme = CustomDesignTheme.fromJson(dynamicJson); + + // Parse effective seed color + final seedColorHex = dynamicJson['seedColor'] as String?; + Color? parsedSeedColor; + if (seedColorHex != null) { + try { + final cleanHex = seedColorHex.replaceAll('#', ''); + if (cleanHex.length == 6) { + parsedSeedColor = Color(int.parse('FF$cleanHex', radix: 16)); + } else if (cleanHex.length == 8) { + parsedSeedColor = Color(int.parse(cleanHex, radix: 16)); + } + } catch (_) {} + } + final effectiveSeedColor = + userThemeColor ?? parsedSeedColor ?? AppPalette.brandPrimary; + + return AppTheme.create( + brightness: brightness, + seedColor: effectiveSeedColor, + designThemeBuilder: (_) => designTheme, + ); + } + + void _onReceiveRouteChanged() { + final router = ref.read(routerProvider); + Future.delayed(Duration.zero).then((_) { + if (!mounted) return; + final GoRoute? page = router + .routerDelegate.currentConfiguration.isNotEmpty + ? router.routerDelegate.currentConfiguration.last.route as GoRoute? + : null; + if (page is LinksysRoute) { + setState(() { + _currentRoute = page; + }); + } else if (_currentRoute != null) { + _currentRoute = null; + } + }); + } +} + +/// A banner indicating that the app is running in demo mode. +class _DemoModeBanner extends StatelessWidget { + const _DemoModeBanner(); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: Colors.orange.shade700, + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(8), + ), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.2), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: const Text( + 'DEMO', + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 12, + letterSpacing: 1.2, + ), + ), + ); + } +} diff --git a/lib/demo/doc/USP_INTEGRATION_ROADMAP.md b/lib/demo/doc/USP_INTEGRATION_ROADMAP.md new file mode 100644 index 000000000..583ec7e5f --- /dev/null +++ b/lib/demo/doc/USP_INTEGRATION_ROADMAP.md @@ -0,0 +1,367 @@ +# PrivacyGUI → USP/TR-369 Integration Roadmap + +## Vision + +Migrate PrivacyGUI from JNAP protocol to USP/TR-369 (gRPC), creating a protocol-agnostic UI application. + +--- + +## System Evolution + +```mermaid +graph TB + subgraph "Phase 1: Current State" + A1[PrivacyGUI] -->|JNAP| B1[Linksys Router] + end + + subgraph "Phase 2: Demo App" + A2[PrivacyGUI Demo] -->|Mock Data| B2[Static State] + end + + subgraph "Phase 3: Abstraction Layer" + A3[PrivacyGUI] -->|Repository Interface| B3{Protocol Switch} + B3 -->|JNAP| C3[Linksys Router] + B3 -->|Mock| D3[Demo Data] + end + + subgraph "Phase 4: USP Integration" + A4[PrivacyGUI] -->|Repository| B4{Protocol Switch} + B4 -->|USP/gRPC| C4[TR-369 Agent] + B4 -->|Mock| D4[Demo Data] + end + + style A1 fill:#f9f,stroke:#333 + style A2 fill:#9f9,stroke:#333 + style A3 fill:#ff9,stroke:#333 + style A4 fill:#9ff,stroke:#333 +``` + +--- + +## High-Level Architecture (Target State) + +```mermaid +graph TB + subgraph "Frontend Layer" + UI[PrivacyGUI UI
Flutter + Riverpod] + end + + subgraph "Application Layer" + P[Providers
State Management] + S[Services
Business Logic] + end + + subgraph "Repository Layer" + R{Repository
Interface} + end + + subgraph "Transport Layer" + J[JNAP Client] + G[gRPC Client] + M[Mock Client] + end + + subgraph "Backend Options" + LR[Linksys Router
JNAP Protocol] + SIM[USP Simulator
TR-369 Agent] + DEMO[Static Data
Demo Mode] + end + + UI --> P + P --> S + S --> R + R --> J & G & M + J --> LR + G --> SIM + M --> DEMO + + style UI fill:#4CAF50,color:white + style R fill:#FF9800,color:white + style SIM fill:#9C27B0,color:white +``` + +--- + +## Technology Stack + +> Reference: [usp_client_poc/ARCHITECTURE.md](file:///Users/austin.chang/flutter-workspaces/usp_client_poc/ARCHITECTURE.md) + +```mermaid +graph TD + subgraph "🖥️ Frontend - Controller" + A[Flutter Client
PrivacyGUI Demo] + A1[Riverpod + GoRouter] + A2[USP Protocol
Msg/Record Builder] + A --> A1 --> A2 + end + + subgraph "📡 Transport" + B[Envoy Proxy
:8090 gRPC-Web] + end + + subgraph "☁️ Infrastructure" + C[Mosquitto Broker
:1883 MQTT] + end + + subgraph "🤖 Backend - Agent" + D[USP Simulator
Dart Server] + D1[USP Engine
Record/Msg Parser] + D2[TR-181 Data Model
Device:2] + D --> D1 --> D2 + end + + subgraph "📦 Shared Package" + S[usp_protocol_common
Protobuf Definitions] + end + + A2 -->|"gRPC-Web / HTTP2"| B + B -->|"TCP / 1883"| C + C -->|"MQTT Pub/Sub"| D + + A2 -.->|uses| S + D1 -.->|uses| S + + style A fill:#4CAF50,color:white + style B fill:#FF9800,color:white + style C fill:#2196F3,color:white + style D fill:#9C27B0,color:white + style S fill:#E91E63,color:white +``` + +### Data Flow (Request → Response) + +``` +1. User Action → Flutter App creates USP Get Message +2. Encapsulation → Wrap in UspRecord (to_id="proto::agent") +3. Transport → Send via gRPC-Web to Envoy (:8090) +4. Routing → Envoy forwards to MQTT Broker +5. Processing → Simulator decodes Record/Msg, queries Data Model +6. Response → Create GetResp, return via reverse path +``` + +### Frontend Technologies (Controller) + +| Technology | Purpose | +|------------|---------| +| **Flutter 3.x** | Cross-platform UI framework | +| **Riverpod** | State Management | +| **GoRouter** | Declarative routing | +| **UI Kit Library** | Design system components | +| **gRPC-Web** | Gateway communication | +| **usp_protocol_common** | Build USP Msg/Record | + +### Backend Technologies (Agent) + +| Technology | Purpose | +|------------|---------| +| **Dart Server** | USP Simulator main process | +| **USP Engine** | Parse USP Record/Msg | +| **TR-181 Data Model** | Device:2 data structure | +| **usp_protocol_common** | Parse USP Msg/Record | + +### Infrastructure + +| Component | Port | Purpose | +|-----------|------|---------| +| **Envoy Proxy** | 8090 | gRPC-Web Gateway | +| **Mosquitto** | 1883 | MQTT Message Bus | + +--- + +## Phase 1: Demo App Completion + +### Goal +Standalone Demo application running without JNAP connectivity + +### Completed ✅ +| Item | File | +|------|------| +| Entry Point | `lib/main_demo.dart` | +| App Widget | `lib/demo/demo_app.dart` | +| Router | `lib/demo/demo_router.dart` | +| Mock Providers | `lib/demo/providers/demo_overrides.dart` | +| Mock Data | `lib/demo/data/` (41 files) | + +### Pending +| Item | Priority | Description | +|------|----------|-------------| +| Asset files | Medium | `theme.json`, `versions.json` | +| Page validation | High | Verify all major routes work | +| TestHelper refactor | Low | Share `lib/demo/data/` | +| CI/CD | Low | GitHub Pages auto-deploy | + +--- + +## Phase 2: Service Layer Refactoring + +### Goal +Extract business logic following `wifi_settings` pattern + +### Target Architecture + +``` +lib/page/{feature}/ +├── services/ +│ ├── {feature}_service.dart # Business logic +│ └── {feature}_mapper.dart # JNAP ↔ State +├── providers/ +│ └── {feature}_provider.dart # State management only +└── views/ + └── {feature}_view.dart # Pure UI +``` + +### Refactoring Priority + +| # | Module | Current | Target | +|---|--------|---------|--------| +| 1 | `instant_device` | Mixed logic in Provider | Service extraction | +| 2 | `instant_topology` | Mixed logic in Provider | Service extraction | +| 3 | `instant_admin` | Partial extraction | Complete extraction | +| 4 | `advanced_settings/internet` | Mixed in Provider | Service extraction | +| 5 | `advanced_settings/firewall` | Mixed in Provider | Service extraction | + +--- + +## Phase 3: Repository Abstraction Layer + +### Goal +Define protocol-agnostic interfaces + +### Interface Design + +```dart +// lib/core/repositories/wifi_repository.dart +abstract class WiFiRepository { + Future getSettings(); + Future saveSettings(WifiBundleSettings settings); + Stream watchSettings(); +} + +// lib/core/repositories/device_repository.dart +abstract class DeviceRepository { + Future> getDevices(); + Future getDeviceById(String id); +} + +// lib/core/repositories/network_repository.dart +abstract class NetworkRepository { + Future getSettings(); + Future saveSettings(NetworkSettings settings); +} +``` + +### Directory Structure + +``` +lib/core/ +├── repositories/ # Abstract interfaces +│ ├── wifi_repository.dart +│ ├── device_repository.dart +│ └── network_repository.dart +│ +├── jnap/ # JNAP implementation +│ ├── jnap_wifi_repository.dart +│ └── jnap_device_repository.dart +│ +├── usp/ # USP implementation (Phase 4) +│ ├── usp_wifi_repository.dart +│ └── usp_device_repository.dart +│ +└── mock/ # Mock implementation (Demo) + ├── mock_wifi_repository.dart + └── mock_device_repository.dart +``` + +--- + +## Phase 4: USP/gRPC Integration + +### Goal +Connect to `usp_client_poc` simulator + +### Integration Architecture + +```mermaid +sequenceDiagram + participant UI as PrivacyGUI + participant Repo as Repository + participant Mapper as TR-181 Mapper + participant gRPC as gRPC Client + participant Envoy as Envoy Proxy + participant SIM as USP Simulator + + UI->>Repo: getWiFiSettings() + Repo->>gRPC: USP Get Request + gRPC->>Envoy: gRPC-Web + Envoy->>SIM: MQTT + SIM-->>Envoy: USP Response + Envoy-->>gRPC: gRPC-Web + gRPC-->>Repo: Raw Response + Repo->>Mapper: TR-181 → State + Mapper-->>Repo: WifiBundleSettings + Repo-->>UI: WifiBundleSettings +``` + +### TR-181 Path Mapping (WiFi Example) + +| TR-181 Path | PrivacyGUI State | +|-------------|------------------| +| `Device.WiFi.SSID.1.SSID` | `WifiBundleSettings.ssid` | +| `Device.WiFi.AccessPoint.1.Security.KeyPassphrase` | `WifiBundleSettings.password` | +| `Device.WiFi.Radio.1.Enable` | `WifiBundleSettings.isEnabled` | +| `Device.WiFi.Radio.1.Channel` | `WifiBundleSettings.channel` | + +### Dependency Configuration + +```yaml +# pubspec.yaml +dependencies: + usp_protocol_common: + path: ../usp_client_poc/packages/usp_protocol_common + grpc: ^3.2.4 +``` + +--- + +## Timeline & Milestones + +```mermaid +gantt + title USP Integration Timeline + dateFormat YYYY-MM-DD + section Phase 1 + Demo App Completion :done, p1, 2024-12-23, 2d + section Phase 2 + Service Refactoring :p2, after p1, 3w + section Phase 3 + Repository Abstraction :p3, after p2, 1w + section Phase 4 + USP Integration :p4, after p3, 3w +``` + +| Phase | Estimate | Deliverable | +|-------|----------|-------------| +| Phase 1 | 1-2 days | Demo App presentable | +| Phase 2 | 2-3 weeks | All Feature Modules follow Service pattern | +| Phase 3 | 1 week | Repository interfaces + DI switching mechanism | +| Phase 4 | 2-3 weeks | USP Simulator connection successful | + +--- + +## Risks & Mitigation + +| Risk | Mitigation Strategy | +|------|---------------------| +| TR-181 fields don't fully map | Build mapping table, fill incrementally | +| Refactoring breaks existing functionality | Maintain JNAP + Demo dual-track operation | +| gRPC connection issues | Validate with Mock first, then connect | +| Timeline delays | Phase 2-3 can run in parallel | + +--- + +## Success Criteria + +1. ✅ **Demo App**: Runs standalone in Chrome, complete UI flow +2. ⬜ **Service Layer**: All feature modules follow wifi_settings pattern +3. ⬜ **Repository Pattern**: Providers don't call JNAP/gRPC directly +4. ⬜ **USP Integration**: Demo App connects to usp_device2_simulator diff --git a/lib/demo/jnap/_jnap.dart b/lib/demo/jnap/_jnap.dart new file mode 100644 index 000000000..d91d3b02b --- /dev/null +++ b/lib/demo/jnap/_jnap.dart @@ -0,0 +1,5 @@ +/// Demo JNAP module exports +library; + +export 'demo_router_repository.dart'; +export 'jnap_mock_registry.dart'; diff --git a/lib/demo/jnap/demo_router_repository.dart b/lib/demo/jnap/demo_router_repository.dart new file mode 100644 index 000000000..03336f1f2 --- /dev/null +++ b/lib/demo/jnap/demo_router_repository.dart @@ -0,0 +1,96 @@ +/// Demo Router Repository +/// +/// A mock implementation of RouterRepository for the Demo App. +/// Intercepts all JNAP requests and returns mock data from JnapMockRegistry. +library; + +import 'package:flutter/foundation.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; +import 'package:privacy_gui/core/jnap/actions/jnap_transaction.dart'; +import 'package:privacy_gui/core/jnap/command/base_command.dart'; +import 'package:privacy_gui/core/jnap/providers/side_effect_provider.dart'; +import 'package:privacy_gui/core/jnap/result/jnap_result.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; +import 'package:privacy_gui/demo/jnap/jnap_mock_registry.dart'; + +/// Demo implementation of RouterRepository that returns mock data +class DemoRouterRepository extends RouterRepository { + DemoRouterRepository(super.ref); + + /// Simulated network delay in milliseconds + static const _mockDelayMs = 50; + + @override + Future send( + JNAPAction action, { + Map data = const {}, + Map extraHeaders = const {}, + bool auth = false, + CommandType? type, + bool fetchRemote = false, + CacheLevel? cacheLevel, + int timeoutMs = 10000, + int retries = 1, + SideEffectPollConfig? pollConfig, + }) async { + // Debug: log when this is called + debugPrint('🔵 DemoRouterRepository.send: ${action.actionValue}'); + + // Simulate network delay + await Future.delayed(const Duration(milliseconds: _mockDelayMs)); + + // Return mock response + final output = JnapMockRegistry.getResponse(action); + return JNAPSuccess(result: 'OK', output: output); + } + + @override + Future transaction( + JNAPTransactionBuilder builder, { + bool fetchRemote = false, + CacheLevel cacheLevel = CacheLevel.localCached, + int timeoutMs = 10000, + int retries = 1, + SideEffectPollConfig? pollConfig, + }) async { + // Debug: log when this is called + final actionNames = + builder.commands.map((e) => e.key.actionValue.split('/').last).toList(); + debugPrint('🔵 DemoRouterRepository.transaction: $actionNames'); + + // Simulate network delay + await Future.delayed(const Duration(milliseconds: _mockDelayMs * 2)); + + // Build results for each action in the transaction + final results = >[]; + for (final entry in builder.commands) { + final action = entry.key; + final output = JnapMockRegistry.getResponse(action); + results.add(MapEntry(action, JNAPSuccess(result: 'OK', output: output))); + } + + return JNAPTransactionSuccessWrap(result: 'OK', data: results); + } + + @override + Stream scheduledCommand({ + required JNAPAction action, + int retryDelayInMilliSec = 5000, + int maxRetry = 10, + int firstDelayInMilliSec = 3000, + Map data = const {}, + bool Function(JNAPResult)? condition, + Function(bool exceedMaxRetry)? onCompleted, + int? requestTimeoutOverride, + bool auth = false, + }) async* { + // For demo, return single result immediately without polling + await Future.delayed(Duration(milliseconds: firstDelayInMilliSec ~/ 10)); + + final output = JnapMockRegistry.getResponse(action); + yield JNAPSuccess(result: 'OK', output: output); + + // Mark as completed (not exceeded) + onCompleted?.call(false); + } +} diff --git a/lib/demo/jnap/jnap_mock_registry.dart b/lib/demo/jnap/jnap_mock_registry.dart new file mode 100644 index 000000000..6d4b8c89b --- /dev/null +++ b/lib/demo/jnap/jnap_mock_registry.dart @@ -0,0 +1,55 @@ +/// JNAP Mock Registry for Demo App +/// +/// Returns mock responses from demo_cache_data.json. +/// Fallback strategy: +/// 1. Cache data from JSON (real device data) +/// 2. SET/DELETE actions return empty {} (no-op) +/// 3. Unmapped actions log warning and return empty {} +library; + +import 'package:flutter/foundation.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; +import 'package:privacy_gui/demo/data/demo_cache_data.dart'; + +/// Central registry for JNAP mock responses +class JnapMockRegistry { + /// Get mock response for the given action + static Map getResponse(JNAPAction action) { + var url = action.actionValue; + + // 1. Check cache data (primary source) + final cacheOutput = DemoCacheDataLoader.instance.getOutput(url); + if (cacheOutput != null) { + return cacheOutput; + } + + // 2. SET/DELETE/Command actions return empty (no-op) + if (_isWriteAction(action)) { + debugPrint('📝 Demo: No-op for ${action.actionValue}'); + return {}; + } + + // 3. Fallback with warning + debugPrint('⚠️ Demo: No mock data for ${action.actionValue}'); + return {}; + } + + static bool _isWriteAction(JNAPAction action) { + final actionUrl = action.actionValue; + // Extract action name from URL (after last '/') + final actionName = actionUrl.split('/').last; + + // Check if action STARTS with write verbs + return actionName.startsWith('Set') || + actionName.startsWith('Delete') || + actionName.startsWith('Reboot') || + actionName.startsWith('Reset') || + actionName.startsWith('Start') || + actionName.startsWith('Stop') || + actionName.startsWith('Run') || + actionName.startsWith('Clear') || + actionName.startsWith('Update') || + actionName.startsWith('Add') || + actionName.startsWith('Remove'); + } +} diff --git a/lib/demo/providers/demo_overrides.dart b/lib/demo/providers/demo_overrides.dart new file mode 100644 index 000000000..469a1c016 --- /dev/null +++ b/lib/demo/providers/demo_overrides.dart @@ -0,0 +1,181 @@ +/// Demo Provider Overrides +/// +/// Minimal overrides for Demo application. Most providers use their +/// original implementation and get data through DemoRouterRepository. +library; + +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacy_gui/core/cloud/providers/geolocation/geolocation_provider.dart'; +import 'package:privacy_gui/core/cloud/providers/geolocation/geolocation_state.dart'; +import 'package:privacy_gui/core/jnap/models/auto_configuration_settings.dart'; +import 'package:privacy_gui/demo/jnap/demo_router_repository.dart'; +import 'package:privacy_gui/core/jnap/providers/polling_provider.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; +import 'package:privacy_gui/page/instant_setup/providers/pnp_provider.dart'; +import 'package:privacy_gui/providers/auth/auth_provider.dart'; +import 'package:privacy_gui/providers/connectivity/connectivity_info.dart'; +import 'package:privacy_gui/providers/connectivity/connectivity_provider.dart'; +import 'package:privacy_gui/providers/connectivity/connectivity_state.dart'; + +/// Demo provider overrides for the Demo application. +/// +/// These overrides replace **only the essential** providers: +/// - RouterRepository: Uses DemoRouterRepository to intercept all JNAP calls +/// - Auth: Always logged in +/// - Connectivity: Always online +/// - Polling: Initializes with empty state (data loaded via DemoRouterRepository) +/// - PnP: Bypasses auto-configuration checks to skip setup wizard +/// +/// All other providers use their **original implementation** and get data +/// through the DemoRouterRepository -> JnapMockRegistry -> demo_cache_data.json +/// pipeline. +class DemoProviders { + /// Returns all provider overrides needed for demo mode. + static List get allOverrides => [ + // 1. Auth: Always logged in + authProvider.overrideWith(() => _DemoAuthNotifier()), + + // 2. Connectivity: Always online + connectivityProvider.overrideWith(() => _DemoConnectivityNotifier()), + + // 3. Router Repository: Intercept JNAP traffic + routerRepositoryProvider + .overrideWith((ref) => DemoRouterRepository(ref)), + + // 4. Polling: Auto-start + pollingProvider.overrideWith(() => _DemoPollingNotifier()), + + // 5. Geolocation: Bypass cloud service call + geolocationProvider.overrideWith(() => _DemoGeolocationNotifier()), + + // 6. PnP: Bypass setup wizard + pnpProvider.overrideWith(() => _DemoPnpNotifier()), + ]; +} + +class _DemoGeolocationNotifier extends GeolocationNotifier { + @override + Future build() async { + debugPrint('🌍 Demo: Using mock geolocation'); + return const GeolocationState( + name: 'Linksys ISP', + city: 'Irvine', + region: 'California', + country: 'United States', + regionCode: 'CA', + countryCode: 'US', + continentCode: 'NA'); + } +} + +// ============================================================================ +// Demo Notifier Implementations - Only for core behavior, not data +// ============================================================================ + +/// Demo auth notifier that simulates logged-in state +class _DemoAuthNotifier extends AuthNotifier { + @override + Future build() async { + debugPrint('🔐 Demo: Auth initialized with local login'); + return AuthState( + loginType: LoginType.local, + localPassword: 'demo-password', + ); + } + + @override + Future init() async { + debugPrint('🔐 Demo: Auth init called - preserving local login'); + // In demo mode, we ignore storage and always return the local login state + final demoState = AuthState( + loginType: LoginType.local, + localPassword: 'demo-password', + ); + state = AsyncValue.data(demoState); + return demoState; + } + + @override + Future localLogin( + String password, { + bool guardError = true, + bool pnp = false, + }) async { + debugPrint('🔐 Demo: Local login called'); + state = AsyncValue.data(AuthState( + loginType: LoginType.local, + localPassword: password, + )); + } + + @override + Future logout() async { + debugPrint('🔐 Demo: Logout called'); + state = AsyncValue.data(AuthState(loginType: LoginType.none)); + } +} + +/// Demo connectivity notifier that simulates online state +class _DemoConnectivityNotifier extends ConnectivityNotifier { + @override + ConnectivityState build() { + debugPrint('📶 Demo: Connectivity initialized as online'); + return const ConnectivityState( + hasInternet: true, + connectivityInfo: ConnectivityInfo( + routerType: RouterType.behindManaged, + gatewayIp: '192.168.1.1', + ), + ); + } +} + +/// Demo polling notifier - uses parent's logic with DemoRouterRepository +class _DemoPollingNotifier extends PollingNotifier { + @override + FutureOr build() { + debugPrint('🔄 Demo: Polling notifier initialized'); + + // Auto-start polling after build + Future.microtask(() { + debugPrint('🔄 Demo: Auto-starting polling...'); + startPolling(); + }); + + // Return empty initial state - data will be loaded when polling starts + return const CoreTransactionData( + lastUpdate: 0, + isReady: false, + data: {}, + ); + } +} + +/// Demo PnP notifier - bypasses setup wizard checks +class _DemoPnpNotifier extends PnpNotifier { + @override + Future autoConfigurationCheck() async { + debugPrint('🔌 Demo: Bypassing auto-configuration check'); + return const AutoConfigurationSettings(isAutoConfigurationSupported: false); + } + + @override + Future isRouterPasswordSet() async { + // Return true to simulate that the router is already configured + return true; + } + + @override + Future fetchDeviceInfo([bool clearCurrentSN = true]) async { + // Just call super, but wrap in try-catch to be safe, + // although DemoRouterRepository should handle it. + try { + await super.fetchDeviceInfo(clearCurrentSN); + } catch (e) { + debugPrint('🔌 Demo: fetchDeviceInfo suppressed error: $e'); + } + } +} diff --git a/lib/demo/widgets/demo_theme_settings_fab.dart b/lib/demo/widgets/demo_theme_settings_fab.dart new file mode 100644 index 000000000..49ca844a5 --- /dev/null +++ b/lib/demo/widgets/demo_theme_settings_fab.dart @@ -0,0 +1,353 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ui_kit_library/ui_kit.dart'; + +/// Provider for dynamic theme configuration in demo mode. +final demoThemeConfigProvider = + StateNotifierProvider((ref) { + return DemoThemeConfigNotifier(); +}); + +/// Demo theme configuration state. +class DemoThemeConfig { + final String style; + final GlobalOverlayType? globalOverlay; + final int visualEffects; + final Color? seedColor; + + const DemoThemeConfig({ + this.style = 'glass', + this.globalOverlay, + this.visualEffects = AppThemeConfig.effectAll, + this.seedColor, + }); + + DemoThemeConfig copyWith({ + String? style, + GlobalOverlayType? globalOverlay, + bool clearOverlay = false, + int? visualEffects, + Color? seedColor, + bool clearSeedColor = false, + }) { + return DemoThemeConfig( + style: style ?? this.style, + globalOverlay: + clearOverlay ? null : (globalOverlay ?? this.globalOverlay), + visualEffects: visualEffects ?? this.visualEffects, + seedColor: clearSeedColor ? null : (seedColor ?? this.seedColor), + ); + } +} + +/// Notifier for demo theme configuration. +class DemoThemeConfigNotifier extends StateNotifier { + DemoThemeConfigNotifier() : super(const DemoThemeConfig()); + + void setStyle(String style) { + state = state.copyWith(style: style); + } + + void setGlobalOverlay(GlobalOverlayType? overlay) { + if (overlay == null) { + state = state.copyWith(clearOverlay: true); + } else { + state = state.copyWith(globalOverlay: overlay); + } + } + + void setVisualEffects(int effects) { + state = state.copyWith(visualEffects: effects); + } + + void toggleVisualEffect(int flag) { + final current = state.visualEffects; + final newEffects = (current & flag) != 0 + ? current & ~flag // Remove flag + : current | flag; // Add flag + state = state.copyWith(visualEffects: newEffects); + } + + void setSeedColor(Color? color) { + if (color == null) { + state = state.copyWith(clearSeedColor: true); + } else { + state = state.copyWith(seedColor: color); + } + } +} + +/// A floating action button for theme settings in demo mode. +class DemoThemeSettingsFab extends ConsumerStatefulWidget { + const DemoThemeSettingsFab({super.key}); + + @override + ConsumerState createState() => + _DemoThemeSettingsFabState(); +} + +class _DemoThemeSettingsFabState extends ConsumerState { + bool _isExpanded = false; + + // Preset color palette + static const List _presetColors = [ + Color(0xFF0870EA), // Blue (default) + Color(0xFF8E08EA), // Purple + Color(0xFFE91E63), // Pink + Color(0xFFFF5722), // Deep Orange + Color(0xFFFF9800), // Orange + Color(0xFF4CAF50), // Green + Color(0xFF009688), // Teal + Color(0xFF00BCD4), // Cyan + Color(0xFF3F51B5), // Indigo + Color(0xFF607D8B), // Blue Grey + Color(0xFF795548), // Brown + Color(0xFF9E9E9E), // Grey + ]; + + @override + Widget build(BuildContext context) { + final config = ref.watch(demoThemeConfigProvider); + + return AppDraggable( + initialPosition: Alignment.bottomRight, + enableSnapping: true, + padding: const EdgeInsets.all(16.0), + builder: (context, isDragging, alignment) { + // Adapt layout based on side (Left aligned -> Panel starts at left) + final isLeft = alignment.x < 0; + + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: + isLeft ? CrossAxisAlignment.start : CrossAxisAlignment.end, + children: [ + // Expanded menu + if (_isExpanded) ...[ + _buildSettingsPanel(context, config), + const SizedBox(height: 8), + ], + // Main FAB + AppIconButton.primary( + icon: AppIcon.font( + _isExpanded ? AppFontIcons.close : AppFontIcons.widgets, + ), + onTap: () => setState(() => _isExpanded = !_isExpanded), + ), + ], + ); + }, + ); + } + + Widget _buildSettingsPanel(BuildContext context, DemoThemeConfig config) { + return SizedBox( + width: 300, + child: AppCard( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Header + const AppText( + 'Theme Settings', + variant: AppTextVariant.titleMedium, + fontWeight: FontWeight.bold, + ), + const SizedBox(height: 12), + const AppDivider(), + const SizedBox(height: 12), + + // Seed Color selector + AppText.labelMedium('Seed Color'), + const SizedBox(height: 8), + _buildSeedColorSelector(context, config), + const SizedBox(height: 16), + + // Style selector + AppText.labelMedium('Style'), + const SizedBox(height: 8), + _buildStyleSelector(context, config), + const SizedBox(height: 16), + + // Global Overlay selector + AppText.labelMedium('Global Overlay'), + const SizedBox(height: 8), + _buildOverlaySelector(context, config), + const SizedBox(height: 16), + + // Visual Effects toggles + AppText.labelMedium('Visual Effects'), + const SizedBox(height: 8), + _buildVisualEffectsToggles(context, config), + ], + ), + ), + ), + ); + } + + Widget _buildSeedColorSelector(BuildContext context, DemoThemeConfig config) { + final currentColor = config.seedColor; + + return Wrap( + spacing: 8, + runSpacing: 8, + children: [ + // Default option (use JSON config) + _ColorCircle( + color: null, + isSelected: currentColor == null, + label: 'Default', + onTap: () { + ref.read(demoThemeConfigProvider.notifier).setSeedColor(null); + }, + ), + // Preset colors + ..._presetColors.map((color) { + return _ColorCircle( + color: color, + isSelected: currentColor?.toARGB32() == color.toARGB32(), + onTap: () { + ref.read(demoThemeConfigProvider.notifier).setSeedColor(color); + }, + ); + }), + ], + ); + } + + Widget _buildStyleSelector(BuildContext context, DemoThemeConfig config) { + final styles = ['glass', 'aurora', 'brutal', 'flat', 'neumorphic', 'pixel']; + + return Wrap( + spacing: 6, + runSpacing: 6, + children: styles.map((style) { + final isSelected = config.style == style; + return AppTag( + label: style[0].toUpperCase() + style.substring(1), + isSelected: isSelected, + onTap: () { + ref.read(demoThemeConfigProvider.notifier).setStyle(style); + }, + ); + }).toList(), + ); + } + + Widget _buildOverlaySelector(BuildContext context, DemoThemeConfig config) { + final overlays = [ + (null, 'None'), + (GlobalOverlayType.snow, 'Snow'), + (GlobalOverlayType.hacker, 'Matrix'), + (GlobalOverlayType.noiseOverlay, 'Noise'), + (GlobalOverlayType.crtShader, 'CRT'), + (GlobalOverlayType.auroraGlow, 'Aurora'), + (GlobalOverlayType.liquid, 'Liquid'), + ]; + + return Wrap( + spacing: 6, + runSpacing: 6, + children: overlays.map((item) { + final (overlay, label) = item; + final isSelected = config.globalOverlay == overlay; + return AppTag( + label: label, + isSelected: isSelected, + onTap: () { + ref + .read(demoThemeConfigProvider.notifier) + .setGlobalOverlay(overlay); + }, + ); + }).toList(), + ); + } + + Widget _buildVisualEffectsToggles( + BuildContext context, DemoThemeConfig config) { + // Use actual effect constants from AppThemeConfig + final effects = [ + (AppThemeConfig.effectDirectionalShadow, 'Shadow'), + (AppThemeConfig.effectGradientBorder, 'Gradient'), + (AppThemeConfig.effectBlur, 'Blur'), + (AppThemeConfig.effectNoiseTexture, 'Noise'), + (AppThemeConfig.effectShimmer, 'Shimmer'), + (AppThemeConfig.effectTopologyAnimation, 'Topology'), + ]; + + return Wrap( + spacing: 6, + runSpacing: 6, + children: effects.map((item) { + final (flag, label) = item; + final isEnabled = (config.visualEffects & flag) != 0; + return AppTag( + label: label, + isSelected: isEnabled, + onTap: () { + ref.read(demoThemeConfigProvider.notifier).toggleVisualEffect(flag); + }, + ); + }).toList(), + ); + } +} + +/// A circular color selector widget. +class _ColorCircle extends StatelessWidget { + final Color? color; + final bool isSelected; + final String? label; + final VoidCallback onTap; + + const _ColorCircle({ + required this.color, + required this.isSelected, + required this.onTap, + this.label, + }); + + @override + Widget build(BuildContext context) { + const size = 28.0; + final isDefault = color == null; + final colorScheme = Theme.of(context).colorScheme; + + // Calculate effective styles based on selection state + final borderColor = isSelected + ? colorScheme.primary + : (isDefault ? colorScheme.outline : color!.withValues(alpha: 0.5)); + + final borderWidth = isSelected ? 3.0 : 1.0; + + return AppInteractionSensor( + onTap: onTap, + child: Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isDefault ? Colors.transparent : color, + border: Border.all( + color: borderColor, + width: borderWidth, + ), + ), + child: isDefault + ? Center( + child: AppIcon.font( + Icons.auto_fix_high, + size: 14, + color: colorScheme.outline, + ), + ) + : null, + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index b0846ab2a..a0da31581 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; @@ -44,6 +45,17 @@ void main() async { } FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); await Storage.init(); + + // Load environment variables for FAQ Agent (AWS Bedrock) + // Create assets/.env with AWS credentials (copy from gen_ui_client) + try { + await dotenv.load(fileName: 'env.template'); + debugPrint('FAQ Agent: .env loaded successfully'); + } catch (e) { + debugPrint('Warning: Could not load .env file for FAQ Agent: $e'); + debugPrint('FAQ Agent will use direct search instead of AI.'); + } + if (kDebugMode) { print(await getPackageInfo()); } diff --git a/lib/main_demo.dart b/lib/main_demo.dart new file mode 100644 index 000000000..2883781c5 --- /dev/null +++ b/lib/main_demo.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:privacy_gui/core/jnap/actions/jnap_service_supported.dart'; +import 'package:privacy_gui/di.dart'; +import 'package:privacy_gui/theme/theme_json_config.dart'; +import 'package:privacy_gui/theme/theme_config_loader.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; + +import 'demo/data/demo_cache_data.dart'; +import 'demo/demo_app.dart'; +import 'demo/providers/demo_overrides.dart'; + +/// Demo mode entry point. +/// +/// This entry point creates a fully functional demo version of the app +/// that uses mock data instead of real network connections. It's perfect for: +/// +/// - **UI Demonstrations**: Show the app to stakeholders without a real router +/// - **Design Reviews**: Let designers see real UI implementations +/// - **Sales Presentations**: Demo features without network dependencies +/// - **Training**: Help new users learn the interface +/// +/// ## Build Commands +/// +/// ```bash +/// # Run locally +/// flutter run -d chrome --target=lib/main_demo.dart +/// +/// # Build for web deployment +/// flutter build web --target=lib/main_demo.dart --base-href /PrivacyGUI-Demo/ +/// ``` +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // Initialize better actions for JNAP + initBetterActions(); + + // Load environment variables (for AWS credentials) + try { + await dotenv.load(fileName: 'env.template'); + } catch (e) { + debugPrint('No .env file found, using defaults'); + } + + // Load demo cache data + await DemoCacheDataLoader.instance.load(); + + // Load theme configuration (no network needed) + final themeConfig = await ThemeConfigLoader.load(); + + // Setup dependencies with demo-specific configuration + _demoDependencySetup(themeConfig: themeConfig); + + runApp( + ProviderScope( + overrides: DemoProviders.allOverrides, + child: const DemoLinksysApp(), + ), + ); +} + +/// Sets up dependencies for demo mode. +/// +/// This is a simplified version of the production `dependencySetup` that +/// skips network-related services and uses mock implementations. +void _demoDependencySetup({ThemeJsonConfig? themeConfig}) { + // Register mock service helper + if (!getIt.isRegistered()) { + getIt.registerSingleton(_DemoServiceHelper()); + } + + final config = themeConfig ?? ThemeJsonConfig.defaultConfig(); + + if (!getIt.isRegistered()) { + getIt.registerSingleton(config); + } + + if (!getIt.isRegistered(instanceName: 'lightThemeData')) { + getIt.registerSingleton( + config.createLightTheme(), + instanceName: 'lightThemeData', + ); + } + + if (!getIt.isRegistered(instanceName: 'darkThemeData')) { + getIt.registerSingleton( + config.createDarkTheme(), + instanceName: 'darkThemeData', + ); + } +} + +/// Demo service helper that intelligently reports support based on available mock data. +class _DemoServiceHelper extends ServiceHelper { + final _loader = DemoCacheDataLoader.instance; + + bool _hasData(String keyword) { + if (!_loader.isLoaded) return false; + return _loader.cachedActions.any((url) => url.contains(keyword)); + } + + @override + bool isSupportGuestNetwork([List? services]) => + _hasData('GetGuestRadioSettings') || _hasData('GetGuestNetworkSettings'); + + @override + bool isSupportLedMode([List? services]) => + _hasData('GetLedNightModeSetting'); + + @override + bool isSupportLedBlinking([List? services]) => true; + + @override + bool isSupportVPN([List? services]) => + _hasData('GetVPNUser') || _hasData('GetVPNGateway'); + + @override + bool isSupportHealthCheck([List? services]) => + _hasData('GetHealthCheckResults'); + + @override + bool isSupportClientDeauth([List? services]) => true; + + @override + bool isSupportAutoOnboarding([List? services]) => true; + + @override + bool isSupportMLO([List? services]) => _hasData('GetMLOSettings'); + + @override + bool isSupportDFS([List? services]) => _hasData('GetDFSSettings'); + + @override + bool isSupportAirtimeFairness([List? services]) => + _hasData('GetAirtimeFairnessSettings'); + + @override + bool isSupportNodeFirmwareUpdate([List? services]) => true; +} diff --git a/lib/main_usp_demo.dart b/lib/main_usp_demo.dart new file mode 100644 index 000000000..0f1ba27a4 --- /dev/null +++ b/lib/main_usp_demo.dart @@ -0,0 +1,204 @@ +/// USP Demo Mode Entry Point +/// +/// This entry point creates a demo version of the app that connects to +/// the USP Simulator via gRPC instead of using mock data. +/// +/// ## Prerequisites +/// +/// Before running, ensure the following are started: +/// 1. Docker infrastructure: `melos run infra:start` +/// +/// ## Build Commands +/// +/// ```bash +/// # Run locally (connect to localhost:8090) +/// flutter run -d chrome --target=lib/main_usp_demo.dart +/// +/// # With custom host/port +/// flutter run -d chrome --target=lib/main_usp_demo.dart --dart-define=USP_HOST=192.168.1.100 --dart-define=USP_PORT=8090 +/// ``` +library; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:privacy_gui/core/jnap/actions/jnap_service_supported.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; +import 'package:usp_client_core/usp_client_core.dart'; +import 'package:privacy_gui/core/usp/usp_mapper_repository.dart'; +import 'package:privacy_gui/core/usp/capabilities/capability_registry.dart'; +import 'package:privacy_gui/core/usp/capabilities/repositories/capability_repository.dart'; +import 'package:privacy_gui/core/usp/capabilities/repositories/usp_capability_repository.dart'; +import 'package:privacy_gui/core/usp/capabilities/repositories/local_capability_repository.dart'; +import 'package:privacy_gui/core/usp/capabilities/models/device_feature.dart'; +import 'package:privacy_gui/di.dart'; +import 'package:privacy_gui/theme/theme_json_config.dart'; +import 'package:privacy_gui/theme/theme_config_loader.dart'; +import 'package:privacy_gui/core/jnap/actions/better_action.dart'; + +import 'demo/demo_app.dart'; +import 'demo/data/demo_cache_data.dart'; +import 'demo/providers/demo_overrides.dart'; + +/// Global gRPC service instance (initialized before app starts) +late final UspGrpcClientService _grpcService; + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + // Initialize JNAP actions (same as main_demo.dart) + initBetterActions(); + + // Get connection config from environment or use defaults + const config = UspConnectionConfig( + host: String.fromEnvironment('USP_HOST', defaultValue: 'localhost'), + port: int.fromEnvironment('USP_PORT', defaultValue: 8090), + ); + + // Initialize gRPC service and connect + _grpcService = UspGrpcClientService(); + + debugPrint('🔌 USP Demo: Connecting to ${config.host}:${config.port}...'); + + // Capability repository to be initialized + late final CapabilityRepository capabilityRepo; + + try { + await _grpcService.connect(config); + debugPrint('✅ USP Demo: Connected to Simulator'); + + // Initialize Capabilities + try { + capabilityRepo = UspCapabilityRepository(_grpcService); + await capabilityRepo.initialize(); + } catch (e) { + debugPrint( + '⚠️ USP Demo: Capability discovery failed, using fallback: $e'); + capabilityRepo = LocalCapabilityRepository(); + await capabilityRepo.initialize(); + } + } catch (e) { + debugPrint('⚠️ USP Demo: Connection failed: $e'); + debugPrint('⚠️ USP Demo: Make sure Simulator and Envoy are running'); + // Fallback to local capabilities + capabilityRepo = LocalCapabilityRepository(); + await capabilityRepo.initialize(); + } + + // Load demo cache data for fallback (same as main_demo.dart) + // This provides complete mock data for JNAP actions not mapped to TR-181 + await DemoCacheDataLoader.instance.load(); + + // Load theme configuration (same as main_demo.dart) + final themeConfig = await ThemeConfigLoader.load(); + _uspDemoDependencySetup( + capabilityRepo: capabilityRepo, themeConfig: themeConfig); + + runApp( + ProviderScope( + overrides: _buildUspOverrides(capabilityRepo), + child: const DemoLinksysApp(), + ), + ); +} + +/// Build provider overrides for USP demo mode. +/// +/// Uses DemoProviders' auth/connectivity/polling/geolocation overrides +/// but replaces RouterRepository with USP implementation. +List _buildUspOverrides(CapabilityRepository capabilityRepo) { + // Explicitly use the same demo overrides for auth bypass + // but replace routerRepositoryProvider with USP version + return [ + // Use DemoProviders' auth, connectivity, polling, geolocation + ...DemoProviders.allOverrides, + + // Capability repository + capabilityRepositoryProvider.overrideWithValue(capabilityRepo), + + // Override with USP RouterRepository (this takes precedence) + routerRepositoryProvider.overrideWith( + (ref) => UspMapperRepository(ref, _grpcService), + ), + ]; +} + +/// Setup dependencies for USP demo mode. +/// +/// Same as _demoDependencySetup in main_demo.dart but with USP-specific ServiceHelper. +void _uspDemoDependencySetup({ + required CapabilityRepository capabilityRepo, + ThemeJsonConfig? themeConfig, +}) { + // Register USP-specific service helper + if (!getIt.isRegistered()) { + getIt.registerSingleton( + _UspDemoServiceHelper(capabilityRepo)); + } + + final config = themeConfig ?? ThemeJsonConfig.defaultConfig(); + + if (!getIt.isRegistered()) { + getIt.registerSingleton(config); + } + + if (!getIt.isRegistered(instanceName: 'lightThemeData')) { + getIt.registerSingleton( + config.createLightTheme(), + instanceName: 'lightThemeData', + ); + } + + if (!getIt.isRegistered(instanceName: 'darkThemeData')) { + getIt.registerSingleton( + config.createDarkTheme(), + instanceName: 'darkThemeData', + ); + } +} + +/// Service helper for USP demo mode. +/// +/// Reports all features as supported since we don't know what the +/// Simulator supports until we query it. +class _UspDemoServiceHelper extends ServiceHelper { + final CapabilityRepository _capabilityRepo; + + _UspDemoServiceHelper(this._capabilityRepo); + + @override + bool isSupportGuestNetwork([List? services]) => + _capabilityRepo.hasFeature(DeviceFeature.guestNetwork); + + @override + bool isSupportLedMode([List? services]) => true; + + @override + bool isSupportLedBlinking([List? services]) => true; + + @override + bool isSupportVPN([List? services]) => false; + + @override + bool isSupportHealthCheck([List? services]) => + _capabilityRepo.hasFeature(DeviceFeature.diagnostics); + + @override + bool isSupportClientDeauth([List? services]) => true; + + @override + bool isSupportAutoOnboarding([List? services]) => true; + + @override + bool isSupportMLO([List? services]) => + _capabilityRepo.hasFeature(DeviceFeature.wifi5Hz); + + @override + bool isSupportDFS([List? services]) => + _capabilityRepo.hasFeature(DeviceFeature.wifi5Hz); + + @override + bool isSupportAirtimeFairness([List? services]) => true; + + @override + bool isSupportNodeFirmwareUpdate([List? services]) => true; +} diff --git a/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_forwarding_list_view.dart b/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_forwarding_list_view.dart index aea62212e..ef2271098 100644 --- a/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_forwarding_list_view.dart +++ b/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_forwarding_list_view.dart @@ -40,6 +40,8 @@ class _PortRangeForwardingContentViewState // Editing state PortRangeForwardingRuleUIModel? _editingRule; bool _isInitializing = false; + bool _isAddInitialized = false; + String _selectedProtocol = 'Both'; StateSetter? _sheetStateSetter; // Validation errors @@ -119,7 +121,23 @@ class _PortRangeForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(rule.description), editBuilder: (_, rule, setSheetState) { _sheetStateSetter = setSheetState; - if (_editingRule != rule) { + + final state = ref.read(portRangeForwardingListProvider); + final isNewRule = !state.current.rules.contains(rule); + bool shouldInitialize = false; + + if (isNewRule) { + if (!_isAddInitialized) { + shouldInitialize = true; + _isAddInitialized = true; + } + } else { + if (_editingRule != rule) { + shouldInitialize = true; + } + } + + if (shouldInitialize) { _isInitializing = true; try { _editingRule = rule; @@ -127,6 +145,7 @@ class _PortRangeForwardingContentViewState _firstPortTextController.text = '${rule.firstExternalPort}'; _lastPortTextController.text = '${rule.lastExternalPort}'; _ipAddressTextController.text = rule.internalServerIPAddress; + _selectedProtocol = rule.protocol; _nameError = null; _ipError = null; _portRangeError = null; @@ -166,7 +185,6 @@ class _PortRangeForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(getProtocolTitle(context, rule.protocol)), editBuilder: (_, rule, setSheetState) { - final currentProtocol = rule.protocol; final protocolDisplayMap = { 'TCP': getProtocolTitle(context, 'TCP'), 'UDP': getProtocolTitle(context, 'UDP'), @@ -175,7 +193,7 @@ class _PortRangeForwardingContentViewState return AppDropdown( key: const Key('protocolDropdown'), items: protocolDisplayMap.values.toList(), - value: protocolDisplayMap[currentProtocol], + value: protocolDisplayMap[_selectedProtocol], hint: loc(context).protocol, onChanged: (displayValue) { if (displayValue != null) { @@ -183,7 +201,9 @@ class _PortRangeForwardingContentViewState .firstWhere((e) => e.value == displayValue, orElse: () => const MapEntry('Both', 'Both')) .key; + _selectedProtocol = protocolKey; _updateProtocol(protocolKey); + setSheetState(() {}); } }, ); @@ -196,11 +216,11 @@ class _PortRangeForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(rule.internalServerIPAddress), editBuilder: (_, rule, setSheetState) { - return AppTextField( + return AppIpv4TextField( key: const Key('ipAddressTextField'), controller: _ipAddressTextController, - hintText: loc(context).ipAddress, errorText: _ipError, + variant: Ipv4InputVariant.unified, ); }, ), @@ -330,6 +350,8 @@ class _PortRangeForwardingContentViewState } void _clearControllers() { + _isAddInitialized = false; + _selectedProtocol = 'Both'; _applicationTextController.clear(); _firstPortTextController.clear(); _lastPortTextController.clear(); @@ -404,8 +426,7 @@ class _PortRangeForwardingContentViewState firstExternalPort: int.tryParse(_firstPortTextController.text) ?? 0, lastExternalPort: int.tryParse(_lastPortTextController.text) ?? 0, internalServerIPAddress: _ipAddressTextController.text, - protocol: - ref.read(portRangeForwardingRuleProvider).rule?.protocol ?? 'Both', + protocol: _selectedProtocol, ); } } diff --git a/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_triggering_list_view.dart b/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_triggering_list_view.dart index 530019ca0..8f0c659d0 100644 --- a/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_triggering_list_view.dart +++ b/lib/page/advanced_settings/apps_and_gaming/ports/views/port_range_triggering_list_view.dart @@ -42,6 +42,7 @@ class _PortRangeTriggeringContentViewState // Editing state PortRangeTriggeringRuleUIModel? _editingRule; bool _isInitializing = false; + bool _isAddInitialized = false; StateSetter? _sheetStateSetter; // Validation errors @@ -124,7 +125,23 @@ class _PortRangeTriggeringContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(rule.description), editBuilder: (_, rule, setSheetState) { _sheetStateSetter = setSheetState; - if (_editingRule != rule) { + + final state = ref.read(portRangeTriggeringListProvider); + final isNewRule = !state.current.rules.contains(rule); + bool shouldInitialize = false; + + if (isNewRule) { + if (!_isAddInitialized) { + shouldInitialize = true; + _isAddInitialized = true; + } + } else { + if (_editingRule != rule) { + shouldInitialize = true; + } + } + + if (shouldInitialize) { _isInitializing = true; try { _editingRule = rule; @@ -317,6 +334,7 @@ class _PortRangeTriggeringContentViewState } void _clearControllers() { + _isAddInitialized = false; _applicationTextController.clear(); _firstTriggerPortController.clear(); _lastTriggerPortController.clear(); diff --git a/lib/page/advanced_settings/apps_and_gaming/ports/views/single_port_forwarding_list_view.dart b/lib/page/advanced_settings/apps_and_gaming/ports/views/single_port_forwarding_list_view.dart index 34611071e..e1ff6194b 100644 --- a/lib/page/advanced_settings/apps_and_gaming/ports/views/single_port_forwarding_list_view.dart +++ b/lib/page/advanced_settings/apps_and_gaming/ports/views/single_port_forwarding_list_view.dart @@ -42,6 +42,8 @@ class _SinglePortForwardingContentViewState // Editing state SinglePortForwardingRuleUIModel? _editingRule; bool _isInitializing = false; + bool _isAddInitialized = false; + String _selectedProtocol = 'Both'; StateSetter? _sheetStateSetter; // Validation errors @@ -121,7 +123,23 @@ class _SinglePortForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(rule.description), editBuilder: (_, rule, setSheetState) { _sheetStateSetter = setSheetState; - if (_editingRule != rule) { + + final state = ref.read(singlePortForwardingListProvider); + final isNewRule = !state.current.rules.contains(rule); + bool shouldInitialize = false; + + if (isNewRule) { + if (!_isAddInitialized) { + shouldInitialize = true; + _isAddInitialized = true; + } + } else { + if (_editingRule != rule) { + shouldInitialize = true; + } + } + + if (shouldInitialize) { _isInitializing = true; try { _editingRule = rule; @@ -129,6 +147,7 @@ class _SinglePortForwardingContentViewState _internalPortTextController.text = '${rule.internalPort}'; _externalPortTextController.text = '${rule.externalPort}'; _ipAddressTextController.text = rule.internalServerIPAddress; + _selectedProtocol = rule.protocol; _nameError = null; _ipError = null; _externalPortError = null; @@ -180,7 +199,6 @@ class _SinglePortForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(getProtocolTitle(context, rule.protocol)), editBuilder: (_, rule, setSheetState) { - final currentProtocol = rule.protocol; final protocolDisplayMap = { 'TCP': getProtocolTitle(context, 'TCP'), 'UDP': getProtocolTitle(context, 'UDP'), @@ -189,7 +207,7 @@ class _SinglePortForwardingContentViewState return AppDropdown( key: const Key('protocolDropdown'), items: protocolDisplayMap.values.toList(), - value: protocolDisplayMap[currentProtocol], + value: protocolDisplayMap[_selectedProtocol], hint: loc(context).protocol, onChanged: (displayValue) { if (displayValue != null) { @@ -197,7 +215,9 @@ class _SinglePortForwardingContentViewState .firstWhere((e) => e.value == displayValue, orElse: () => const MapEntry('Both', 'Both')) .key; + _selectedProtocol = protocolKey; _updateProtocol(protocolKey); + setSheetState(() {}); } }, ); @@ -210,11 +230,11 @@ class _SinglePortForwardingContentViewState cellBuilder: (_, rule) => AppText.bodyMedium(rule.internalServerIPAddress), editBuilder: (_, rule, setSheetState) { - return AppTextField( + return AppIpv4TextField( key: const Key('ipAddressTextField'), controller: _ipAddressTextController, - hintText: loc(context).ipAddress, errorText: _ipError, + variant: Ipv4InputVariant.unified, ); }, ), @@ -329,6 +349,8 @@ class _SinglePortForwardingContentViewState } void _clearControllers() { + _isAddInitialized = false; + _selectedProtocol = 'Both'; _applicationTextController.clear(); _internalPortTextController.clear(); _externalPortTextController.clear(); @@ -403,8 +425,7 @@ class _SinglePortForwardingContentViewState internalPort: int.tryParse(_internalPortTextController.text) ?? 0, externalPort: int.tryParse(_externalPortTextController.text) ?? 0, internalServerIPAddress: _ipAddressTextController.text, - protocol: - ref.read(singlePortForwardingRuleProvider).rule?.protocol ?? 'Both', + protocol: _selectedProtocol, ); } } diff --git a/lib/page/advanced_settings/apps_and_gaming/views/apps_and_gaming_view.dart b/lib/page/advanced_settings/apps_and_gaming/views/apps_and_gaming_view.dart index 2927d5d03..e933c7ee4 100644 --- a/lib/page/advanced_settings/apps_and_gaming/views/apps_and_gaming_view.dart +++ b/lib/page/advanced_settings/apps_and_gaming/views/apps_and_gaming_view.dart @@ -66,7 +66,6 @@ class _AppsGamingSettingsViewState extends ConsumerState final isDirty = ref.read(appsAndGamingProvider.notifier).isDirty(); return UiKitPageView.withSliver( title: loc(context).appsGaming, - padding: EdgeInsets.zero, tabController: _tabController, bottomBar: UiKitBottomBarConfig( isPositiveEnabled: diff --git a/lib/page/advanced_settings/firewall/views/firewall_view.dart b/lib/page/advanced_settings/firewall/views/firewall_view.dart index 029a5d90a..c798b29a4 100644 --- a/lib/page/advanced_settings/firewall/views/firewall_view.dart +++ b/lib/page/advanced_settings/firewall/views/firewall_view.dart @@ -61,7 +61,6 @@ class _FirewallViewState extends ConsumerState final isDirty = firewallState.isDirty || ipv6State.isDirty; return UiKitPageView.withSliver( - padding: EdgeInsets.zero, tabController: _tabController, title: loc(context).firewall, bottomBar: UiKitBottomBarConfig( diff --git a/lib/page/advanced_settings/firewall/views/ipv6_port_service_list_view.dart b/lib/page/advanced_settings/firewall/views/ipv6_port_service_list_view.dart index 57b2f02de..93e3a5400 100644 --- a/lib/page/advanced_settings/firewall/views/ipv6_port_service_list_view.dart +++ b/lib/page/advanced_settings/firewall/views/ipv6_port_service_list_view.dart @@ -38,6 +38,8 @@ class _Ipv6PortServiceListViewState // Track which rule is being edited to avoid reinitializing controllers IPv6PortServiceRuleUI? _editingRule; bool _isInitializing = false; + bool _isAddInitialized = false; + String _selectedProtocol = 'Both'; StateSetter? _sheetStateSetter; @override @@ -119,8 +121,24 @@ class _Ipv6PortServiceListViewState editBuilder: (_, rule, setSheetState) { // Store the sheet's StateSetter for validation updates _sheetStateSetter = setSheetState; + + final state = ref.read(ipv6PortServiceListProvider); + final isNewRule = !state.current.rules.contains(rule); + bool shouldInitialize = false; + + if (isNewRule) { + if (!_isAddInitialized) { + shouldInitialize = true; + _isAddInitialized = true; + } + } else { + if (_editingRule != rule) { + shouldInitialize = true; + } + } + // Only initialize when starting to edit a new rule - if (_editingRule != rule) { + if (shouldInitialize) { _isInitializing = true; try { _editingRule = rule; @@ -130,6 +148,8 @@ class _Ipv6PortServiceListViewState _lastPortTextController.text = '${rule.portRanges.firstOrNull?.lastPort ?? 0}'; _ipAddressTextController.text = rule.ipv6Address; + _selectedProtocol = + rule.portRanges.firstOrNull?.protocol ?? 'Both'; _nameError = null; _portRangeError = null; } finally { @@ -153,8 +173,6 @@ class _Ipv6PortServiceListViewState context, rule.portRanges.firstOrNull?.protocol ?? 'Both'), ), editBuilder: (_, rule, setSheetState) { - final currentProtocol = - rule.portRanges.firstOrNull?.protocol ?? 'Both'; // Map protocol values to display names final protocolDisplayMap = { 'TCP': getProtocolTitle(context, 'TCP'), @@ -164,7 +182,7 @@ class _Ipv6PortServiceListViewState return AppDropdown( key: const Key('protocol'), items: protocolDisplayMap.values.toList(), - value: protocolDisplayMap[currentProtocol], + value: protocolDisplayMap[_selectedProtocol], hint: loc(context).protocol, onChanged: (displayValue) { if (displayValue != null) { @@ -173,7 +191,9 @@ class _Ipv6PortServiceListViewState .firstWhere((e) => e.value == displayValue, orElse: () => const MapEntry('Both', 'Both')) .key; + _selectedProtocol = protocolKey; _updateProtocol(protocolKey); + setSheetState(() {}); } }, ); @@ -351,6 +371,8 @@ class _Ipv6PortServiceListViewState } void _clearControllers() { + _isAddInitialized = false; + _selectedProtocol = 'Both'; _applicationTextController.clear(); _firstPortTextController.clear(); _lastPortTextController.clear(); diff --git a/lib/page/advanced_settings/internet_settings/views/internet_settings_view.dart b/lib/page/advanced_settings/internet_settings/views/internet_settings_view.dart index 4a67ce908..6340dba5d 100644 --- a/lib/page/advanced_settings/internet_settings/views/internet_settings_view.dart +++ b/lib/page/advanced_settings/internet_settings/views/internet_settings_view.dart @@ -116,8 +116,6 @@ class _InternetSettingsViewState extends ConsumerState ]; final isDirty = ref.read(internetSettingsProvider.notifier).isDirty(); return UiKitPageView.withSliver( - padding: EdgeInsets.zero, - useMainPadding: false, title: loc(context).internetSettings.capitalizeWords(), bottomBar: isEditing ? UiKitBottomBarConfig( diff --git a/lib/page/advanced_settings/internet_settings/views/ipv4_connection_view.dart b/lib/page/advanced_settings/internet_settings/views/ipv4_connection_view.dart index 817f8f641..57a244464 100644 --- a/lib/page/advanced_settings/internet_settings/views/ipv4_connection_view.dart +++ b/lib/page/advanced_settings/internet_settings/views/ipv4_connection_view.dart @@ -102,7 +102,6 @@ class Ipv4ConnectionView extends StatelessWidget { key: const Key('ipv4EditButton'), icon: Icon( isEditing ? AppFontIcons.close : AppFontIcons.edit, - color: isEditing ? null : Theme.of(context).colorScheme.primary, ), onTap: isRemote ? null : onEditToggle, )); diff --git a/lib/page/advanced_settings/internet_settings/views/ipv6_connection_view.dart b/lib/page/advanced_settings/internet_settings/views/ipv6_connection_view.dart index 32e66f5d7..30a55847a 100644 --- a/lib/page/advanced_settings/internet_settings/views/ipv6_connection_view.dart +++ b/lib/page/advanced_settings/internet_settings/views/ipv6_connection_view.dart @@ -102,7 +102,6 @@ class Ipv6ConnectionView extends StatelessWidget { key: const Key('ipv6EditButton'), icon: Icon( isEditing ? AppFontIcons.close : AppFontIcons.edit, - color: isEditing ? null : Theme.of(context).colorScheme.primary, ), onTap: isRemote ? null : onEditToggle, )); diff --git a/lib/page/advanced_settings/internet_settings/widgets/base_widgets_mixin.dart b/lib/page/advanced_settings/internet_settings/widgets/base_widgets_mixin.dart index d0420810d..c732595df 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/base_widgets_mixin.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/base_widgets_mixin.dart @@ -21,7 +21,7 @@ mixin BaseWidgetsMixin { } Widget buildInfoCard(String title, String description) { - return AppCard( + return AppCard.noBorder( padding: EdgeInsets.symmetric( vertical: AppSpacing.md, ), @@ -51,6 +51,7 @@ mixin BaseWidgetsMixin { TextInputType keyboardType = TextInputType.text, bool obscureText = false, bool enable = true, + FocusNode? focusNode, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -60,6 +61,7 @@ mixin BaseWidgetsMixin { AppTextField( key: key, controller: controller, + focusNode: focusNode, onChanged: onChanged, errorText: errorText, keyboardType: keyboardType, diff --git a/lib/page/advanced_settings/internet_settings/widgets/optional_settings_form.dart b/lib/page/advanced_settings/internet_settings/widgets/optional_settings_form.dart index ef98d5cbe..511b323be 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/optional_settings_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/optional_settings_form.dart @@ -66,14 +66,22 @@ class _OptionalSettingsFormState extends ConsumerState { final oldState = ref.read(internetSettingsProvider).settings.original; final newState = ref.read(internetSettingsProvider).settings.current; - if (oldState.ipv4Setting.domainName != newState.ipv4Setting.domainName) { + // Fix: Compare against current controller text to avoid cursor reset + if ((newState.ipv4Setting.domainName ?? '') != _domainNameController.text) { _domainNameController.text = newState.ipv4Setting.domainName ?? ''; } + + // MTU Logic + final newMtuStr = '${newState.ipv4Setting.mtu}'; + if (newMtuStr != _mtuSizeController.text) { + _mtuSizeController.text = newMtuStr; + } + // Update local state for UI toggle if needed if (oldState.ipv4Setting.mtu != newState.ipv4Setting.mtu) { - _mtuSizeController.text = '${newState.ipv4Setting.mtu}'; isMtuAuto = newState.ipv4Setting.mtu == 0; } - if (oldState.macCloneAddress != newState.macCloneAddress) { + + if ((newState.macCloneAddress ?? '') != _macAddressCloneController.text) { _macAddressCloneController.text = newState.macCloneAddress ?? ''; } } @@ -203,14 +211,10 @@ class _OptionalSettingsFormState extends ConsumerState { onFocusChange: (hasFocus) { if (!hasFocus) { setState(() => _mtuSizeTouched = true); - if (int.parse(_mtuSizeController.text) < 576) { - _mtuSizeController.text = '576'; - notifier.updateMtu(576); - } } }, child: AppMinMaxInput( - key: const ValueKey('mtuManualSizeText'), + key: ValueKey('mtuManualSizeText_$isMtuAuto'), value: int.tryParse(_mtuSizeController.text), enabled: !isMtuAuto, label: loc(context).size, @@ -218,10 +222,18 @@ class _OptionalSettingsFormState extends ConsumerState { max: _getMaxMtu(ipv4Setting.ipv4ConnectionType), errorText: _mtuSizeTouched && !isMtuAuto && - int.tryParse(_mtuSizeController.text) == null + _isMtuInvalid( + _mtuSizeController.text, + _getMaxMtu(ipv4Setting.ipv4ConnectionType), + ) ? loc(context).invalidInput : null, onChanged: (value) { + if (!_mtuSizeTouched) { + setState(() { + _mtuSizeTouched = true; + }); + } _mtuSizeController.text = value?.toString() ?? ''; notifier.updateMtu(value ?? 0); }, @@ -387,4 +399,10 @@ class _OptionalSettingsFormState extends ConsumerState { int _getMaxMtu(String wanType) { return NetworkUtils.getMaxMtu(wanType); } + + bool _isMtuInvalid(String text, int max) { + final value = int.tryParse(text); + if (value == null) return true; + return value < 576 || value > max; + } } diff --git a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/ipv6/automatic_ipv6_form.dart b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/ipv6/automatic_ipv6_form.dart index 1b7e97453..103678573 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/ipv6/automatic_ipv6_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/ipv6/automatic_ipv6_form.dart @@ -58,25 +58,28 @@ class _AutomaticIPv6FormState extends BaseIPv6WanFormState { @override void didUpdateWidget(AutomaticIPv6Form oldWidget) { super.didUpdateWidget(oldWidget); - final oldIpv6Setting = - ref.read(internetSettingsProvider).settings.original.ipv6Setting; final newIpv6Setting = ref.read(internetSettingsProvider).settings.current.ipv6Setting; - if (oldIpv6Setting.ipv6Prefix != newIpv6Setting.ipv6Prefix) { + // Fix: Compare against current controller text to avoid cursor reset + if ((newIpv6Setting.ipv6Prefix ?? '') != _ipv6PrefixController.text) { _ipv6PrefixController.text = newIpv6Setting.ipv6Prefix ?? ''; } - if (oldIpv6Setting.ipv6PrefixLength != newIpv6Setting.ipv6PrefixLength) { - _ipv6PrefixLengthController.text = - newIpv6Setting.ipv6PrefixLength?.toString() ?? ''; + + final newPrefixLenStr = newIpv6Setting.ipv6PrefixLength?.toString() ?? ''; + if (newPrefixLenStr != _ipv6PrefixLengthController.text) { + _ipv6PrefixLengthController.text = newPrefixLenStr; } - if (oldIpv6Setting.ipv6BorderRelay != newIpv6Setting.ipv6BorderRelay) { + + if ((newIpv6Setting.ipv6BorderRelay ?? '') != + _ipv6BorderRelayController.text) { _ipv6BorderRelayController.text = newIpv6Setting.ipv6BorderRelay ?? ''; } - if (oldIpv6Setting.ipv6BorderRelayPrefixLength != - newIpv6Setting.ipv6BorderRelayPrefixLength) { - _ipv6BorderRelayPrefixLengthController.text = - newIpv6Setting.ipv6BorderRelayPrefixLength?.toString() ?? ''; + + final newRelayLenStr = + newIpv6Setting.ipv6BorderRelayPrefixLength?.toString() ?? ''; + if (newRelayLenStr != _ipv6BorderRelayPrefixLengthController.text) { + _ipv6BorderRelayPrefixLengthController.text = newRelayLenStr; } } @@ -111,7 +114,7 @@ class _AutomaticIPv6FormState extends BaseIPv6WanFormState { return Column( children: [ AppGap.md(), - AppCard( + AppCard.noBorder( padding: EdgeInsets.symmetric(vertical: AppSpacing.lg), child: Row( children: [ diff --git a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/l2tp_form.dart b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/l2tp_form.dart index 9897b7775..5b8e1a746 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/l2tp_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/l2tp_form.dart @@ -52,18 +52,17 @@ class _L2tpFormState extends BaseWanFormState { @override void didUpdateWidget(L2tpForm oldWidget) { super.didUpdateWidget(oldWidget); - final oldIpv4Setting = - ref.read(internetSettingsProvider).settings.original.ipv4Setting; final newIpv4Setting = ref.read(internetSettingsProvider).settings.current.ipv4Setting; - if (oldIpv4Setting.username != newIpv4Setting.username) { + // Fix: Compare against current controller text to avoid cursor reset + if ((newIpv4Setting.username ?? '') != _usernameController.text) { _usernameController.text = newIpv4Setting.username ?? ''; } - if (oldIpv4Setting.password != newIpv4Setting.password) { + if ((newIpv4Setting.password ?? '') != _passwordController.text) { _passwordController.text = newIpv4Setting.password ?? ''; } - if (oldIpv4Setting.serverIp != newIpv4Setting.serverIp) { + if ((newIpv4Setting.serverIp ?? '') != _serverIpController.text) { _serverIpController.text = newIpv4Setting.serverIp ?? ''; } } diff --git a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pppoe_form.dart b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pppoe_form.dart index 5190a1a23..e0901e941 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pppoe_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pppoe_form.dart @@ -23,6 +23,10 @@ class _PppoeFormState extends BaseWanFormState { late final TextEditingController _vlanIdController; late final TextEditingController _serviceNameController; + late final FocusNode _usernameFocusNode; + late final FocusNode _passwordFocusNode; + late final FocusNode _vlanIdFocusNode; + bool _usernameTouched = false; bool _passwordTouched = false; @@ -40,6 +44,33 @@ class _PppoeFormState extends BaseWanFormState { text: ipv4Setting.vlanId != null ? '${ipv4Setting.vlanId}' : ''); _serviceNameController = TextEditingController(text: ipv4Setting.serviceName ?? ''); + + // Initialize focus nodes with listeners + _usernameFocusNode = FocusNode() + ..addListener(() { + if (!_usernameFocusNode.hasFocus) + setState(() => _usernameTouched = true); + }); + _passwordFocusNode = FocusNode() + ..addListener(() { + if (!_passwordFocusNode.hasFocus) + setState(() => _passwordTouched = true); + }); + _vlanIdFocusNode = FocusNode() + ..addListener(() { + if (!_vlanIdFocusNode.hasFocus) { + final value = _vlanIdController.text; + if (value.isNotEmpty && int.parse(value) < 5) { + _vlanIdController.text = '5'; + final notifier = ref.read(internetSettingsProvider.notifier); + final ipv4Setting = + ref.read(internetSettingsProvider).settings.current.ipv4Setting; + notifier.updateIpv4Settings(ipv4Setting.copyWith( + vlanId: () => 5, + )); + } + } + }); } @override @@ -48,28 +79,34 @@ class _PppoeFormState extends BaseWanFormState { _passwordController.dispose(); _vlanIdController.dispose(); _serviceNameController.dispose(); + _usernameFocusNode.dispose(); + _passwordFocusNode.dispose(); + _vlanIdFocusNode.dispose(); super.dispose(); } @override void didUpdateWidget(PppoeForm oldWidget) { super.didUpdateWidget(oldWidget); - final oldIpv4Setting = - ref.read(internetSettingsProvider).settings.original.ipv4Setting; final newIpv4Setting = ref.read(internetSettingsProvider).settings.current.ipv4Setting; - if (oldIpv4Setting.username != newIpv4Setting.username) { + // Fix: Compare against current controller text to avoid cursor reset + // Only update controller if the new value is actually different from what's currently in the input + if ((newIpv4Setting.username ?? '') != _usernameController.text) { _usernameController.text = newIpv4Setting.username ?? ''; } - if (oldIpv4Setting.password != newIpv4Setting.password) { + if ((newIpv4Setting.password ?? '') != _passwordController.text) { _passwordController.text = newIpv4Setting.password ?? ''; } - if (oldIpv4Setting.vlanId != newIpv4Setting.vlanId) { - _vlanIdController.text = - newIpv4Setting.vlanId != null ? '${newIpv4Setting.vlanId}' : ''; + + final newVlanStr = + newIpv4Setting.vlanId != null ? '${newIpv4Setting.vlanId}' : ''; + if (newVlanStr != _vlanIdController.text) { + _vlanIdController.text = newVlanStr; } - if (oldIpv4Setting.serviceName != newIpv4Setting.serviceName) { + + if ((newIpv4Setting.serviceName ?? '') != _serviceNameController.text) { _serviceNameController.text = newIpv4Setting.serviceName ?? ''; } } @@ -109,76 +146,55 @@ class _PppoeFormState extends BaseWanFormState { children: [ Padding( padding: inputPadding, - child: Focus( - onFocusChange: (hasFocus) { - if (!hasFocus) setState(() => _usernameTouched = true); + child: buildEditableField( + key: const ValueKey('pppoeUsername'), + label: loc(context).username, + controller: _usernameController, + focusNode: _usernameFocusNode, + errorText: + _usernameTouched && (ipv4Setting.username?.isEmpty ?? true) + ? loc(context).invalidUsername + : null, + onChanged: (value) { + notifier.updateIpv4Settings(ipv4Setting.copyWith( + username: () => value, + )); }, - child: buildEditableField( - key: const ValueKey('pppoeUsername'), - label: loc(context).username, - controller: _usernameController, - errorText: - _usernameTouched && (ipv4Setting.username?.isEmpty ?? true) - ? loc(context).invalidUsername - : null, - onChanged: (value) { - notifier.updateIpv4Settings(ipv4Setting.copyWith( - username: () => value, - )); - }, - ), ), ), Padding( padding: inputPadding, - child: Focus( - onFocusChange: (hasFocus) { - if (!hasFocus) setState(() => _passwordTouched = true); + child: buildEditableField( + key: const ValueKey('pppoePassword'), + label: loc(context).password, + controller: _passwordController, + focusNode: _passwordFocusNode, + obscureText: true, + errorText: + _passwordTouched && (ipv4Setting.password?.isEmpty ?? true) + ? loc(context).invalidPassword + : null, + onChanged: (value) { + notifier.updateIpv4Settings(ipv4Setting.copyWith( + password: () => value, + )); }, - child: buildEditableField( - key: const ValueKey('pppoePassword'), - label: loc(context).password, - controller: _passwordController, - obscureText: true, - errorText: - _passwordTouched && (ipv4Setting.password?.isEmpty ?? true) - ? loc(context).invalidPassword - : null, - onChanged: (value) { - notifier.updateIpv4Settings(ipv4Setting.copyWith( - password: () => value, - )); - }, - ), ), ), Padding( padding: inputPadding, - child: Focus( - onFocusChange: (hasFocus) { - if (!hasFocus) { - final value = _vlanIdController.text; - if (value.isNotEmpty && int.parse(value) < 5) { - _vlanIdController.text = '5'; - notifier.updateIpv4Settings(ipv4Setting.copyWith( - vlanId: () => 5, - )); - } - } + child: AppMinMaxInput( + key: const ValueKey('pppoeVlanId'), + min: 5, + max: 4094, + label: loc(context).vlanIdOptional, + value: int.tryParse(_vlanIdController.text), + onChanged: (value) { + _vlanIdController.text = value?.toString() ?? ''; + notifier.updateIpv4Settings(ipv4Setting.copyWith( + vlanId: () => value, + )); }, - child: AppMinMaxInput( - key: const ValueKey('pppoeVlanId'), - min: 5, - max: 4094, - label: loc(context).vlanIdOptional, - value: int.tryParse(_vlanIdController.text), - onChanged: (value) { - _vlanIdController.text = value?.toString() ?? ''; - notifier.updateIpv4Settings(ipv4Setting.copyWith( - vlanId: () => value, - )); - }, - ), ), ), Padding( diff --git a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pptp_form.dart b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pptp_form.dart index fadd43159..2e5e069e4 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pptp_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/pptp_form.dart @@ -92,43 +92,45 @@ class _PptpFormState extends BaseWanFormState { @override void didUpdateWidget(PptpForm oldWidget) { super.didUpdateWidget(oldWidget); - final oldIpv4Setting = - ref.read(internetSettingsProvider).settings.original.ipv4Setting; final newIpv4Setting = ref.read(internetSettingsProvider).settings.current.ipv4Setting; + // Fix: Compare against current controller text to avoid cursor reset + // Update static fields - if (oldIpv4Setting.staticIpAddress != newIpv4Setting.staticIpAddress) { + if ((newIpv4Setting.staticIpAddress ?? '') != _ipAddressController.text) { _ipAddressController.text = newIpv4Setting.staticIpAddress ?? ''; } - if (oldIpv4Setting.networkPrefixLength != - newIpv4Setting.networkPrefixLength) { - _subnetController.text = newIpv4Setting.networkPrefixLength != null - ? NetworkUtils.prefixLengthToSubnetMask( - newIpv4Setting.networkPrefixLength!) - : ''; + + final newSubnet = newIpv4Setting.networkPrefixLength != null + ? NetworkUtils.prefixLengthToSubnetMask( + newIpv4Setting.networkPrefixLength!) + : ''; + if (newSubnet != _subnetController.text) { + _subnetController.text = newSubnet; } - if (oldIpv4Setting.staticGateway != newIpv4Setting.staticGateway) { + + if ((newIpv4Setting.staticGateway ?? '') != _gatewayController.text) { _gatewayController.text = newIpv4Setting.staticGateway ?? ''; } - if (oldIpv4Setting.staticDns1 != newIpv4Setting.staticDns1) { + if ((newIpv4Setting.staticDns1 ?? '') != _dns1Controller.text) { _dns1Controller.text = newIpv4Setting.staticDns1 ?? ''; } - if (oldIpv4Setting.staticDns2 != newIpv4Setting.staticDns2) { + if ((newIpv4Setting.staticDns2 ?? '') != _dns2Controller.text) { _dns2Controller.text = newIpv4Setting.staticDns2 ?? ''; } - if (oldIpv4Setting.staticDns3 != newIpv4Setting.staticDns3) { + if ((newIpv4Setting.staticDns3 ?? '') != _dns3Controller.text) { _dns3Controller.text = newIpv4Setting.staticDns3 ?? ''; } // Update PPTP fields - if (oldIpv4Setting.serverIp != newIpv4Setting.serverIp) { + if ((newIpv4Setting.serverIp ?? '') != _serverIpController.text) { _serverIpController.text = newIpv4Setting.serverIp ?? ''; } - if (oldIpv4Setting.username != newIpv4Setting.username) { + if ((newIpv4Setting.username ?? '') != _usernameController.text) { _usernameController.text = newIpv4Setting.username ?? ''; } - if (oldIpv4Setting.password != newIpv4Setting.password) { + if ((newIpv4Setting.password ?? '') != _passwordController.text) { _passwordController.text = newIpv4Setting.password ?? ''; } } diff --git a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/static_ip_form.dart b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/static_ip_form.dart index 48ba0b752..8f0ecb122 100644 --- a/lib/page/advanced_settings/internet_settings/widgets/wan_forms/static_ip_form.dart +++ b/lib/page/advanced_settings/internet_settings/widgets/wan_forms/static_ip_form.dart @@ -70,31 +70,32 @@ class _StaticIpFormState extends BaseWanFormState { @override void didUpdateWidget(StaticIpForm oldWidget) { super.didUpdateWidget(oldWidget); - final oldIpv4Setting = - ref.read(internetSettingsProvider).settings.original.ipv4Setting; final newIpv4Setting = ref.read(internetSettingsProvider).settings.current.ipv4Setting; - if (oldIpv4Setting.staticIpAddress != newIpv4Setting.staticIpAddress) { + // Fix: Compare against current controller text to avoid cursor reset + if ((newIpv4Setting.staticIpAddress ?? '') != _ipAddressController.text) { _ipAddressController.text = newIpv4Setting.staticIpAddress ?? ''; } - if (oldIpv4Setting.networkPrefixLength != - newIpv4Setting.networkPrefixLength) { - _subnetController.text = newIpv4Setting.networkPrefixLength != null - ? NetworkUtils.prefixLengthToSubnetMask( - newIpv4Setting.networkPrefixLength!) - : ''; + + final newSubnet = newIpv4Setting.networkPrefixLength != null + ? NetworkUtils.prefixLengthToSubnetMask( + newIpv4Setting.networkPrefixLength!) + : ''; + if (newSubnet != _subnetController.text) { + _subnetController.text = newSubnet; } - if (oldIpv4Setting.staticGateway != newIpv4Setting.staticGateway) { + + if ((newIpv4Setting.staticGateway ?? '') != _gatewayController.text) { _gatewayController.text = newIpv4Setting.staticGateway ?? ''; } - if (oldIpv4Setting.staticDns1 != newIpv4Setting.staticDns1) { + if ((newIpv4Setting.staticDns1 ?? '') != _dns1Controller.text) { _dns1Controller.text = newIpv4Setting.staticDns1 ?? ''; } - if (oldIpv4Setting.staticDns2 != newIpv4Setting.staticDns2) { + if ((newIpv4Setting.staticDns2 ?? '') != _dns2Controller.text) { _dns2Controller.text = newIpv4Setting.staticDns2 ?? ''; } - if (oldIpv4Setting.staticDns3 != newIpv4Setting.staticDns3) { + if ((newIpv4Setting.staticDns3 ?? '') != _dns3Controller.text) { _dns3Controller.text = newIpv4Setting.staticDns3 ?? ''; } } diff --git a/lib/page/advanced_settings/local_network_settings/views/local_network_settings_view.dart b/lib/page/advanced_settings/local_network_settings/views/local_network_settings_view.dart index 99e26f0d8..d54f4ce2b 100644 --- a/lib/page/advanced_settings/local_network_settings/views/local_network_settings_view.dart +++ b/lib/page/advanced_settings/local_network_settings/views/local_network_settings_view.dart @@ -89,15 +89,18 @@ class _LocalNetworkSettingsViewState ref.listen(localNetworkSettingProvider, (previous, next) { if (previous?.settings.current.hostName != - next.settings.current.hostName) { + next.settings.current.hostName && + hostNameController.text != next.settings.current.hostName) { hostNameController.text = next.settings.current.hostName; } if (previous?.settings.current.ipAddress != - next.settings.current.ipAddress) { + next.settings.current.ipAddress && + ipAddressController.text != next.settings.current.ipAddress) { ipAddressController.text = next.settings.current.ipAddress; } if (previous?.settings.current.subnetMask != - next.settings.current.subnetMask) { + next.settings.current.subnetMask && + subnetMaskController.text != next.settings.current.subnetMask) { subnetMaskController.text = next.settings.current.subnetMask; } }); @@ -108,9 +111,7 @@ class _LocalNetworkSettingsViewState _dhcpServerView(state), ]; return UiKitPageView.withSliver( - padding: EdgeInsets.zero, tabController: _tabController, - useMainPadding: false, bottomBar: UiKitBottomBarConfig( isPositiveEnabled: _notifier.isDirty() && !_hasError(state), onPositiveTap: _saveSettings, diff --git a/lib/page/advanced_settings/static_routing/static_routing_view.dart b/lib/page/advanced_settings/static_routing/static_routing_view.dart index 87510e763..ca20f71dc 100644 --- a/lib/page/advanced_settings/static_routing/static_routing_view.dart +++ b/lib/page/advanced_settings/static_routing/static_routing_view.dart @@ -39,6 +39,7 @@ class _StaticRoutingViewState extends ConsumerState StaticRouteEntryUIModel? _editingRule; RoutingSettingInterface _selectedInterface = RoutingSettingInterface.lan; bool _isInitializing = false; + bool _isAddInitialized = false; StateSetter? _sheetStateSetter; // Validation errors @@ -181,11 +182,11 @@ class _StaticRoutingViewState extends ConsumerState label: loc(context).destinationIPAddress, cellBuilder: (_, rule) => AppText.bodyMedium(rule.destinationIP), editBuilder: (_, rule, setSheetState) { - return AppTextField( + return AppIpv4TextField( key: const Key('destinationIP'), controller: _destinationIPController, - hintText: loc(context).destinationIPAddress, errorText: _destIpError, + variant: Ipv4InputVariant.unified, ); }, ), @@ -195,11 +196,11 @@ class _StaticRoutingViewState extends ConsumerState label: loc(context).subnetMask, cellBuilder: (_, rule) => AppText.bodyMedium(rule.subnetMask), editBuilder: (_, rule, setSheetState) { - return AppTextField( + return AppIpv4TextField( key: const Key('subnetMask'), controller: _subnetMaskController, - hintText: loc(context).subnetMask, errorText: _subnetError, + variant: Ipv4InputVariant.unified, ); }, ), @@ -210,11 +211,11 @@ class _StaticRoutingViewState extends ConsumerState cellBuilder: (_, rule) => AppText.bodyMedium(rule.gateway.isEmpty ? '--' : rule.gateway), editBuilder: (_, rule, setSheetState) { - return AppTextField( + return AppIpv4TextField( key: const Key('gateway'), controller: _gatewayController, - hintText: loc(context).gateway, errorText: _gatewayError, + variant: Ipv4InputVariant.unified, ); }, ), @@ -252,7 +253,22 @@ class _StaticRoutingViewState extends ConsumerState } void _initEditingState(StaticRouteEntryUIModel rule) { - if (_editingRule != rule) { + bool shouldInitialize = false; + final state = ref.read(staticRoutingProvider); + final isNewRule = !state.current.entries.contains(rule); + + if (isNewRule) { + if (!_isAddInitialized) { + shouldInitialize = true; + _isAddInitialized = true; + } + } else { + if (_editingRule != rule) { + shouldInitialize = true; + } + } + + if (shouldInitialize) { _editingRule = rule; Future.microtask(() { _isInitializing = true; @@ -424,6 +440,7 @@ class _StaticRoutingViewState extends ConsumerState } void _clearControllers() { + _isAddInitialized = false; _routerNameController.clear(); _destinationIPController.clear(); _subnetMaskController.clear(); diff --git a/lib/page/ai_assistant/views/router_assistant_view.dart b/lib/page/ai_assistant/views/router_assistant_view.dart new file mode 100644 index 000000000..89082ca27 --- /dev/null +++ b/lib/page/ai_assistant/views/router_assistant_view.dart @@ -0,0 +1,479 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:generative_ui/generative_ui.dart'; +import 'package:ui_kit_library/ui_kit.dart'; + +import 'package:privacy_gui/ai/_ai.dart'; +import 'package:privacy_gui/core/jnap/router_repository.dart'; + +/// Provider for the command provider. +final routerCommandProviderProvider = Provider((ref) { + final router = ref.watch(routerRepositoryProvider); + return JnapCommandProvider(router); +}); + +/// Main view for the Router AI Assistant. +/// +/// This provides a chat interface for users to interact with their router +/// using natural language. +class RouterAssistantView extends ConsumerStatefulWidget { + const RouterAssistantView({super.key}); + + @override + ConsumerState createState() => + _RouterAssistantViewState(); +} + +class _RouterAssistantViewState extends ConsumerState { + late final IComponentRegistry _registry; + late final IConversationGenerator _generator; + final _inputController = TextEditingController(); + final _scrollController = ScrollController(); + + final List<_ChatMessage> _messages = []; + bool _isLoading = false; + bool _isMock = false; + + @override + void initState() { + super.initState(); + _registry = RouterComponentRegistry.create(); + _initGenerator(); + } + + void _initGenerator() { + final commandProvider = ref.read(routerCommandProviderProvider); + + // Try to create AWS generator, fallback to mock + IConversationGenerator baseGenerator; + try { + final awsConfig = AWSConfig.fromEnvironment(); + baseGenerator = AwsContentGenerator(config: awsConfig); + _isMock = false; + } catch (e) { + debugPrint('RouterAssistantView: Using mock generator: $e'); + baseGenerator = _MockConversationGenerator(); + _isMock = true; + } + + _generator = RouterAgentOrchestrator( + llmGenerator: baseGenerator, + commandProvider: commandProvider, + onConfirmationRequired: _handleConfirmationRequired, + ); + } + + @override + void dispose() { + _inputController.dispose(); + _scrollController.dispose(); + super.dispose(); + } + + void _handleConfirmationRequired( + RouterCommand command, Map params) { + // Show confirmation dialog + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Confirmation Required'), + content: + Text('Are you sure you want to execute "${command.description}"?'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context); + _executeConfirmedCommand(command.name, params); + }, + child: const Text('Confirm'), + ), + ], + ), + ); + } + + Future _executeConfirmedCommand( + String commandName, Map params) async { + final orchestrator = _generator as RouterAgentOrchestrator; + try { + final result = + await orchestrator.executeConfirmedCommand(commandName, params); + if (result.success) { + _addMessage('✅ Operation completed', isUser: false); + } else { + _addMessage('❌ Operation failed: ${result.error}', isUser: false); + } + } catch (e) { + _addMessage('❌ Execution error: $e', isUser: false); + } + } + + void _addMessage(String text, {required bool isUser, bool isA2UI = false}) { + setState(() { + _messages.add(_ChatMessage( + text: text, + isUser: isUser, + isA2UI: isA2UI, + )); + }); + _scrollToBottom(); + } + + void _scrollToBottom() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_scrollController.hasClients) { + _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ); + } + }); + } + + Future _sendMessage() async { + final text = _inputController.text.trim(); + if (text.isEmpty || _isLoading) return; + + _inputController.clear(); + _addMessage(text, isUser: true); + + setState(() { + _isLoading = true; + }); + + try { + final response = await _generator.generateWithHistory( + _messages + .map((m) => m.isUser + ? ChatMessage.user(m.text) + : ChatMessage.assistant( + LLMResponse( + id: 'history', + model: 'history', + content: [TextBlock(text: m.text)], + ), + )) // Use assistant role for history + .toList(), + ); + + // Process response content + for (final block in response.content) { + if (block is TextBlock) { + // Check for A2UI content + if (A2UIResponseRenderer.containsA2UI(block.text)) { + _addMessage(block.text, isUser: false, isA2UI: true); + } else { + _addMessage(block.text, isUser: false); + } + } + } + } catch (e) { + _addMessage('❌ Error occurred: $e', isUser: false); + } finally { + setState(() { + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Row( + children: [ + const Text('AI Router Assistant'), + const SizedBox(width: 8), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: _isMock ? Colors.orange : Colors.green, + borderRadius: BorderRadius.circular(12), + ), + child: Text( + _isMock ? 'Mock' : 'Live', + style: const TextStyle( + color: Colors.white, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + actions: [ + IconButton( + icon: const Icon(Icons.refresh), + onPressed: () { + setState(() { + _messages.clear(); + }); + if (_generator is RouterAgentOrchestrator) { + (_generator as RouterAgentOrchestrator).clearCache(); + } + }, + tooltip: 'Clear conversation', + ), + ], + ), + body: Column( + children: [ + Expanded( + child: _buildChatArea(), + ), + _buildInputArea(), + ], + ), + ); + } + + Widget _buildChatArea() { + if (_messages.isEmpty) { + return _buildWelcomeScreen(); + } + + return ListView.builder( + controller: _scrollController, + padding: const EdgeInsets.all(16), + itemCount: _messages.length + (_isLoading ? 1 : 0), + itemBuilder: (context, index) { + if (index == _messages.length) { + return _buildLoadingIndicator(); + } + return _buildMessageBubble(_messages[index]); + }, + ); + } + + Widget _buildLoadingIndicator() { + return const Padding( + padding: EdgeInsets.all(16), + child: Center( + child: AppLoader(), + ), + ); + } + + Widget _buildWelcomeScreen() { + final theme = Theme.of(context); + + return Center( + child: Padding( + padding: const EdgeInsets.all(24), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.router, + size: 64, + color: theme.colorScheme.primary.withValues(alpha: 0.5), + ), + const SizedBox(height: 16), + AppText.headline('AI Router Assistant'), + const SizedBox(height: 8), + AppText.body( + 'I can help you check network status, manage connected devices, or adjust WiFi settings.\nTry asking "Show all connected devices" or "What is my WiFi password?"', + textAlign: TextAlign.center, + color: theme.colorScheme.onSurface.withValues(alpha: 0.7), + ), + ], + ), + ), + ); + } + + Widget _buildMessageBubble(_ChatMessage message) { + return Padding( + padding: const EdgeInsets.only(bottom: 12), + child: Row( + mainAxisAlignment: + message.isUser ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + // Add spacer on the left for USER messages (to push them right) + // For BOT messages, we want them on the left, so no left spacer (or maybe a small one if needed, but usually spacer is for the empty side) + if (message.isUser) const Spacer(flex: 1), + Flexible( + flex: 3, + child: message.isA2UI + ? A2UIResponseRenderer( + content: message.text, + registry: _registry, + onAction: (data) { + debugPrint('A2UI Action: $data'); + final action = data['action'] as String?; + if (action != null) { + _addMessage('Executing action: $action', isUser: true); + } + }, + ) + : AppSurface( + variant: message.isUser + ? SurfaceVariant.highlight + : SurfaceVariant.elevated, + child: Padding( + padding: const EdgeInsets.all(12), + child: AppText.body(message.text), + ), + ), + ), + // Add spacer on the right for BOT messages (to keep them left) + if (!message.isUser) const Spacer(flex: 1), + ], + ), + ); + } + + Widget _buildInputArea() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + color: Theme.of(context).colorScheme.outline.withValues(alpha: 0.2), + ), + ), + ), + child: SafeArea( + child: Row( + children: [ + Expanded( + child: AppTextField( + controller: _inputController, + hintText: 'Type a message...', + textInputAction: TextInputAction.send, + onSubmitted: (_) => _sendMessage(), + ), + ), + const SizedBox(width: 8), + AppIconButton( + icon: const Icon(Icons.send), + onTap: _isLoading ? null : _sendMessage, + ), + ], + ), + ), + ); + } +} + +/// Simple chat message model. +class _ChatMessage { + final String text; + final bool isUser; + final bool isA2UI; + + _ChatMessage({ + required this.text, + required this.isUser, + this.isA2UI = false, + }); +} + +/// Mock conversation generator for demo without AWS credentials. +class _MockConversationGenerator implements IConversationGenerator { + @override + Future generateWithHistory( + List messages, { + List? tools, + String? systemPrompt, + bool forceToolUse = false, + }) async { + await Future.delayed(const Duration(milliseconds: 500)); + + final lastMessage = messages.lastWhere( + (m) => m.role == ChatRole.user, + orElse: () => ChatMessage.user(''), + ); + + final userText = (lastMessage.content as String).toLowerCase(); + + // Simple keyword matching for demo + if (userText.contains('設備') || userText.contains('device')) { + return _createDeviceListResponse(); + } else if (userText.contains('wifi') || userText.contains('密碼')) { + return _createWifiResponse(); + } else if (userText.contains('網路') || userText.contains('狀態')) { + return _createStatusResponse(); + } + + return LLMResponse( + id: 'mock-${DateTime.now().millisecondsSinceEpoch}', + model: 'mock', + content: [ + TextBlock( + text: + 'Hello! I am your AI Router Assistant. You can ask me about connected devices, WiFi settings, or network status.'), + ], + ); + } + + LLMResponse _createDeviceListResponse() { + const a2ui = ''' +{"surfaceUpdate":{"surfaceId":"main","components":[ + {"id":"root","type":"Column","childIds":["header","list"]}, + {"id":"header","type":"AppText","properties":{"text":"Connected Devices","variant":"headline"}}, + {"id":"list","type":"DeviceListView","properties":{"devices":{"boundPath":"/data/devices"}}} +]}} +{"dataModelUpdate":{"surfaceId":"main","contents":[ + {"path":"/data/devices","value":[ + {"name":"iPhone 15 Pro","ip":"192.168.1.101","connectionType":"5GHz"}, + {"name":"MacBook Pro","ip":"192.168.1.102","connectionType":"5GHz"}, + {"name":"Smart TV","ip":"192.168.1.103","connectionType":"2.4GHz"} + ]} +]}} +{"beginRendering":{"surfaceId":"main","root":"root"}} +'''; + + return LLMResponse( + id: 'mock-devices', + model: 'mock', + content: [TextBlock(text: a2ui)], + ); + } + + LLMResponse _createWifiResponse() { + const a2ui = ''' +{"surfaceUpdate":{"surfaceId":"main","components":[ + {"id":"root","type":"WifiSettingsCard","properties":{ + "ssid":"MyNetwork", + "password":"12345678", + "securityMode":"WPA2-Personal", + "band":"2.4GHz + 5GHz" + }} +]}} +{"beginRendering":{"surfaceId":"main","root":"root"}} +'''; + + return LLMResponse( + id: 'mock-wifi', + model: 'mock', + content: [TextBlock(text: a2ui)], + ); + } + + LLMResponse _createStatusResponse() { + const a2ui = ''' +{"surfaceUpdate":{"surfaceId":"main","components":[ + {"id":"root","type":"NetworkStatusCard","properties":{ + "wanStatus":"Connected", + "connectedDevices":8, + "uploadSpeed":"50 Mbps", + "downloadSpeed":"100 Mbps" + }} +]}} +{"beginRendering":{"surfaceId":"main","root":"root"}} +'''; + + return LLMResponse( + id: 'mock-status', + model: 'mock', + content: [TextBlock(text: a2ui)], + ); + } +} diff --git a/lib/page/components/composed/app_list_card.dart b/lib/page/components/composed/app_list_card.dart index 22b3535f3..9cb3ddfb1 100644 --- a/lib/page/components/composed/app_list_card.dart +++ b/lib/page/components/composed/app_list_card.dart @@ -82,7 +82,6 @@ class AppListCard extends StatelessWidget { VoidCallback? onTap, EdgeInsets? padding, Color? color, - Color? borderColor, EdgeInsets? margin, bool selectableDescription = false, }) { @@ -98,8 +97,8 @@ class AppListCard extends StatelessWidget { vertical: AppSpacing.lg, horizontal: AppSpacing.xxl, ), - color: color, - borderColor: borderColor, + color: color ?? Colors.transparent, + borderColor: Colors.transparent, margin: margin, selectableDescription: selectableDescription, ); @@ -119,10 +118,27 @@ class AppListCard extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context).extension(); + + // Determine if border should be hidden + final hideBorder = !showBorder || borderColor == Colors.transparent; + + // Create style override if needed + SurfaceStyle? effectiveStyle; + if (theme != null && (hideBorder || color != null || borderColor != null)) { + effectiveStyle = theme.surfaceBase.copyWith( + borderWidth: hideBorder ? 0 : null, + borderColor: (!hideBorder && borderColor != null) ? borderColor : null, + backgroundColor: color, + shadows: const [], + ); + } + return Container( margin: margin, child: AppCard( onTap: onTap, + style: effectiveStyle, padding: padding ?? EdgeInsets.symmetric( vertical: AppSpacing.md, diff --git a/lib/page/components/layouts/idle_checker.dart b/lib/page/components/layouts/idle_checker.dart index 40dbe6d98..c98da3032 100644 --- a/lib/page/components/layouts/idle_checker.dart +++ b/lib/page/components/layouts/idle_checker.dart @@ -50,20 +50,15 @@ class _IdleCheckerState extends State { @override Widget build(BuildContext context) { - return MouseRegion( - onHover: (event) { - if (_debounce?.isActive ?? false) _debounce?.cancel(); - _debounce = Timer(const Duration(milliseconds: 500), () { - handleUserInteraction(); - }); - }, - child: GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - handleUserInteraction(); - }, - onPanDown: (details) { - handleUserInteraction(); + return Listener( + behavior: HitTestBehavior.translucent, + onPointerDown: (_) => handleUserInteraction(), + child: MouseRegion( + onHover: (event) { + if (_debounce?.isActive ?? false) _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 500), () { + handleUserInteraction(); + }); }, child: widget.child, ), diff --git a/lib/page/components/styled/general_settings_widget/general_settings_widget.dart b/lib/page/components/styled/general_settings_widget/general_settings_widget.dart index 512403f68..acd2db04d 100644 --- a/lib/page/components/styled/general_settings_widget/general_settings_widget.dart +++ b/lib/page/components/styled/general_settings_widget/general_settings_widget.dart @@ -32,6 +32,9 @@ class _GeneralSettingsWidgetState extends ConsumerState { LoginType.none; final isRemote = loginType == LoginType.remote; + // Watch Theme.of(context) to trigger rebuild when global theme changes + Theme.of(context); + // Use dark theme's color scheme for icon color final darkTheme = getIt.get(instanceName: 'darkThemeData'); final colorScheme = darkTheme.colorScheme; diff --git a/lib/page/components/styled/menus/widgets/bottom_navigation_menu.dart b/lib/page/components/styled/menus/widgets/bottom_navigation_menu.dart index d3117da94..f52cbe05b 100644 --- a/lib/page/components/styled/menus/widgets/bottom_navigation_menu.dart +++ b/lib/page/components/styled/menus/widgets/bottom_navigation_menu.dart @@ -49,6 +49,9 @@ class _BottomNavigationMenuState extends State { .map((e) => _createNavItem(e)) .toList(); + // Watch Theme.of(context) to trigger rebuild when global theme changes + Theme.of(context); + // Force dark theme for AppNavigationBar final darkTheme = getIt.get(instanceName: 'darkThemeData'); return Theme( diff --git a/lib/page/components/styled/menus/widgets/top_navigation_menu.dart b/lib/page/components/styled/menus/widgets/top_navigation_menu.dart index bde333ed3..3e4c346ae 100644 --- a/lib/page/components/styled/menus/widgets/top_navigation_menu.dart +++ b/lib/page/components/styled/menus/widgets/top_navigation_menu.dart @@ -26,6 +26,9 @@ class _TopNavigationMenuState extends State { @override Widget build(BuildContext context) { + // Watch Theme.of(context) to trigger rebuild when global theme changes + Theme.of(context); + // Use dark theme for navigation chips final darkTheme = getIt.get(instanceName: 'darkThemeData'); final selectedIndex = diff --git a/lib/page/components/styled/top_bar.dart b/lib/page/components/styled/top_bar.dart index ea6127948..3ca58c262 100644 --- a/lib/page/components/styled/top_bar.dart +++ b/lib/page/components/styled/top_bar.dart @@ -49,6 +49,9 @@ class _TopBarState extends ConsumerState with DebugObserver { final expiredCountdown = isRemote ? ref.watch(remoteClientProvider).expiredCountdown : null; + // Watch Theme.of(context) to trigger rebuild when global theme changes + Theme.of(context); + // Use dark theme's color scheme for TopBar final darkTheme = getIt.get(instanceName: 'darkThemeData'); final colorScheme = darkTheme.colorScheme; diff --git a/lib/page/components/ui_kit_page_view.dart b/lib/page/components/ui_kit_page_view.dart index 6d03b2666..7fba2b06c 100644 --- a/lib/page/components/ui_kit_page_view.dart +++ b/lib/page/components/ui_kit_page_view.dart @@ -5,7 +5,7 @@ import 'package:privacy_gui/localization/localization_hook.dart'; import 'package:privacy_gui/page/components/styled/top_bar.dart'; import 'package:ui_kit_library/ui_kit.dart'; -const double kDefaultToolbarHeight = 80; +const double kDefaultToolbarHeight = kToolbarHeight; // 56 const double kDefaultBottomHeight = 80; /// Custom AppBar styles @@ -372,7 +372,7 @@ class _UiKitPageViewState extends ConsumerState { // Wrap in SliverToBoxAdapter for sliver compatibility headerWidget = SliverToBoxAdapter( child: SizedBox( - height: 80, // Fixed height to match PreferredSize + height: kDefaultToolbarHeight, // Use constant for consistency child: topBarContent, ), ); @@ -461,7 +461,7 @@ class _UiKitPageViewState extends ConsumerState { // Create default TopBar for PrivacyGUI integration return const PreferredSize( - preferredSize: Size.fromHeight(80), + preferredSize: Size.fromHeight(kDefaultToolbarHeight), child: TopBar(), ); } diff --git a/lib/page/dashboard/views/components/internet_status.dart b/lib/page/dashboard/views/components/internet_status.dart index e82b03e52..0083bef77 100644 --- a/lib/page/dashboard/views/components/internet_status.dart +++ b/lib/page/dashboard/views/components/internet_status.dart @@ -101,9 +101,9 @@ class _InternetConnectionWidgetState return Padding( padding: const EdgeInsets.all(0.0), child: AppIconButton( - icon: AppIcon.font(AppFontIcons.refresh, - color: - Theme.of(context).colorScheme.primary), + icon: AppIcon.font( + AppFontIcons.refresh, + ), onTap: () { controller.repeat(); ref diff --git a/lib/page/dashboard/views/components/loading_tile.dart b/lib/page/dashboard/views/components/loading_tile.dart index 80fb0a8e6..fa71415ef 100644 --- a/lib/page/dashboard/views/components/loading_tile.dart +++ b/lib/page/dashboard/views/components/loading_tile.dart @@ -20,82 +20,66 @@ class LoadingTile extends StatefulWidget { State createState() => _LoadingTileState(); } -class _LoadingTileState extends State - with SingleTickerProviderStateMixin { - late AnimationController _controller; - late Animation _animation; - +class _LoadingTileState extends State { @override void initState() { super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 1500), - vsync: this, - )..repeat(); - - _animation = CurvedAnimation( - parent: _controller, - curve: Curves.easeInOut, - ); } @override void dispose() { - _controller.dispose(); super.dispose(); } - Widget _createSkeletonFromWidget(Widget widget) { - if (widget is Text) { - return Container( - width: widget.data?.length != null ? widget.data!.length * 8.0 : 100, - height: 16, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + Widget _createSkeletonFromWidget(Widget w) { + // Determine effective colors from widget properties or context if needed, + // but AppSkeleton handles theme-aware colors by default. + // If specific overrides are passed to LoadingTile, we can use them. + final baseColor = widget.baseColor; + final highlightColor = widget.shimmerColor; + + if (w is Text) { + return AppSkeleton.text( + width: w.data?.length != null ? w.data!.length * 8.0 : 100, + baseColor: baseColor, + highlightColor: highlightColor, ); - } else if (widget is AppText) { - // AppText from ui_kit doesn't expose text property, use fixed size - return Container( + } else if (w is AppText) { + // AppText from ui_kit doesn't expose text property easily without reflection or extending, + // assume a standard width or try to infer. For now use fixed size. + return AppSkeleton.text( width: 100, - height: 16, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ); - } else if (widget is Icon) { - return Container( + } else if (w is Icon) { + return AppSkeleton( width: 24, height: 24, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ); - } else if (widget is Image) { - return Container( - width: widget.width, - height: widget.height, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + } else if (w is Image) { + return AppSkeleton( + width: w.width, + height: w.height, + baseColor: baseColor, + highlightColor: highlightColor, ); - } else if (widget is AppSwitch) { - return AppSwitch( - value: widget.value, - onChanged: null, + } else if (w is AppSwitch) { + // Simulate switch shape + return AppSkeleton.capsule( + width: 40, + height: 20, // Approx switch size + baseColor: baseColor, + highlightColor: highlightColor, ); } - return Container( + return AppSkeleton( width: 100, height: 24, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ); } @@ -125,7 +109,6 @@ class _LoadingTileState extends State child.children.map((w) => _buildSkeletonFromChild(w)).toList(), ); } else if (child is AppCard) { - // AppCard from ui_kit doesn't expose margin property return Container( padding: child.padding, child: _buildSkeletonFromChild(child.child), @@ -137,8 +120,6 @@ class _LoadingTileState extends State child: _buildSkeletonFromChild(child.child ?? Container()), ); } else if (child is AppResponsiveLayout) { - // Note: child.desktop/mobile are now builders, so we create new builders - // that wrap the skeleton versions return AppResponsiveLayout( desktop: (ctx) => _buildSkeletonFromChild(child.desktop(ctx)), mobile: (ctx) => _buildSkeletonFromChild(child.mobile(ctx)), @@ -148,18 +129,35 @@ class _LoadingTileState extends State child: _buildSkeletonFromChild(child.child), ); } else if (child is Container) { - return Container( - width: child.constraints?.maxWidth, - height: child.constraints?.maxHeight, - padding: child.padding, - margin: child.margin, - constraints: child.constraints, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), - child: _buildSkeletonFromChild(child.child ?? Container()), - ); + // If container has child, recurse. If it's a spacer/block, replace with skeleton (via _createSkeletonFromWidget implicitly if we return it directly, but here we recurse) + // Actually, if 'child' is null, we should probably render a skeleton for the container's box. + if (child.child != null) { + return Container( + width: child.constraints?.maxWidth, + height: child.constraints?.maxHeight, + padding: child.padding, + margin: child.margin, + constraints: child.constraints, + // We don't want the container's background color in skeleton mode, usually + decoration: const BoxDecoration(color: Colors.transparent), + child: _buildSkeletonFromChild(child.child!), + ); + } else { + // Empty container usually acts as spacer or block + // Use maxWidth/maxHeight if bounded, else custom default + double? w = child.constraints?.maxWidth; + double? h = child.constraints?.maxHeight; + + if (w == double.infinity || w == null) w = 100; + if (h == double.infinity || h == null) h = 24; + + return AppSkeleton( + width: w, + height: h, + baseColor: widget.baseColor, + highlightColor: widget.shimmerColor, + ); + } } else if (child is Table) { return Table( border: const TableBorder(), @@ -179,102 +177,44 @@ class _LoadingTileState extends State @override Widget build(BuildContext context) { - final baseColor = widget.baseColor ?? - Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withValues(alpha: 0.3); - final shimmerColor = widget.shimmerColor ?? - Theme.of(context).colorScheme.primary.withValues(alpha: 0.5); - return widget.isLoading - ? AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return Container( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [ - baseColor, - shimmerColor, - baseColor, - ], - stops: [ - 0.0, - _animation.value, - 1.0, - ], - ), - ), - child: widget.child != null - ? _buildSkeletonFromChild(widget.child!) - : _buildDefaultSkeleton(), - ); - }, - ) + ? (widget.child != null + ? _buildSkeletonFromChild(widget.child!) + : _buildDefaultSkeleton()) : widget.child ?? const SizedBox.shrink(); } Widget _buildDefaultSkeleton() { + final baseColor = widget.baseColor; + final highlightColor = widget.shimmerColor; + return Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( + AppSkeleton( width: double.infinity, height: 24, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ), AppGap.lg(), - Container( + AppSkeleton( width: 200, height: 16, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ), AppGap.sm(), - Container( + AppSkeleton( width: 150, height: 16, - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(4), - ), + baseColor: baseColor, + highlightColor: highlightColor, ), ], ), ); } } - -class FillPainter extends CustomPainter { - final Color fillColor; - final double fillAmount; - - FillPainter({ - required this.fillColor, - required this.fillAmount, - }); - - @override - void paint(Canvas canvas, Size size) { - final fillHeight = size.height * fillAmount; - final rect = - Rect.fromLTWH(0, size.height - fillHeight, size.width, fillHeight); - canvas.clipRect(rect); - } - - @override - bool shouldRepaint(FillPainter oldDelegate) { - return oldDelegate.fillAmount != fillAmount || - oldDelegate.fillColor != fillColor; - } -} diff --git a/lib/page/dashboard/views/components/networks.dart b/lib/page/dashboard/views/components/networks.dart index 71fd13282..f8b260f57 100644 --- a/lib/page/dashboard/views/components/networks.dart +++ b/lib/page/dashboard/views/components/networks.dart @@ -91,11 +91,19 @@ class _DashboardNetworksState extends ConsumerState { showStatusIndicator: true, titleBuilder: (meshNode) => meshNode.name, subtitleBuilder: (meshNode) { - final originalNode = _findOriginalNode( - topologyState.root.children, - meshNode.id, - ); - return originalNode?.data.model ?? ''; + // Use metadata directly instead of searching the tree again + final model = meshNode.extra; + final deviceCount = meshNode + .metadata?['connectedDeviceCount'] as int? ?? + 0; + final deviceLabel = deviceCount <= 1 + ? loc(context).device + : loc(context).devices; + + if (model == null || model.isEmpty) { + return '$deviceCount $deviceLabel'; + } + return '$model • $deviceCount $deviceLabel'; }, ), ), @@ -105,20 +113,6 @@ class _DashboardNetworksState extends ConsumerState { ); } - /// Find the original RouterTreeNode by mesh node ID. - RouterTreeNode? _findOriginalNode( - List rootNodes, String nodeId) { - for (final rootNode in rootNodes) { - final flatNodes = rootNode.toFlatList(); - for (final node in flatNodes) { - if (TopologyAdapter.getNodeId(node) == nodeId) { - return node; - } - } - } - return null; - } - Widget _desktopHorizontal(BuildContext context, WidgetRef ref) { final topologyState = ref.watch(instantTopologyProvider); final wanStatus = ref.watch(internetStatusProvider); diff --git a/lib/page/health_check/views/speed_test_view.dart b/lib/page/health_check/views/speed_test_view.dart index 058f9315b..1712d6112 100644 --- a/lib/page/health_check/views/speed_test_view.dart +++ b/lib/page/health_check/views/speed_test_view.dart @@ -40,7 +40,7 @@ class SpeedTestView extends ConsumerWidget { final historyWidget = _buildHistoryPanel(context, historicalTests); - return UiKitPageView( + return UiKitPageView.withSliver( scrollable: true, title: loc(context).speedTest, child: (context, constraints) => AppResponsiveLayout( diff --git a/lib/page/instant_device/views/device_list_widget.dart b/lib/page/instant_device/views/device_list_widget.dart index 45e4114ec..013b59c90 100644 --- a/lib/page/instant_device/views/device_list_widget.dart +++ b/lib/page/instant_device/views/device_list_widget.dart @@ -145,7 +145,6 @@ class _DeviceListWidgetState extends ConsumerState { AppIconButton( icon: AppIcon.font( AppFontIcons.bidirectional, - color: Theme.of(context).colorScheme.primary, ), onTap: () { widget.onItemDeauth?.call(device); diff --git a/lib/page/instant_topology/helpers/topology_menu_helper.dart b/lib/page/instant_topology/helpers/topology_menu_helper.dart index a435c61d1..d900359d6 100644 --- a/lib/page/instant_topology/helpers/topology_menu_helper.dart +++ b/lib/page/instant_topology/helpers/topology_menu_helper.dart @@ -72,6 +72,18 @@ class TopologyMenuHelper { value: 'pair', label: loc(context).instantPair, icon: Icons.link, + children: [ + AppPopupMenuItem( + value: 'pairWired', + label: loc(context).pairWiredNode, + icon: Icons.cable, + ), + AppPopupMenuItem( + value: 'pairWireless', + label: loc(context).pairWirelessNode, + icon: Icons.wifi, + ), + ], )); } @@ -116,6 +128,12 @@ class TopologyMenuHelper { case 'pair': nodeAction = NodeInstantActions.pair; break; + case 'pairWired': + nodeAction = NodeInstantActions.pairWired; + break; + case 'pairWireless': + nodeAction = NodeInstantActions.pairWireless; + break; case 'reset': nodeAction = NodeInstantActions.reset; break; diff --git a/lib/page/instant_topology/views/instant_topology_view.dart b/lib/page/instant_topology/views/instant_topology_view.dart index 010c07a4b..6f40deffa 100644 --- a/lib/page/instant_topology/views/instant_topology_view.dart +++ b/lib/page/instant_topology/views/instant_topology_view.dart @@ -11,11 +11,13 @@ import 'package:privacy_gui/localization/localization_hook.dart'; import 'package:privacy_gui/page/components/customs/animated_refresh_container.dart'; import 'package:privacy_gui/page/components/shortcuts/dialogs.dart'; import 'package:privacy_gui/page/components/shortcuts/snack_bar.dart'; +import 'package:privacy_gui/page/instant_topology/widgets/app_mesh_wired_connection.dart'; import 'package:privacy_gui/page/components/ui_kit_page_view.dart'; import 'package:privacy_gui/page/components/views/arguments_view.dart'; import 'package:privacy_gui/page/instant_topology/helpers/topology_menu_helper.dart'; import 'package:privacy_gui/page/instant_topology/views/model/node_instant_actions.dart'; +import 'package:privacy_gui/page/nodes/providers/add_wired_nodes_provider.dart'; import 'package:privacy_gui/page/nodes/providers/node_detail_id_provider.dart'; import 'package:privacy_gui/page/instant_topology/_instant_topology.dart'; import 'package:privacy_gui/route/constants.dart'; @@ -61,7 +63,7 @@ class _InstantTopologyViewState extends ConsumerState { child: CircularProgressIndicator(), ), ) - : UiKitPageView( + : UiKitPageView.withSliver( title: _isWidget ? null : loc(context).instantTopology, hideTopbar: _isWidget, backState: _isWidget ? UiKitBackState.none : UiKitBackState.enabled, @@ -133,58 +135,92 @@ class _InstantTopologyViewState extends ConsumerState { action, originalNodes, ), - // Custom badge builder for client counts - nodeContentBuilder: (context, meshNode, style, isOffline) { - // NOTE: Data Provider Limitation - // The `InstantTopologyProvider` currently filters out client nodes. - // As requested, we are reverting to standard `clientVisibility` logic. - // Since there are no client nodes in the graph, the system count will be 0, - // and the system badge will NOT appear. + // Tree View Configuration (List Mode) + treeConfig: TopologyTreeConfiguration( + titleBuilder: (meshNode) => meshNode.name, + subtitleBuilder: (meshNode) { + // Don't show device count for Internet node + if (meshNode.type == MeshNodeType.internet) { + return meshNode.extra ?? ''; + } - final originalNode = - _menuHelper.findOriginalNode(originalNodes, meshNode.id); + final model = meshNode.extra ?? ''; + final deviceCount = meshNode.metadata?['connectedDeviceCount'] ?? 0; + final deviceLabel = + deviceCount <= 1 ? loc(context).device : loc(context).devices; + + if (model.isEmpty) { + return '$deviceCount $deviceLabel'; + } + return '$model • $deviceCount $deviceLabel'; + }, + preferAnimationNode: true, + showType: true, + showStatusText: true, + showStatusIndicator: true, + ), + // Graph View Configuration (Concentric/Force Mode) + nodeContentBuilder: (context, meshNode, style, isOffline) { + final deviceCount = meshNode.metadata?['connectedDeviceCount'] ?? 0; - // Return simple content without manual badge (System handles badge in Graph View) + Widget content; if (meshNode.image != null) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Image( - image: meshNode.image!, - fit: BoxFit.contain, - ), + content = Image( + image: meshNode.image!, + fit: BoxFit.contain, + ); + } else { + content = Icon( + meshNode.iconData ?? Icons.router, + color: style.iconColor, + size: style.size * 0.5, ); } - final iconData = meshNode.iconData ?? - (originalNode != null - ? _getIconForNode(originalNode.data) - : Icons.devices); - - return AppIcon.font( - iconData, - size: 24, - color: isOffline - ? Theme.of(context).colorScheme.outline - : Theme.of(context).colorScheme.onPrimary, + // Wrap content in padding to respect node borders/glow + final paddedContent = Padding( + padding: const EdgeInsets.all(8.0), + child: content, ); - }, - treeConfig: TopologyTreeConfiguration( - preferAnimationNode: false, - showType: true, - showStatusText: true, - showStatusIndicator: true, - titleBuilder: (meshNode) => meshNode.name, - subtitleBuilder: (meshNode) { - final originalNode = - _menuHelper.findOriginalNode(originalNodes, meshNode.id); - return originalNode?.data.model ?? ''; - }, - ), + if (deviceCount > 0) { + return Stack( + clipBehavior: Clip.none, + fit: StackFit.expand, + children: [ + paddedContent, + Positioned( + right: 0, + top: 0, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 6, vertical: 2), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: Theme.of(context).colorScheme.onPrimaryContainer, + width: 1, + ), + ), + child: Text( + '$deviceCount', + style: TextStyle( + color: Theme.of(context).colorScheme.onPrimaryContainer, + fontSize: 10, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ); + } + + return paddedContent; + }, enableAnimation: true, clientVisibility: ClientVisibility.onHover, - // Use unified registry: RippleNode for Graph View, Pulse/Liquid for Tree View - nodeRendererRegistry: NodeRendererRegistry.unified, // Disable interaction for Internet node nodeTapFilter: (node) => !node.isInternet, // Detail panel configuration @@ -451,15 +487,6 @@ class _InstantTopologyViewState extends ConsumerState { }); } - /// Get appropriate icon for topology node - IconData _getIconForNode(TopologyModel topology) { - if (topology.isRouter) { - return topology.isMaster ? Icons.router : Icons.wifi_tethering; - } - // Simple fallback - return Icons.devices; - } - _doBlinkNodeLed(WidgetRef ref, String deviceId) { ref.read(instantTopologyProvider.notifier).toggleBlinkNode(deviceId); } @@ -473,13 +500,87 @@ class _InstantTopologyViewState extends ConsumerState { } _doInstantPairWired(WidgetRef ref) { - // Implementation needed - this is complex dialog logic - // For now, use the simpler approach - context.pushNamed(RouteNamed.addNodes).then((result) { - if (result is bool && result) { - _showMoveChildNodesModal(); - } - }); + showSimpleAppDialog( + context, + title: loc(context).instantPair, + dismissible: false, + content: Consumer(builder: (context, ref, child) { + final addWiredNodesNotifier = ref.read(addWiredNodesProvider.notifier); + // Start onboarding once dialog is built + WidgetsBinding.instance.addPostFrameCallback((_) { + if (context.mounted) { + addWiredNodesNotifier.startAutoOnboarding(context); + } + }); + + final addWiredNodesState = ref.watch(addWiredNodesProvider); + final isCompleted = addWiredNodesState.isLoading == false && + addWiredNodesState.onboardingProceed == true; + final anyOnboarded = addWiredNodesState.anyOnboarded == true; + + final message = isCompleted + ? anyOnboarded + ? loc(context).wiredPairComplete + : loc(context).wiredPairCompleteNotFound + : loc(context).pairingWiredChildNodeDesc; + + final theme = Theme.of(context).extension(); + final colors = theme?.colorScheme; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + AppGap.md(), + SizedBox( + width: 300, + height: 200, + child: Stack(children: [ + AppMeshWiredConnection(animate: !isCompleted), + if (isCompleted) + Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: const EdgeInsets.only(bottom: 2.0), + child: AppIcon.font( + anyOnboarded + ? Icons.check_circle_outline + : Icons.warning_rounded, + size: 48, + color: anyOnboarded + ? colors?.semanticSuccess + : colors?.semanticWarning, + ), + )), + ]), + ), + AppGap.md(), + SizedBox( + width: kDefaultDialogWidth, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AppText.bodyMedium(message), + AppGap.xs(), + AppText.bodyMedium(isCompleted && anyOnboarded + ? addWiredNodesState.loadingMessage ?? '' + : ''), + ], + ), + ), + ], + ); + }), + actions: [ + AppButton( + label: loc(context).donePairing, + variant: SurfaceVariant.highlight, + onTap: () { + ref.read(addWiredNodesProvider.notifier).forceStopAutoOnboarding(); + context.pop(); + }, + ), + ], + ); } _showMoveChildNodesModal() { diff --git a/lib/page/instant_topology/widgets/app_mesh_wired_connection.dart b/lib/page/instant_topology/widgets/app_mesh_wired_connection.dart new file mode 100644 index 000000000..3b43ce51a --- /dev/null +++ b/lib/page/instant_topology/widgets/app_mesh_wired_connection.dart @@ -0,0 +1,173 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:ui_kit_library/ui_kit.dart'; + +class AppMeshWiredConnection extends ConsumerStatefulWidget { + final bool animate; + final Duration duration; + + const AppMeshWiredConnection({ + super.key, + this.animate = true, + this.duration = const Duration(seconds: 2), + }); + + @override + ConsumerState createState() => + _AppMeshWiredConnectionState(); +} + +class _AppMeshWiredConnectionState extends ConsumerState + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: widget.duration, + ); + + if (widget.animate) { + _controller.repeat(); + } + } + + @override + void didUpdateWidget(AppMeshWiredConnection oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.animate != oldWidget.animate) { + if (widget.animate) { + _controller.repeat(); + } else { + _controller.stop(); + _controller.reset(); + } + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context).extension(); + final colors = theme?.colorScheme; + + // Default icon color if theme is missing (fallback) + final iconColor = colors?.onSurface ?? Colors.black; + // Connection line color + final lineColor = colors?.outline ?? Colors.grey; + // Animated dot color + final highlightColor = colors?.primary ?? Colors.blue; + + return SizedBox( + height: 64, // Sufficient height for icons + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + // Parent Node + Column( + mainAxisSize: MainAxisSize.min, + children: [ + AppIcon.font( + AppFontIcons.router, + size: 40, + color: iconColor, + ), + ], + ), + + // Connection Line with Animation + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return CustomPaint( + painter: _ConnectionPainter( + progress: _controller.value, + lineColor: lineColor, + dotColor: highlightColor, + enabled: widget.animate, + ), + size: Size.infinite, + ); + }, + ), + ), + ), + + // Child Node + Column( + mainAxisSize: MainAxisSize.min, + children: [ + AppIcon.font( + AppFontIcons.router, + size: 40, + color: iconColor, + ), + ], + ), + ], + ), + ); + } +} + +class _ConnectionPainter extends CustomPainter { + final double progress; + final Color lineColor; + final Color dotColor; + final bool enabled; + + _ConnectionPainter({ + required this.progress, + required this.lineColor, + required this.dotColor, + required this.enabled, + }); + + @override + void paint(Canvas canvas, Size size) { + final centerY = size.height / 2; + + // Draw the static connecting line + final linePaint = Paint() + ..color = lineColor + ..strokeWidth = 2.0 + ..strokeCap = StrokeCap.round; + + final p1 = Offset(0, centerY); + final p2 = Offset(size.width, centerY); + + canvas.drawLine(p1, p2, linePaint); + + if (enabled) { + // Draw the moving dot + final dotPaint = Paint() + ..color = dotColor + ..style = PaintingStyle.fill; + + final dotX = size.width * progress; + final dotCenter = Offset(dotX, centerY); + + canvas.drawCircle(dotCenter, 4.0, dotPaint); + + // Optional: Add a subtle trail or glow if desired, but simple circle is clear + } + } + + @override + bool shouldRepaint(_ConnectionPainter oldDelegate) { + return oldDelegate.progress != progress || + oldDelegate.lineColor != lineColor || + oldDelegate.dotColor != dotColor || + oldDelegate.enabled != enabled; + } +} diff --git a/lib/page/login/views/local_reset_router_password_view.dart b/lib/page/login/views/local_reset_router_password_view.dart index 63407e373..f14f94d26 100644 --- a/lib/page/login/views/local_reset_router_password_view.dart +++ b/lib/page/login/views/local_reset_router_password_view.dart @@ -195,9 +195,9 @@ class _LocalResetRouterPasswordViewState }; }).whenComplete(() { if (!mounted) return; - showAdaptiveDialog( + showAppDialog( context: context, - builder: (BuildContext context) => AlertDialog.adaptive( + builder: (context) => AppDialog( key: const ValueKey('resetSavedDialog'), title: AppText.titleLarge(dialogTitle), content: AppText.bodyMedium(dialogContent), diff --git a/lib/page/login/views/login_cloud_view.dart b/lib/page/login/views/login_cloud_view.dart index 6c87b8896..5808902ef 100644 --- a/lib/page/login/views/login_cloud_view.dart +++ b/lib/page/login/views/login_cloud_view.dart @@ -104,7 +104,7 @@ class _LoginCloudViewState extends ConsumerState { AppGap.lg(), AppPasswordInput( controller: _passwordController, - hint: loc(context).password, + hintText: loc(context).password, errorText: errorCodeHelper(context, _error), onSubmitted: (_) { _cloudLogin(); diff --git a/lib/page/login/views/login_local_view.dart b/lib/page/login/views/login_local_view.dart index 5ebb7bb8f..e41abe866 100644 --- a/lib/page/login/views/login_local_view.dart +++ b/lib/page/login/views/login_local_view.dart @@ -219,7 +219,7 @@ class _LoginViewState extends ConsumerState { SizedBox( child: AppPasswordInput( controller: _passwordController, - hint: loc(context).routerPassword, + hintText: loc(context).routerPassword, onChanged: (value) { setState(() { _shouldEnableLoginButton(); @@ -235,7 +235,7 @@ class _LoginViewState extends ConsumerState { ), ), if (_passwordHint != null && _passwordHint?.isNotEmpty == true) - AppExpansionPanel.single( + AppExpansionPanel.compactSingle( headerTitle: _showPassword ? loc(context).hideHint : loc(context).showHint, diff --git a/lib/page/nodes/views/light_different_color_modal.dart b/lib/page/nodes/views/light_different_color_modal.dart index c3a76e4da..aa8ffd01c 100644 --- a/lib/page/nodes/views/light_different_color_modal.dart +++ b/lib/page/nodes/views/light_different_color_modal.dart @@ -13,24 +13,30 @@ class LightDifferentColorModal extends StatelessWidget { @override Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - AppText.labelLarge(loc(context).modalLightDifferentDesc), - AppGap.xxl(), - ...isCogntive - ? _cognitiveNodeLightSet(context) - : _meshNodeLightSet(context), - AppGap.xxl(), - AppText.labelLarge(loc(context).modalLightDifferentToFactoryReset), - AppGap.xxl(), - _buildNumberedList(context, [ - loc(context).modalLightDifferentToFactoryResetStep1, - loc(context).modalLightDifferentToFactoryResetStep2, - ]), - ], + // Constrain the height to 60% of the screen height to prevent overflow in dialogs + return ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.6, + ), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + AppText.labelLarge(loc(context).modalLightDifferentDesc), + AppGap.xxl(), + ...isCogntive + ? _cognitiveNodeLightSet(context) + : _meshNodeLightSet(context), + AppGap.xxl(), + AppText.labelLarge(loc(context).modalLightDifferentToFactoryReset), + AppGap.xxl(), + _buildNumberedList(context, [ + loc(context).modalLightDifferentToFactoryResetStep1, + loc(context).modalLightDifferentToFactoryResetStep2, + ]), + ], + ), ), ); } diff --git a/lib/page/nodes/views/node_detail_view.dart b/lib/page/nodes/views/node_detail_view.dart index 2b8fe8760..d54d243ff 100644 --- a/lib/page/nodes/views/node_detail_view.dart +++ b/lib/page/nodes/views/node_detail_view.dart @@ -238,8 +238,7 @@ class _NodeDetailViewState extends ConsumerState }, ), mobile: (ctx) => AppIconButton( - icon: AppIcon.font(AppFontIcons.filter, - color: Theme.of(context).colorScheme.primary), + icon: AppIcon.font(AppFontIcons.filter), onTap: () { showModalBottomSheet( context: context, diff --git a/lib/page/support/faq_list_view.dart b/lib/page/support/faq_list_view.dart index c4481744f..bcb5c9a62 100644 --- a/lib/page/support/faq_list_view.dart +++ b/lib/page/support/faq_list_view.dart @@ -9,6 +9,7 @@ import 'package:flutter/material.dart'; import 'package:privacy_gui/providers/app_settings/app_settings_provider.dart'; import 'package:ui_kit_library/ui_kit.dart'; import 'package:privacy_gui/page/support/faq_data.dart'; +import 'package:privacy_gui/page/support/widgets/faq_agent_fab.dart'; class FaqListView extends ArgumentsConsumerStatefulView { const FaqListView({super.key}); @@ -34,64 +35,76 @@ class _FaqListViewState extends ConsumerState { @override Widget build(BuildContext context) { - return UiKitPageView.withSliver( - title: loc(context).faqs, - backState: UiKitBackState.none, - menuPosition: MenuPosition.right, - menuView: PageMenuView( - icon: AppFontIcons.menu, - label: loc(context).faqLookingFor, - content: AppCard( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - AppText.titleMedium(loc(context).faqLookingFor), - AppGap.sm(), - AppButton.text( - label: loc(context).faqVisitLinksysSupport, - onTap: () { - gotoOfficialWebUrl(FaqItem.faqVisitLinksysSupport.url, - locale: ref.read(appSettingsProvider).locale); - }, + return Stack( + children: [ + // Main content + UiKitPageView.withSliver( + title: loc(context).faqs, + backState: UiKitBackState.none, + menuPosition: MenuPosition.right, + menuView: PageMenuView( + icon: AppFontIcons.menu, + label: loc(context).faqLookingFor, + content: AppCard( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AppText.titleMedium(loc(context).faqLookingFor), + AppGap.sm(), + AppButton.text( + label: loc(context).faqVisitLinksysSupport, + onTap: () { + gotoOfficialWebUrl(FaqItem.faqVisitLinksysSupport.url, + locale: ref.read(appSettingsProvider).locale); + }, + ), + ], ), - ], - ), - )), - pageContentType: UiKitPageContentType.flexible, - child: (context, constraints) { - return SizedBox( - width: context.colWidth(9), - child: ListView( - primary: true, - shrinkWrap: true, - children: [ - ...categories.map((category) => Column( - children: [ - _buildExpansionCard( - context, - title: category.displayString(context), - children: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: category.items - .map((item) => AppButton.text( - label: item.displayString(context), - onTap: () { - gotoOfficialWebUrl(item.url, - locale: ref - .read(appSettingsProvider) - .locale); - }, - )) - .toList(), - ), - ), - AppGap.sm(), - ], - )), - ], - ), - ); - }, + )), + pageContentType: UiKitPageContentType.flexible, + child: (context, constraints) { + return SizedBox( + width: context.colWidth(9), + child: ListView( + primary: true, + shrinkWrap: true, + children: [ + ...categories.map((category) => Column( + children: [ + _buildExpansionCard( + context, + title: category.displayString(context), + children: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: category.items + .map((item) => AppButton.text( + label: item.displayString(context), + onTap: () { + gotoOfficialWebUrl(item.url, + locale: ref + .read(appSettingsProvider) + .locale); + }, + )) + .toList(), + ), + ), + AppGap.sm(), + ], + )), + ], + ), + ); + }, + ), + + // FAQ Agent floating button + const Positioned( + bottom: 24, + right: 24, + child: FAQAgentFab(), + ), + ], ); } diff --git a/lib/page/support/widgets/faq_agent_fab.dart b/lib/page/support/widgets/faq_agent_fab.dart new file mode 100644 index 000000000..7c07575a5 --- /dev/null +++ b/lib/page/support/widgets/faq_agent_fab.dart @@ -0,0 +1,492 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:generative_ui/generative_ui.dart'; +import 'package:http/http.dart' as http; +import 'package:privacy_gui/constants/url_links.dart'; +import 'package:ui_kit_library/ui_kit.dart'; + +/// 浮動式 FAQ Agent 元件 +/// +/// 右下角浮動按鈕,點擊後展開搜尋對話框。 +/// 使用 Linksys Support API 搜尋 FAQ 文章,並透過 AWS Bedrock LLM 分析結果。 +class FAQAgentFab extends StatefulWidget { + const FAQAgentFab({super.key}); + + @override + State createState() => _FAQAgentFabState(); +} + +class _FAQAgentFabState extends State + with SingleTickerProviderStateMixin { + bool _isExpanded = false; + final _containerKey = GlobalKey(); + final _inputController = TextEditingController(); + + late final AnimationController _animController; + late final OrchestrateUIFlowUseCase _orchestrator; + late final IComponentRegistry _registry; + bool _useMock = true; + + @override + void initState() { + super.initState(); + _animController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _setupGenUI(); + } + + void _setupGenUI() { + _registry = _createRegistry(); + + // Try to create AWS Bedrock generator, fall back to direct search + IContentGenerator generator; + try { + final config = AWSConfig.fromEnvironment(); + debugPrint('FAQ Agent: Using AWS Bedrock - $config'); + generator = BedrockFAQGenerator(config: config); + _useMock = false; + } on ConfigurationException catch (e) { + debugPrint('FAQ Agent: AWS config failed: ${e.message}'); + debugPrint('FAQ Agent: Falling back to direct Linksys search'); + generator = DirectSearchGenerator(); + _useMock = true; + } catch (e) { + debugPrint('FAQ Agent: Unexpected error: $e'); + generator = DirectSearchGenerator(); + _useMock = true; + } + + _orchestrator = OrchestrateUIFlowUseCase(contentGenerator: generator); + } + + ComponentRegistry _createRegistry() { + final registry = ComponentRegistry(); + + // FAQ 搜尋結果項目 + registry.register('FAQResult', (context, props, {onAction, children}) { + return AppListTile( + leading: Icon( + props['type'] == 'article' + ? Icons.article_outlined + : Icons.forum_outlined, + size: 20, + ), + title: AppText.bodyMedium(props['title'] ?? ''), + trailing: const Icon(Icons.open_in_new, size: 16), + onTap: () { + final id = props['id']; + // type is not used in the new URL structure + final url = 'https://support.linksys.com/kb/article/$id'; + gotoOfficialWebUrl(url); + }, + ); + }); + + // 無結果提示 + registry.register('NoResults', (context, props, {onAction, children}) { + return Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.search_off, size: 48, color: Colors.grey), + AppGap.md(), + AppText.bodyMedium( + props['message'] ?? 'No results found', + textAlign: TextAlign.center, + ), + AppGap.md(), + AppButton.text( + label: 'Visit Linksys Support', + onTap: () => gotoOfficialWebUrl('https://support.linksys.com'), + ), + ], + ), + ); + }); + + return registry; + } + + void _toggle() { + setState(() { + _isExpanded = !_isExpanded; + if (_isExpanded) { + _animController.forward(); + } else { + _animController.reverse(); + } + }); + } + + void _ask() { + final text = _inputController.text.trim(); + if (text.isEmpty) return; + _containerKey.currentState?.sendMessage(text); + _inputController.clear(); + FocusScope.of(context).unfocus(); + } + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + + return SizedBox( + width: _isExpanded ? 394 : 56, + height: _isExpanded ? 580 : 56, + child: Stack( + clipBehavior: Clip.none, + alignment: Alignment.bottomRight, + children: [ + // 展開的對話框 + if (_isExpanded) + Positioned( + bottom: 64, + right: 0, + child: _buildChatPanel(colorScheme), + ), + + // 浮動按鈕 + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton( + heroTag: 'faq-agent-fab', + onPressed: _toggle, + backgroundColor: colorScheme.primaryContainer, + foregroundColor: colorScheme.onPrimaryContainer, + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: Icon( + _isExpanded ? Icons.close : Icons.support_agent, + key: ValueKey(_isExpanded), + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildChatPanel(ColorScheme colorScheme) { + return Material( + elevation: 8, + borderRadius: BorderRadius.circular(16), + child: Container( + width: 380, + height: 500, + decoration: BoxDecoration( + color: colorScheme.surface, + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: colorScheme.outlineVariant, + width: 1, + ), + ), + child: Column( + children: [ + // 標題列 + _buildHeader(colorScheme), + + // 內容區域 + Expanded( + child: GenUiContainer( + key: _containerKey, + orchestrator: _orchestrator, + registry: _registry, + ), + ), + + // 輸入區域 + _buildInputBar(colorScheme), + ], + ), + ), + ); + } + + Widget _buildHeader(ColorScheme colorScheme) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + decoration: BoxDecoration( + color: colorScheme.primaryContainer, + borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), + ), + child: Row( + children: [ + Icon(Icons.support_agent, color: colorScheme.onPrimaryContainer), + AppGap.sm(), + AppText.titleMedium( + 'FAQ Assistant', + color: colorScheme.onPrimaryContainer, + ), + const Spacer(), + // Mode indicator + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: _useMock + ? Colors.orange.withValues(alpha: 0.3) + : Colors.green.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + _useMock ? 'Search' : 'AI', + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.bold, + color: _useMock ? Colors.orange : Colors.green, + ), + ), + ), + const SizedBox(width: 8), + IconButton( + icon: Icon(Icons.close, + size: 20, color: colorScheme.onPrimaryContainer), + onPressed: _toggle, + padding: EdgeInsets.zero, + constraints: const BoxConstraints(), + ), + ], + ), + ); + } + + Widget _buildInputBar(ColorScheme colorScheme) { + return Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border( + top: BorderSide(color: colorScheme.outlineVariant), + ), + ), + child: Row( + children: [ + Expanded( + child: AppTextField( + controller: _inputController, + hintText: 'Search for help topics...', + onSubmitted: (_) => _ask(), + ), + ), + AppGap.sm(), + AppIconButton( + icon: const Icon(Icons.search), + onTap: _ask, + ), + ], + ), + ); + } + + @override + void dispose() { + _animController.dispose(); + _inputController.dispose(); + super.dispose(); + } +} + +// ============================================================================= +// Content Generators +// ============================================================================= + +/// AWS Bedrock-based FAQ generator with LLM analysis +class BedrockFAQGenerator implements IContentGenerator { + final AWSConfig config; + late final AwsContentGenerator _awsGenerator; + + static const _systemPrompt = ''' +You are a helpful FAQ assistant for Linksys networking products. Your role is to analyze user questions and provide helpful answers with relevant support articles. + +**Response Format Requirements:** + +1. **First, provide your conclusion/analysis** - Give a clear, helpful text response that directly addresses the user's question. Explain the likely cause of the issue and possible solutions. + +2. **Then, list source articles** - Use the FAQResult tool to display clickable links to relevant support articles that back up your answer. + +3. **If no articles were found in the search results**, you MUST still suggest 3 possible article topics that would likely help. Create reasonable FAQResult entries based on common Linksys support topics related to the question. Use estimated IDs (like 1000, 1001, 1002) and descriptive titles. + +**Available UI Components:** + +1. FAQResult - Display a clickable article link + Properties: { "id": number, "title": string, "type": "article" | "forum" } + +2. NoResults - ONLY use when you cannot suggest ANY articles at all (avoid using this) + Properties: { "message": string } + +**Important:** +- Always start with your conclusion text BEFORE using any tools +- Limit to 5 most relevant articles maximum +- Always respond in the user's language +- Prefer creating helpful FAQResult suggestions over using NoResults +'''; + + BedrockFAQGenerator({required this.config}) { + _awsGenerator = AwsContentGenerator(config: config); + } + + /// Tool definitions for Claude to use FAQResult format + List get _tools => [ + GenTool( + name: 'FAQResult', + description: 'Display a clickable FAQ article link to the user', + inputSchema: { + 'type': 'object', + 'properties': { + 'id': { + 'type': 'integer', + 'description': 'Article ID from Linksys Support', + }, + 'title': { + 'type': 'string', + 'description': 'Article title', + }, + 'type': { + 'type': 'string', + 'enum': ['article', 'forum'], + 'description': 'Content type', + }, + }, + 'required': ['id', 'title', 'type'], + }, + ), + GenTool( + name: 'NoResults', + description: 'Display when no relevant articles were found', + inputSchema: { + 'type': 'object', + 'properties': { + 'message': { + 'type': 'string', + 'description': 'Message to display to the user', + }, + }, + 'required': ['message'], + }, + ), + ]; + + @override + Future generate(String prompt) async { + try { + // Step 1: Search Linksys API for related articles + final searchResults = await _fetchSearchResults(prompt); + + // Step 2: Build the user message with search context + final userContent = ''' +User question: $prompt + +Search results from Linksys Support: +${searchResults.isEmpty ? 'No articles found.' : searchResults.take(10).map((r) => '- [ID: ${r['id']}] ${r['title']} (Type: ${r['type'] ?? 'article'})').join('\n')} + +Please analyze these results and recommend the most relevant articles using the FAQResult tool. Explain briefly why these articles might help before listing them. +'''; + + // Step 3: Use generateWithHistory with tools for proper tool_use format + // Note: forceToolUse is false to allow Claude to include text analysis first + final response = await _awsGenerator.generateWithHistory( + [ChatMessage.user(userContent)], + tools: _tools, + systemPrompt: _systemPrompt, + forceToolUse: false, // Allow text analysis + tool_use together + ); + + return response; + } catch (e) { + debugPrint('BedrockFAQGenerator error: $e'); + // Fallback to direct search + return DirectSearchGenerator()._formatDirectResponse( + prompt, + await _fetchSearchResults(prompt), + ); + } + } + + Future>> _fetchSearchResults(String keyword) async { + if (keyword.isEmpty) return []; + + final encodedKeyword = Uri.encodeComponent(keyword); + final apiUrl = + 'https://support.linksys.com/get_related_kb_forums/?text=$encodedKeyword'; + + try { + final response = await http.get(Uri.parse(apiUrl)); + if (response.statusCode == 200) { + final data = json.decode(utf8.decode(response.bodyBytes)); + return (data as List).cast>(); + } + } catch (e) { + debugPrint('Linksys API error: $e'); + } + return []; + } +} + +/// Direct search generator without LLM (fallback) +class DirectSearchGenerator implements IContentGenerator { + @override + Future generate(String prompt) async { + final results = await _fetchSearchResults(prompt); + return _formatDirectResponse(prompt, results); + } + + Future>> _fetchSearchResults(String keyword) async { + if (keyword.isEmpty) return []; + + final encodedKeyword = Uri.encodeComponent(keyword); + final apiUrl = + 'https://support.linksys.com/get_related_kb_forums/?text=$encodedKeyword'; + + try { + final response = await http.get(Uri.parse(apiUrl)); + if (response.statusCode == 200) { + final data = json.decode(utf8.decode(response.bodyBytes)); + return (data as List).cast>(); + } + } catch (e) { + debugPrint('Linksys API error: $e'); + } + return []; + } + + LLMResponse _formatDirectResponse( + String query, + List> results, + ) { + if (results.isEmpty) { + return LLMResponse( + id: 'faq-${DateTime.now().millisecondsSinceEpoch}', + model: 'linksys-search', + content: [ + ToolUseBlock( + id: 'no-result', + name: 'NoResults', + input: {'message': 'No results found for "$query"'}, + ), + ], + ); + } + + return LLMResponse( + id: 'faq-${DateTime.now().millisecondsSinceEpoch}', + model: 'linksys-search', + content: [ + TextBlock(text: 'Found ${results.length} related articles:'), + ...results.take(10).map((result) => ToolUseBlock( + id: 'result-${result['id']}', + name: 'FAQResult', + input: { + 'id': result['id'], + 'title': result['title'], + 'type': result['type'] ?? 'article', + }, + )), + ], + ); + } +} diff --git a/lib/page/vpn/views/vpn_settings_page.dart b/lib/page/vpn/views/vpn_settings_page.dart index 371101141..bdbc464ef 100644 --- a/lib/page/vpn/views/vpn_settings_page.dart +++ b/lib/page/vpn/views/vpn_settings_page.dart @@ -341,11 +341,9 @@ class _VPNSettingsPageState extends ConsumerState title: loc(context).vpnUserCredentialsSection, isEnabled: state.settings.isEditingCredentials, trailing: AppIconButton( - icon: AppIcon.font( - state.settings.isEditingCredentials - ? AppFontIcons.close - : AppFontIcons.edit, - color: Theme.of(context).colorScheme.primary), + icon: AppIcon.font(state.settings.isEditingCredentials + ? AppFontIcons.close + : AppFontIcons.edit), onTap: () { setState(() { if (state.settings.isEditingCredentials) { diff --git a/lib/route/constants.dart b/lib/route/constants.dart index ecf8fb25f..03b047b11 100644 --- a/lib/route/constants.dart +++ b/lib/route/constants.dart @@ -20,6 +20,7 @@ class RoutePath { static const dashboardHome = '/dashboardHome'; static const dashboardMenu = '/dashboardMenu'; static const dashboardSupport = '/dashboardSupport'; + static const dashboardAiAssistant = '/dashboardAiAssistant'; /// menu static const menuInstantVerify = 'menuInstantVerify'; @@ -153,6 +154,7 @@ class RouteNamed { static const dashboardMenu = 'dashboardMenu'; static const dashboardHome = 'dashboardHome'; static const dashboardSupport = 'dashboardSupport'; + static const dashboardAiAssistant = 'dashboardAiAssistant'; static const menuInstantVerify = 'menuInstantVerify'; static const menuInstantDevices = 'menuInstantDevices'; diff --git a/lib/route/route_dashboard.dart b/lib/route/route_dashboard.dart index a29a689dc..774fd89d8 100644 --- a/lib/route/route_dashboard.dart +++ b/lib/route/route_dashboard.dart @@ -55,5 +55,10 @@ final dashboardRoute = ShellRoute( ), ], ), + LinksysRoute( + name: RouteNamed.dashboardAiAssistant, + path: RoutePath.dashboardAiAssistant, + builder: (context, state) => const RouterAssistantView(), + ), ], ); diff --git a/lib/route/router_provider.dart b/lib/route/router_provider.dart index d3e9c94d0..86db549f7 100644 --- a/lib/route/router_provider.dart +++ b/lib/route/router_provider.dart @@ -66,6 +66,7 @@ import 'package:privacy_gui/core/jnap/providers/ip_getter/get_local_ip.dart' if (dart.library.html) 'package:privacy_gui/core/jnap/providers/ip_getter/web_get_local_ip.dart'; import 'package:privacy_gui/page/instant_safety/providers/_providers.dart'; +import 'package:privacy_gui/page/ai_assistant/views/router_assistant_view.dart'; part 'route_home.dart'; part 'route_cloud_login.dart'; diff --git a/lib/theme/theme_json_config.dart b/lib/theme/theme_json_config.dart index 153194911..c047894dd 100644 --- a/lib/theme/theme_json_config.dart +++ b/lib/theme/theme_json_config.dart @@ -17,11 +17,29 @@ class ThemeJsonConfig { }) : _lightJson = lightJson, _darkJson = darkJson; + /// Public getter for light theme JSON configuration. + Map get lightJson => _lightJson; + + /// Public getter for dark theme JSON configuration. + Map get darkJson => _darkJson; + /// Default configuration (Glass style). - factory ThemeJsonConfig.defaultConfig() => ThemeJsonConfig._( - lightJson: {'style': 'pixel', 'brightness': 'light'}, - darkJson: {'style': 'pixel', 'brightness': 'dark'}, - ); + factory ThemeJsonConfig.defaultConfig() { + const defaultVisualEffects = + int.fromEnvironment('visualEffects', defaultValue: 0); + return ThemeJsonConfig._( + lightJson: { + 'style': 'flat', + 'visualEffects': defaultVisualEffects, + 'brightness': 'light' + }, + darkJson: { + 'style': 'flat', + 'visualEffects': defaultVisualEffects, + 'brightness': 'dark' + }, + ); + } /// Constructs from a complete JSON object (including light/dark colors). /// @@ -43,10 +61,13 @@ class ThemeJsonConfig { final overrides = json['overrides'] as Map?; final colors = json['colors'] as Map?; + final visualEffects = json['visualEffects'] as int?; + // Compose light theme JSON final lightJson = { 'style': style, 'brightness': 'light', + if (visualEffects != null) 'visualEffects': visualEffects, if (seedColor != null) 'seedColor': seedColor, if (overrides != null) 'overrides': overrides, ...?(colors?['light'] as Map?), @@ -56,6 +77,7 @@ class ThemeJsonConfig { final darkJson = { 'style': style, 'brightness': 'dark', + if (visualEffects != null) 'visualEffects': visualEffects, if (seedColor != null) 'seedColor': seedColor, if (overrides != null) 'overrides': overrides, ...?(colors?['dark'] as Map?), @@ -95,9 +117,16 @@ class ThemeJsonConfig { } final designTheme = CustomDesignTheme.fromJson(json); + // Parse seedColor from JSON or use override or fallback to brandPrimary + final seedColorHex = json['seedColor'] as String?; + final parsedSeedColor = + seedColorHex != null ? _parseColor(seedColorHex) : null; + final effectiveSeedColor = + overrideSeedColor ?? parsedSeedColor ?? AppPalette.brandPrimary; + return AppTheme.create( brightness: Brightness.light, - seedColor: overrideSeedColor ?? AppPalette.brandPrimary, + seedColor: effectiveSeedColor, designThemeBuilder: (_) => designTheme, ); } @@ -111,17 +140,30 @@ class ThemeJsonConfig { } final designTheme = CustomDesignTheme.fromJson(json); - // return AppTheme.create( - // brightness: Brightness.dark, - // seedColor: overrideSeedColor ?? AppPalette.brandPrimary, - // designThemeBuilder: (_) => designTheme, - // ); - // TODO: Temporary workaround for dark mode creation to ensure consistency, - // verifying parameter passing. + // Parse seedColor from JSON or use override or fallback to brandPrimary + final seedColorHex = json['seedColor'] as String?; + final parsedSeedColor = + seedColorHex != null ? _parseColor(seedColorHex) : null; + final effectiveSeedColor = + overrideSeedColor ?? parsedSeedColor ?? AppPalette.brandPrimary; + return AppTheme.create( brightness: Brightness.dark, - seedColor: overrideSeedColor ?? AppPalette.brandPrimary, + seedColor: effectiveSeedColor, designThemeBuilder: (_) => designTheme, ); } + + /// Helper to parse color from hex string. + static Color? _parseColor(String hex) { + try { + final cleanHex = hex.replaceAll('#', ''); + if (cleanHex.length == 6) { + return Color(int.parse('FF$cleanHex', radix: 16)); + } else if (cleanHex.length == 8) { + return Color(int.parse(cleanHex, radix: 16)); + } + } catch (_) {} + return null; + } } diff --git a/packages/usp_client_core/lib/src/connection/usp_connection_config.dart b/packages/usp_client_core/lib/src/connection/usp_connection_config.dart new file mode 100644 index 000000000..e5c06bfe6 --- /dev/null +++ b/packages/usp_client_core/lib/src/connection/usp_connection_config.dart @@ -0,0 +1,31 @@ +/// USP Connection Configuration +/// +/// Configuration for connecting to a USP Agent via gRPC. +library; + +import 'package:equatable/equatable.dart'; + +/// Configuration for connecting to a USP Agent. +class UspConnectionConfig extends Equatable { + /// The host address of the gRPC-Web proxy (Envoy). + final String host; + + /// The port number of the gRPC-Web proxy. + final int port; + + /// Creates a new [UspConnectionConfig]. + /// + /// Default values match the Envoy proxy configuration: + /// - host: 'localhost' + /// - port: 8090 (Envoy gRPC-Web port) + const UspConnectionConfig({ + this.host = 'localhost', + this.port = 8090, + }); + + @override + List get props => [host, port]; + + @override + String toString() => 'UspConnectionConfig(host: $host, port: $port)'; +} diff --git a/packages/usp_client_core/lib/src/connection/usp_grpc_client_service.dart b/packages/usp_client_core/lib/src/connection/usp_grpc_client_service.dart new file mode 100644 index 000000000..0567ade49 --- /dev/null +++ b/packages/usp_client_core/lib/src/connection/usp_grpc_client_service.dart @@ -0,0 +1,160 @@ +/// USP gRPC Client Service +/// +/// Provides gRPC communication with the USP Simulator. +library; + +import 'dart:async'; +import 'dart:developer' as developer; +import 'package:grpc/grpc_connection_interface.dart'; +import 'package:meta/meta.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; +import '../grpc_creator/grpc_creator.dart'; +import 'usp_connection_config.dart'; + +/// Simple logger for USP operations. +void _log(String message) { + developer.log(message, name: 'USP'); +} + +/// Service for communicating with USP Agent via gRPC. +/// +/// This service handles: +/// - Connection management (connect/disconnect) +/// - USP message encoding/decoding +/// - gRPC transport layer +class UspGrpcClientService { + // Dependencies + final TransportFactory _transportFactory; + final UspProtobufConverter _converter; + final UspRecordHelper _recordHelper; + + // State + ClientChannelBase? _channel; + UspTransportServiceClient? _stub; + bool _isConnected = false; + + // Configuration + final String _controllerId = "proto::privacygui-client"; + final String _agentId = "proto::agent"; + + /// Creates a new [UspGrpcClientService]. + /// + /// Dependencies can be injected for testing. + UspGrpcClientService({ + TransportFactory? transportFactory, + UspProtobufConverter? converter, + UspRecordHelper? recordHelper, + }) : _transportFactory = transportFactory ?? getTransportFactory(), + _converter = converter ?? UspProtobufConverter(), + _recordHelper = recordHelper ?? UspRecordHelper(); + + /// Whether the service is connected to the USP Agent. + bool get isConnected => _isConnected; + + /// Connects to the USP Agent at the specified host and port. + Future connect(UspConnectionConfig config) async { + // Avoid duplicate connections + await disconnect(); + + _log('🔌 USP: Connecting to ${config.host}:${config.port}...'); + + try { + // Create cross-platform gRPC channel + _channel = _transportFactory.createClientChannel( + host: config.host, + port: config.port, + ); + + // Create gRPC stub + _stub = UspTransportServiceClient(_channel!); + + _isConnected = true; + _log('✅ USP: Connected successfully'); + } catch (e) { + _log('❌ USP: Connection failed: $e'); + _isConnected = false; + rethrow; + } + } + + /// Sends a USP request and returns the response. + /// + /// Throws [UspException] if not connected or on communication error. + Future sendRequest(UspRequest requestDto) async { + if (_stub == null || !_isConnected) { + throw UspException( + 7000, + 'USP Client is not connected. Call connect() first.', + ); + } + + try { + _log('📤 USP: Sending ${requestDto.runtimeType}...'); + + // 1. Pack request into USP Record + final msgId = 'req-${DateTime.now().millisecondsSinceEpoch}'; + final reqMsg = _converter.toProto(requestDto, msgId: msgId); + final reqRecord = _recordHelper.wrap( + reqMsg, + fromId: _controllerId, + toId: _agentId, + ); + + // 2. Send via gRPC transport + final transportReq = UspTransportRequest() + ..uspRecordPayload = reqRecord.writeToBuffer(); + + final transportRes = await _stub!.sendUspMessage(transportReq); + + // 3. Unpack response + final resBytes = transportRes.uspRecordResponse; + if (resBytes.isEmpty) { + throw UspException(7002, "Received empty response from Gateway"); + } + + final resMsg = _recordHelper.unwrap(resBytes); + final resDto = _converter.fromProto(resMsg); + + _log('📩 USP: Received ${resDto.runtimeType}'); + + if (resDto is UspResponse) { + return resDto; + } else { + throw UspException( + 7000, + "Unexpected response type: ${resDto.runtimeType}", + ); + } + } catch (e) { + _log('❌ USP: Request failed: $e'); + if (e is UspException) rethrow; + throw UspException(7000, "Communication Error: $e"); + } + } + + /// Disconnects from the USP Agent. + Future disconnect() async { + if (_channel != null) { + await _channel!.shutdown(); + _channel = null; + _stub = null; + _isConnected = false; + _log('🔌 USP: Disconnected'); + } + } + + // --- Testing Helpers --- + + /// Allows tests to inject a mock stub. + @visibleForTesting + void setMockStub(UspTransportServiceClient stub) { + _stub = stub; + _isConnected = true; + } + + /// Allows tests to inject a mock channel. + @visibleForTesting + void setMockChannel(ClientChannelBase channel) { + _channel = channel; + } +} diff --git a/packages/usp_client_core/lib/src/enums/_enums.dart b/packages/usp_client_core/lib/src/enums/_enums.dart new file mode 100644 index 000000000..007aa5d75 --- /dev/null +++ b/packages/usp_client_core/lib/src/enums/_enums.dart @@ -0,0 +1,8 @@ +/// USP Enums barrel export. +/// +/// Re-exports all USP enumeration types for convenient importing. +library; + +export 'wifi_enums.dart'; +export 'network_enums.dart'; +export 'device_enums.dart'; diff --git a/packages/usp_client_core/lib/src/enums/device_enums.dart b/packages/usp_client_core/lib/src/enums/device_enums.dart new file mode 100644 index 000000000..752f30b0a --- /dev/null +++ b/packages/usp_client_core/lib/src/enums/device_enums.dart @@ -0,0 +1,125 @@ +/// Device-related enumerations for USP/TR-181 protocol. +/// +/// These enums provide type-safe values for device configuration. +library; + +/// Network interface type. +enum InterfaceType { + wireless('Wireless'), + wired('Wired'), + infrastructure('Infrastructure'); + + const InterfaceType(this.value); + final String value; + + static InterfaceType? fromValue(String? value) { + if (value == null) return null; + return InterfaceType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } + + /// Convert from TR-181 InterfaceType value. + static InterfaceType fromTr181(String? tr181Type) { + if (tr181Type == '802.11') return InterfaceType.wireless; + if (tr181Type == 'Ethernet') return InterfaceType.wired; + return InterfaceType.wired; // Default + } +} + +/// Device type in the network. +enum DeviceType { + computer('Computer'), + mobile('Mobile'), + tablet('Tablet'), + printer('Printer'), + camera('Camera'), + tv('TV'), + speaker('Speaker'), + infrastructure('Infrastructure'), + unknown('Unknown'); + + const DeviceType(this.value); + final String value; + + static DeviceType? fromValue(String? value) { + if (value == null) return null; + return DeviceType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// Mesh node type. +enum NodeType { + master('Master'), + slave('Slave'); + + const NodeType(this.value); + final String value; + + static NodeType? fromValue(String? value) { + if (value == null) return null; + return NodeType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// Backhaul connection type. +enum BackhaulType { + none('None'), + wifi('Wi-Fi'), + ethernet('Ethernet'); + + const BackhaulType(this.value); + final String value; + + static BackhaulType? fromValue(String? value) { + if (value == null) return null; + return BackhaulType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } + + /// Check if this is the master node (no backhaul). + bool get isMaster => this == BackhaulType.none; + + /// Convert to connection type string. + String get connectionType { + switch (this) { + case BackhaulType.wifi: + return 'Wireless'; + case BackhaulType.ethernet: + return 'Wired'; + case BackhaulType.none: + return 'None'; + } + } +} + +/// MAC filter mode. +enum MacFilterMode { + allow('Allow'), + deny('Deny'); + + const MacFilterMode(this.value); + final String value; + + static MacFilterMode? fromValue(String? value) { + if (value == null) return null; + return MacFilterMode.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } + + /// Convert from boolean isEnabled. + static MacFilterMode fromEnabled(bool isEnabled) { + return isEnabled ? MacFilterMode.allow : MacFilterMode.deny; + } +} diff --git a/packages/usp_client_core/lib/src/enums/network_enums.dart b/packages/usp_client_core/lib/src/enums/network_enums.dart new file mode 100644 index 000000000..36b6b9e3f --- /dev/null +++ b/packages/usp_client_core/lib/src/enums/network_enums.dart @@ -0,0 +1,105 @@ +/// Network-related enumerations for USP/TR-181 protocol. +/// +/// These enums provide type-safe values for network configuration. +library; + +/// WAN connection type. +enum WanType { + dhcp('DHCP'), + static_('Static'), + pppoe('PPPoE'), + pptp('PPTP'), + l2tp('L2TP'), + bridge('Bridge'); + + const WanType(this.value); + final String value; + + static WanType? fromValue(String? value) { + if (value == null) return null; + return WanType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// WAN connection status. +enum WanStatus { + connected('Connected'), + disconnected('Disconnected'), + connecting('Connecting'); + + const WanStatus(this.value); + final String value; + + static WanStatus? fromValue(String? value) { + if (value == null) return null; + return WanStatus.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// Internet connection status. +enum InternetConnectionStatus { + connected('InternetConnected'), + disconnected('InternetDisconnected'), + checking('Checking'); + + const InternetConnectionStatus(this.value); + final String value; + + static InternetConnectionStatus? fromValue(String? value) { + if (value == null) return null; + return InternetConnectionStatus.values + .cast() + .firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// Ethernet port speed. +enum EthernetSpeed { + disconnected('Disconnected'), + mbps10('10Mbps'), + mbps100('100Mbps'), + gbps1('1Gbps'), + gbps2_5('2.5Gbps'), + gbps5('5Gbps'), + gbps10('10Gbps'); + + const EthernetSpeed(this.value); + final String value; + + static EthernetSpeed? fromValue(String? value) { + if (value == null) return null; + return EthernetSpeed.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } + + /// Convert from MaxBitRate value (in Mbps). + static EthernetSpeed fromBitRate(int bitRate) { + switch (bitRate) { + case 10: + return EthernetSpeed.mbps10; + case 100: + return EthernetSpeed.mbps100; + case 1000: + return EthernetSpeed.gbps1; + case 2500: + return EthernetSpeed.gbps2_5; + case 5000: + return EthernetSpeed.gbps5; + case 10000: + return EthernetSpeed.gbps10; + default: + return EthernetSpeed.disconnected; + } + } +} diff --git a/packages/usp_client_core/lib/src/enums/wifi_enums.dart b/packages/usp_client_core/lib/src/enums/wifi_enums.dart new file mode 100644 index 000000000..a328226b6 --- /dev/null +++ b/packages/usp_client_core/lib/src/enums/wifi_enums.dart @@ -0,0 +1,96 @@ +/// WiFi-related enumerations for USP/TR-181 protocol. +/// +/// These enums provide type-safe values for WiFi configuration. +library; + +/// WiFi frequency band. +enum WifiBand { + ghz2_4('2.4GHz'), + ghz5('5GHz'), + ghz6('6GHz'); + + const WifiBand(this.value); + final String value; + + /// Parse from string value. + static WifiBand? fromValue(String? value) { + if (value == null) return null; + return WifiBand.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// WiFi operating mode (802.11 standard). +enum WifiMode { + bg('802.11bg'), + bgn('802.11bgn'), + bgnax('802.11bgnax'), + a('802.11a'), + anac('802.11anac'), + anacax('802.11anacax'), + anacaxbe('802.11anacaxbe'), + ax('802.11ax'), + ac('802.11ac'), + n('802.11n'), + be('802.11be'), + mixed('802.11mixed'); + + const WifiMode(this.value); + final String value; + + static WifiMode? fromValue(String? value) { + if (value == null) return null; + return WifiMode.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// WiFi security type. +enum WifiSecurityType { + none('None'), + wepOpen('WEP-Open'), + wepShared('WEP-Shared'), + wpaPersonal('WPA-Personal'), + wpa2Personal('WPA2-Personal'), + wpa3Personal('WPA3-Personal'), + wpaEnterprise('WPA-Enterprise'), + wpa2Enterprise('WPA2-Enterprise'), + wpa3Enterprise('WPA3-Enterprise'), + wpaWpa2Personal('WPA-WPA2-Personal'), + wpa2Wpa3Personal('WPA2-WPA3-Personal'); + + const WifiSecurityType(this.value); + final String value; + + static WifiSecurityType? fromValue(String? value) { + if (value == null) return null; + return WifiSecurityType.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} + +/// WiFi channel width. +enum WifiChannelWidth { + auto('Auto'), + standard('Standard'), // 20MHz + wide('Wide'), // 40MHz + wide80('Wide80'), // 80MHz + wide160('Wide160'); // 160MHz + + const WifiChannelWidth(this.value); + final String value; + + static WifiChannelWidth? fromValue(String? value) { + if (value == null) return null; + return WifiChannelWidth.values.cast().firstWhere( + (e) => e?.value == value, + orElse: () => null, + ); + } +} diff --git a/packages/usp_client_core/lib/src/grpc_creator/grpc_creator.dart b/packages/usp_client_core/lib/src/grpc_creator/grpc_creator.dart new file mode 100644 index 000000000..abb266b81 --- /dev/null +++ b/packages/usp_client_core/lib/src/grpc_creator/grpc_creator.dart @@ -0,0 +1,12 @@ +/// gRPC Creator - Cross-platform gRPC client channel factory. +/// +/// Automatically selects the correct implementation based on platform: +/// - Web: Uses gRPC-Web (requires Envoy proxy) +/// - Native (iOS/Android/Desktop): Uses standard gRPC +library; + +export 'transport_factory_interface.dart'; + +export 'transport_factory_stub.dart' + if (dart.library.io) 'transport_factory_io.dart' + if (dart.library.html) 'transport_factory_web.dart'; diff --git a/packages/usp_client_core/lib/src/grpc_creator/transport_factory_interface.dart b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_interface.dart new file mode 100644 index 000000000..91a3d9ab0 --- /dev/null +++ b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_interface.dart @@ -0,0 +1,11 @@ +import 'package:grpc/grpc_connection_interface.dart'; + +/// Abstract factory for creating gRPC client channels. +/// +/// This allows the same code to work on both Web (gRPC-Web) and Native (gRPC) platforms. +abstract class TransportFactory { + ClientChannelBase createClientChannel({ + required String host, + required int port, + }); +} diff --git a/packages/usp_client_core/lib/src/grpc_creator/transport_factory_io.dart b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_io.dart new file mode 100644 index 000000000..84042f443 --- /dev/null +++ b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_io.dart @@ -0,0 +1,16 @@ +import 'package:grpc/grpc.dart'; +import 'transport_factory_interface.dart'; + +/// Native platform (iOS/Android/Desktop) implementation of [TransportFactory]. +class NativeTransportFactory implements TransportFactory { + @override + ClientChannel createClientChannel({required String host, required int port}) { + return ClientChannel( + host, + port: port, + options: const ChannelOptions(credentials: ChannelCredentials.insecure()), + ); + } +} + +TransportFactory getTransportFactory() => NativeTransportFactory(); diff --git a/packages/usp_client_core/lib/src/grpc_creator/transport_factory_stub.dart b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_stub.dart new file mode 100644 index 000000000..78bec1a6c --- /dev/null +++ b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_stub.dart @@ -0,0 +1,17 @@ +import 'package:grpc/grpc_connection_interface.dart'; +import 'transport_factory_interface.dart'; + +/// Stub implementation for unsupported platforms. +class TransportFactoryStub implements TransportFactory { + @override + ClientChannelBase createClientChannel({ + required String host, + required int port, + }) { + throw UnsupportedError( + 'ClientChannel not supported on this platform without dart:io or dart:html.', + ); + } +} + +TransportFactory getTransportFactory() => TransportFactoryStub(); diff --git a/packages/usp_client_core/lib/src/grpc_creator/transport_factory_web.dart b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_web.dart new file mode 100644 index 000000000..c3cb5213c --- /dev/null +++ b/packages/usp_client_core/lib/src/grpc_creator/transport_factory_web.dart @@ -0,0 +1,16 @@ +import 'package:grpc/grpc_connection_interface.dart'; +import 'package:grpc/grpc_web.dart'; +import 'transport_factory_interface.dart'; + +/// Web platform implementation of [TransportFactory]. +/// +/// Uses gRPC-Web which requires a proxy (like Envoy) to convert to standard gRPC. +class WebTransportFactory implements TransportFactory { + @override + ClientChannelBase createClientChannel( + {required String host, required int port}) { + return GrpcWebClientChannel.xhr(Uri.parse('http://$host:$port')); + } +} + +TransportFactory getTransportFactory() => WebTransportFactory(); diff --git a/packages/usp_client_core/lib/src/monitoring/polling_manager.dart b/packages/usp_client_core/lib/src/monitoring/polling_manager.dart new file mode 100644 index 000000000..3d88b4b5d --- /dev/null +++ b/packages/usp_client_core/lib/src/monitoring/polling_manager.dart @@ -0,0 +1,37 @@ +import 'dart:async'; +import 'package:logging/logging.dart'; + +/// Manages the lifecycle of polling operations across the application. +/// +/// Allows for global pausing/resuming of all polling (e.g., when app is in background +/// or user is logged out). +/// +/// NOTE: This class is NOT a singleton. The App layer should create and manage +/// the instance via Riverpod Provider or similar DI mechanism. +class PollingManager { + final Logger _logger = Logger('PollingManager'); + final _pauseController = StreamController.broadcast(); + + bool _isPaused = false; + bool get isPaused => _isPaused; + + Stream get pauseStateStream => _pauseController.stream; + + void pauseAll() { + if (_isPaused) return; + _isPaused = true; + _logger.info('⏸️ Polling globally paused'); + _pauseController.add(true); + } + + void resumeAll() { + if (!_isPaused) return; + _isPaused = false; + _logger.info('▶️ Polling globally resumed'); + _pauseController.add(false); + } + + void dispose() { + _pauseController.close(); + } +} diff --git a/packages/usp_client_core/lib/src/monitoring/resource_watcher.dart b/packages/usp_client_core/lib/src/monitoring/resource_watcher.dart new file mode 100644 index 000000000..b010879b8 --- /dev/null +++ b/packages/usp_client_core/lib/src/monitoring/resource_watcher.dart @@ -0,0 +1,177 @@ +import 'dart:async'; +import 'package:logging/logging.dart'; +import 'watch_strategy.dart'; +import 'polling_manager.dart'; + +/// A reusable watcher that handles data monitoring via Push (Subscription) or Pull (Polling). +/// +/// NOTE: [pollingManager] must be injected. The App layer controls the PollingManager instance. +class ResourceWatcher { + final String path; + final WatchStrategy strategy; + final Duration pollingInterval; + final PollingManager pollingManager; + + /// Function to fetch data (Pull) + final Future Function() fetchData; + + /// Function to establish subscription (Push) + /// Returns valid/invalid result. + final Future Function()? ensureSubscription; + + /// Stream of notifications for this resource. + final Stream? notificationStream; + + /// Callback to check if events are supported for this resource. + final bool Function()? checkEventSupport; + + final Logger _logger; + Timer? _pollingTimer; + final StreamController _controller = StreamController.broadcast(); + bool _isDisposed = false; + StreamSubscription? _pauseSubscription; + StreamSubscription? _notificationSubscription; + + ResourceWatcher({ + required this.path, + required this.fetchData, + required this.pollingManager, + this.strategy = WatchStrategy.eventPreferred, + this.pollingInterval = const Duration(seconds: 5), + this.ensureSubscription, + this.notificationStream, + this.checkEventSupport, + }) : _logger = Logger('ResourceWatcher[$path]'); + + /// Starts the watcher. + Stream start() { + if (_isDisposed) { + _logger.warning('Attempted to start disposed watcher'); + return const Stream.empty(); + } + + _decideAndExecuteStrategy(); + + // Listen to global pause/resume + _pauseSubscription = pollingManager.pauseStateStream.listen((paused) { + if (paused) { + _stopPollingTimer(); + } else { + // Resume polling if strategy dictates + if (_shouldPoll()) { + _startPolling(); + } + } + }); + + return _controller.stream; + } + + bool _shouldPoll() { + // Check global pause + if (pollingManager.isPaused) return false; + return _isPollingMode; + } + + bool _isPollingMode = false; + + Future _decideAndExecuteStrategy() async { + bool supportsEvents = + checkEventSupport?.call() ?? (notificationStream != null); + + bool usePolling = false; + bool useEvents = false; + + switch (strategy) { + case WatchStrategy.pollingOnly: + usePolling = true; + break; + case WatchStrategy.eventOnly: + if (supportsEvents) useEvents = true; + break; + case WatchStrategy.eventPreferred: + if (supportsEvents) { + useEvents = true; + } else { + usePolling = true; + } + break; + case WatchStrategy.hybrid: + useEvents = supportsEvents; + usePolling = true; + break; + } + + if (useEvents) { + bool subscriptionSuccess = await _setupSubscription(); + if (!subscriptionSuccess && strategy == WatchStrategy.eventPreferred) { + _logger + .info('Subscription failed/unsupported, falling back to polling'); + usePolling = true; + } + } + + if (usePolling) { + _isPollingMode = true; + _startPolling(); + } else { + _isPollingMode = false; + } + } + + Future _setupSubscription() async { + if (ensureSubscription == null || notificationStream == null) return false; + + try { + await ensureSubscription!(); + + // Listen to notification stream and store subscription for cleanup + _notificationSubscription = notificationStream!.listen((data) { + if (!_isDisposed && !_controller.isClosed) { + _controller.add(data); + } + }); + return true; + } catch (e) { + _logger.warning('Subscription error: $e'); + return false; + } + } + + void _startPolling() { + if (pollingManager.isPaused) return; + + _logger.fine('Starting polling (${pollingInterval.inSeconds}s)'); + _fetchSafe(); // Initial fetch + + _pollingTimer?.cancel(); + _pollingTimer = Timer.periodic(pollingInterval, (_) => _fetchSafe()); + } + + void _stopPollingTimer() { + _pollingTimer?.cancel(); + _pollingTimer = null; + } + + Future _fetchSafe() async { + if (_isDisposed || _controller.isClosed) return; + try { + final data = await fetchData(); + if (!_controller.isClosed) { + _controller.add(data); + } + } catch (e) { + if (!_controller.isClosed) { + _controller.addError(e); + } + } + } + + void stop() { + _isDisposed = true; + _stopPollingTimer(); + _pauseSubscription?.cancel(); + _notificationSubscription?.cancel(); + _controller.close(); + } +} diff --git a/packages/usp_client_core/lib/src/monitoring/watch_strategy.dart b/packages/usp_client_core/lib/src/monitoring/watch_strategy.dart new file mode 100644 index 000000000..2ddbc6a23 --- /dev/null +++ b/packages/usp_client_core/lib/src/monitoring/watch_strategy.dart @@ -0,0 +1,17 @@ +/// Strategies for monitoring a resource. +enum WatchStrategy { + /// Only use polling (Timer-based). + pollingOnly, + + /// Only use events (Subscription-based). + /// If events fail or are unsupported, data will not update. + eventOnly, + + /// Prefer events, but fallback to polling if subscription fails or is unsupported. + eventPreferred, + + /// Use both events and polling. + /// Useful if events are reliable for changes but you still want a periodic sync + /// to ensure data integrity. + hybrid, +} diff --git a/packages/usp_client_core/lib/src/services/_services.dart b/packages/usp_client_core/lib/src/services/_services.dart new file mode 100644 index 000000000..446ab5c1a --- /dev/null +++ b/packages/usp_client_core/lib/src/services/_services.dart @@ -0,0 +1,10 @@ +/// USP Services barrel export. +/// +/// Re-exports all USP service classes for convenient importing. +library; + +export 'usp_device_service.dart'; +export 'usp_wifi_service.dart'; +export 'usp_network_service.dart'; +export 'usp_topology_service.dart'; +export 'usp_diagnostics_service.dart'; diff --git a/packages/usp_client_core/lib/src/services/usp_capability_service.dart b/packages/usp_client_core/lib/src/services/usp_capability_service.dart new file mode 100644 index 000000000..d34fbb734 --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_capability_service.dart @@ -0,0 +1,89 @@ +import 'dart:async'; +import 'package:logging/logging.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart' hide Set; + +import '../connection/usp_grpc_client_service.dart'; + +/// Service responsible for discovering and caching the device's supported Data Model. +/// +/// This service encapsulates the [UspGetSupportedDMRequest] logic and provides +/// efficient lookups to check if specific paths, commands, or events are supported +/// by the connected Agent. +class UspCapabilityService { + final UspGrpcClientService _grpcService; + final Logger _logger = Logger('UspCapabilityService'); + + // Set of supported paths (Objects and Commands) + final Set _supportedSchema = {}; + + UspCapabilityService(this._grpcService); + + /// Initializes the capability cache by querying the device. + /// + /// This should be called after the gRPC connection is established. + Future initialize() async { + _supportedSchema.clear(); + + // Query for the entire Device model. + // We use UspPath.parse to ensure correct type usage for the request. + final request = UspGetSupportedDMRequest( + [UspPath.parse('Device.')], + firstLevelOnly: false, + returnCommands: true, + returnEvents: true, + returnParams: true, + ); + + try { + _logger.info('Querying Device model capabilities...'); + final response = await _grpcService.sendRequest(request); + + if (response is UspGetSupportedDMResponse) { + _parseResponse(response); + _logger.info( + 'Capabilities loaded: ${_supportedSchema.length} schema items found.'); + } + } catch (e) { + _logger.severe('Failed to get capabilities', e); + // We don't rethrow here to allow partial app function, + // but in a strict mode we might want to. + } + } + + void _parseResponse(UspGetSupportedDMResponse response) { + for (final entry in response.results.entries) { + final path = entry.key; + final def = entry.value; + + _supportedSchema.add(path); + + // Supported Commands + // Note: USP Command paths often look like "Device.WiFi.Reset()" + // The definition provides just the command name "Reset". + // We store the fully qualified path for easier verification. + for (final cmdName in def.supportedCommands.keys) { + _supportedSchema.add('$path$cmdName()'); + } + + // Supported Events + // (If available in your UspObjectDefinition structure) + } + } + + /// Checks if a raw TR-181 path (Object, Param, or Command) is supported. + /// + /// Supports partial matches for objects (e.g. searching for "Device.WiFi." + /// might match "Device.WiFi.SSID."). + bool isPathSupported(String path) { + if (_supportedSchema.contains(path)) return true; + + // Check if the schema contains any path that implies this path + // OR if this path is a parent of something in the schema (less likely for features). + // Usually we check: "Do we have Device.WiFi.?" -> yes. + return _supportedSchema + .any((s) => s.startsWith(path) || path.startsWith(s)); + } + + /// Returns simple stats about the loaded schema. + int get schemaSize => _supportedSchema.length; +} diff --git a/packages/usp_client_core/lib/src/services/usp_device_service.dart b/packages/usp_client_core/lib/src/services/usp_device_service.dart new file mode 100644 index 000000000..ab738eb1d --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_device_service.dart @@ -0,0 +1,142 @@ +/// USP Device Service +/// +/// Provides device information via USP/TR-181 protocol. +library; + +import 'package:usp_protocol_common/usp_protocol_common.dart'; +import '../tr181_paths.dart'; +import '../connection/usp_grpc_client_service.dart'; + +/// Service for retrieving device information via USP. +/// +/// This service communicates with the USP Agent to retrieve +/// TR-181 Device.DeviceInfo.* data. +class UspDeviceService { + final UspGrpcClientService _grpcService; + + /// Creates a new [UspDeviceService]. + UspDeviceService(this._grpcService); + + /// Retrieves device information as a Map. + /// + /// Returns a map containing manufacturer, modelNumber, serialNumber, + /// firmwareVersion, etc. in JNAP-compatible format. + Future> getDeviceInfo() async { + final paths = + Tr181Paths.deviceInfoPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapDeviceInfo(response); + } + + /// Retrieves system statistics. + /// + /// Returns a map containing uptimeSeconds, CPULoad, MemoryLoad. + Future> getSystemStats() async { + final paths = + Tr181Paths.systemStatsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapSystemStats(response); + } + + // =========================================================================== + // Private Mapping Methods + // =========================================================================== + + /// Maps USP response to device info map. + Map _mapDeviceInfo(UspGetResponse response) { + final values = _flattenResults(response); + + return { + 'manufacturer': + values['Device.DeviceInfo.Manufacturer']?.toString() ?? 'Unknown', + 'modelNumber': + values['Device.DeviceInfo.ModelName']?.toString() ?? 'Unknown', + 'serialNumber': + values['Device.DeviceInfo.SerialNumber']?.toString() ?? 'Unknown', + 'hardwareVersion': + values['Device.DeviceInfo.HardwareVersion']?.toString() ?? 'v1.0', + 'firmwareVersion': + values['Device.DeviceInfo.SoftwareVersion']?.toString() ?? '1.0.0', + 'firmwareDate': '', // TR-181 doesn't have this field + 'description': values['Device.DeviceInfo.Description']?.toString() ?? '', + 'services': _supportedServices, + }; + } + + /// Maps USP response to system stats map. + Map _mapSystemStats(UspGetResponse response) { + final values = _flattenResults(response); + + final uptime = + int.tryParse(values['Device.DeviceInfo.UpTime']?.toString() ?? '0') ?? + 0; + final cpuUsage = int.tryParse( + values['Device.DeviceInfo.ProcessStatus.CPUUsage']?.toString() ?? + '0') ?? + 0; + final memTotal = int.tryParse( + values['Device.DeviceInfo.MemoryStatus.Total']?.toString() ?? + '1') ?? + 1; + final memFree = int.tryParse( + values['Device.DeviceInfo.MemoryStatus.Free']?.toString() ?? '0') ?? + 0; + + // Calculate memory load (0.0 - 1.0) + final memLoad = (memTotal > 0) ? (memTotal - memFree) / memTotal : 0.0; + + return { + 'uptimeSeconds': uptime, + 'CPULoad': (cpuUsage / 100).toString(), + 'MemoryLoad': memLoad.toStringAsFixed(2), + }; + } + + // =========================================================================== + // Helper Methods + // =========================================================================== + + /// Flattens USP results into a simple key-value map. + Map _flattenResults(UspGetResponse response) { + final result = {}; + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + return result; + } + + /// List of JNAP services supported. + /// NOTE: These must be SERVICE URLs (e.g. .../core/Core), NOT Action URLs. + static const _supportedServices = [ + 'http://linksys.com/jnap/core/Core', + 'http://linksys.com/jnap/wirelessap/WirelessAP', + 'http://linksys.com/jnap/wirelessap/WirelessAP2', + 'http://linksys.com/jnap/wirelessap/WirelessAP3', + 'http://linksys.com/jnap/devicelist/DeviceList', + 'http://linksys.com/jnap/devicelist/DeviceList2', + 'http://linksys.com/jnap/nodes/diagnostics/Diagnostics', + 'http://linksys.com/jnap/nodes/diagnostics/Diagnostics2', + 'http://linksys.com/jnap/router/Router', + 'http://linksys.com/jnap/router/Router2', + 'http://linksys.com/jnap/router/Router3', + 'http://linksys.com/jnap/locale/Locale', + 'http://linksys.com/jnap/guestnetwork/GuestNetwork', + 'http://linksys.com/jnap/guestnetwork/GuestNetwork2', + 'http://linksys.com/jnap/macfilter/MACFilter', + 'http://linksys.com/jnap/macfilter/MACFilter2', + 'http://linksys.com/jnap/networkconnections/NetworkConnections', + 'http://linksys.com/jnap/networkconnections/NetworkConnections2', + ]; +} diff --git a/packages/usp_client_core/lib/src/services/usp_diagnostics_service.dart b/packages/usp_client_core/lib/src/services/usp_diagnostics_service.dart new file mode 100644 index 000000000..8777d5538 --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_diagnostics_service.dart @@ -0,0 +1,130 @@ +/// USP Diagnostics Service +/// +/// Provides diagnostics information via USP/TR-181 protocol. +library; + +import '../enums/_enums.dart'; +import '../tr181_paths.dart'; +import '../connection/usp_grpc_client_service.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// Service for retrieving diagnostics information via USP. +/// +/// This service handles network connectivity status and port connections. +class UspDiagnosticsService { + final UspGrpcClientService _grpcService; + + /// Creates a new [UspDiagnosticsService]. + UspDiagnosticsService(this._grpcService); + + /// Retrieves internet connection status. + /// + /// Returns a map containing connectionStatus (InternetConnected/Disconnected). + Future> getInternetConnectionStatus() async { + final paths = Tr181Paths.internetConnectionStatusPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapInternetConnectionStatus(response); + } + + /// Retrieves ethernet port connection status. + /// + /// Returns a map containing wanPortConnection and lanPortConnections list. + Future> getEthernetPortConnections() async { + final paths = Tr181Paths.ethernetPortConnectionsPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapEthernetPortConnections(response); + } + + // =========================================================================== + // Private Mapping Methods + // =========================================================================== + + Map _mapInternetConnectionStatus(UspGetResponse response) { + final values = _flattenResults(response); + final status = values['Device.IP.Interface.1.Status']?.toString() ?? 'Down'; + + final connectionStatus = (status == 'Up') + ? InternetConnectionStatus.connected + : InternetConnectionStatus.disconnected; + + return { + 'connectionStatus': connectionStatus.value, + }; + } + + Map _mapEthernetPortConnections(UspGetResponse response) { + final values = _flattenResults(response); + + // WAN -> Device.Ethernet.Interface.1 + final wanEnable = + values['Device.Ethernet.Interface.1.Enable']?.toString() == 'true'; + final wanBitRate = int.tryParse( + values['Device.Ethernet.Interface.1.MaxBitRate']?.toString() ?? + '0') ?? + 0; + + final wanSpeed = wanEnable + ? EthernetSpeed.fromBitRate(wanBitRate) + : EthernetSpeed.disconnected; + + // LAN Ports (Interfaces 3+ in mock data) + final lanList = []; + final interfaceCount = int.tryParse( + values['Device.Ethernet.InterfaceNumberOfEntries']?.toString() ?? + '6') ?? + 6; + + // LAN ports usually start at index 3 (1=WAN, 2=Bridge) + for (int i = 3; i <= interfaceCount; i++) { + final enableKey = 'Device.Ethernet.Interface.$i.Enable'; + if (!values.containsKey(enableKey)) { + continue; + } + + final enable = values[enableKey]?.toString() == 'true'; + final bitRate = int.tryParse( + values['Device.Ethernet.Interface.$i.MaxBitRate']?.toString() ?? + '0') ?? + 0; + + final portSpeed = enable + ? EthernetSpeed.fromBitRate(bitRate) + : EthernetSpeed.disconnected; + + lanList.add(portSpeed.value); + } + + return { + 'wanPortConnection': wanSpeed.value, + 'lanPortConnections': lanList, + }; + } + + // =========================================================================== + // Helper Methods + // =========================================================================== + + Map _flattenResults(UspGetResponse response) { + final result = {}; + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + return result; + } +} diff --git a/packages/usp_client_core/lib/src/services/usp_network_service.dart b/packages/usp_client_core/lib/src/services/usp_network_service.dart new file mode 100644 index 000000000..c84121462 --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_network_service.dart @@ -0,0 +1,206 @@ +/// USP Network Service +/// +/// Provides network settings (WAN/LAN/Time) via USP/TR-181 protocol. +library; + +import '../enums/_enums.dart'; +import '../tr181_paths.dart'; +import '../connection/usp_grpc_client_service.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// Service for retrieving network settings via USP. +/// +/// This service handles WAN status, LAN settings, and time configuration. +class UspNetworkService { + final UspGrpcClientService _grpcService; + + /// Creates a new [UspNetworkService]. + UspNetworkService(this._grpcService); + + /// Retrieves WAN status. + /// + /// Returns a map containing macAddress, detectedWANType, wanStatus, + /// wanConnection, supportedWANTypes, etc. + Future> getWANStatus() async { + final paths = + Tr181Paths.wanStatusPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapWANStatus(response); + } + + /// Retrieves LAN settings. + /// + /// Returns a map containing hostName, ipAddress, subnetMask, dhcpSettings, etc. + Future> getLANSettings() async { + final paths = + Tr181Paths.lanSettingsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapLANSettings(response); + } + + /// Retrieves time settings. + /// + /// Returns a map containing timeZone, autoAdjustForDST, currentLocalTime. + Future> getTimeSettings() async { + final paths = + Tr181Paths.timeSettingsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapTimeSettings(response); + } + + // =========================================================================== + // Private Mapping Methods + // =========================================================================== + + Map _mapWANStatus(UspGetResponse response) { + final values = _flattenResults(response); + + // Get WAN IP info + final ipAddress = + values['Device.IP.Interface.1.IPv4Address.1.IPAddress']?.toString() ?? + ''; + final subnetMask = + values['Device.IP.Interface.1.IPv4Address.1.SubnetMask']?.toString() ?? + ''; + final gateway = + values['Device.Routing.Router.1.IPv4Forwarding.1.GatewayIPAddress'] + ?.toString() ?? + ''; + final macAddress = + values['Device.Ethernet.Interface.1.MACAddress']?.toString() ?? ''; + + // Determine WAN type from AddressingType + final addressingType = + values['Device.IP.Interface.1.IPv4Address.1.AddressingType'] + ?.toString() ?? + 'DHCP'; + final wanType = addressingType == 'Static' ? WanType.static_ : WanType.dhcp; + + // Determine WAN status from interface status + final ifStatus = values['Device.IP.Interface.1.Status']?.toString() ?? 'Up'; + final wanStatus = + ifStatus == 'Up' ? WanStatus.connected : WanStatus.disconnected; + + return { + 'macAddress': macAddress, + 'detectedWANType': wanType.value, + 'wanStatus': wanStatus.value, + 'wanIPv6Status': WanStatus.disconnected.value, // Simplified for demo + 'isDetectingWANType': false, + 'wanConnection': { + 'wanType': wanType.value, + 'ipAddress': ipAddress, + 'networkPrefixLength': _subnetMaskToPrefix(subnetMask), + 'gateway': gateway, + 'mtu': 1500, + 'dhcpLeaseMinutes': wanType == WanType.dhcp ? 4320 : null, + 'dnsServer1': gateway.isNotEmpty ? gateway : '8.8.8.8', + }, + // Use enum values for supported types + 'supportedWANTypes': WanType.values.map((e) => e.value).toList(), + 'supportedIPv6WANTypes': ['Automatic', 'PPPoE', 'Pass-through'], + 'supportedWANCombinations': [ + {'wanType': WanType.dhcp.value, 'wanIPv6Type': 'Automatic'}, + {'wanType': WanType.static_.value, 'wanIPv6Type': 'Automatic'}, + {'wanType': WanType.pppoe.value, 'wanIPv6Type': 'Automatic'}, + ], + }; + } + + Map _mapLANSettings(UspGetResponse response) { + final values = _flattenResults(response); + + final lanIp = + values['Device.IP.Interface.2.IPv4Address.1.IPAddress']?.toString() ?? + '192.168.1.1'; + final subnetMask = + values['Device.IP.Interface.2.IPv4Address.1.SubnetMask']?.toString() ?? + '255.255.255.0'; + + return { + 'hostName': 'LinksysRouter', + 'ipAddress': lanIp, + 'networkPrefixLength': _subnetMaskToPrefix(subnetMask), + 'isDHCPEnabled': + values['Device.DHCPv4.Server.Pool.1.Enable']?.toString() == 'true', + 'dhcpSettings': { + 'firstClientIPAddress': + values['Device.DHCPv4.Server.Pool.1.MinAddress']?.toString() ?? + '192.168.1.100', + 'lastClientIPAddress': + values['Device.DHCPv4.Server.Pool.1.MaxAddress']?.toString() ?? + '192.168.1.149', + 'leaseMinutes': int.tryParse( + values['Device.DHCPv4.Server.Pool.1.LeaseTime']?.toString() ?? + '1440') ?? + 1440, + }, + 'minNetworkPrefixLength': 8, + 'maxNetworkPrefixLength': 30, + 'minAllowedDHCPLeaseMinutes': 60, + 'maxAllowedDHCPLeaseMinutes': 2880, + }; + } + + Map _mapTimeSettings(UspGetResponse response) { + final values = _flattenResults(response); + + return { + 'timeZone': values['Device.Time.LocalTimeZone']?.toString() ?? 'UTC', + 'autoAdjustForDST': true, // TR-181 doesn't have direct mapping + 'currentLocalTime': + values['Device.Time.CurrentLocalTime']?.toString() ?? '', + }; + } + + // =========================================================================== + // Helper Methods + // =========================================================================== + + Map _flattenResults(UspGetResponse response) { + final result = {}; + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + return result; + } + + /// Convert subnet mask to prefix length. + /// e.g., "255.255.255.0" -> 24 + int _subnetMaskToPrefix(String subnetMask) { + if (subnetMask.isEmpty) return 24; + try { + final parts = subnetMask.split('.'); + if (parts.length != 4) return 24; + int prefix = 0; + for (final part in parts) { + int octet = int.parse(part); + while (octet > 0) { + prefix += octet & 1; + octet >>= 1; + } + } + return prefix; + } catch (_) { + return 24; + } + } +} diff --git a/packages/usp_client_core/lib/src/services/usp_topology_service.dart b/packages/usp_client_core/lib/src/services/usp_topology_service.dart new file mode 100644 index 000000000..401bd94bd --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_topology_service.dart @@ -0,0 +1,488 @@ +/// USP Topology Service +/// +/// Provides device topology and connection information via USP/TR-181 protocol. +library; + +import '../tr181_paths.dart'; +import '../utils/usp_logger.dart'; +import '../connection/usp_grpc_client_service.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// Service for retrieving device topology via USP. +/// +/// This service handles connected devices, mesh nodes, backhaul info, +/// and network connections. +class UspTopologyService { + final UspGrpcClientService _grpcService; + + /// Creates a new [UspTopologyService]. + UspTopologyService(this._grpcService); + + /// Retrieves all connected devices. + /// + /// Returns a map containing devices list (clients + mesh nodes). + Future> getDevices() async { + final paths = Tr181Paths.devicesPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapDevices(response); + } + + /// Retrieves backhaul information for mesh nodes. + /// + /// Returns a map containing backhaulDevices list. + Future> getBackhaulInfo() async { + final paths = + Tr181Paths.backhaulInfoPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapBackhaulInfo(response); + } + + /// Retrieves network connections for all hosts. + /// + /// Returns a map containing connections list with wireless/wired info. + Future> getNetworkConnections() async { + final paths = Tr181Paths.networkConnectionsPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapNetworkConnections(response); + } + + /// Retrieves nodes wireless network connections. + /// + /// Returns a map containing nodeWirelessConnections for mesh topology. + Future> getNodesWirelessNetworkConnections() async { + final paths = Tr181Paths.nodesWirelessConnectionsPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapNodesWirelessNetworkConnections(response); + } + + // =========================================================================== + // Private Mapping Methods + // =========================================================================== + + Map _mapDevices(UspGetResponse response) { + UspLogger.logMappingStart('GetDevices'); + + final values = _flattenResults(response); + UspLogger.logTr181Data(values); + + final devices = >[]; + + // 1. Add client devices from Hosts + final hostCount = int.tryParse( + values['Device.Hosts.HostNumberOfEntries']?.toString() ?? '0') ?? + 0; + UspLogger.logCount('client hosts', hostCount); + + // Find Master Node ID for parentDeviceID linkage + String masterNodeId = ''; + final multiApCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + for (int i = 1; i <= multiApCount; i++) { + if (values['Device.WiFi.MultiAP.APDevice.$i.BackhaulLinkType'] + ?.toString() == + 'None') { + masterNodeId = + values['Device.WiFi.MultiAP.APDevice.$i.MACAddress']?.toString() ?? + ''; + break; + } + } + if (masterNodeId.isEmpty) masterNodeId = '00:00:00:00:00:00'; // Fallback + + for (int i = 1; i <= hostCount; i++) { + final prefix = 'Device.Hosts.Host.$i'; + final mac = values['$prefix.PhysAddress']?.toString() ?? ''; + final ifType = values['$prefix.InterfaceType']?.toString() ?? ''; + final layer1 = values['$prefix.Layer1Interface']?.toString() ?? ''; + + String? band; + if (ifType == '802.11') { + // Derive band from SSID reference: Device.WiFi.SSID.1 -> 2.4GHz + final ssidIndex = int.tryParse(layer1.split('.').lastOrNull ?? '') ?? 0; + if (ssidIndex == 1 || ssidIndex == 4) band = '2.4GHz'; + if (ssidIndex == 2) band = '5GHz'; + if (ssidIndex == 3) band = '6GHz'; + } + + devices.add({ + 'deviceID': mac.isNotEmpty ? mac : 'unknown-$i', + 'properties': >[], + 'unit': {}, + 'maxAllowedProperties': 10, + 'model': {'deviceType': 'Computer'}, + 'lastChangeRevision': 0, + 'knownMACAddresses': [mac], + 'knownInterfaces': [ + { + 'macAddress': mac, + 'interfaceType': ifType == '802.11' ? 'Wireless' : 'Wired', + if (band != null) 'band': band, + } + ], + 'friendlyName': values['$prefix.HostName']?.toString() ?? 'Device $i', + 'isAuthority': false, + 'connections': [ + { + 'macAddress': mac, + 'ipAddress': values['$prefix.IPAddress']?.toString() ?? '', + 'isOnline': values['$prefix.Active']?.toString() == 'true', + 'interfaceType': ifType == '802.11' ? 'Wireless' : 'Wired', + 'parentDeviceID': masterNodeId, + } + ], + }); + } + + // 2. Add mesh nodes from MultiAP.APDevice + final apCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apCount; i++) { + final prefix = 'Device.WiFi.MultiAP.APDevice.$i'; + final mac = values['$prefix.MACAddress']?.toString() ?? ''; + final backhaulType = + values['$prefix.BackhaulLinkType']?.toString() ?? 'None'; + final isMaster = backhaulType == 'None'; // Master has no backhaul + final serial = values['$prefix.SerialNumber']?.toString() ?? ''; + final parentMac = values['$prefix.BackhaulMACAddress']?.toString() ?? ''; + + devices.add({ + 'deviceID': mac, + 'properties': >[], + 'unit': {'serialNumber': serial}, + 'maxAllowedProperties': 10, + 'model': { + 'deviceType': 'Infrastructure', + 'manufacturer': values['$prefix.Manufacturer']?.toString(), + 'modelNumber': isMaster ? 'Master' : 'Slave', + }, + 'lastChangeRevision': 0, + 'knownMACAddresses': [mac], + 'knownInterfaces': [ + { + 'macAddress': mac, + 'interfaceType': + (backhaulType == 'Wi-Fi' || isMaster) ? 'Wireless' : 'Wired', + } + ], + 'friendlyName': isMaster ? 'Master Router' : 'Mesh Node', + 'isAuthority': isMaster, + 'nodeType': isMaster ? 'Master' : 'Slave', + 'connections': [ + { + 'macAddress': mac, + 'ipAddress': '', // Nodes might have IP but not exposed easily + 'isOnline': 'true', // Mesh nodes derived from APDevice are active + 'interfaceType': 'Infrastructure', + 'parentDeviceID': isMaster ? null : parentMac, + } + ], + }); + } + + UspLogger.logMappingResult( + 'devices', + devices + .map((d) => ( + d['friendlyName']?.toString() ?? 'Unknown', + d['deviceID']?.toString() ?? '', + )) + .toList(), + ); + + return {'devices': devices}; + } + + Map _mapBackhaulInfo(UspGetResponse response) { + final values = _flattenResults(response); + final backhaulDevices = >[]; + + final apCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apCount; i++) { + final prefix = 'Device.WiFi.MultiAP.APDevice.$i'; + final backhaulType = + values['$prefix.BackhaulLinkType']?.toString() ?? 'None'; + + // Skip master node (no backhaul) + if (backhaulType == 'None') continue; + + final rssi = int.tryParse( + values['$prefix.BackhaulSignalStrength']?.toString() ?? '0') ?? + 0; + + int rssiDbm; + // If RSSI is negative, it's already in dBm. If positive (0-220), it's RCPI. + if (rssi < 0) { + rssiDbm = rssi; + } else { + // Convert RCPI (0-220) to dBm: (RCPI / 2) - 110 + rssiDbm = (rssi / 2).round() - 110; + } + + final mac = values['$prefix.MACAddress']?.toString() ?? ''; + final parentMac = values['$prefix.BackhaulMACAddress']?.toString() ?? ''; + + // Mock IPs for demonstration + final mockIp = '192.168.1.${10 + i}'; + final mockParentIp = '192.168.1.1'; // Assume master is parent + + backhaulDevices.add({ + 'deviceUUID': mac, + 'ipAddress': mockIp, + 'parentIPAddress': mockParentIp, + 'timestamp': DateTime.now().toIso8601String(), + 'connectionType': backhaulType == 'Wi-Fi' ? 'Wireless' : 'Wired', + 'wirelessConnectionInfo': backhaulType == 'Wi-Fi' + ? { + 'radioID': 'RADIO_5GHz', // Mock band + 'channel': 0, + 'apRSSI': rssiDbm, + 'stationRSSI': rssiDbm, + 'apBSSID': parentMac, + 'stationBSSID': mac, + 'txRate': 0, + 'rxRate': 0, + 'isMultiLinkOperation': false, + } + : null, // Must be null for Wired + 'speedMbps': '0', + }); + } + + return {'backhaulDevices': backhaulDevices}; + } + + Map _mapNetworkConnections(UspGetResponse response) { + final values = _flattenResults(response); + final connections = >[]; + + final hostCount = int.tryParse( + values['Device.Hosts.HostNumberOfEntries']?.toString() ?? '0') ?? + 0; + + // Helper to find Associated Device for a MAC + Map? findWirelessInfo(String mac) { + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '4') ?? + 4; + + for (int i = 1; i <= apCount; i++) { + final assocCount = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDeviceNumberOfEntries'] + ?.toString() ?? + '0') ?? + 0; + for (int j = 1; j <= assocCount; j++) { + final assocMac = values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.MACAddress']; + if (assocMac?.toString().toLowerCase() == mac.toLowerCase()) { + return { + 'downlinkRate': values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.LastDataDownlinkRate'], + 'signalStrength': values[ + 'Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.SignalStrength'], + 'apIndex': i, + }; + } + } + } + return null; + } + + for (int i = 1; i <= hostCount; i++) { + final mac = values['Device.Hosts.Host.$i.PhysAddress']?.toString() ?? ''; + final ip = values['Device.Hosts.Host.$i.IPAddress']?.toString() ?? ''; + final interfaceType = + values['Device.Hosts.Host.$i.InterfaceType']?.toString() ?? ''; + + if (mac.isEmpty) continue; + + final connection = { + 'macAddress': mac, + 'ipAddress': ip, + }; + + if (interfaceType == '802.11' || interfaceType == 'Wireless') { + final wirelessInfo = findWirelessInfo(mac); + if (wirelessInfo != null) { + final rateKbps = + int.tryParse(wirelessInfo['downlinkRate']?.toString() ?? '0') ?? + 0; + connection['negotiatedMbps'] = (rateKbps / 1000).round(); + + final apIndex = wirelessInfo['apIndex']; + String band = '5GHz'; + if (apIndex == 1) band = '2.4GHz'; + if (apIndex == 3) band = '6GHz'; + + connection['wireless'] = { + 'signalDecibels': int.tryParse( + wirelessInfo['signalStrength']?.toString() ?? '-50') ?? + -50, + 'band': band, + 'bssid': values['Device.WiFi.SSID.$apIndex.MACAddress'] ?? + '00:00:00:00:00:A$apIndex', + 'isGuest': apIndex == 4, + 'radioID': 'RADIO_$band', + 'txRate': rateKbps, + 'rxRate': rateKbps, + 'isMLOCapable': false, + }; + } else { + connection['negotiatedMbps'] = 0; + connection['wireless'] = { + 'signalDecibels': -99, + 'band': '2.4GHz', + 'isGuest': false + }; + } + } else { + connection['negotiatedMbps'] = 1000; // Assume Gigabit for wired + } + connections.add(connection); + } + + return {'connections': connections}; + } + + Map _mapNodesWirelessNetworkConnections( + UspGetResponse response) { + final values = _flattenResults(response); + final nodeWirelessConnections = >[]; + + // 1. Find Master Node ID + String masterNodeId = ''; + final apDeviceCount = int.tryParse( + values['Device.WiFi.MultiAP.APDeviceNumberOfEntries']?.toString() ?? + '0') ?? + 0; + + for (int i = 1; i <= apDeviceCount; i++) { + final backhaulType = + values['Device.WiFi.MultiAP.APDevice.$i.BackhaulLinkType'] + ?.toString(); + if (backhaulType == 'None') { + masterNodeId = + values['Device.WiFi.MultiAP.APDevice.$i.MACAddress']?.toString() ?? + ''; + break; + } + } + + if (masterNodeId.isEmpty) { + masterNodeId = '00:00:00:00:00:00'; + } + + // 2. Aggregate all local Associated Devices + final connections = >[]; + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '4') ?? + 4; + + for (int i = 1; i <= apCount; i++) { + final assocCount = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDeviceNumberOfEntries'] + ?.toString() ?? + '0') ?? + 0; + + for (int j = 1; j <= assocCount; j++) { + final mac = + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.MACAddress']; + if (mac != null) { + final rateKbps = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.LastDataDownlinkRate'] + ?.toString() ?? + '0') ?? + 0; + final signal = int.tryParse( + values['Device.WiFi.AccessPoint.$i.AssociatedDevice.$j.SignalStrength'] + ?.toString() ?? + '-50') ?? + -50; + + String band = '5GHz'; + if (i == 1) band = '2.4GHz'; + if (i == 3) band = '6GHz'; + + connections.add({ + 'macAddress': mac, + 'negotiatedMbps': (rateKbps / 1000).round(), + 'timestamp': DateTime.now().toIso8601String(), + 'wireless': { + 'bssid': values['Device.WiFi.SSID.$i.MACAddress'] ?? + '00:00:00:00:00:00', + 'isGuest': i == 4, + 'radioID': 'RADIO_$band', + 'band': band, + 'signalDecibels': signal, + 'txRate': rateKbps, + 'rxRate': rateKbps, + 'isMLOCapable': false, + } + }); + } + } + } + + // 3. Construct response structure + if (connections.isNotEmpty) { + nodeWirelessConnections.add({ + 'deviceID': masterNodeId, + 'connections': connections, + }); + } + + return {'nodeWirelessConnections': nodeWirelessConnections}; + } + + // =========================================================================== + // Helper Methods + // =========================================================================== + + Map _flattenResults(UspGetResponse response) { + final result = {}; + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + return result; + } +} diff --git a/packages/usp_client_core/lib/src/services/usp_wifi_service.dart b/packages/usp_client_core/lib/src/services/usp_wifi_service.dart new file mode 100644 index 000000000..a555b3077 --- /dev/null +++ b/packages/usp_client_core/lib/src/services/usp_wifi_service.dart @@ -0,0 +1,525 @@ +/// USP WiFi Service +/// +/// Provides WiFi settings via USP/TR-181 protocol. +library; + +import '../tr181_paths.dart'; +import '../connection/usp_grpc_client_service.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// Service for retrieving WiFi settings via USP. +/// +/// This service handles radio configuration, guest networks, and MAC filtering. +class UspWifiService { + final UspGrpcClientService _grpcService; + + /// Creates a new [UspWifiService]. + UspWifiService(this._grpcService); + + /// Retrieves radio information. + /// + /// Returns a map containing isBandSteeringSupported and radios list. + Future> getRadioInfo() async { + final paths = + Tr181Paths.radioInfoPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapRadioInfo(response); + } + + /// Retrieves guest radio settings. + /// + /// Returns a map containing isGuestNetworkEnabled and radios list with guest settings. + Future> getGuestRadioSettings() async { + final paths = Tr181Paths.guestRadioSettingsPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapGuestRadioSettings(response); + } + + /// Retrieves MAC filter settings. + /// + /// Returns a map containing macFilterMode, isEnabled, macAddresses list. + Future> getMACFilterSettings() async { + final paths = + Tr181Paths.macFilterSettingsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapMACFilterSettings(response); + } + + /// Retrieves client/node steering settings. + /// + /// Returns a map containing isClientSteeringEnabled and isNodeSteeringEnabled. + Future> getClientSteeringSettings() async { + final paths = Tr181Paths.clientSteeringSettingsPaths + .map((p) => UspPath.parse(p)) + .toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapClientSteeringSettings(response); + } + + /// Retrieves DFS (Dynamic Frequency Selection) settings. + /// + /// Returns a map containing isDFSSupported and isDFSEnabled. + /// DFS is typically controlled via AutoChannelEnable on 5GHz radios. + Future> getDFSSettings() async { + final paths = + Tr181Paths.dfsSettingsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapDFSSettings(response); + } + + /// Retrieves MLO (Multi-Link Operation) settings for Wi-Fi 7. + /// + /// Returns a map containing isMLOSupported and isMLOEnabled. + Future> getMLOSettings() async { + final paths = + Tr181Paths.mloSettingsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapMLOSettings(response); + } + + /// Retrieves STA (Station) BSSIDs. + /// + /// Returns a map containing staBSSIDS list. + Future> getSTABSSIDs() async { + final paths = + Tr181Paths.staBssidsPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapSTABSSIDs(response); + } + + /// Retrieves local device MAC address. + /// + /// Returns a map containing deviceID and macAddress. + Future> getLocalDeviceMAC() async { + final paths = + Tr181Paths.localDeviceMacPaths.map((p) => UspPath.parse(p)).toList(); + + final response = await _grpcService.sendRequest(UspGetRequest(paths)); + + if (response is! UspGetResponse) { + throw Exception('Unexpected response type: ${response.runtimeType}'); + } + + return _mapLocalDeviceMAC(response); + } + + // =========================================================================== + // Private Mapping Methods + // =========================================================================== + + Map _mapRadioInfo(UspGetResponse response) { + final values = _flattenResults(response); + + // Group by radio instances + final radios = >[]; + final radioCount = int.tryParse( + values['Device.WiFi.RadioNumberOfEntries']?.toString() ?? '0') ?? + 0; + + for (int i = 1; i <= radioCount; i++) { + final prefix = 'Device.WiFi.Radio.$i'; + final ssidPrefix = 'Device.WiFi.SSID.$i'; + final apPrefix = 'Device.WiFi.AccessPoint.$i'; + + // Parse PossibleChannels: "1,2,3,4,5,6,7,8,9,10,11,12,13" -> [1,2,...] + final possibleChannelsStr = + values['$prefix.PossibleChannels']?.toString() ?? ''; + final possibleChannels = possibleChannelsStr.isNotEmpty + ? possibleChannelsStr + .split(',') + .map((c) => int.tryParse(c.trim()) ?? 0) + .toList() + : []; + + // Parse SupportedStandards: "b,g,n,ax" -> ["802.11bg", "802.11bgn", ...] + final supportedStandardsStr = + values['$prefix.SupportedStandards']?.toString() ?? 'n,ax'; + final supportedModes = _convertToJnapModes(supportedStandardsStr); + + // Get current operating mode + final operatingStandards = + values['$prefix.OperatingStandards']?.toString() ?? 'ax'; + final currentMode = _convertToJnapMode(operatingStandards); + + // Get channel width + final channelWidth = + values['$prefix.OperatingChannelBandwidth']?.toString() ?? 'Auto'; + + // Build supportedChannelsForChannelWidths based on SupportedStandards + final supportedChannelsForWidths = _buildSupportedChannelsForWidths( + supportedStandardsStr, possibleChannels); + + // Parse Security.ModesSupported + final securityModesStr = + values['$apPrefix.Security.ModesSupported']?.toString() ?? ''; + final supportedSecurityTypes = securityModesStr.isNotEmpty + ? securityModesStr.split(',').map((s) => s.trim()).toList() + : ['None', 'WPA2-Personal', 'WPA3-Personal']; + + // Get radio ID and band from TR-181 Name field + final radioName = values['$prefix.Name']?.toString() ?? 'RADIO_$i'; + final band = values['$prefix.OperatingFrequencyBand']?.toString() ?? ''; + + radios.add({ + 'radioID': radioName, + 'physicalRadioID': 'ath${i - 1}0', + 'bssid': values['$ssidPrefix.MACAddress']?.toString() ?? '', + 'band': band, + 'supportedModes': supportedModes, + 'defaultMixedMode': + supportedModes.isNotEmpty ? supportedModes.last : currentMode, + 'supportedChannelsForChannelWidths': supportedChannelsForWidths, + 'supportedSecurityTypes': supportedSecurityTypes, + 'maxRADIUSSharedKeyLength': 64, + 'settings': { + 'isEnabled': values['$prefix.Enable']?.toString() == 'true', + 'mode': currentMode, + 'ssid': values['$ssidPrefix.SSID']?.toString() ?? '', + 'broadcastSSID': + values['$apPrefix.SSIDAdvertisementEnabled']?.toString() == + 'true', + 'channelWidth': channelWidth, + 'channel': + int.tryParse(values['$prefix.Channel']?.toString() ?? '0') ?? 0, + 'security': values['$apPrefix.Security.ModeEnabled']?.toString() ?? + 'WPA2-Personal', + 'wpaPersonalSettings': { + 'passphrase': + values['$apPrefix.Security.KeyPassphrase']?.toString() ?? '', + }, + }, + }); + } + + return { + 'isBandSteeringSupported': false, // Demo default + 'radios': radios, + }; + } + + Map _mapGuestRadioSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // Look for guest access points (typically IsolationEnable = true or index 4+) + final guestRadios = >[]; + final apCount = int.tryParse( + values['Device.WiFi.AccessPointNumberOfEntries']?.toString() ?? + '4') ?? + 4; + + // Guest AP is typically index 4 in Linksys setups + for (int i = 4; i <= apCount; i++) { + final apPrefix = 'Device.WiFi.AccessPoint.$i'; + final ssidPrefix = 'Device.WiFi.SSID.$i'; + + // Check if this looks like a guest AP + final isEnabled = values['$apPrefix.Enable']?.toString() == 'true'; + final ssid = values['$ssidPrefix.SSID']?.toString() ?? 'Guest'; + + // Derive radio from SSID LowerLayers or use index heuristic + String radioId = 'RADIO_2.4GHz'; + if (i == 5) radioId = 'RADIO_5GHz'; + if (i == 6) radioId = 'RADIO_6GHz'; + + guestRadios.add({ + 'radioID': radioId, + 'isEnabled': isEnabled, + 'broadcastGuestSSID': + values['$apPrefix.SSIDAdvertisementEnabled']?.toString() == 'true', + 'guestSSID': ssid, + 'guestPassword': + values['$apPrefix.Security.KeyPassphrase']?.toString() ?? '', + 'canEnableRadio': true, + 'maxSimultaneousGuests': 50, + }); + } + + // If no specific guest APs found, provide default + if (guestRadios.isEmpty) { + guestRadios.add({ + 'radioID': 'RADIO_2.4GHz', + 'isEnabled': false, + 'broadcastGuestSSID': true, + 'guestSSID': 'Guest', + 'guestPassword': '', + 'canEnableRadio': true, + 'maxSimultaneousGuests': 50, + }); + } + + return { + 'isGuestNetworkACaptivePortal': false, // Not supported via TR-181 + 'isGuestNetworkEnabled': guestRadios.any((r) => r['isEnabled'] == true), + 'radios': guestRadios, + 'maxSimultaneousGuests': 50, + }; + } + + Map _mapMACFilterSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // Usually MAC filtering is global or per-AP + const prefix = 'Device.WiFi.AccessPoint.1'; + + final isEnabled = + values['$prefix.MACAddressControlEnabled']?.toString() == 'true'; + final macListStr = values['$prefix.AllowedMACAddress']?.toString() ?? ''; + + // TR-181 AllowedMACAddress is CSV + final macList = macListStr.isNotEmpty + ? macListStr.split(',').map((s) => s.trim()).toList() + : []; + + return { + 'macFilterMode': isEnabled ? 'Allow' : 'Deny', // Simple mapping + 'isEnabled': isEnabled, + 'macAddresses': macList, + 'maxMACAddresses': 32, + }; + } + + // =========================================================================== + // Helper Methods - WiFi Standards Conversion + // =========================================================================== + + /// Convert TR-181 standards string to JNAP mode list. + /// e.g., "b,g,n,ax" -> ["802.11bg", "802.11bgn", "802.11bgnax"] + List _convertToJnapModes(String standardsStr) { + final standards = standardsStr.split(',').map((s) => s.trim()).toList(); + final modes = []; + + if (standards.contains('b') && standards.contains('g')) { + modes.add('802.11bg'); + } + if (standards.contains('n')) { + modes.add('802.11bgn'); + } + if (standards.contains('ax')) { + modes.add('802.11bgnax'); + } + if (standards.contains('a')) { + modes.add('802.11a'); + } + if (standards.contains('ac')) { + modes.add('802.11anac'); + } + if (standards.contains('be')) { + modes.add('802.11anacaxbe'); + } + + // Always add mixed mode as default + if (modes.isNotEmpty) { + modes.add('802.11mixed'); + } + + return modes.isEmpty ? ['802.11mixed'] : modes; + } + + /// Convert single TR-181 standard to JNAP mode. + String _convertToJnapMode(String standard) { + switch (standard) { + case 'ax': + return '802.11ax'; + case 'ac': + return '802.11ac'; + case 'n': + return '802.11n'; + case 'be': + return '802.11be'; + default: + return '802.11mixed'; + } + } + + /// Build supportedChannelsForChannelWidths from standards. + List> _buildSupportedChannelsForWidths( + String standardsStr, List channels) { + final result = >[]; + + // Auto width always included + result.add({ + 'channelWidth': 'Auto', + 'channels': [0, ...channels], + }); + + // Standard (20MHz) always available + result.add({ + 'channelWidth': 'Standard', + 'channels': [0, ...channels], + }); + + // Wide (40MHz) if n/ac/ax/be + if (standardsStr.contains('n') || + standardsStr.contains('ac') || + standardsStr.contains('ax') || + standardsStr.contains('be')) { + result.add({ + 'channelWidth': 'Wide', + 'channels': [0, ...channels], + }); + } + + return result; + } + + Map _flattenResults(UspGetResponse response) { + final result = {}; + for (final entry in response.results.entries) { + result[entry.key.fullPath] = entry.value.value; + } + return result; + } + + // =========================================================================== + // New Feature Mapping Methods + // =========================================================================== + + Map _mapClientSteeringSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // Wi-Fi DataElements typically has steering policies + // Check for DataElements.Network entries + final hasDataElements = + values.keys.any((k) => k.startsWith('Device.WiFi.DataElements')); + + // Default to supported if DataElements exists + return { + 'isClientSteeringEnabled': hasDataElements, + 'isNodeSteeringEnabled': hasDataElements, + }; + } + + Map _mapDFSSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // DFS is typically available on 5GHz radios with AutoChannelEnable + // Check if any 5GHz radio has AutoChannelEnable + bool isDFSSupported = false; + bool isDFSEnabled = false; + + final radioCount = int.tryParse( + values['Device.WiFi.RadioNumberOfEntries']?.toString() ?? '0') ?? + 0; + + for (int i = 1; i <= radioCount; i++) { + final prefix = 'Device.WiFi.Radio.$i'; + final band = values['$prefix.OperatingFrequencyBand']?.toString() ?? ''; + + // DFS is only relevant for 5GHz band + if (band.contains('5')) { + isDFSSupported = true; + // AutoChannelEnable being true implies DFS is active + final autoChannel = + values['$prefix.AutoChannelEnable']?.toString() == 'true'; + if (autoChannel) { + isDFSEnabled = true; + } + } + } + + return { + 'isDFSSupported': isDFSSupported, + 'isDFSEnabled': isDFSEnabled, + }; + } + + Map _mapMLOSettings(UspGetResponse response) { + final values = _flattenResults(response); + + // Check for MultiLink support + final hasMultiLink = + values.keys.any((k) => k.startsWith('Device.WiFi.MultiLink')); + final mloEnabled = + values['Device.WiFi.MultiLink.Enable']?.toString() == 'true'; + + return { + 'isMLOSupported': hasMultiLink, + 'isMLOEnabled': hasMultiLink ? mloEnabled : null, + }; + } + + Map _mapSTABSSIDs(UspGetResponse response) { + final values = _flattenResults(response); + + // Collect all SSID BSSIDs + final bssids = []; + final ssidCount = int.tryParse( + values['Device.WiFi.SSIDNumberOfEntries']?.toString() ?? '0') ?? + 0; + + for (int i = 1; i <= ssidCount; i++) { + final bssid = values['Device.WiFi.SSID.$i.BSSID']?.toString(); + if (bssid != null && bssid.isNotEmpty) { + bssids.add(bssid.toUpperCase()); + } + // Also try MACAddress as fallback + final macAddress = values['Device.WiFi.SSID.$i.MACAddress']?.toString(); + if (macAddress != null && + macAddress.isNotEmpty && + !bssids.contains(macAddress.toUpperCase())) { + bssids.add(macAddress.toUpperCase()); + } + } + + return { + 'staBSSIDS': bssids, + }; + } + + Map _mapLocalDeviceMAC(UspGetResponse response) { + final values = _flattenResults(response); + + // Get first Ethernet interface MAC address + final macAddress = + values['Device.Ethernet.Interface.1.MACAddress']?.toString() ?? ''; + + return { + 'deviceID': macAddress.replaceAll(':', '').toUpperCase(), + 'macAddress': macAddress.toUpperCase(), + }; + } +} diff --git a/packages/usp_client_core/lib/src/tr181_paths.dart b/packages/usp_client_core/lib/src/tr181_paths.dart new file mode 100644 index 000000000..688858c7e --- /dev/null +++ b/packages/usp_client_core/lib/src/tr181_paths.dart @@ -0,0 +1,228 @@ +/// TR-181 Path Constants +/// +/// Standard TR-181 data model paths used for USP communication. +/// These paths are protocol-agnostic and represent the Device:2 data model. +library; + +/// TR-181 path constants for device data model. +/// +/// Paths follow the TR-181 Issue 2 Device:2 naming convention. +/// Use with [UspPath.parse] to create path objects for USP requests. +class Tr181Paths { + Tr181Paths._(); + + // =========================================================================== + // Device Info + // =========================================================================== + + /// Device information root path. + /// Contains: Manufacturer, ModelName, SerialNumber, HardwareVersion, + /// SoftwareVersion, Description, UpTime, etc. + static const deviceInfo = 'Device.DeviceInfo.'; + + /// Process status for CPU usage. + static const processStatus = 'Device.DeviceInfo.ProcessStatus.'; + + /// Memory status for memory usage. + static const memoryStatus = 'Device.DeviceInfo.MemoryStatus.'; + + // =========================================================================== + // WiFi + // =========================================================================== + + /// WiFi root path (for RadioNumberOfEntries, AccessPointNumberOfEntries). + static const wifi = 'Device.WiFi.'; + + /// WiFi Radio configuration. + /// Contains: Enable, Status, Channel, PossibleChannels, OperatingFrequencyBand, + /// SupportedStandards, OperatingStandards, OperatingChannelBandwidth, etc. + static const wifiRadio = 'Device.WiFi.Radio.'; + + /// WiFi SSID configuration. + /// Contains: Enable, Status, SSID, MACAddress, etc. + static const wifiSsid = 'Device.WiFi.SSID.'; + + /// WiFi Access Point configuration. + /// Contains: Enable, SSIDAdvertisementEnabled, Security.*, AssociatedDevice.*, etc. + static const wifiAccessPoint = 'Device.WiFi.AccessPoint.'; + + /// WiFi MultiAP for mesh networks. + /// Contains: APDeviceNumberOfEntries, APDevice.* (mesh nodes). + static const wifiMultiAp = 'Device.WiFi.MultiAP.'; + + /// WiFi MultiAP APDevice (mesh node). + /// Contains: MACAddress, BackhaulLinkType, BackhaulMACAddress, SerialNumber, etc. + static const wifiMultiApDevice = 'Device.WiFi.MultiAP.APDevice.'; + + /// WiFi DataElements (Wi-Fi Alliance Data Elements). + /// Contains: Network.*, steering statistics, client capabilities, etc. + /// Used for Client/Node Steering in Multi-AP networks. + static const wifiDataElements = 'Device.WiFi.DataElements.'; + + /// WiFi DataElements Network. + /// Contains: SSID, ID, steering policies, etc. + static const wifiDataElementsNetwork = 'Device.WiFi.DataElements.Network.'; + + /// WiFi MultiLink (Wi-Fi 7 MLO - Multi-Link Operation). + /// Contains: Enable, Status, MultiLinkDevice.*, etc. + static const wifiMultiLink = 'Device.WiFi.MultiLink.'; + + // =========================================================================== + // Network - Hosts + // =========================================================================== + + /// Hosts root path (for HostNumberOfEntries). + static const hosts = 'Device.Hosts.'; + + /// Host entries (connected devices). + /// Contains: PhysAddress, IPAddress, HostName, Active, InterfaceType, etc. + static const hostsHost = 'Device.Hosts.Host.'; + + // =========================================================================== + // Network - IP Interface + // =========================================================================== + + /// IP Interface for WAN (typically Interface.1). + /// Contains: Enable, Status, IPv4Address.*, etc. + static const ipInterfaceWan = 'Device.IP.Interface.1.'; + + /// IP Interface for LAN (typically Interface.2). + /// Contains: Enable, Status, IPv4Address.*, etc. + static const ipInterfaceLan = 'Device.IP.Interface.2.'; + + // =========================================================================== + // Network - Ethernet + // =========================================================================== + + /// Ethernet interfaces. + /// Contains: Enable, Status, MACAddress, MaxBitRate, etc. + static const ethernet = 'Device.Ethernet.Interface.'; + + // =========================================================================== + // Network - DHCP + // =========================================================================== + + /// DHCPv4 configuration. + /// Contains: Server.Pool.*, Client.*, etc. + static const dhcpv4 = 'Device.DHCPv4.'; + + /// DHCPv4 client. + static const dhcpv4Client = 'Device.DHCPv4.Client.'; + + // =========================================================================== + // Network - Routing + // =========================================================================== + + /// IPv4 routing table. + static const routing = 'Device.Routing.Router.1.'; + + // =========================================================================== + // Time + // =========================================================================== + + /// Time settings. + /// Contains: CurrentLocalTime, LocalTimeZone, NTPServer, etc. + static const time = 'Device.Time.'; + + // =========================================================================== + // Helper Methods + // =========================================================================== + + /// Returns all paths needed for GetDeviceInfo. + static List get deviceInfoPaths => [deviceInfo]; + + /// Returns all paths needed for GetRadioInfo. + static List get radioInfoPaths => [ + wifi, + wifiRadio, + wifiSsid, + wifiAccessPoint, + ]; + + /// Returns all paths needed for GetDevices. + static List get devicesPaths => [ + hosts, + hostsHost, + wifiMultiAp, + wifiMultiApDevice, + ]; + + /// Returns all paths needed for GetBackhaulInfo. + static List get backhaulInfoPaths => [ + wifiMultiAp, + wifiMultiApDevice, + ]; + + /// Returns all paths needed for GetWANStatus. + static List get wanStatusPaths => [ + ipInterfaceWan, + ethernet, + dhcpv4Client, + routing, + ]; + + /// Returns all paths needed for GetLANSettings. + static List get lanSettingsPaths => [ + ipInterfaceLan, + dhcpv4, + ]; + + /// Returns all paths needed for GetGuestRadioSettings. + static List get guestRadioSettingsPaths => [ + wifi, + wifiAccessPoint, + wifiSsid, + wifiRadio, + ]; + + /// Returns all paths needed for GetMACFilterSettings. + static List get macFilterSettingsPaths => [ + wifiAccessPoint, + ]; + + /// Returns all paths needed for GetNetworkConnections. + static List get networkConnectionsPaths => [ + hostsHost, + wifiRadio, + wifiAccessPoint, + wifiSsid, + ]; + + /// Returns all paths needed for GetNodesWirelessNetworkConnections. + static List get nodesWirelessConnectionsPaths => [ + wifiMultiApDevice, + wifiAccessPoint, + wifiSsid, + ]; + + /// Returns all paths needed for GetTimeSettings. + static List get timeSettingsPaths => [time]; + + /// Returns all paths needed for GetSystemStats. + static List get systemStatsPaths => [deviceInfo]; + + /// Returns all paths needed for GetInternetConnectionStatus. + static List get internetConnectionStatusPaths => [ipInterfaceWan]; + + /// Returns all paths needed for GetEthernetPortConnections. + static List get ethernetPortConnectionsPaths => [ethernet]; + + /// Returns all paths needed for GetClientSteeringSettings. + static List get clientSteeringSettingsPaths => [ + wifiDataElements, + wifiDataElementsNetwork, + ]; + + /// Returns all paths needed for GetDFSSettings. + /// DFS is controlled via AutoChannelEnable on 5GHz radios. + static List get dfsSettingsPaths => [wifiRadio]; + + /// Returns all paths needed for GetMLOSettings. + static List get mloSettingsPaths => [wifiMultiLink]; + + /// Returns all paths needed for GetSTABSSIDs. + static List get staBssidsPaths => [wifiSsid]; + + /// Returns all paths needed for GetLocalDeviceMAC. + static List get localDeviceMacPaths => [ethernet]; +} diff --git a/packages/usp_client_core/lib/src/utils/usp_logger.dart b/packages/usp_client_core/lib/src/utils/usp_logger.dart new file mode 100644 index 000000000..eb51292be --- /dev/null +++ b/packages/usp_client_core/lib/src/utils/usp_logger.dart @@ -0,0 +1,64 @@ +/// USP Logger Utility +/// +/// Provides consistent logging for USP TR-181 to JNAP mapping. +library; + +/// Logger utility for USP operations. +/// +/// Provides structured logging for TR-181 ↔ JNAP mapping process. +/// Uses print() to ensure visibility in Flutter web release builds. +class UspLogger { + static const _prefix = '[USP]'; + + /// Log the start of a mapping operation. + static void logMappingStart(String methodName) { + // ignore: avoid_print + print('$_prefix 🔄 [TR-181 → JNAP] Mapping $methodName...'); + } + + /// Log raw TR-181 data received from the simulator. + /// + /// Combines all entries into a single log call for cleaner output. + static void logTr181Data(Map values) { + final buffer = StringBuffer(); + buffer.writeln('$_prefix 📦 Raw TR-181 Data (${values.length} items):'); + for (final entry in values.entries) { + buffer.writeln(' ${entry.key} = ${entry.value}'); + } + // ignore: avoid_print + print(buffer.toString()); + } + + /// Log a count of items found. + static void logCount(String itemType, int count) { + // ignore: avoid_print + print('$_prefix 📱 Found $count $itemType'); + } + + /// Log the result of a mapping operation. + /// + /// [items] is a list of (name, id) pairs for each mapped item. + static void logMappingResult( + String resultType, List<(String name, String id)> items) { + final buffer = StringBuffer(); + buffer.writeln( + '$_prefix ✅ Mapped ${items.length} $resultType to JNAP format'); + for (final (name, id) in items) { + buffer.writeln(' 📍 $name ($id)'); + } + // ignore: avoid_print + print(buffer.toString()); + } + + /// Log a simple message. + static void log(String message) { + // ignore: avoid_print + print('$_prefix $message'); + } + + /// Log a warning. + static void warn(String message) { + // ignore: avoid_print + print('$_prefix ⚠️ $message'); + } +} diff --git a/packages/usp_client_core/lib/usp_client_core.dart b/packages/usp_client_core/lib/usp_client_core.dart new file mode 100644 index 000000000..329860361 --- /dev/null +++ b/packages/usp_client_core/lib/usp_client_core.dart @@ -0,0 +1,29 @@ +/// USP Client Core +/// +/// Provides USP/gRPC integration for communicating with TR-181 Data Model. +library usp_client_core; + +// Connection +export 'src/connection/usp_connection_config.dart'; +export 'src/connection/usp_grpc_client_service.dart'; + +// gRPC Creator +export 'src/grpc_creator/grpc_creator.dart'; + +// TR-181 Paths +export 'src/tr181_paths.dart'; + +// Enums +export 'src/enums/_enums.dart'; + +// Services +export 'src/services/_services.dart'; +export 'src/services/usp_capability_service.dart'; + +// Monitoring +export 'src/monitoring/watch_strategy.dart'; +export 'src/monitoring/polling_manager.dart'; +export 'src/monitoring/resource_watcher.dart'; + +// Utils +export 'src/utils/usp_logger.dart'; diff --git a/packages/usp_client_core/pubspec.lock b/packages/usp_client_core/pubspec.lock new file mode 100644 index 000000000..f314aec49 --- /dev/null +++ b/packages/usp_client_core/pubspec.lock @@ -0,0 +1,196 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + equatable: + dependency: transitive + description: + name: equatable + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" + url: "https://pub.dev" + source: hosted + version: "2.0.8" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db + url: "https://pub.dev" + source: hosted + version: "2.0.0" + grpc: + dependency: "direct main" + description: + name: grpc + sha256: d5711432e7fcb41d23f88461651d503db98661af73ac7df211ace9c92fd03753 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http2: + dependency: transitive + description: + name: http2 + sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + meta: + dependency: "direct main" + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "2fcc8a202ca7ec17dab7c97d6b6d91cf03aa07fe6f65f8afbb6dfa52cc5bd902" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + usp_protocol_common: + dependency: "direct main" + description: + path: "../usp_protocol_common" + relative: true + source: path + version: "1.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.8.1 <4.0.0" diff --git a/packages/usp_client_core/pubspec.yaml b/packages/usp_client_core/pubspec.yaml new file mode 100644 index 000000000..e7774a778 --- /dev/null +++ b/packages/usp_client_core/pubspec.yaml @@ -0,0 +1,14 @@ +name: usp_client_core +description: "USP/gRPC client integration for communicating with TR-181 Data Model." +version: 1.0.0 +publish_to: 'none' + +environment: + sdk: ^3.0.0 + +dependencies: + usp_protocol_common: + path: ../usp_protocol_common + grpc: ^5.0.0 + logging: ^1.2.0 + meta: ^1.9.0 diff --git a/packages/usp_protocol_common/CHANGELOG.md b/packages/usp_protocol_common/CHANGELOG.md new file mode 100644 index 000000000..e3d0fc914 --- /dev/null +++ b/packages/usp_protocol_common/CHANGELOG.md @@ -0,0 +1,9 @@ +# Changelog + +## 1.0.0 + +- Initial release of the `usp_protocol_common` library. +- Provides type-safe Dart DTOs for all standard USP messages (Get, Set, Add, Delete, Operate, Error). +- Includes converters for serializing and deserializing messages to and from the Protobuf wire format. +- Implements value objects for handling USP paths and values. +- Provides a stateless path resolver for searching tree-like data structures. \ No newline at end of file diff --git a/packages/usp_protocol_common/LICENSE b/packages/usp_protocol_common/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/packages/usp_protocol_common/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/packages/usp_protocol_common/README.md b/packages/usp_protocol_common/README.md new file mode 100644 index 000000000..5fc9805dd --- /dev/null +++ b/packages/usp_protocol_common/README.md @@ -0,0 +1,107 @@ +# USP Protocol Common + +This package serves as the **Shared Kernel** (The Contract) for the USP Client POC monorepo. It contains the official TR-369 Protocol Buffer definitions, generated Dart code, and shared utilities for handling USP Messages and Records. + +## 📦 Core Components + +### 1. Protobuf Definitions (`proto/`) + +The source of truth for the communication protocol. + +* **`usp_msg.proto`**: Defines the **USP Message**. Contains the actual commands and responses (e.g., `Get`, `Set`, `Notify`). + * *Analogy*: The letter inside the envelope. +* **`usp_record.proto`**: Defines the **USP Record**. The transport-agnostic envelope containing routing (`to_id`, `from_id`) and security info. + * *Analogy*: The physical envelope with addresses. +* **`usp_transport.proto`**: A custom wrapper for tunneling USP Records over **gRPC**. + +### 2. Dart Implementation (`lib/src/`) + +Provides a high-level API to interact with the raw Protobuf classes. + +* **`generated/`**: The Dart code generated by `protoc`. Avoid editing these files directly. +* **`dtos/`**: **Data Transfer Objects**. Dart-friendly classes used by the application layer. +* **`converter/`**: Utilities to serialize/deserialize between DTOs and Protobuf bytes. + +----- + +## 🛠️ Usage Guide + +### 📱 For Frontend (Flutter Client) + +The frontend uses this package to **construct requests** and **parse responses**. + +**Typical Workflow:** + +1. **Create a Request**: + Use the helper classes to build a USP Message. + ```dart + import 'package:usp_protocol_common/usp_protocol_common.dart'; + + final request = UspMsg( + header: Header(msgId: '123', msgType: Header_MsgType.GET), + body: Body(request: Request(get: Get(paramPaths: ['Device.DeviceInfo.']))) + ); + ``` + +2. **Wrap in Record**: + Encapsulate the message into a Record for transport. + ```dart + final record = UspRecord( + toId: 'proto::agent', + fromId: 'proto::controller', + payload: request.writeToBuffer(), // Serialize Msg to bytes + ); + ``` + +3. **Send via Transport**: + Serialize the Record and send it (e.g., via gRPC). + ```dart + final bytes = record.writeToBuffer(); + // transport.send(bytes); + ``` + +### 🤖 For Backend (Simulator / Agent) + +The backend uses this package to **decode requests**, **route messages**, and **generate responses**. + +**Typical Workflow:** + +1. **Receive & Decode**: + Parse the incoming raw bytes into a Record. + ```dart + import 'package:usp_protocol_common/usp_protocol_common.dart'; + + void onDataReceived(List data) { + final record = UspRecord.fromBuffer(data); + print('Received message from: ${record.fromId}'); + } + ``` + +2. **Process Message**: + Extract the payload (USP Message) and handle the command. + ```dart + final msg = UspMsg.fromBuffer(record.payload); + if (msg.header.msgType == Header_MsgType.GET) { + // Handle Get Request... + } + ``` + +3. **Send Response**: + Create a response message, wrap it in a new Record (swapping to/from IDs), and send it back. + +----- + +## 🔄 Development Workflow + +### Updating the Protocol + +If you modify any `.proto` files in the `proto/` directory: + +1. **Run the Generator**: + Execute the following Melos script to regenerate the Dart code. + ```bash + melos run proto:gen + ``` + +2. **Verify**: + Check `lib/src/generated/` to ensure the new fields or messages are present. diff --git a/packages/usp_protocol_common/analysis_options.yaml b/packages/usp_protocol_common/analysis_options.yaml new file mode 100644 index 000000000..12e713abf --- /dev/null +++ b/packages/usp_protocol_common/analysis_options.yaml @@ -0,0 +1,4 @@ +include: package:lints/recommended.yaml + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/packages/usp_protocol_common/build.yaml b/packages/usp_protocol_common/build.yaml new file mode 100644 index 000000000..0dfd1e0f5 --- /dev/null +++ b/packages/usp_protocol_common/build.yaml @@ -0,0 +1,7 @@ +targets: + $default: + builders: + json_serializable: + options: + explicit_to_json: true + any_map: true diff --git a/packages/usp_protocol_common/lib/src/converter/usp_protobuf_converter.dart b/packages/usp_protocol_common/lib/src/converter/usp_protobuf_converter.dart new file mode 100644 index 000000000..4aac94e1a --- /dev/null +++ b/packages/usp_protocol_common/lib/src/converter/usp_protobuf_converter.dart @@ -0,0 +1,963 @@ +import 'package:collection/collection.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; +import '../generated/usp_msg.pb.dart' as pb; +import '../dtos/base_dto.dart'; +import '../dtos/requests/usp_requests.dart'; +import '../dtos/responses/usp_responses.dart'; +import '../value_objects/usp_path.dart'; +import '../value_objects/usp_value.dart'; +import '../value_objects/usp_value_type.dart'; +import '../exceptions/usp_exception.dart'; + +/// A utility class for converting between USP DTOs and Protobuf messages. +class UspProtobufConverter { + /// Converts a [UspMessage] DTO to a Protobuf [pb.Msg]. + pb.Msg toProto(UspMessage dto, {required String msgId}) { + final header = pb.Header() + ..msgId = msgId + ..msgType = _getMsgType(dto); + + final body = pb.Body(); + + // --- Requests (Including Notify) --- + if (dto is UspRequest) { + switch (dto) { + // CRUD + case UspGetRequest(): + final getMsg = pb.Get() + ..paramPaths.addAll(dto.paths.map((p) => p.fullPath)); + body.request = pb.Request()..get = getMsg; + + case UspSetRequest(): + body.request = pb.Request()..set = _buildSetMsg(dto); + + case UspAddRequest(): + body.request = pb.Request()..add = _buildAddMsg(dto); + + case UspDeleteRequest(): + final delMsg = pb.Delete() + ..objPaths.addAll(dto.objPaths.map((p) => p.fullPath)) + ..allowPartial = dto.allowPartial; + body.request = pb.Request()..delete = delMsg; + + case UspOperateRequest(): + body.request = pb.Request()..operate = _buildOperateMsg(dto); + + case UspGetSupportedDMRequest(): + body.request = pb.Request() + ..getSupportedDm = _buildGetSupportedDMMsg(dto); + + case UspGetInstancesRequest(): + body.request = pb.Request() + ..getInstances = _buildGetInstancesMsg(dto); + + // Notifications (Special Request) + case UspNotify(): + body.request = pb.Request()..notify = _buildNotifyMsg(dto); + } + } + // --- Responses --- + else if (dto is UspResponse) { + switch (dto) { + case UspGetResponse(): + body.response = pb.Response()..getResp = _buildGetResp(dto); + + case UspSetResponse(): + body.response = pb.Response()..setResp = _buildSetResp(dto); + + case UspAddResponse(): + body.response = pb.Response()..addResp = _buildAddResp(dto); + + case UspDeleteResponse(): + body.response = pb.Response()..deleteResp = _buildDeleteResp(dto); + + case UspOperateResponse(): + body.response = pb.Response()..operateResp = _buildOperateResp(dto); + + case UspGetSupportedDMResponse(): + body.response = pb.Response() + ..getSupportedDmResp = _buildGetSupportedDMResp(dto); + + case UspGetInstancesResponse(): + body.response = pb.Response() + ..getInstancesResp = _buildGetInstancesResp(dto); + + case UspNotifyResponse(): + body.response = pb.Response() + ..notifyResp = (pb.NotifyResp() + ..subscriptionId = dto.subscriptionId); + + case UspErrorResponse(): + final err = pb.Error() + ..errCode = dto.exception.errorCode + ..errMsg = dto.exception.message; // 假設你的 exception 有 message 欄位 + body.error = err; + } + } else { + throw UspException(7004, "Unsupported DTO type: ${dto.runtimeType}"); + } + + return pb.Msg() + ..header = header + ..body = body; + } + + /// Converts a Protobuf [pb.Msg] to a [UspMessage] DTO. + UspMessage fromProto(pb.Msg protoMsg) { + final body = protoMsg.body; + + switch (body.whichMsgBody()) { + case pb.Body_MsgBody.request: + return _parseRequest(body.request); + case pb.Body_MsgBody.response: + return _parseResponse(body.response); + case pb.Body_MsgBody.error: + return UspErrorResponse( + UspException(body.error.errCode, body.error.errMsg), + ); + case pb.Body_MsgBody.notSet: + throw UspException(7004, "Empty or unknown Message Body"); + } + } + + UspRequest _parseRequest(pb.Request req) { + switch (req.whichReqType()) { + case pb.Request_ReqType.get: + return UspGetRequest( + req.get.paramPaths.map((s) => UspPath.parse(s)).toList(), + ); + case pb.Request_ReqType.set: + return _parseSetMsg(req.set); + case pb.Request_ReqType.add: + return _parseAddMsg(req.add); + case pb.Request_ReqType.delete: + return UspDeleteRequest( + req.delete.objPaths.map((s) => UspPath.parse(s)).toList(), + allowPartial: req.delete.allowPartial, + ); + case pb.Request_ReqType.operate: + return _parseOperateMsg(req.operate); + case pb.Request_ReqType.notify: + return _parseNotifyMsg(req.notify); + case pb.Request_ReqType.getSupportedDm: + return _parseGetSupportedDMRequest(req.getSupportedDm); + case pb.Request_ReqType.getInstances: + return _parseGetInstancesMsg(req.getInstances); + default: + throw UspException( + 7004, + "Unsupported Request Type: ${req.whichReqType()}", + ); + } + } + + UspResponse _parseResponse(pb.Response resp) { + switch (resp.whichRespType()) { + case pb.Response_RespType.getResp: + return _parseGetResp(resp.getResp); + case pb.Response_RespType.setResp: + return _parseSetResp(resp.setResp); + case pb.Response_RespType.addResp: + return _parseAddResp(resp.addResp); + case pb.Response_RespType.deleteResp: + return _parseDeleteResp(resp.deleteResp); + case pb.Response_RespType.operateResp: + return _parseOperateResponse(resp.operateResp); + case pb.Response_RespType.notifyResp: + return UspNotifyResponse( + subscriptionId: resp.notifyResp.subscriptionId, + ); + case pb.Response_RespType.getSupportedDmResp: + return _parseGetSupportedDMResp(resp.getSupportedDmResp); + case pb.Response_RespType.getInstancesResp: + return _parseGetInstancesResp(resp.getInstancesResp); + default: + throw UspException( + 7004, + "Unsupported Response Type: ${resp.whichRespType()}", + ); + } + } + + // ======================================================================== + // Helpers: Logic & Mapping + // ======================================================================== + + pb.Header_MsgType _getMsgType(UspMessage dto) { + return switch (dto) { + UspGetRequest() => pb.Header_MsgType.GET, + UspSetRequest() => pb.Header_MsgType.SET, + UspAddRequest() => pb.Header_MsgType.ADD, + UspDeleteRequest() => pb.Header_MsgType.DELETE, + UspOperateRequest() => pb.Header_MsgType.OPERATE, + UspGetSupportedDMRequest() => pb.Header_MsgType.GET_SUPPORTED_DM, + UspGetInstancesRequest() => pb.Header_MsgType.GET_INSTANCES, + UspNotify() => pb.Header_MsgType.NOTIFY, + + UspGetResponse() => pb.Header_MsgType.GET_RESP, + UspSetResponse() => pb.Header_MsgType.SET_RESP, + UspAddResponse() => pb.Header_MsgType.ADD_RESP, + UspDeleteResponse() => pb.Header_MsgType.DELETE_RESP, + UspOperateResponse() => pb.Header_MsgType.OPERATE_RESP, + UspNotifyResponse() => pb.Header_MsgType.NOTIFY_RESP, + UspGetSupportedDMResponse() => pb.Header_MsgType.GET_SUPPORTED_DM_RESP, + UspGetInstancesResponse() => pb.Header_MsgType.GET_INSTANCES_RESP, + UspErrorResponse() => pb.Header_MsgType.ERROR, + _ => pb.Header_MsgType.ERROR, + }; + } + + // ------------------------------------------------------------------------ + // Build Helpers (DTO -> Proto) + // ------------------------------------------------------------------------ + + pb.Set _buildSetMsg(UspSetRequest dto) { + final setMsg = pb.Set()..allowPartial = dto.allowPartial; + + final groupedUpdates = dto.updates.entries.groupListsBy( + (entry) => entry.key.parent?.fullPath ?? "", + ); + + groupedUpdates.forEach((objPath, entries) { + if (objPath.isEmpty) return; + + final updateObj = pb.Set_UpdateObject()..objPath = objPath; + for (final entry in entries) { + updateObj.paramSettings.add( + pb.Set_UpdateParamSetting() + ..param = entry.key.name + ..value = entry.value.value.toString() + ..required = true, + ); + } + setMsg.updateObjs.add(updateObj); + }); + + return setMsg; + } + + pb.Add _buildAddMsg(UspAddRequest dto) { + final addMsg = pb.Add()..allowPartial = dto.allowPartial; + + for (final objCreation in dto.objects) { + final createObj = pb.Add_CreateObject() + ..objPath = objCreation.parentPath.fullPath; + + objCreation.parameters.forEach((key, val) { + createObj.paramSettings.add( + pb.Add_CreateParamSetting() + ..param = key + ..value = val.value.toString() + ..required = true, + ); + }); + addMsg.createObjs.add(createObj); + } + return addMsg; + } + + /// [Lossy Conversion] Note: According to the TR-369 (USP) specification, all + /// input/output arguments for an Operate message MUST be strings at the + /// Protobuf layer. + /// + /// This means that during the `toProto` process, the `value.toString()` + /// representation will be used, even if the original `UspValue` held an + /// integer or boolean. + /// + /// Consequently, during the `fromProto` process, all arguments are parsed back + /// as a `UspValue` with a `String` type, as the original type information is + /// lost at the protocol level. + /// + /// Round-trip tests for Operate-related DTOs should therefore account for + /// this lossy conversion and expect non-string types to become strings. + pb.Operate _buildOperateMsg(UspOperateRequest dto) { + final operateMsg = pb.Operate()..command = dto.command.fullPath; + dto.inputArgs.forEach((key, value) { + operateMsg.inputArgs[key] = value.value + .toString(); // Assuming all inputArgs are string for now + }); + return operateMsg; + } + + pb.Notify_OperationComplete _buildOperationCompleteNotifyMsg( + UspOperationCompleteNotify n, + ) { + final operComplete = pb.Notify_OperationComplete() + ..objPath = n.objPath.fullPath + ..commandName = n.commandName + ..commandKey = n.commandKey; + + if (n.commandFailure == null) { + operComplete.reqOutputArgs = pb.Notify_OperationComplete_OutputArgs() + ..outputArgs.addAll(n.outputArgs); + } else { + operComplete.cmdFailure = pb.Notify_OperationComplete_CommandFailure() + ..errCode = n.commandFailure!.errorCode + ..errMsg = n.commandFailure!.message; + } + return operComplete; + } + + // --- GetSupportedDM Request --- + + pb.GetSupportedDM _buildGetSupportedDMMsg(UspGetSupportedDMRequest dto) { + return pb.GetSupportedDM() + ..objPaths.addAll(dto.objPaths.map((p) => p.fullPath)) + ..firstLevelOnly = dto.firstLevelOnly + ..returnCommands = dto.returnCommands + ..returnEvents = dto.returnEvents + ..returnParams = dto.returnParams; + // 如果 DTO 有 returnUniqueKeySets,也要加進來 + // ..returnUniqueKeySets = dto.returnUniqueKeySets; + } + + // --- GetInstances Request --- + + pb.GetInstances _buildGetInstancesMsg(UspGetInstancesRequest dto) { + return pb.GetInstances() + ..objPaths.addAll(dto.objPaths.map((p) => p.fullPath)) + ..firstLevelOnly = dto.firstLevelOnly; + } + + pb.Notify _buildNotifyMsg(UspNotify dto) { + final notifyMsg = pb.Notify() + ..subscriptionId = dto.subscriptionId + ..sendResp = dto.sendResp; + + if (dto is UspValueChangeNotify) { + notifyMsg.valueChange = (pb.Notify_ValueChange() + ..paramPath = dto.paramPath.fullPath + ..paramValue = dto.paramValue); + } else if (dto is UspObjectCreationNotify) { + notifyMsg.objCreation = (pb.Notify_ObjectCreation() + ..objPath = dto.objPath.fullPath + ..uniqueKeys.addAll(dto.uniqueKeys)); + } else if (dto is UspObjectDeletionNotify) { + notifyMsg.objDeletion = (pb.Notify_ObjectDeletion() + ..objPath = dto.objPath.fullPath); + } else if (dto is UspOperationCompleteNotify) { + notifyMsg.operComplete = _buildOperationCompleteNotifyMsg(dto); + } else if (dto is UspOnBoardRequestNotify) { + notifyMsg.onBoardReq = (pb.Notify_OnBoardRequest() + ..oui = dto.oui + ..productClass = dto.productClass + ..serialNumber = dto.serialNumber + ..agentSupportedProtocolVersions = dto.agentProtocolVersions); + } else { + throw UspException(7004, "Unsupported Notify type: ${dto.runtimeType}"); + } + return notifyMsg; + } + + pb.OperateResp _buildOperateResp(UspOperateResponse dto) { + final operateResp = pb.OperateResp(); + + final operationResult = pb.OperateResp_OperationResult() + ..executedCommand = + "placeholder.command()"; // Using the name we corrected in the last round + + // --- Start of fix --- + + // 1. Explicitly create a new OutputArgs wrapper object + final outputArgsWrapper = pb.OperateResp_OperationResult_OutputArgs(); + + // 2. Fill the new object's map with data + dto.outputArgs.forEach((key, value) { + // Also solving problem 2 here (convert to string) + outputArgsWrapper.outputArgs[key] = value.value.toString(); + }); + + // 3. Assign the populated object to the parent + // This will overwrite the original read-only default instance + operationResult.reqOutputArgs = outputArgsWrapper; + + // --- End of fix --- + + operateResp.operationResults.add(operationResult); + return operateResp; + } + + pb.GetResp _buildGetResp(UspGetResponse dto) { + final getResp = pb.GetResp(); + + final groupedResults = + < + String, + Map + >{}; // > + + dto.results.forEach((path, value) { + final parentPath = path.parent?.fullPath ?? ""; + final paramName = path.name; + + if (!groupedResults.containsKey(parentPath)) { + groupedResults[parentPath] = {}; + } + groupedResults[parentPath]![paramName] = value; + }); + + // Create one RequestedPathResult for simplification, or one per actual request path in the future + // Assuming requestedPath is empty or a generic one for simplicity as it's not in UspGetResponse DTO + final reqPathRes = pb.GetResp_RequestedPathResult() + ..errCode = 0; // Simplified + + groupedResults.forEach((objPath, params) { + final resolved = pb.GetResp_ResolvedPathResult() + ..resolvedPath = objPath; // This is the object path + params.forEach((paramName, uspValue) { + resolved.resultParams[paramName] = uspValue.value + .toString(); // Param name -> value + }); + reqPathRes.resolvedPathResults.add(resolved); + }); + + getResp.reqPathResults.add(reqPathRes); + return getResp; + } + + pb.SetResp _buildSetResp(UspSetResponse dto) { + final setResp = pb.SetResp(); + + // Success + for (final path in dto.successPaths) { + final successResult = pb.SetResp_UpdatedObjectResult() + ..requestedPath = path + .fullPath // Simplify: assuming object path + ..operStatus = (pb.SetResp_UpdatedObjectResult_OperationStatus() + ..operSuccess = + pb.SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess()); + setResp.updatedObjResults.add(successResult); + } + + // Failure + dto.failurePaths.forEach((path, error) { + final failureResult = pb.SetResp_UpdatedObjectResult() + ..requestedPath = path.fullPath + ..operStatus = (pb.SetResp_UpdatedObjectResult_OperationStatus() + ..operFailure = + (pb.SetResp_UpdatedObjectResult_OperationStatus_OperationFailure() + ..errCode = error.errorCode + ..errMsg = error.message)); + setResp.updatedObjResults.add(failureResult); + }); + + return setResp; + } + + pb.AddResp _buildAddResp(UspAddResponse dto) { + final addResp = pb.AddResp(); + + for (final created in dto.createdObjects) { + final result = pb.AddResp_CreatedObjectResult() + ..requestedPath = + created.instantiatedPath.parent?.fullPath ?? + "" // Or the requested path if available + ..operStatus = (pb.AddResp_CreatedObjectResult_OperationStatus() + ..operSuccess = + (pb.AddResp_CreatedObjectResult_OperationStatus_OperationSuccess() + ..instantiatedPath = created.instantiatedPath.fullPath)); + addResp.createdObjResults.add(result); + } + + // Handle errors from the DTO + for (final error in dto.errors) { + final errorResult = pb.AddResp_CreatedObjectResult() + ..requestedPath = + "error.path" // Placeholder: DTO UspException doesn't carry path + ..operStatus = (pb.AddResp_CreatedObjectResult_OperationStatus() + ..operFailure = + (pb.AddResp_CreatedObjectResult_OperationStatus_OperationFailure() + ..errCode = error.errorCode + ..errMsg = error.message)); + addResp.createdObjResults.add(errorResult); + } + + return addResp; + } + + pb.DeleteResp _buildDeleteResp(UspDeleteResponse dto) { + final delResp = pb.DeleteResp(); + + for (final path in dto.deletedPaths) { + final result = pb.DeleteResp_DeletedObjectResult() + ..requestedPath = path.fullPath + ..operStatus = (pb.DeleteResp_DeletedObjectResult_OperationStatus() + ..operSuccess = + pb.DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess()); + delResp.deletedObjResults.add(result); + } + + // Handle errors from the DTO + for (final error in dto.errors) { + final errorResult = pb.DeleteResp_DeletedObjectResult() + ..requestedPath = + "error.path" // Placeholder: DTO UspException doesn't carry path + ..operStatus = (pb.DeleteResp_DeletedObjectResult_OperationStatus() + ..operFailure = + (pb.DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure() + ..errCode = error.errorCode + ..errMsg = error.message)); + delResp.deletedObjResults.add(errorResult); + } + + return delResp; + } + + // --- GetSupportedDM Response --- + + pb.GetSupportedDMResp _buildGetSupportedDMResp( + UspGetSupportedDMResponse dto, + ) { + final resp = pb.GetSupportedDMResp(); + + // USP allows multiple Request Path. + // Our DTO is flat Map. + // Here we simplify the processing: pack all results in a "empty path" or "Device." RequestedObjectResult. + + final reqObjResult = pb.GetSupportedDMResp_RequestedObjectResult() + ..reqObjPath = + "" // or "Device." depends on the original request + ..errCode = 0; + + dto.results.forEach((path, def) { + final supportedObj = pb.GetSupportedDMResp_SupportedObjectResult() + ..supportedObjPath = def.path + ..isMultiInstance = def.isMultiInstance + ..access = _mapObjAccessToProto(def.access); + + // 1. Convert Parameters + if (def.supportedParams.isNotEmpty) { + def.supportedParams.forEach((name, paramDef) { + final supportedParam = pb.GetSupportedDMResp_SupportedParamResult() + ..paramName = name + ..access = paramDef.isWritable + ? pb.GetSupportedDMResp_ParamAccessType.PARAM_READ_WRITE + : pb.GetSupportedDMResp_ParamAccessType.PARAM_READ_ONLY + ..valueType = _mapParamTypeToProto(paramDef.type); + + supportedObj.supportedParams.add(supportedParam); + }); + } + + // 2. Convert Commands + if (def.supportedCommands.isNotEmpty) { + def.supportedCommands.forEach((name, cmdDef) { + final supportedCmd = pb.GetSupportedDMResp_SupportedCommandResult() + ..commandName = name + ..commandType = cmdDef.isAsync + ? pb.GetSupportedDMResp_CmdType.CMD_ASYNC + : pb.GetSupportedDMResp_CmdType.CMD_SYNC; + + supportedCmd.inputArgNames.addAll(cmdDef.inputArgs.keys); + supportedCmd.outputArgNames.addAll(cmdDef.outputArgs.keys); + + supportedObj.supportedCommands.add(supportedCmd); + }); + } + + // 3. Convert Events (if defined) + // if (def.supportedEvents.isNotEmpty) { ... } + + reqObjResult.supportedObjs.add(supportedObj); + }); + + resp.reqObjResults.add(reqObjResult); + return resp; + } + + // --- GetInstances Response --- + pb.GetInstancesResp _buildGetInstancesResp(UspGetInstancesResponse dto) { + final resp = pb.GetInstancesResp(); + + dto.results.forEach((reqPath, instances) { + final reqResult = pb.GetInstancesResp_RequestedPathResult() + ..requestedPath = reqPath + ..errCode = 0; + + for (final inst in instances) { + final currInst = pb.GetInstancesResp_CurrInstance() + ..instantiatedObjPath = inst.instantiatedPath; + + if (inst.uniqueKeys.isNotEmpty) { + currInst.uniqueKeys.addAll(inst.uniqueKeys); + } + + reqResult.currInsts.add(currInst); + } + resp.reqPathResults.add(reqResult); + }); + + return resp; + } + + // --- Enum Mapping Helpers --- + + pb.GetSupportedDMResp_ObjAccessType _mapObjAccessToProto(String access) { + switch (access) { + case 'ReadWrite': + case 'readWrite': + return pb.GetSupportedDMResp_ObjAccessType.OBJ_ADD_DELETE; + + case 'AddOnly': + case 'addOnly': + return pb.GetSupportedDMResp_ObjAccessType.OBJ_ADD_ONLY; + + case 'DeleteOnly': + case 'deleteOnly': + return pb.GetSupportedDMResp_ObjAccessType.OBJ_DELETE_ONLY; + + case 'ReadOnly': + case 'readOnly': + default: + // 預設為最安全的唯讀 + return pb.GetSupportedDMResp_ObjAccessType.OBJ_READ_ONLY; + } + } + + pb.GetSupportedDMResp_ParamValueType _mapParamTypeToProto(UspValueType type) { + switch (type) { + case UspValueType.string: + return pb.GetSupportedDMResp_ParamValueType.PARAM_STRING; + case UspValueType.int: + return pb.GetSupportedDMResp_ParamValueType.PARAM_INT; + case UspValueType.unsignedInt: + return pb.GetSupportedDMResp_ParamValueType.PARAM_UNSIGNED_INT; + case UspValueType.long: + return pb.GetSupportedDMResp_ParamValueType.PARAM_LONG; + case UspValueType.unsignedLong: + return pb.GetSupportedDMResp_ParamValueType.PARAM_UNSIGNED_LONG; + case UspValueType.boolean: + return pb.GetSupportedDMResp_ParamValueType.PARAM_BOOLEAN; + case UspValueType.dateTime: + return pb.GetSupportedDMResp_ParamValueType.PARAM_DATE_TIME; + case UspValueType.base64: + return pb.GetSupportedDMResp_ParamValueType.PARAM_BASE_64; + case UspValueType.hexBinary: + return pb.GetSupportedDMResp_ParamValueType.PARAM_HEX_BINARY; + } + } + + // ------------------------------------------------------------------------ + // Parse Helpers (Proto -> DTO) + // ------------------------------------------------------------------------ + + UspSetRequest _parseSetMsg(pb.Set setMsg) { + final updates = {}; + + for (final obj in setMsg.updateObjs) { + for (final param in obj.paramSettings) { + final fullPath = "${obj.objPath}.${param.param}"; + updates[UspPath.parse(fullPath)] = UspValue( + param.value, + UspValueType.string, + ); + } + } + return UspSetRequest(updates, allowPartial: setMsg.allowPartial); + } + + UspAddRequest _parseAddMsg(pb.Add addMsg) { + final objects = []; + + for (final obj in addMsg.createObjs) { + final params = {}; + for (final p in obj.paramSettings) { + params[p.param] = UspValue(p.value, UspValueType.string); + } + objects.add( + UspObjectCreation(UspPath.parse(obj.objPath), parameters: params), + ); + } + return UspAddRequest(objects, allowPartial: addMsg.allowPartial); + } + + UspOperateRequest _parseOperateMsg(pb.Operate operateMsg) { + final inputArgs = {}; + operateMsg.inputArgs.forEach((key, value) { + inputArgs[key] = UspValue( + value, + UspValueType.string, + ); // Assuming string for simplicity + }); + return UspOperateRequest( + command: UspPath.parse(operateMsg.command), + inputArgs: inputArgs, + ); + } + + UspGetResponse _parseGetResp(pb.GetResp getResp) { + final results = {}; + + for (final reqRes in getResp.reqPathResults) { + for (final resolved in reqRes.resolvedPathResults) { + final objPath = resolved.resolvedPath; // This is the object path + resolved.resultParams.forEach((paramName, val) { + final fullParamPath = UspPath.parse( + "$objPath.$paramName", + ); // Reconstruct full parameter path + results[fullParamPath] = UspValue( + val, + UspValueType.string, + ); // Assuming string for simplicity + }); + } + } + return UspGetResponse(results); + } + + UspNotify _parseNotifyMsg(pb.Notify notifyMsg) { + return switch (notifyMsg.whichNotification()) { + pb.Notify_Notification.valueChange => UspValueChangeNotify( + paramPath: UspPath.parse(notifyMsg.valueChange.paramPath), + paramValue: notifyMsg.valueChange.paramValue, + subscriptionId: notifyMsg.subscriptionId, + sendResp: notifyMsg.sendResp, + ), + pb.Notify_Notification.objCreation => UspObjectCreationNotify( + objPath: UspPath.parse(notifyMsg.objCreation.objPath), + uniqueKeys: notifyMsg.objCreation.uniqueKeys, + subscriptionId: notifyMsg.subscriptionId, + sendResp: notifyMsg.sendResp, + ), + pb.Notify_Notification.objDeletion => UspObjectDeletionNotify( + objPath: UspPath.parse(notifyMsg.objDeletion.objPath), + subscriptionId: notifyMsg.subscriptionId, + sendResp: notifyMsg.sendResp, + ), + pb.Notify_Notification.operComplete => UspOperationCompleteNotify( + objPath: UspPath.parse(notifyMsg.operComplete.objPath), + commandName: notifyMsg.operComplete.commandName, + commandKey: notifyMsg.operComplete.commandKey, + outputArgs: notifyMsg.operComplete.hasReqOutputArgs() + ? notifyMsg.operComplete.reqOutputArgs.outputArgs + : {}, + commandFailure: notifyMsg.operComplete.hasCmdFailure() + ? UspException( + notifyMsg.operComplete.cmdFailure.errCode, + notifyMsg.operComplete.cmdFailure.errMsg, + ) + : null, + subscriptionId: notifyMsg.subscriptionId, + sendResp: notifyMsg.sendResp, + ), + pb.Notify_Notification.onBoardReq => UspOnBoardRequestNotify( + oui: notifyMsg.onBoardReq.oui, + productClass: notifyMsg.onBoardReq.productClass, + serialNumber: notifyMsg.onBoardReq.serialNumber, + agentProtocolVersions: + notifyMsg.onBoardReq.agentSupportedProtocolVersions, + sendResp: notifyMsg.sendResp, + ), + _ => throw UspException( + 7004, + "Unsupported Notification Type: ${notifyMsg.whichNotification()}", + ), + }; + } + + UspSetResponse _parseSetResp(pb.SetResp setResp) { + final success = []; + final failures = {}; + + for (final res in setResp.updatedObjResults) { + final path = UspPath.parse(res.requestedPath); + + if (res.operStatus.hasOperSuccess()) { + success.add(path); + } else if (res.operStatus.hasOperFailure()) { + final err = res.operStatus.operFailure; + failures[path] = UspException(err.errCode, err.errMsg); + } + } + return UspSetResponse(successPaths: success, failurePaths: failures); + } + + UspAddResponse _parseAddResp(pb.AddResp addResp) { + final created = []; + final errors = []; + + for (final res in addResp.createdObjResults) { + if (res.operStatus.hasOperSuccess()) { + final pathStr = res.operStatus.operSuccess.instantiatedPath; + created.add(UspCreatedObject(UspPath.parse(pathStr))); + } else if (res.operStatus.hasOperFailure()) { + final err = res.operStatus.operFailure; + errors.add(UspException(err.errCode, err.errMsg)); + } + } + return UspAddResponse(createdObjects: created, errors: errors); + } + + UspDeleteResponse _parseDeleteResp(pb.DeleteResp delResp) { + final deleted = []; + final errors = []; + + for (final res in delResp.deletedObjResults) { + if (res.operStatus.hasOperSuccess()) { + deleted.add(UspPath.parse(res.requestedPath)); + } else if (res.operStatus.hasOperFailure()) { + final err = res.operStatus.operFailure; + errors.add(UspException(err.errCode, err.errMsg)); + } + } + return UspDeleteResponse(deletedPaths: deleted, errors: errors); + } + + UspOperateResponse _parseOperateResponse(pb.OperateResp operateResp) { + // Renamed to avoid confusion + final outputArgs = {}; + if (operateResp.operationResults.isNotEmpty) { + // For simplicity, take the first operation result's reqOutputArgs + operateResp.operationResults.first.reqOutputArgs.outputArgs.forEach(( + key, + value, + ) { + // Changed to reqOutputArgs + outputArgs[key] = UspValue(value, UspValueType.string); + }); + } + return UspOperateResponse(outputArgs: outputArgs); + } + + // --- Parse GetSupportedDM Request --- + UspGetSupportedDMRequest _parseGetSupportedDMRequest(pb.GetSupportedDM msg) { + return UspGetSupportedDMRequest( + msg.objPaths.map((s) => UspPath.parse(s)).toList(), + firstLevelOnly: msg.firstLevelOnly, + returnCommands: msg.returnCommands, + returnEvents: msg.returnEvents, + returnParams: msg.returnParams, + ); + } + + // --- Parse GetSupportedDM Response --- + UspGetSupportedDMResponse _parseGetSupportedDMResp( + pb.GetSupportedDMResp resp, + ) { + final results = {}; + + for (final reqRes in resp.reqObjResults) { + for (final supportedObj in reqRes.supportedObjs) { + // 1. Parse Params + final params = {}; + for (final p in supportedObj.supportedParams) { + params[p.paramName] = UspParamDefinition( + name: p.paramName, + type: _mapProtoToParamType(p.valueType), + isWritable: + p.access == + pb.GetSupportedDMResp_ParamAccessType.PARAM_READ_WRITE, + constraints: UspParamConstraints(), + ); + } + + // 2. Parse Commands + final commands = {}; + for (final c in supportedObj.supportedCommands) { + final inputMap = {}; + for (final argName in c.inputArgNames) { + inputMap[argName] = UspArgumentDefinition( + name: argName, + type: UspValueType.string, // Default fallback + ); + } + + final outputMap = {}; + for (final argName in c.outputArgNames) { + outputMap[argName] = UspArgumentDefinition( + name: argName, + type: UspValueType.string, // Default fallback + ); + } + + commands[c.commandName] = UspCommandDefinition( + name: c.commandName, // 這裡修正建構子參數名稱 + inputArgs: inputMap, + outputArgs: outputMap, + isAsync: _isCommandAsync(c.commandType), + ); + } + + // 3. Parse Object + final def = UspObjectDefinition( + path: supportedObj.supportedObjPath, + isMultiInstance: supportedObj.isMultiInstance, + access: _mapProtoToObjAccess(supportedObj.access), + supportedParams: params, + supportedCommands: commands, + ); + + results[supportedObj.supportedObjPath] = def; + } + } + + return UspGetSupportedDMResponse(results); + } + + // --- Parse GetInstances Request --- + UspGetInstancesRequest _parseGetInstancesMsg(pb.GetInstances msg) { + return UspGetInstancesRequest( + msg.objPaths.map((s) => UspPath.parse(s)).toList(), + firstLevelOnly: msg.firstLevelOnly, + ); + } + + // --- Parse GetInstances Response --- + UspGetInstancesResponse _parseGetInstancesResp(pb.GetInstancesResp resp) { + final results = >{}; + + for (final reqRes in resp.reqPathResults) { + final instances = []; + + for (final curr in reqRes.currInsts) { + instances.add( + UspInstanceResult( + curr.instantiatedObjPath, + uniqueKeys: curr.uniqueKeys, // Map + ), + ); + } + + results[reqRes.requestedPath] = instances; + } + + return UspGetInstancesResponse(results); + } + + // --- Enum Mapping Helpers (Reverse) --- + + UspValueType _mapProtoToParamType(pb.GetSupportedDMResp_ParamValueType type) { + switch (type) { + case pb.GetSupportedDMResp_ParamValueType.PARAM_INT: + return UspValueType.int; + case pb.GetSupportedDMResp_ParamValueType.PARAM_UNSIGNED_INT: + return UspValueType.unsignedInt; + case pb.GetSupportedDMResp_ParamValueType.PARAM_BOOLEAN: + return UspValueType.boolean; + case pb.GetSupportedDMResp_ParamValueType.PARAM_DATE_TIME: + return UspValueType.dateTime; + // ... 其他對應 ... + default: + return UspValueType.string; + } + } + + String _mapProtoToObjAccess(pb.GetSupportedDMResp_ObjAccessType access) { + switch (access) { + case pb.GetSupportedDMResp_ObjAccessType.OBJ_ADD_DELETE: + return "ReadWrite"; + case pb.GetSupportedDMResp_ObjAccessType.OBJ_READ_ONLY: + return "ReadOnly"; + case pb.GetSupportedDMResp_ObjAccessType.OBJ_ADD_ONLY: + return "AddOnly"; + case pb.GetSupportedDMResp_ObjAccessType.OBJ_DELETE_ONLY: + return "DeleteOnly"; + default: + return "ReadOnly"; + } + } + + bool _isCommandAsync(pb.GetSupportedDMResp_CmdType type) { + return type == pb.GetSupportedDMResp_CmdType.CMD_ASYNC; + } +} diff --git a/packages/usp_protocol_common/lib/src/dtos/base_dto.dart b/packages/usp_protocol_common/lib/src/dtos/base_dto.dart new file mode 100644 index 000000000..d31b4f40f --- /dev/null +++ b/packages/usp_protocol_common/lib/src/dtos/base_dto.dart @@ -0,0 +1,10 @@ +import 'package:equatable/equatable.dart'; + +/// The base class for all USP messages (requests and responses). +abstract class UspMessage extends Equatable { + /// Creates a const [UspMessage]. + const UspMessage(); + + @override + List get props => []; +} diff --git a/packages/usp_protocol_common/lib/src/dtos/requests/usp_requests.dart b/packages/usp_protocol_common/lib/src/dtos/requests/usp_requests.dart new file mode 100644 index 000000000..e447ccbf7 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/dtos/requests/usp_requests.dart @@ -0,0 +1,288 @@ +import 'package:equatable/equatable.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// The base class for all USP requests. +sealed class UspRequest extends UspMessage { + /// Creates a const [UspRequest]. + const UspRequest(); +} + +/// A USP request to add a new object to the data model. +class UspAddRequest extends UspRequest { + /// A list of [UspObjectCreation] objects, each describing an object to be created. + final List objects; + + /// Whether partial creation is allowed. If true, the agent will attempt to create all + /// objects, even if some fail. If false, the entire operation will fail if any + /// object creation fails. + final bool allowPartial; + + /// Creates a new [UspAddRequest]. + const UspAddRequest(this.objects, {this.allowPartial = false}); + + @override + List get props => [objects, allowPartial]; +} + +/// A helper class that describes the creation of a single object. +class UspObjectCreation extends Equatable { + /// The path to the parent object under which the new object will be created. + final UspPath parentPath; + + /// A map of parameters to be set on the newly created object. + final Map parameters; + + /// Creates a new [UspObjectCreation]. + const UspObjectCreation(this.parentPath, {this.parameters = const {}}); + + @override + List get props => [parentPath, parameters]; +} + +/// A USP request to delete an object from the data model. +class UspDeleteRequest extends UspRequest { + /// A list of paths to the objects to be deleted. + final List objPaths; + + /// Whether partial deletion is allowed. If true, the agent will attempt to delete all + /// objects, even if some fail. If false, the entire operation will fail if any + /// object deletion fails. + final bool allowPartial; + + /// Creates a new [UspDeleteRequest]. + const UspDeleteRequest(this.objPaths, {this.allowPartial = false}); + + @override + List get props => [objPaths, allowPartial]; +} + +/// A USP request to get parameter values from the data model. +class UspGetRequest extends UspRequest { + /// A list of paths to the parameters to be retrieved. Wildcards are supported. + final List paths; + + /// Creates a new [UspGetRequest]. + const UspGetRequest(this.paths); + + @override + List get props => [paths]; +} + +/// A USP request to execute a command on the agent. +class UspOperateRequest extends UspRequest { + /// The command to be executed. + final UspPath command; + + /// A map of input arguments for the command. + final Map inputArgs; + + /// Creates a new [UspOperateRequest]. + const UspOperateRequest({required this.command, this.inputArgs = const {}}); + + @override + List get props => [command, inputArgs]; +} + +/// A USP request to set parameter values in the data model. +class UspSetRequest extends UspRequest { + /// A map of paths to the parameters to be updated, and the new values. + final Map updates; + + /// Whether partial updates are allowed. If true, the agent will attempt to update all + /// parameters, even if some fail. If false, the entire operation will fail if any + /// parameter update fails. + final bool allowPartial; + + /// Creates a new [UspSetRequest]. + const UspSetRequest(this.updates, {this.allowPartial = false}); + + @override + List get props => [updates, allowPartial]; +} + +// A USP request to get supported data model information from the agent. +class UspGetSupportedDMRequest extends UspRequest { + // Use ["Device."] to get all + final List objPaths; + + // Whether to only fetch the first level (Lazy Loading) + final bool firstLevelOnly; + + // Whether to include Command information + final bool returnCommands; + + // Whether to include Event information + final bool returnEvents; + + // Whether to include Parameter information + final bool returnParams; + + const UspGetSupportedDMRequest( + this.objPaths, { + this.firstLevelOnly = false, + this.returnCommands = true, + this.returnEvents = true, + this.returnParams = true, + }); + + @override + List get props => [ + objPaths, + firstLevelOnly, + returnCommands, + returnEvents, + returnParams, + ]; +} + +class UspGetInstancesRequest extends UspRequest { + final List objPaths; + final bool firstLevelOnly; + + const UspGetInstancesRequest(this.objPaths, {this.firstLevelOnly = false}); + + @override + List get props => [objPaths, firstLevelOnly]; +} + +// ========================================================================== +// Notification DTOs +// ========================================================================== + +/// The base class for all USP Notifications. +/// In USP, a Notify is technically a Request sent by the Agent to the Controller. +sealed class UspNotify extends UspRequest { + /// An identifier for the subscription that triggered this notification. + final String subscriptionId; + + /// Whether the Agent expects a response from the Controller. + final bool sendResp; + + const UspNotify({this.subscriptionId = "", this.sendResp = false}); + + @override + List get props => [subscriptionId, sendResp]; +} + +/// Notify of type ValueChange. +/// Triggered when a subscribed parameter's value changes. +class UspValueChangeNotify extends UspNotify { + /// The path of the parameter that changed. + final UspPath paramPath; + + /// The new value of the parameter (transmitted as String in Protobuf). + final String paramValue; + + const UspValueChangeNotify({ + required this.paramPath, + required this.paramValue, + super.subscriptionId, + super.sendResp, + }); + + @override + List get props => [...super.props, paramPath, paramValue]; +} + +/// Notify of type ObjectCreation. +/// Triggered when a new object instance is added to a subscribed table. +class UspObjectCreationNotify extends UspNotify { + /// The path of the newly created object (e.g., Device.WiFi.SSID.1). + final UspPath objPath; + + /// Unique keys of the created object (Optional map of param name -> value). + final Map uniqueKeys; + + const UspObjectCreationNotify({ + required this.objPath, + this.uniqueKeys = const {}, + super.subscriptionId, + super.sendResp, + }); + + @override + List get props => [...super.props, objPath, uniqueKeys]; +} + +/// Notify of type ObjectDeletion. +/// Triggered when an object instance is removed from a subscribed table. +class UspObjectDeletionNotify extends UspNotify { + /// The path of the deleted object. + final UspPath objPath; + + const UspObjectDeletionNotify({ + required this.objPath, + super.subscriptionId, + super.sendResp, + }); + + @override + List get props => [...super.props, objPath]; +} + +/// Notify of type OperationComplete. +/// Triggered when an asynchronous [Operate] command finishes. +class UspOperationCompleteNotify extends UspNotify { + /// The path of the object where the command was executed. + final UspPath objPath; + + /// The name of the command (e.g., "Reboot"). + final String commandName; + + /// The unique key associated with the original Operate request. + final String commandKey; + + /// The output arguments if the operation succeeded (Map in Proto). + final Map outputArgs; + + /// The error details if the operation failed. Null if successful. + final UspException? commandFailure; + + const UspOperationCompleteNotify({ + required this.objPath, + required this.commandName, + required this.commandKey, + this.outputArgs = const {}, + this.commandFailure, + super.subscriptionId, + super.sendResp, + }); + + bool get isSuccess => commandFailure == null; + + @override + List get props => [ + ...super.props, + objPath, + commandName, + commandKey, + outputArgs, + commandFailure, + ]; +} + +/// Notify of type OnBoardRequest. +/// Sent by the Agent when it first comes online or needs to establish a relationship. +class UspOnBoardRequestNotify extends UspNotify { + final String oui; + final String productClass; + final String serialNumber; + final String agentProtocolVersions; + + const UspOnBoardRequestNotify({ + required this.oui, + required this.productClass, + required this.serialNumber, + required this.agentProtocolVersions, + super.sendResp = true, // OnBoard usually requires a response + }); + + @override + List get props => [ + ...super.props, + oui, + productClass, + serialNumber, + agentProtocolVersions, + ]; +} diff --git a/packages/usp_protocol_common/lib/src/dtos/responses/usp_responses.dart b/packages/usp_protocol_common/lib/src/dtos/responses/usp_responses.dart new file mode 100644 index 000000000..6f278507b --- /dev/null +++ b/packages/usp_protocol_common/lib/src/dtos/responses/usp_responses.dart @@ -0,0 +1,161 @@ +import 'package:equatable/equatable.dart'; +import 'package:usp_protocol_common/usp_protocol_common.dart'; + +/// The base class for all USP responses. +sealed class UspResponse extends UspMessage { + /// Creates a const [UspResponse]. + const UspResponse(); +} + +/// A USP response to an [UspAddRequest]. +class UspAddResponse extends UspResponse { + /// A list of [UspCreatedObject] objects, each representing a successfully created object. + final List createdObjects; + + /// A list of exceptions that occurred during the add operation. + final List errors; + + /// Creates a new [UspAddResponse]. + const UspAddResponse({ + this.createdObjects = const [], + this.errors = const [], + }); + + @override + List get props => [createdObjects, errors]; +} + +/// A helper class that represents a successfully created object. +class UspCreatedObject extends Equatable { + /// The path to the newly created object instance. + final UspPath instantiatedPath; + + /// Creates a new [UspCreatedObject]. + const UspCreatedObject(this.instantiatedPath); + + @override + List get props => [instantiatedPath]; +} + +/// A USP response to a [UspDeleteRequest]. +class UspDeleteResponse extends UspResponse { + /// A list of paths to the objects that were successfully deleted. + final List deletedPaths; + + /// A list of exceptions that occurred during the delete operation. + final List errors; + + /// Creates a new [UspDeleteResponse]. + const UspDeleteResponse({ + this.deletedPaths = const [], + this.errors = const [], + }); + + @override + List get props => [deletedPaths, errors]; +} + +/// A USP error response. +class UspErrorResponse extends UspResponse { + /// The exception that occurred. + final UspException exception; + + /// Creates a new [UspErrorResponse]. + const UspErrorResponse(this.exception); + + @override + List get props => [exception]; +} + +/// A USP response to a [UspGetRequest]. +class UspGetResponse extends UspResponse { + /// A map of paths to the retrieved parameter values. + final Map results; + + /// Creates a new [UspGetResponse]. + const UspGetResponse(this.results); + + @override + List get props => [results]; +} + +/// A USP response to an [UspOperateRequest]. +class UspOperateResponse extends UspResponse { + /// A map of output arguments from the command execution. + final Map outputArgs; + + /// Creates a new [UspOperateResponse]. + const UspOperateResponse({this.outputArgs = const {}}); + + @override + List get props => [outputArgs]; +} + +/// A USP response to a [UspSetRequest]. +class UspSetResponse extends UspResponse { + /// A list of paths to the parameters that were successfully updated. + final List successPaths; + + /// A map of paths to the parameters that failed to update, and the corresponding exceptions. + final Map failurePaths; + + /// Creates a new [UspSetResponse]. + const UspSetResponse({ + this.successPaths = const [], + this.failurePaths = const {}, + }); + + /// Whether the set operation had any failures. + bool get hasErrors => failurePaths.isNotEmpty; + + @override + List get props => [successPaths, failurePaths]; +} + +// A USP response to a [UspGetSupportedDMRequest]. +class UspGetSupportedDMResponse extends UspResponse { + // Path -> Object Definition + final Map results; + + const UspGetSupportedDMResponse(this.results); + + @override + List get props => [results]; +} + +/// A USP response to a [UspGetInstancesRequest]. +class UspGetInstancesResponse extends UspResponse { + // Path -> List of Instance Results (Map) + // If no Unique Key, Map is empty + final Map> results; + + const UspGetInstancesResponse(this.results); + + @override + List get props => [results]; +} + +/// Describes the result of an instance retrieval. +class UspInstanceResult extends Equatable { + final String instantiatedPath; + final Map uniqueKeys; + + const UspInstanceResult(this.instantiatedPath, {this.uniqueKeys = const {}}); + + @override + List get props => [instantiatedPath, uniqueKeys]; +} + +/// A USP response to a [UspNotify] request. +/// Sent by the Controller to acknowledge receipt of a notification. +class UspNotifyResponse extends UspResponse { + /// The subscription ID that caused the notification. + /// This confirms to the Agent which subscription is being acknowledged. + final String subscriptionId; + + /// Creates a new [UspNotifyResponse]. + const UspNotifyResponse({required this.subscriptionId}); + + @override + List get props => [subscriptionId]; +} diff --git a/packages/usp_protocol_common/lib/src/exceptions/usp_exception.dart b/packages/usp_protocol_common/lib/src/exceptions/usp_exception.dart new file mode 100644 index 000000000..b4e531a40 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/exceptions/usp_exception.dart @@ -0,0 +1,19 @@ +import 'package:equatable/equatable.dart'; + +/// An exception that occurred during a USP operation. +class UspException extends Equatable implements Exception { + /// The USP error code. + final int errorCode; + + /// A human-readable message describing the error. + final String message; + + /// Creates a new [UspException]. + const UspException(this.errorCode, this.message); + + @override + String toString() => 'UspException($errorCode): $message'; + + @override + List get props => [errorCode, message]; +} diff --git a/packages/usp_protocol_common/lib/src/generated/usp_msg.pb.dart b/packages/usp_protocol_common/lib/src/generated/usp_msg.pb.dart new file mode 100644 index 000000000..5180172a3 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_msg.pb.dart @@ -0,0 +1,6559 @@ +// This is a generated file - do not edit. +// +// Generated from usp_msg.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +import 'usp_msg.pbenum.dart'; + +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + +export 'usp_msg.pbenum.dart'; + +class Msg extends $pb.GeneratedMessage { + factory Msg({ + Header? header, + Body? body, + }) { + final result = create(); + if (header != null) result.header = header; + if (body != null) result.body = body; + return result; + } + + Msg._(); + + factory Msg.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Msg.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Msg', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOM
(1, _omitFieldNames ? '' : 'header', subBuilder: Header.create) + ..aOM(2, _omitFieldNames ? '' : 'body', subBuilder: Body.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Msg clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Msg copyWith(void Function(Msg) updates) => + super.copyWith((message) => updates(message as Msg)) as Msg; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Msg create() => Msg._(); + @$core.override + Msg createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Msg getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Msg? _defaultInstance; + + @$pb.TagNumber(1) + Header get header => $_getN(0); + @$pb.TagNumber(1) + set header(Header value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasHeader() => $_has(0); + @$pb.TagNumber(1) + void clearHeader() => $_clearField(1); + @$pb.TagNumber(1) + Header ensureHeader() => $_ensure(0); + + @$pb.TagNumber(2) + Body get body => $_getN(1); + @$pb.TagNumber(2) + set body(Body value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasBody() => $_has(1); + @$pb.TagNumber(2) + void clearBody() => $_clearField(2); + @$pb.TagNumber(2) + Body ensureBody() => $_ensure(1); +} + +class Header extends $pb.GeneratedMessage { + factory Header({ + $core.String? msgId, + Header_MsgType? msgType, + }) { + final result = create(); + if (msgId != null) result.msgId = msgId; + if (msgType != null) result.msgType = msgType; + return result; + } + + Header._(); + + factory Header.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Header.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Header', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'msgId') + ..aE(2, _omitFieldNames ? '' : 'msgType', + enumValues: Header_MsgType.values) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Header clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Header copyWith(void Function(Header) updates) => + super.copyWith((message) => updates(message as Header)) as Header; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Header create() => Header._(); + @$core.override + Header createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Header getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor
(create); + static Header? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get msgId => $_getSZ(0); + @$pb.TagNumber(1) + set msgId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasMsgId() => $_has(0); + @$pb.TagNumber(1) + void clearMsgId() => $_clearField(1); + + @$pb.TagNumber(2) + Header_MsgType get msgType => $_getN(1); + @$pb.TagNumber(2) + set msgType(Header_MsgType value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasMsgType() => $_has(1); + @$pb.TagNumber(2) + void clearMsgType() => $_clearField(2); +} + +enum Body_MsgBody { request, response, error, notSet } + +class Body extends $pb.GeneratedMessage { + factory Body({ + Request? request, + Response? response, + Error? error, + }) { + final result = create(); + if (request != null) result.request = request; + if (response != null) result.response = response; + if (error != null) result.error = error; + return result; + } + + Body._(); + + factory Body.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Body.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Body_MsgBody> _Body_MsgBodyByTag = { + 1: Body_MsgBody.request, + 2: Body_MsgBody.response, + 3: Body_MsgBody.error, + 0: Body_MsgBody.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Body', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2, 3]) + ..aOM(1, _omitFieldNames ? '' : 'request', + subBuilder: Request.create) + ..aOM(2, _omitFieldNames ? '' : 'response', + subBuilder: Response.create) + ..aOM(3, _omitFieldNames ? '' : 'error', subBuilder: Error.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Body clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Body copyWith(void Function(Body) updates) => + super.copyWith((message) => updates(message as Body)) as Body; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Body create() => Body._(); + @$core.override + Body createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Body getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Body? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + Body_MsgBody whichMsgBody() => _Body_MsgBodyByTag[$_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + void clearMsgBody() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + Request get request => $_getN(0); + @$pb.TagNumber(1) + set request(Request value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasRequest() => $_has(0); + @$pb.TagNumber(1) + void clearRequest() => $_clearField(1); + @$pb.TagNumber(1) + Request ensureRequest() => $_ensure(0); + + @$pb.TagNumber(2) + Response get response => $_getN(1); + @$pb.TagNumber(2) + set response(Response value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasResponse() => $_has(1); + @$pb.TagNumber(2) + void clearResponse() => $_clearField(2); + @$pb.TagNumber(2) + Response ensureResponse() => $_ensure(1); + + @$pb.TagNumber(3) + Error get error => $_getN(2); + @$pb.TagNumber(3) + set error(Error value) => $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasError() => $_has(2); + @$pb.TagNumber(3) + void clearError() => $_clearField(3); + @$pb.TagNumber(3) + Error ensureError() => $_ensure(2); +} + +enum Request_ReqType { + get, + getSupportedDm, + getInstances, + set, + add, + delete, + operate, + notify, + getSupportedProtocol, + register, + deregister, + notSet +} + +class Request extends $pb.GeneratedMessage { + factory Request({ + Get? get, + GetSupportedDM? getSupportedDm, + GetInstances? getInstances, + Set? set, + Add? add, + Delete? delete, + Operate? operate, + Notify? notify, + GetSupportedProtocol? getSupportedProtocol, + Register? register, + Deregister? deregister, + }) { + final result = create(); + if (get != null) result.get = get; + if (getSupportedDm != null) result.getSupportedDm = getSupportedDm; + if (getInstances != null) result.getInstances = getInstances; + if (set != null) result.set = set; + if (add != null) result.add = add; + if (delete != null) result.delete = delete; + if (operate != null) result.operate = operate; + if (notify != null) result.notify = notify; + if (getSupportedProtocol != null) + result.getSupportedProtocol = getSupportedProtocol; + if (register != null) result.register = register; + if (deregister != null) result.deregister = deregister; + return result; + } + + Request._(); + + factory Request.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Request.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Request_ReqType> _Request_ReqTypeByTag = { + 1: Request_ReqType.get, + 2: Request_ReqType.getSupportedDm, + 3: Request_ReqType.getInstances, + 4: Request_ReqType.set, + 5: Request_ReqType.add, + 6: Request_ReqType.delete, + 7: Request_ReqType.operate, + 8: Request_ReqType.notify, + 9: Request_ReqType.getSupportedProtocol, + 10: Request_ReqType.register, + 11: Request_ReqType.deregister, + 0: Request_ReqType.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Request', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + ..aOM(1, _omitFieldNames ? '' : 'get', subBuilder: Get.create) + ..aOM(2, _omitFieldNames ? '' : 'getSupportedDm', + subBuilder: GetSupportedDM.create) + ..aOM(3, _omitFieldNames ? '' : 'getInstances', + subBuilder: GetInstances.create) + ..aOM(4, _omitFieldNames ? '' : 'set', subBuilder: Set.create) + ..aOM(5, _omitFieldNames ? '' : 'add', subBuilder: Add.create) + ..aOM(6, _omitFieldNames ? '' : 'delete', subBuilder: Delete.create) + ..aOM(7, _omitFieldNames ? '' : 'operate', + subBuilder: Operate.create) + ..aOM(8, _omitFieldNames ? '' : 'notify', subBuilder: Notify.create) + ..aOM( + 9, _omitFieldNames ? '' : 'getSupportedProtocol', + subBuilder: GetSupportedProtocol.create) + ..aOM(10, _omitFieldNames ? '' : 'register', + subBuilder: Register.create) + ..aOM(11, _omitFieldNames ? '' : 'deregister', + subBuilder: Deregister.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Request clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Request copyWith(void Function(Request) updates) => + super.copyWith((message) => updates(message as Request)) as Request; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Request create() => Request._(); + @$core.override + Request createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Request getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Request? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + Request_ReqType whichReqType() => _Request_ReqTypeByTag[$_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + void clearReqType() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + Get get get => $_getN(0); + @$pb.TagNumber(1) + set get(Get value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasGet() => $_has(0); + @$pb.TagNumber(1) + void clearGet() => $_clearField(1); + @$pb.TagNumber(1) + Get ensureGet() => $_ensure(0); + + @$pb.TagNumber(2) + GetSupportedDM get getSupportedDm => $_getN(1); + @$pb.TagNumber(2) + set getSupportedDm(GetSupportedDM value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasGetSupportedDm() => $_has(1); + @$pb.TagNumber(2) + void clearGetSupportedDm() => $_clearField(2); + @$pb.TagNumber(2) + GetSupportedDM ensureGetSupportedDm() => $_ensure(1); + + @$pb.TagNumber(3) + GetInstances get getInstances => $_getN(2); + @$pb.TagNumber(3) + set getInstances(GetInstances value) => $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasGetInstances() => $_has(2); + @$pb.TagNumber(3) + void clearGetInstances() => $_clearField(3); + @$pb.TagNumber(3) + GetInstances ensureGetInstances() => $_ensure(2); + + @$pb.TagNumber(4) + Set get set => $_getN(3); + @$pb.TagNumber(4) + set set(Set value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasSet() => $_has(3); + @$pb.TagNumber(4) + void clearSet() => $_clearField(4); + @$pb.TagNumber(4) + Set ensureSet() => $_ensure(3); + + @$pb.TagNumber(5) + Add get add => $_getN(4); + @$pb.TagNumber(5) + set add(Add value) => $_setField(5, value); + @$pb.TagNumber(5) + $core.bool hasAdd() => $_has(4); + @$pb.TagNumber(5) + void clearAdd() => $_clearField(5); + @$pb.TagNumber(5) + Add ensureAdd() => $_ensure(4); + + @$pb.TagNumber(6) + Delete get delete => $_getN(5); + @$pb.TagNumber(6) + set delete(Delete value) => $_setField(6, value); + @$pb.TagNumber(6) + $core.bool hasDelete() => $_has(5); + @$pb.TagNumber(6) + void clearDelete() => $_clearField(6); + @$pb.TagNumber(6) + Delete ensureDelete() => $_ensure(5); + + @$pb.TagNumber(7) + Operate get operate => $_getN(6); + @$pb.TagNumber(7) + set operate(Operate value) => $_setField(7, value); + @$pb.TagNumber(7) + $core.bool hasOperate() => $_has(6); + @$pb.TagNumber(7) + void clearOperate() => $_clearField(7); + @$pb.TagNumber(7) + Operate ensureOperate() => $_ensure(6); + + @$pb.TagNumber(8) + Notify get notify => $_getN(7); + @$pb.TagNumber(8) + set notify(Notify value) => $_setField(8, value); + @$pb.TagNumber(8) + $core.bool hasNotify() => $_has(7); + @$pb.TagNumber(8) + void clearNotify() => $_clearField(8); + @$pb.TagNumber(8) + Notify ensureNotify() => $_ensure(7); + + @$pb.TagNumber(9) + GetSupportedProtocol get getSupportedProtocol => $_getN(8); + @$pb.TagNumber(9) + set getSupportedProtocol(GetSupportedProtocol value) => $_setField(9, value); + @$pb.TagNumber(9) + $core.bool hasGetSupportedProtocol() => $_has(8); + @$pb.TagNumber(9) + void clearGetSupportedProtocol() => $_clearField(9); + @$pb.TagNumber(9) + GetSupportedProtocol ensureGetSupportedProtocol() => $_ensure(8); + + @$pb.TagNumber(10) + Register get register => $_getN(9); + @$pb.TagNumber(10) + set register(Register value) => $_setField(10, value); + @$pb.TagNumber(10) + $core.bool hasRegister() => $_has(9); + @$pb.TagNumber(10) + void clearRegister() => $_clearField(10); + @$pb.TagNumber(10) + Register ensureRegister() => $_ensure(9); + + @$pb.TagNumber(11) + Deregister get deregister => $_getN(10); + @$pb.TagNumber(11) + set deregister(Deregister value) => $_setField(11, value); + @$pb.TagNumber(11) + $core.bool hasDeregister() => $_has(10); + @$pb.TagNumber(11) + void clearDeregister() => $_clearField(11); + @$pb.TagNumber(11) + Deregister ensureDeregister() => $_ensure(10); +} + +enum Response_RespType { + getResp, + getSupportedDmResp, + getInstancesResp, + setResp, + addResp, + deleteResp, + operateResp, + notifyResp, + getSupportedProtocolResp, + registerResp, + deregisterResp, + notSet +} + +class Response extends $pb.GeneratedMessage { + factory Response({ + GetResp? getResp, + GetSupportedDMResp? getSupportedDmResp, + GetInstancesResp? getInstancesResp, + SetResp? setResp, + AddResp? addResp, + DeleteResp? deleteResp, + OperateResp? operateResp, + NotifyResp? notifyResp, + GetSupportedProtocolResp? getSupportedProtocolResp, + RegisterResp? registerResp, + DeregisterResp? deregisterResp, + }) { + final result = create(); + if (getResp != null) result.getResp = getResp; + if (getSupportedDmResp != null) + result.getSupportedDmResp = getSupportedDmResp; + if (getInstancesResp != null) result.getInstancesResp = getInstancesResp; + if (setResp != null) result.setResp = setResp; + if (addResp != null) result.addResp = addResp; + if (deleteResp != null) result.deleteResp = deleteResp; + if (operateResp != null) result.operateResp = operateResp; + if (notifyResp != null) result.notifyResp = notifyResp; + if (getSupportedProtocolResp != null) + result.getSupportedProtocolResp = getSupportedProtocolResp; + if (registerResp != null) result.registerResp = registerResp; + if (deregisterResp != null) result.deregisterResp = deregisterResp; + return result; + } + + Response._(); + + factory Response.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Response.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Response_RespType> _Response_RespTypeByTag = + { + 1: Response_RespType.getResp, + 2: Response_RespType.getSupportedDmResp, + 3: Response_RespType.getInstancesResp, + 4: Response_RespType.setResp, + 5: Response_RespType.addResp, + 6: Response_RespType.deleteResp, + 7: Response_RespType.operateResp, + 8: Response_RespType.notifyResp, + 9: Response_RespType.getSupportedProtocolResp, + 10: Response_RespType.registerResp, + 11: Response_RespType.deregisterResp, + 0: Response_RespType.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Response', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) + ..aOM(1, _omitFieldNames ? '' : 'getResp', + subBuilder: GetResp.create) + ..aOM(2, _omitFieldNames ? '' : 'getSupportedDmResp', + subBuilder: GetSupportedDMResp.create) + ..aOM(3, _omitFieldNames ? '' : 'getInstancesResp', + subBuilder: GetInstancesResp.create) + ..aOM(4, _omitFieldNames ? '' : 'setResp', + subBuilder: SetResp.create) + ..aOM(5, _omitFieldNames ? '' : 'addResp', + subBuilder: AddResp.create) + ..aOM(6, _omitFieldNames ? '' : 'deleteResp', + subBuilder: DeleteResp.create) + ..aOM(7, _omitFieldNames ? '' : 'operateResp', + subBuilder: OperateResp.create) + ..aOM(8, _omitFieldNames ? '' : 'notifyResp', + subBuilder: NotifyResp.create) + ..aOM( + 9, _omitFieldNames ? '' : 'getSupportedProtocolResp', + subBuilder: GetSupportedProtocolResp.create) + ..aOM(10, _omitFieldNames ? '' : 'registerResp', + subBuilder: RegisterResp.create) + ..aOM(11, _omitFieldNames ? '' : 'deregisterResp', + subBuilder: DeregisterResp.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Response copyWith(void Function(Response) updates) => + super.copyWith((message) => updates(message as Response)) as Response; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Response create() => Response._(); + @$core.override + Response createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Response getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Response? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + Response_RespType whichRespType() => + _Response_RespTypeByTag[$_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + void clearRespType() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + GetResp get getResp => $_getN(0); + @$pb.TagNumber(1) + set getResp(GetResp value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasGetResp() => $_has(0); + @$pb.TagNumber(1) + void clearGetResp() => $_clearField(1); + @$pb.TagNumber(1) + GetResp ensureGetResp() => $_ensure(0); + + @$pb.TagNumber(2) + GetSupportedDMResp get getSupportedDmResp => $_getN(1); + @$pb.TagNumber(2) + set getSupportedDmResp(GetSupportedDMResp value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasGetSupportedDmResp() => $_has(1); + @$pb.TagNumber(2) + void clearGetSupportedDmResp() => $_clearField(2); + @$pb.TagNumber(2) + GetSupportedDMResp ensureGetSupportedDmResp() => $_ensure(1); + + @$pb.TagNumber(3) + GetInstancesResp get getInstancesResp => $_getN(2); + @$pb.TagNumber(3) + set getInstancesResp(GetInstancesResp value) => $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasGetInstancesResp() => $_has(2); + @$pb.TagNumber(3) + void clearGetInstancesResp() => $_clearField(3); + @$pb.TagNumber(3) + GetInstancesResp ensureGetInstancesResp() => $_ensure(2); + + @$pb.TagNumber(4) + SetResp get setResp => $_getN(3); + @$pb.TagNumber(4) + set setResp(SetResp value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasSetResp() => $_has(3); + @$pb.TagNumber(4) + void clearSetResp() => $_clearField(4); + @$pb.TagNumber(4) + SetResp ensureSetResp() => $_ensure(3); + + @$pb.TagNumber(5) + AddResp get addResp => $_getN(4); + @$pb.TagNumber(5) + set addResp(AddResp value) => $_setField(5, value); + @$pb.TagNumber(5) + $core.bool hasAddResp() => $_has(4); + @$pb.TagNumber(5) + void clearAddResp() => $_clearField(5); + @$pb.TagNumber(5) + AddResp ensureAddResp() => $_ensure(4); + + @$pb.TagNumber(6) + DeleteResp get deleteResp => $_getN(5); + @$pb.TagNumber(6) + set deleteResp(DeleteResp value) => $_setField(6, value); + @$pb.TagNumber(6) + $core.bool hasDeleteResp() => $_has(5); + @$pb.TagNumber(6) + void clearDeleteResp() => $_clearField(6); + @$pb.TagNumber(6) + DeleteResp ensureDeleteResp() => $_ensure(5); + + @$pb.TagNumber(7) + OperateResp get operateResp => $_getN(6); + @$pb.TagNumber(7) + set operateResp(OperateResp value) => $_setField(7, value); + @$pb.TagNumber(7) + $core.bool hasOperateResp() => $_has(6); + @$pb.TagNumber(7) + void clearOperateResp() => $_clearField(7); + @$pb.TagNumber(7) + OperateResp ensureOperateResp() => $_ensure(6); + + @$pb.TagNumber(8) + NotifyResp get notifyResp => $_getN(7); + @$pb.TagNumber(8) + set notifyResp(NotifyResp value) => $_setField(8, value); + @$pb.TagNumber(8) + $core.bool hasNotifyResp() => $_has(7); + @$pb.TagNumber(8) + void clearNotifyResp() => $_clearField(8); + @$pb.TagNumber(8) + NotifyResp ensureNotifyResp() => $_ensure(7); + + @$pb.TagNumber(9) + GetSupportedProtocolResp get getSupportedProtocolResp => $_getN(8); + @$pb.TagNumber(9) + set getSupportedProtocolResp(GetSupportedProtocolResp value) => + $_setField(9, value); + @$pb.TagNumber(9) + $core.bool hasGetSupportedProtocolResp() => $_has(8); + @$pb.TagNumber(9) + void clearGetSupportedProtocolResp() => $_clearField(9); + @$pb.TagNumber(9) + GetSupportedProtocolResp ensureGetSupportedProtocolResp() => $_ensure(8); + + @$pb.TagNumber(10) + RegisterResp get registerResp => $_getN(9); + @$pb.TagNumber(10) + set registerResp(RegisterResp value) => $_setField(10, value); + @$pb.TagNumber(10) + $core.bool hasRegisterResp() => $_has(9); + @$pb.TagNumber(10) + void clearRegisterResp() => $_clearField(10); + @$pb.TagNumber(10) + RegisterResp ensureRegisterResp() => $_ensure(9); + + @$pb.TagNumber(11) + DeregisterResp get deregisterResp => $_getN(10); + @$pb.TagNumber(11) + set deregisterResp(DeregisterResp value) => $_setField(11, value); + @$pb.TagNumber(11) + $core.bool hasDeregisterResp() => $_has(10); + @$pb.TagNumber(11) + void clearDeregisterResp() => $_clearField(11); + @$pb.TagNumber(11) + DeregisterResp ensureDeregisterResp() => $_ensure(10); +} + +class Error_ParamError extends $pb.GeneratedMessage { + factory Error_ParamError({ + $core.String? paramPath, + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (paramPath != null) result.paramPath = paramPath; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + Error_ParamError._(); + + factory Error_ParamError.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Error_ParamError.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Error.ParamError', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'paramPath') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Error_ParamError clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Error_ParamError copyWith(void Function(Error_ParamError) updates) => + super.copyWith((message) => updates(message as Error_ParamError)) + as Error_ParamError; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Error_ParamError create() => Error_ParamError._(); + @$core.override + Error_ParamError createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Error_ParamError getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Error_ParamError? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get paramPath => $_getSZ(0); + @$pb.TagNumber(1) + set paramPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParamPath() => $_has(0); + @$pb.TagNumber(1) + void clearParamPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); +} + +class Error extends $pb.GeneratedMessage { + factory Error({ + $core.int? errCode, + $core.String? errMsg, + $core.Iterable? paramErrs, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + if (paramErrs != null) result.paramErrs.addAll(paramErrs); + return result; + } + + Error._(); + + factory Error.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Error.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Error', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..pPM(3, _omitFieldNames ? '' : 'paramErrs', + subBuilder: Error_ParamError.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Error clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Error copyWith(void Function(Error) updates) => + super.copyWith((message) => updates(message as Error)) as Error; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Error create() => Error._(); + @$core.override + Error createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Error getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Error? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); + + @$pb.TagNumber(3) + $pb.PbList get paramErrs => $_getList(2); +} + +class Get extends $pb.GeneratedMessage { + factory Get({ + $core.Iterable<$core.String>? paramPaths, + $core.int? maxDepth, + }) { + final result = create(); + if (paramPaths != null) result.paramPaths.addAll(paramPaths); + if (maxDepth != null) result.maxDepth = maxDepth; + return result; + } + + Get._(); + + factory Get.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Get.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Get', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'paramPaths') + ..aI(2, _omitFieldNames ? '' : 'maxDepth', fieldType: $pb.PbFieldType.OF3) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Get clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Get copyWith(void Function(Get) updates) => + super.copyWith((message) => updates(message as Get)) as Get; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Get create() => Get._(); + @$core.override + Get createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Get getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Get? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get paramPaths => $_getList(0); + + @$pb.TagNumber(2) + $core.int get maxDepth => $_getIZ(1); + @$pb.TagNumber(2) + set maxDepth($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasMaxDepth() => $_has(1); + @$pb.TagNumber(2) + void clearMaxDepth() => $_clearField(2); +} + +class GetResp_RequestedPathResult extends $pb.GeneratedMessage { + factory GetResp_RequestedPathResult({ + $core.String? requestedPath, + $core.int? errCode, + $core.String? errMsg, + $core.Iterable? resolvedPathResults, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + if (resolvedPathResults != null) + result.resolvedPathResults.addAll(resolvedPathResults); + return result; + } + + GetResp_RequestedPathResult._(); + + factory GetResp_RequestedPathResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetResp_RequestedPathResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetResp.RequestedPathResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..pPM( + 4, _omitFieldNames ? '' : 'resolvedPathResults', + subBuilder: GetResp_ResolvedPathResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp_RequestedPathResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp_RequestedPathResult copyWith( + void Function(GetResp_RequestedPathResult) updates) => + super.copyWith( + (message) => updates(message as GetResp_RequestedPathResult)) + as GetResp_RequestedPathResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetResp_RequestedPathResult create() => + GetResp_RequestedPathResult._(); + @$core.override + GetResp_RequestedPathResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetResp_RequestedPathResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetResp_RequestedPathResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); + + @$pb.TagNumber(4) + $pb.PbList get resolvedPathResults => + $_getList(3); +} + +class GetResp_ResolvedPathResult extends $pb.GeneratedMessage { + factory GetResp_ResolvedPathResult({ + $core.String? resolvedPath, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? resultParams, + }) { + final result = create(); + if (resolvedPath != null) result.resolvedPath = resolvedPath; + if (resultParams != null) result.resultParams.addEntries(resultParams); + return result; + } + + GetResp_ResolvedPathResult._(); + + factory GetResp_ResolvedPathResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetResp_ResolvedPathResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetResp.ResolvedPathResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'resolvedPath') + ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'resultParams', + entryClassName: 'GetResp.ResolvedPathResult.ResultParamsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp_ResolvedPathResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp_ResolvedPathResult copyWith( + void Function(GetResp_ResolvedPathResult) updates) => + super.copyWith( + (message) => updates(message as GetResp_ResolvedPathResult)) + as GetResp_ResolvedPathResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetResp_ResolvedPathResult create() => GetResp_ResolvedPathResult._(); + @$core.override + GetResp_ResolvedPathResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetResp_ResolvedPathResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetResp_ResolvedPathResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get resolvedPath => $_getSZ(0); + @$pb.TagNumber(1) + set resolvedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasResolvedPath() => $_has(0); + @$pb.TagNumber(1) + void clearResolvedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbMap<$core.String, $core.String> get resultParams => $_getMap(1); +} + +class GetResp extends $pb.GeneratedMessage { + factory GetResp({ + $core.Iterable? reqPathResults, + }) { + final result = create(); + if (reqPathResults != null) result.reqPathResults.addAll(reqPathResults); + return result; + } + + GetResp._(); + + factory GetResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'reqPathResults', + subBuilder: GetResp_RequestedPathResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetResp copyWith(void Function(GetResp) updates) => + super.copyWith((message) => updates(message as GetResp)) as GetResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetResp create() => GetResp._(); + @$core.override + GetResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetResp getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static GetResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get reqPathResults => $_getList(0); +} + +class GetSupportedDM extends $pb.GeneratedMessage { + factory GetSupportedDM({ + $core.Iterable<$core.String>? objPaths, + $core.bool? firstLevelOnly, + $core.bool? returnCommands, + $core.bool? returnEvents, + $core.bool? returnParams, + $core.bool? returnUniqueKeySets, + }) { + final result = create(); + if (objPaths != null) result.objPaths.addAll(objPaths); + if (firstLevelOnly != null) result.firstLevelOnly = firstLevelOnly; + if (returnCommands != null) result.returnCommands = returnCommands; + if (returnEvents != null) result.returnEvents = returnEvents; + if (returnParams != null) result.returnParams = returnParams; + if (returnUniqueKeySets != null) + result.returnUniqueKeySets = returnUniqueKeySets; + return result; + } + + GetSupportedDM._(); + + factory GetSupportedDM.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDM.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDM', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'objPaths') + ..aOB(2, _omitFieldNames ? '' : 'firstLevelOnly') + ..aOB(3, _omitFieldNames ? '' : 'returnCommands') + ..aOB(4, _omitFieldNames ? '' : 'returnEvents') + ..aOB(5, _omitFieldNames ? '' : 'returnParams') + ..aOB(6, _omitFieldNames ? '' : 'returnUniqueKeySets') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDM clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDM copyWith(void Function(GetSupportedDM) updates) => + super.copyWith((message) => updates(message as GetSupportedDM)) + as GetSupportedDM; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDM create() => GetSupportedDM._(); + @$core.override + GetSupportedDM createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDM getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetSupportedDM? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get objPaths => $_getList(0); + + @$pb.TagNumber(2) + $core.bool get firstLevelOnly => $_getBF(1); + @$pb.TagNumber(2) + set firstLevelOnly($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasFirstLevelOnly() => $_has(1); + @$pb.TagNumber(2) + void clearFirstLevelOnly() => $_clearField(2); + + @$pb.TagNumber(3) + $core.bool get returnCommands => $_getBF(2); + @$pb.TagNumber(3) + set returnCommands($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasReturnCommands() => $_has(2); + @$pb.TagNumber(3) + void clearReturnCommands() => $_clearField(3); + + @$pb.TagNumber(4) + $core.bool get returnEvents => $_getBF(3); + @$pb.TagNumber(4) + set returnEvents($core.bool value) => $_setBool(3, value); + @$pb.TagNumber(4) + $core.bool hasReturnEvents() => $_has(3); + @$pb.TagNumber(4) + void clearReturnEvents() => $_clearField(4); + + @$pb.TagNumber(5) + $core.bool get returnParams => $_getBF(4); + @$pb.TagNumber(5) + set returnParams($core.bool value) => $_setBool(4, value); + @$pb.TagNumber(5) + $core.bool hasReturnParams() => $_has(4); + @$pb.TagNumber(5) + void clearReturnParams() => $_clearField(5); + + @$pb.TagNumber(6) + $core.bool get returnUniqueKeySets => $_getBF(5); + @$pb.TagNumber(6) + set returnUniqueKeySets($core.bool value) => $_setBool(5, value); + @$pb.TagNumber(6) + $core.bool hasReturnUniqueKeySets() => $_has(5); + @$pb.TagNumber(6) + void clearReturnUniqueKeySets() => $_clearField(6); +} + +class GetSupportedDMResp_RequestedObjectResult extends $pb.GeneratedMessage { + factory GetSupportedDMResp_RequestedObjectResult({ + $core.String? reqObjPath, + $core.int? errCode, + $core.String? errMsg, + $core.String? dataModelInstUri, + $core.Iterable? supportedObjs, + }) { + final result = create(); + if (reqObjPath != null) result.reqObjPath = reqObjPath; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + if (dataModelInstUri != null) result.dataModelInstUri = dataModelInstUri; + if (supportedObjs != null) result.supportedObjs.addAll(supportedObjs); + return result; + } + + GetSupportedDMResp_RequestedObjectResult._(); + + factory GetSupportedDMResp_RequestedObjectResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_RequestedObjectResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.RequestedObjectResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'reqObjPath') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..aOS(4, _omitFieldNames ? '' : 'dataModelInstUri') + ..pPM( + 5, _omitFieldNames ? '' : 'supportedObjs', + subBuilder: GetSupportedDMResp_SupportedObjectResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_RequestedObjectResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_RequestedObjectResult copyWith( + void Function(GetSupportedDMResp_RequestedObjectResult) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_RequestedObjectResult)) + as GetSupportedDMResp_RequestedObjectResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_RequestedObjectResult create() => + GetSupportedDMResp_RequestedObjectResult._(); + @$core.override + GetSupportedDMResp_RequestedObjectResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_RequestedObjectResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_RequestedObjectResult>(create); + static GetSupportedDMResp_RequestedObjectResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get reqObjPath => $_getSZ(0); + @$pb.TagNumber(1) + set reqObjPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasReqObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearReqObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); + + @$pb.TagNumber(4) + $core.String get dataModelInstUri => $_getSZ(3); + @$pb.TagNumber(4) + set dataModelInstUri($core.String value) => $_setString(3, value); + @$pb.TagNumber(4) + $core.bool hasDataModelInstUri() => $_has(3); + @$pb.TagNumber(4) + void clearDataModelInstUri() => $_clearField(4); + + @$pb.TagNumber(5) + $pb.PbList get supportedObjs => + $_getList(4); +} + +class GetSupportedDMResp_SupportedObjectResult extends $pb.GeneratedMessage { + factory GetSupportedDMResp_SupportedObjectResult({ + $core.String? supportedObjPath, + GetSupportedDMResp_ObjAccessType? access, + $core.bool? isMultiInstance, + $core.Iterable? + supportedCommands, + $core.Iterable? supportedEvents, + $core.Iterable? supportedParams, + $core.Iterable<$core.String>? divergentPaths, + $core.Iterable? uniqueKeySets, + }) { + final result = create(); + if (supportedObjPath != null) result.supportedObjPath = supportedObjPath; + if (access != null) result.access = access; + if (isMultiInstance != null) result.isMultiInstance = isMultiInstance; + if (supportedCommands != null) + result.supportedCommands.addAll(supportedCommands); + if (supportedEvents != null) result.supportedEvents.addAll(supportedEvents); + if (supportedParams != null) result.supportedParams.addAll(supportedParams); + if (divergentPaths != null) result.divergentPaths.addAll(divergentPaths); + if (uniqueKeySets != null) result.uniqueKeySets.addAll(uniqueKeySets); + return result; + } + + GetSupportedDMResp_SupportedObjectResult._(); + + factory GetSupportedDMResp_SupportedObjectResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_SupportedObjectResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.SupportedObjectResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'supportedObjPath') + ..aE(2, _omitFieldNames ? '' : 'access', + enumValues: GetSupportedDMResp_ObjAccessType.values) + ..aOB(3, _omitFieldNames ? '' : 'isMultiInstance') + ..pPM( + 4, _omitFieldNames ? '' : 'supportedCommands', + subBuilder: GetSupportedDMResp_SupportedCommandResult.create) + ..pPM( + 5, _omitFieldNames ? '' : 'supportedEvents', + subBuilder: GetSupportedDMResp_SupportedEventResult.create) + ..pPM( + 6, _omitFieldNames ? '' : 'supportedParams', + subBuilder: GetSupportedDMResp_SupportedParamResult.create) + ..pPS(7, _omitFieldNames ? '' : 'divergentPaths') + ..pPM( + 8, _omitFieldNames ? '' : 'uniqueKeySets', + subBuilder: GetSupportedDMResp_SupportedUniqueKeySet.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedObjectResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedObjectResult copyWith( + void Function(GetSupportedDMResp_SupportedObjectResult) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_SupportedObjectResult)) + as GetSupportedDMResp_SupportedObjectResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedObjectResult create() => + GetSupportedDMResp_SupportedObjectResult._(); + @$core.override + GetSupportedDMResp_SupportedObjectResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedObjectResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_SupportedObjectResult>(create); + static GetSupportedDMResp_SupportedObjectResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get supportedObjPath => $_getSZ(0); + @$pb.TagNumber(1) + set supportedObjPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasSupportedObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearSupportedObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + GetSupportedDMResp_ObjAccessType get access => $_getN(1); + @$pb.TagNumber(2) + set access(GetSupportedDMResp_ObjAccessType value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasAccess() => $_has(1); + @$pb.TagNumber(2) + void clearAccess() => $_clearField(2); + + @$pb.TagNumber(3) + $core.bool get isMultiInstance => $_getBF(2); + @$pb.TagNumber(3) + set isMultiInstance($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasIsMultiInstance() => $_has(2); + @$pb.TagNumber(3) + void clearIsMultiInstance() => $_clearField(3); + + @$pb.TagNumber(4) + $pb.PbList get supportedCommands => + $_getList(3); + + @$pb.TagNumber(5) + $pb.PbList get supportedEvents => + $_getList(4); + + @$pb.TagNumber(6) + $pb.PbList get supportedParams => + $_getList(5); + + @$pb.TagNumber(7) + $pb.PbList<$core.String> get divergentPaths => $_getList(6); + + @$pb.TagNumber(8) + $pb.PbList get uniqueKeySets => + $_getList(7); +} + +class GetSupportedDMResp_SupportedParamResult extends $pb.GeneratedMessage { + factory GetSupportedDMResp_SupportedParamResult({ + $core.String? paramName, + GetSupportedDMResp_ParamAccessType? access, + GetSupportedDMResp_ParamValueType? valueType, + GetSupportedDMResp_ValueChangeType? valueChange, + }) { + final result = create(); + if (paramName != null) result.paramName = paramName; + if (access != null) result.access = access; + if (valueType != null) result.valueType = valueType; + if (valueChange != null) result.valueChange = valueChange; + return result; + } + + GetSupportedDMResp_SupportedParamResult._(); + + factory GetSupportedDMResp_SupportedParamResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_SupportedParamResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.SupportedParamResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'paramName') + ..aE(2, _omitFieldNames ? '' : 'access', + enumValues: GetSupportedDMResp_ParamAccessType.values) + ..aE( + 3, _omitFieldNames ? '' : 'valueType', + enumValues: GetSupportedDMResp_ParamValueType.values) + ..aE( + 4, _omitFieldNames ? '' : 'valueChange', + enumValues: GetSupportedDMResp_ValueChangeType.values) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedParamResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedParamResult copyWith( + void Function(GetSupportedDMResp_SupportedParamResult) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_SupportedParamResult)) + as GetSupportedDMResp_SupportedParamResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedParamResult create() => + GetSupportedDMResp_SupportedParamResult._(); + @$core.override + GetSupportedDMResp_SupportedParamResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedParamResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_SupportedParamResult>(create); + static GetSupportedDMResp_SupportedParamResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get paramName => $_getSZ(0); + @$pb.TagNumber(1) + set paramName($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParamName() => $_has(0); + @$pb.TagNumber(1) + void clearParamName() => $_clearField(1); + + @$pb.TagNumber(2) + GetSupportedDMResp_ParamAccessType get access => $_getN(1); + @$pb.TagNumber(2) + set access(GetSupportedDMResp_ParamAccessType value) => $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasAccess() => $_has(1); + @$pb.TagNumber(2) + void clearAccess() => $_clearField(2); + + @$pb.TagNumber(3) + GetSupportedDMResp_ParamValueType get valueType => $_getN(2); + @$pb.TagNumber(3) + set valueType(GetSupportedDMResp_ParamValueType value) => + $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasValueType() => $_has(2); + @$pb.TagNumber(3) + void clearValueType() => $_clearField(3); + + @$pb.TagNumber(4) + GetSupportedDMResp_ValueChangeType get valueChange => $_getN(3); + @$pb.TagNumber(4) + set valueChange(GetSupportedDMResp_ValueChangeType value) => + $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasValueChange() => $_has(3); + @$pb.TagNumber(4) + void clearValueChange() => $_clearField(4); +} + +class GetSupportedDMResp_SupportedCommandResult extends $pb.GeneratedMessage { + factory GetSupportedDMResp_SupportedCommandResult({ + $core.String? commandName, + $core.Iterable<$core.String>? inputArgNames, + $core.Iterable<$core.String>? outputArgNames, + GetSupportedDMResp_CmdType? commandType, + }) { + final result = create(); + if (commandName != null) result.commandName = commandName; + if (inputArgNames != null) result.inputArgNames.addAll(inputArgNames); + if (outputArgNames != null) result.outputArgNames.addAll(outputArgNames); + if (commandType != null) result.commandType = commandType; + return result; + } + + GetSupportedDMResp_SupportedCommandResult._(); + + factory GetSupportedDMResp_SupportedCommandResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_SupportedCommandResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.SupportedCommandResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'commandName') + ..pPS(2, _omitFieldNames ? '' : 'inputArgNames') + ..pPS(3, _omitFieldNames ? '' : 'outputArgNames') + ..aE(4, _omitFieldNames ? '' : 'commandType', + enumValues: GetSupportedDMResp_CmdType.values) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedCommandResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedCommandResult copyWith( + void Function(GetSupportedDMResp_SupportedCommandResult) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_SupportedCommandResult)) + as GetSupportedDMResp_SupportedCommandResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedCommandResult create() => + GetSupportedDMResp_SupportedCommandResult._(); + @$core.override + GetSupportedDMResp_SupportedCommandResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedCommandResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_SupportedCommandResult>(create); + static GetSupportedDMResp_SupportedCommandResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get commandName => $_getSZ(0); + @$pb.TagNumber(1) + set commandName($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasCommandName() => $_has(0); + @$pb.TagNumber(1) + void clearCommandName() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList<$core.String> get inputArgNames => $_getList(1); + + @$pb.TagNumber(3) + $pb.PbList<$core.String> get outputArgNames => $_getList(2); + + @$pb.TagNumber(4) + GetSupportedDMResp_CmdType get commandType => $_getN(3); + @$pb.TagNumber(4) + set commandType(GetSupportedDMResp_CmdType value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasCommandType() => $_has(3); + @$pb.TagNumber(4) + void clearCommandType() => $_clearField(4); +} + +class GetSupportedDMResp_SupportedEventResult extends $pb.GeneratedMessage { + factory GetSupportedDMResp_SupportedEventResult({ + $core.String? eventName, + $core.Iterable<$core.String>? argNames, + }) { + final result = create(); + if (eventName != null) result.eventName = eventName; + if (argNames != null) result.argNames.addAll(argNames); + return result; + } + + GetSupportedDMResp_SupportedEventResult._(); + + factory GetSupportedDMResp_SupportedEventResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_SupportedEventResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.SupportedEventResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'eventName') + ..pPS(2, _omitFieldNames ? '' : 'argNames') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedEventResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedEventResult copyWith( + void Function(GetSupportedDMResp_SupportedEventResult) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_SupportedEventResult)) + as GetSupportedDMResp_SupportedEventResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedEventResult create() => + GetSupportedDMResp_SupportedEventResult._(); + @$core.override + GetSupportedDMResp_SupportedEventResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedEventResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_SupportedEventResult>(create); + static GetSupportedDMResp_SupportedEventResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get eventName => $_getSZ(0); + @$pb.TagNumber(1) + set eventName($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasEventName() => $_has(0); + @$pb.TagNumber(1) + void clearEventName() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList<$core.String> get argNames => $_getList(1); +} + +class GetSupportedDMResp_SupportedUniqueKeySet extends $pb.GeneratedMessage { + factory GetSupportedDMResp_SupportedUniqueKeySet({ + $core.Iterable<$core.String>? keyNames, + }) { + final result = create(); + if (keyNames != null) result.keyNames.addAll(keyNames); + return result; + } + + GetSupportedDMResp_SupportedUniqueKeySet._(); + + factory GetSupportedDMResp_SupportedUniqueKeySet.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp_SupportedUniqueKeySet.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp.SupportedUniqueKeySet', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'keyNames') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedUniqueKeySet clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp_SupportedUniqueKeySet copyWith( + void Function(GetSupportedDMResp_SupportedUniqueKeySet) updates) => + super.copyWith((message) => + updates(message as GetSupportedDMResp_SupportedUniqueKeySet)) + as GetSupportedDMResp_SupportedUniqueKeySet; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedUniqueKeySet create() => + GetSupportedDMResp_SupportedUniqueKeySet._(); + @$core.override + GetSupportedDMResp_SupportedUniqueKeySet createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp_SupportedUniqueKeySet getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetSupportedDMResp_SupportedUniqueKeySet>(create); + static GetSupportedDMResp_SupportedUniqueKeySet? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get keyNames => $_getList(0); +} + +class GetSupportedDMResp extends $pb.GeneratedMessage { + factory GetSupportedDMResp({ + $core.Iterable? reqObjResults, + }) { + final result = create(); + if (reqObjResults != null) result.reqObjResults.addAll(reqObjResults); + return result; + } + + GetSupportedDMResp._(); + + factory GetSupportedDMResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedDMResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedDMResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'reqObjResults', + subBuilder: GetSupportedDMResp_RequestedObjectResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedDMResp copyWith(void Function(GetSupportedDMResp) updates) => + super.copyWith((message) => updates(message as GetSupportedDMResp)) + as GetSupportedDMResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp create() => GetSupportedDMResp._(); + @$core.override + GetSupportedDMResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedDMResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetSupportedDMResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get reqObjResults => + $_getList(0); +} + +class GetInstances extends $pb.GeneratedMessage { + factory GetInstances({ + $core.Iterable<$core.String>? objPaths, + $core.bool? firstLevelOnly, + }) { + final result = create(); + if (objPaths != null) result.objPaths.addAll(objPaths); + if (firstLevelOnly != null) result.firstLevelOnly = firstLevelOnly; + return result; + } + + GetInstances._(); + + factory GetInstances.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetInstances.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetInstances', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'objPaths') + ..aOB(2, _omitFieldNames ? '' : 'firstLevelOnly') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstances clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstances copyWith(void Function(GetInstances) updates) => + super.copyWith((message) => updates(message as GetInstances)) + as GetInstances; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetInstances create() => GetInstances._(); + @$core.override + GetInstances createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetInstances getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetInstances? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get objPaths => $_getList(0); + + @$pb.TagNumber(2) + $core.bool get firstLevelOnly => $_getBF(1); + @$pb.TagNumber(2) + set firstLevelOnly($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasFirstLevelOnly() => $_has(1); + @$pb.TagNumber(2) + void clearFirstLevelOnly() => $_clearField(2); +} + +class GetInstancesResp_RequestedPathResult extends $pb.GeneratedMessage { + factory GetInstancesResp_RequestedPathResult({ + $core.String? requestedPath, + $core.int? errCode, + $core.String? errMsg, + $core.Iterable? currInsts, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + if (currInsts != null) result.currInsts.addAll(currInsts); + return result; + } + + GetInstancesResp_RequestedPathResult._(); + + factory GetInstancesResp_RequestedPathResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetInstancesResp_RequestedPathResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetInstancesResp.RequestedPathResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..pPM(4, _omitFieldNames ? '' : 'currInsts', + subBuilder: GetInstancesResp_CurrInstance.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp_RequestedPathResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp_RequestedPathResult copyWith( + void Function(GetInstancesResp_RequestedPathResult) updates) => + super.copyWith((message) => + updates(message as GetInstancesResp_RequestedPathResult)) + as GetInstancesResp_RequestedPathResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetInstancesResp_RequestedPathResult create() => + GetInstancesResp_RequestedPathResult._(); + @$core.override + GetInstancesResp_RequestedPathResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetInstancesResp_RequestedPathResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + GetInstancesResp_RequestedPathResult>(create); + static GetInstancesResp_RequestedPathResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); + + @$pb.TagNumber(4) + $pb.PbList get currInsts => $_getList(3); +} + +class GetInstancesResp_CurrInstance extends $pb.GeneratedMessage { + factory GetInstancesResp_CurrInstance({ + $core.String? instantiatedObjPath, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? uniqueKeys, + }) { + final result = create(); + if (instantiatedObjPath != null) + result.instantiatedObjPath = instantiatedObjPath; + if (uniqueKeys != null) result.uniqueKeys.addEntries(uniqueKeys); + return result; + } + + GetInstancesResp_CurrInstance._(); + + factory GetInstancesResp_CurrInstance.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetInstancesResp_CurrInstance.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetInstancesResp.CurrInstance', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'instantiatedObjPath') + ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'uniqueKeys', + entryClassName: 'GetInstancesResp.CurrInstance.UniqueKeysEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp_CurrInstance clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp_CurrInstance copyWith( + void Function(GetInstancesResp_CurrInstance) updates) => + super.copyWith( + (message) => updates(message as GetInstancesResp_CurrInstance)) + as GetInstancesResp_CurrInstance; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetInstancesResp_CurrInstance create() => + GetInstancesResp_CurrInstance._(); + @$core.override + GetInstancesResp_CurrInstance createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetInstancesResp_CurrInstance getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetInstancesResp_CurrInstance? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get instantiatedObjPath => $_getSZ(0); + @$pb.TagNumber(1) + set instantiatedObjPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasInstantiatedObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearInstantiatedObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbMap<$core.String, $core.String> get uniqueKeys => $_getMap(1); +} + +class GetInstancesResp extends $pb.GeneratedMessage { + factory GetInstancesResp({ + $core.Iterable? reqPathResults, + }) { + final result = create(); + if (reqPathResults != null) result.reqPathResults.addAll(reqPathResults); + return result; + } + + GetInstancesResp._(); + + factory GetInstancesResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetInstancesResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetInstancesResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'reqPathResults', + subBuilder: GetInstancesResp_RequestedPathResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetInstancesResp copyWith(void Function(GetInstancesResp) updates) => + super.copyWith((message) => updates(message as GetInstancesResp)) + as GetInstancesResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetInstancesResp create() => GetInstancesResp._(); + @$core.override + GetInstancesResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetInstancesResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetInstancesResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get reqPathResults => + $_getList(0); +} + +class GetSupportedProtocol extends $pb.GeneratedMessage { + factory GetSupportedProtocol({ + $core.String? controllerSupportedProtocolVersions, + }) { + final result = create(); + if (controllerSupportedProtocolVersions != null) + result.controllerSupportedProtocolVersions = + controllerSupportedProtocolVersions; + return result; + } + + GetSupportedProtocol._(); + + factory GetSupportedProtocol.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedProtocol.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedProtocol', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'controllerSupportedProtocolVersions') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedProtocol clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedProtocol copyWith(void Function(GetSupportedProtocol) updates) => + super.copyWith((message) => updates(message as GetSupportedProtocol)) + as GetSupportedProtocol; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedProtocol create() => GetSupportedProtocol._(); + @$core.override + GetSupportedProtocol createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedProtocol getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetSupportedProtocol? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get controllerSupportedProtocolVersions => $_getSZ(0); + @$pb.TagNumber(1) + set controllerSupportedProtocolVersions($core.String value) => + $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasControllerSupportedProtocolVersions() => $_has(0); + @$pb.TagNumber(1) + void clearControllerSupportedProtocolVersions() => $_clearField(1); +} + +class GetSupportedProtocolResp extends $pb.GeneratedMessage { + factory GetSupportedProtocolResp({ + $core.String? agentSupportedProtocolVersions, + }) { + final result = create(); + if (agentSupportedProtocolVersions != null) + result.agentSupportedProtocolVersions = agentSupportedProtocolVersions; + return result; + } + + GetSupportedProtocolResp._(); + + factory GetSupportedProtocolResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory GetSupportedProtocolResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'GetSupportedProtocolResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'agentSupportedProtocolVersions') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedProtocolResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + GetSupportedProtocolResp copyWith( + void Function(GetSupportedProtocolResp) updates) => + super.copyWith((message) => updates(message as GetSupportedProtocolResp)) + as GetSupportedProtocolResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static GetSupportedProtocolResp create() => GetSupportedProtocolResp._(); + @$core.override + GetSupportedProtocolResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static GetSupportedProtocolResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static GetSupportedProtocolResp? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get agentSupportedProtocolVersions => $_getSZ(0); + @$pb.TagNumber(1) + set agentSupportedProtocolVersions($core.String value) => + $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasAgentSupportedProtocolVersions() => $_has(0); + @$pb.TagNumber(1) + void clearAgentSupportedProtocolVersions() => $_clearField(1); +} + +class Add_CreateObject extends $pb.GeneratedMessage { + factory Add_CreateObject({ + $core.String? objPath, + $core.Iterable? paramSettings, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + if (paramSettings != null) result.paramSettings.addAll(paramSettings); + return result; + } + + Add_CreateObject._(); + + factory Add_CreateObject.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Add_CreateObject.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Add.CreateObject', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..pPM(2, _omitFieldNames ? '' : 'paramSettings', + subBuilder: Add_CreateParamSetting.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add_CreateObject clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add_CreateObject copyWith(void Function(Add_CreateObject) updates) => + super.copyWith((message) => updates(message as Add_CreateObject)) + as Add_CreateObject; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Add_CreateObject create() => Add_CreateObject._(); + @$core.override + Add_CreateObject createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Add_CreateObject getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Add_CreateObject? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get paramSettings => $_getList(1); +} + +class Add_CreateParamSetting extends $pb.GeneratedMessage { + factory Add_CreateParamSetting({ + $core.String? param, + $core.String? value, + $core.bool? required, + }) { + final result = create(); + if (param != null) result.param = param; + if (value != null) result.value = value; + if (required != null) result.required = required; + return result; + } + + Add_CreateParamSetting._(); + + factory Add_CreateParamSetting.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Add_CreateParamSetting.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Add.CreateParamSetting', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'param') + ..aOS(2, _omitFieldNames ? '' : 'value') + ..aOB(3, _omitFieldNames ? '' : 'required') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add_CreateParamSetting clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add_CreateParamSetting copyWith( + void Function(Add_CreateParamSetting) updates) => + super.copyWith((message) => updates(message as Add_CreateParamSetting)) + as Add_CreateParamSetting; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Add_CreateParamSetting create() => Add_CreateParamSetting._(); + @$core.override + Add_CreateParamSetting createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Add_CreateParamSetting getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Add_CreateParamSetting? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get param => $_getSZ(0); + @$pb.TagNumber(1) + set param($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParam() => $_has(0); + @$pb.TagNumber(1) + void clearParam() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get value => $_getSZ(1); + @$pb.TagNumber(2) + set value($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasValue() => $_has(1); + @$pb.TagNumber(2) + void clearValue() => $_clearField(2); + + @$pb.TagNumber(3) + $core.bool get required => $_getBF(2); + @$pb.TagNumber(3) + set required($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasRequired() => $_has(2); + @$pb.TagNumber(3) + void clearRequired() => $_clearField(3); +} + +class Add extends $pb.GeneratedMessage { + factory Add({ + $core.bool? allowPartial, + $core.Iterable? createObjs, + }) { + final result = create(); + if (allowPartial != null) result.allowPartial = allowPartial; + if (createObjs != null) result.createObjs.addAll(createObjs); + return result; + } + + Add._(); + + factory Add.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Add.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Add', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'allowPartial') + ..pPM(2, _omitFieldNames ? '' : 'createObjs', + subBuilder: Add_CreateObject.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Add copyWith(void Function(Add) updates) => + super.copyWith((message) => updates(message as Add)) as Add; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Add create() => Add._(); + @$core.override + Add createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Add getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Add? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get allowPartial => $_getBF(0); + @$pb.TagNumber(1) + set allowPartial($core.bool value) => $_setBool(0, value); + @$pb.TagNumber(1) + $core.bool hasAllowPartial() => $_has(0); + @$pb.TagNumber(1) + void clearAllowPartial() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get createObjs => $_getList(1); +} + +class AddResp_CreatedObjectResult_OperationStatus_OperationFailure + extends $pb.GeneratedMessage { + factory AddResp_CreatedObjectResult_OperationStatus_OperationFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + AddResp_CreatedObjectResult_OperationStatus_OperationFailure._(); + + factory AddResp_CreatedObjectResult_OperationStatus_OperationFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp_CreatedObjectResult_OperationStatus_OperationFailure.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'AddResp.CreatedObjectResult.OperationStatus.OperationFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus_OperationFailure clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus_OperationFailure copyWith( + void Function( + AddResp_CreatedObjectResult_OperationStatus_OperationFailure) + updates) => + super.copyWith((message) => updates(message + as AddResp_CreatedObjectResult_OperationStatus_OperationFailure)) + as AddResp_CreatedObjectResult_OperationStatus_OperationFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus_OperationFailure + create() => + AddResp_CreatedObjectResult_OperationStatus_OperationFailure._(); + @$core.override + AddResp_CreatedObjectResult_OperationStatus_OperationFailure + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus_OperationFailure + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + AddResp_CreatedObjectResult_OperationStatus_OperationFailure>(create); + static AddResp_CreatedObjectResult_OperationStatus_OperationFailure? + _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +class AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + extends $pb.GeneratedMessage { + factory AddResp_CreatedObjectResult_OperationStatus_OperationSuccess({ + $core.String? instantiatedPath, + $core.Iterable? paramErrs, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? uniqueKeys, + }) { + final result = create(); + if (instantiatedPath != null) result.instantiatedPath = instantiatedPath; + if (paramErrs != null) result.paramErrs.addAll(paramErrs); + if (uniqueKeys != null) result.uniqueKeys.addEntries(uniqueKeys); + return result; + } + + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess._(); + + factory AddResp_CreatedObjectResult_OperationStatus_OperationSuccess.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp_CreatedObjectResult_OperationStatus_OperationSuccess.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'AddResp.CreatedObjectResult.OperationStatus.OperationSuccess', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'instantiatedPath') + ..pPM(2, _omitFieldNames ? '' : 'paramErrs', + subBuilder: AddResp_ParameterError.create) + ..m<$core.String, $core.String>(3, _omitFieldNames ? '' : 'uniqueKeys', + entryClassName: + 'AddResp.CreatedObjectResult.OperationStatus.OperationSuccess.UniqueKeysEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess copyWith( + void Function( + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess) + updates) => + super.copyWith((message) => updates(message + as AddResp_CreatedObjectResult_OperationStatus_OperationSuccess)) + as AddResp_CreatedObjectResult_OperationStatus_OperationSuccess; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + create() => + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess._(); + @$core.override + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess>(create); + static AddResp_CreatedObjectResult_OperationStatus_OperationSuccess? + _defaultInstance; + + @$pb.TagNumber(1) + $core.String get instantiatedPath => $_getSZ(0); + @$pb.TagNumber(1) + set instantiatedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasInstantiatedPath() => $_has(0); + @$pb.TagNumber(1) + void clearInstantiatedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get paramErrs => $_getList(1); + + @$pb.TagNumber(3) + $pb.PbMap<$core.String, $core.String> get uniqueKeys => $_getMap(2); +} + +enum AddResp_CreatedObjectResult_OperationStatus_OperStatus { + operFailure, + operSuccess, + notSet +} + +class AddResp_CreatedObjectResult_OperationStatus extends $pb.GeneratedMessage { + factory AddResp_CreatedObjectResult_OperationStatus({ + AddResp_CreatedObjectResult_OperationStatus_OperationFailure? operFailure, + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess? operSuccess, + }) { + final result = create(); + if (operFailure != null) result.operFailure = operFailure; + if (operSuccess != null) result.operSuccess = operSuccess; + return result; + } + + AddResp_CreatedObjectResult_OperationStatus._(); + + factory AddResp_CreatedObjectResult_OperationStatus.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp_CreatedObjectResult_OperationStatus.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core + .Map<$core.int, AddResp_CreatedObjectResult_OperationStatus_OperStatus> + _AddResp_CreatedObjectResult_OperationStatus_OperStatusByTag = { + 1: AddResp_CreatedObjectResult_OperationStatus_OperStatus.operFailure, + 2: AddResp_CreatedObjectResult_OperationStatus_OperStatus.operSuccess, + 0: AddResp_CreatedObjectResult_OperationStatus_OperStatus.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AddResp.CreatedObjectResult.OperationStatus', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2]) + ..aOM( + 1, _omitFieldNames ? '' : 'operFailure', + subBuilder: + AddResp_CreatedObjectResult_OperationStatus_OperationFailure.create) + ..aOM( + 2, _omitFieldNames ? '' : 'operSuccess', + subBuilder: + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult_OperationStatus copyWith( + void Function(AddResp_CreatedObjectResult_OperationStatus) updates) => + super.copyWith((message) => + updates(message as AddResp_CreatedObjectResult_OperationStatus)) + as AddResp_CreatedObjectResult_OperationStatus; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus create() => + AddResp_CreatedObjectResult_OperationStatus._(); + @$core.override + AddResp_CreatedObjectResult_OperationStatus createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult_OperationStatus getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + AddResp_CreatedObjectResult_OperationStatus>(create); + static AddResp_CreatedObjectResult_OperationStatus? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + AddResp_CreatedObjectResult_OperationStatus_OperStatus whichOperStatus() => + _AddResp_CreatedObjectResult_OperationStatus_OperStatusByTag[ + $_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + AddResp_CreatedObjectResult_OperationStatus_OperationFailure + get operFailure => $_getN(0); + @$pb.TagNumber(1) + set operFailure( + AddResp_CreatedObjectResult_OperationStatus_OperationFailure value) => + $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasOperFailure() => $_has(0); + @$pb.TagNumber(1) + void clearOperFailure() => $_clearField(1); + @$pb.TagNumber(1) + AddResp_CreatedObjectResult_OperationStatus_OperationFailure + ensureOperFailure() => $_ensure(0); + + @$pb.TagNumber(2) + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + get operSuccess => $_getN(1); + @$pb.TagNumber(2) + set operSuccess( + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperSuccess() => $_has(1); + @$pb.TagNumber(2) + void clearOperSuccess() => $_clearField(2); + @$pb.TagNumber(2) + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess + ensureOperSuccess() => $_ensure(1); +} + +class AddResp_CreatedObjectResult extends $pb.GeneratedMessage { + factory AddResp_CreatedObjectResult({ + $core.String? requestedPath, + AddResp_CreatedObjectResult_OperationStatus? operStatus, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (operStatus != null) result.operStatus = operStatus; + return result; + } + + AddResp_CreatedObjectResult._(); + + factory AddResp_CreatedObjectResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp_CreatedObjectResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AddResp.CreatedObjectResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aOM( + 2, _omitFieldNames ? '' : 'operStatus', + subBuilder: AddResp_CreatedObjectResult_OperationStatus.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_CreatedObjectResult copyWith( + void Function(AddResp_CreatedObjectResult) updates) => + super.copyWith( + (message) => updates(message as AddResp_CreatedObjectResult)) + as AddResp_CreatedObjectResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult create() => + AddResp_CreatedObjectResult._(); + @$core.override + AddResp_CreatedObjectResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp_CreatedObjectResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static AddResp_CreatedObjectResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + AddResp_CreatedObjectResult_OperationStatus get operStatus => $_getN(1); + @$pb.TagNumber(2) + set operStatus(AddResp_CreatedObjectResult_OperationStatus value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperStatus() => $_has(1); + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField(2); + @$pb.TagNumber(2) + AddResp_CreatedObjectResult_OperationStatus ensureOperStatus() => $_ensure(1); +} + +class AddResp_ParameterError extends $pb.GeneratedMessage { + factory AddResp_ParameterError({ + $core.String? param, + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (param != null) result.param = param; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + AddResp_ParameterError._(); + + factory AddResp_ParameterError.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp_ParameterError.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AddResp.ParameterError', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'param') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_ParameterError clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp_ParameterError copyWith( + void Function(AddResp_ParameterError) updates) => + super.copyWith((message) => updates(message as AddResp_ParameterError)) + as AddResp_ParameterError; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp_ParameterError create() => AddResp_ParameterError._(); + @$core.override + AddResp_ParameterError createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp_ParameterError getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static AddResp_ParameterError? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get param => $_getSZ(0); + @$pb.TagNumber(1) + set param($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParam() => $_has(0); + @$pb.TagNumber(1) + void clearParam() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); +} + +class AddResp extends $pb.GeneratedMessage { + factory AddResp({ + $core.Iterable? createdObjResults, + }) { + final result = create(); + if (createdObjResults != null) + result.createdObjResults.addAll(createdObjResults); + return result; + } + + AddResp._(); + + factory AddResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory AddResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'AddResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'createdObjResults', + subBuilder: AddResp_CreatedObjectResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + AddResp copyWith(void Function(AddResp) updates) => + super.copyWith((message) => updates(message as AddResp)) as AddResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static AddResp create() => AddResp._(); + @$core.override + AddResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static AddResp getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static AddResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get createdObjResults => $_getList(0); +} + +class Delete extends $pb.GeneratedMessage { + factory Delete({ + $core.bool? allowPartial, + $core.Iterable<$core.String>? objPaths, + }) { + final result = create(); + if (allowPartial != null) result.allowPartial = allowPartial; + if (objPaths != null) result.objPaths.addAll(objPaths); + return result; + } + + Delete._(); + + factory Delete.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Delete.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Delete', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'allowPartial') + ..pPS(2, _omitFieldNames ? '' : 'objPaths') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Delete clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Delete copyWith(void Function(Delete) updates) => + super.copyWith((message) => updates(message as Delete)) as Delete; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Delete create() => Delete._(); + @$core.override + Delete createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Delete getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Delete? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get allowPartial => $_getBF(0); + @$pb.TagNumber(1) + set allowPartial($core.bool value) => $_setBool(0, value); + @$pb.TagNumber(1) + $core.bool hasAllowPartial() => $_has(0); + @$pb.TagNumber(1) + void clearAllowPartial() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList<$core.String> get objPaths => $_getList(1); +} + +class DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + extends $pb.GeneratedMessage { + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure._(); + + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'DeleteResp.DeletedObjectResult.OperationStatus.OperationFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure copyWith( + void Function( + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure) + updates) => + super.copyWith((message) => updates(message + as DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure)) + as DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + create() => + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure._(); + @$core.override + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure>( + create); + static DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure? + _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +class DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + extends $pb.GeneratedMessage { + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess({ + $core.Iterable<$core.String>? affectedPaths, + $core.Iterable? unaffectedPathErrs, + }) { + final result = create(); + if (affectedPaths != null) result.affectedPaths.addAll(affectedPaths); + if (unaffectedPathErrs != null) + result.unaffectedPathErrs.addAll(unaffectedPathErrs); + return result; + } + + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess._(); + + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'DeleteResp.DeletedObjectResult.OperationStatus.OperationSuccess', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'affectedPaths') + ..pPM( + 2, _omitFieldNames ? '' : 'unaffectedPathErrs', + subBuilder: DeleteResp_UnaffectedPathError.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess copyWith( + void Function( + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess) + updates) => + super.copyWith((message) => updates(message + as DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess)) + as DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + create() => + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess._(); + @$core.override + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess>( + create); + static DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess? + _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get affectedPaths => $_getList(0); + + @$pb.TagNumber(2) + $pb.PbList get unaffectedPathErrs => + $_getList(1); +} + +enum DeleteResp_DeletedObjectResult_OperationStatus_OperStatus { + operFailure, + operSuccess, + notSet +} + +class DeleteResp_DeletedObjectResult_OperationStatus + extends $pb.GeneratedMessage { + factory DeleteResp_DeletedObjectResult_OperationStatus({ + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure? + operFailure, + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess? + operSuccess, + }) { + final result = create(); + if (operFailure != null) result.operFailure = operFailure; + if (operSuccess != null) result.operSuccess = operSuccess; + return result; + } + + DeleteResp_DeletedObjectResult_OperationStatus._(); + + factory DeleteResp_DeletedObjectResult_OperationStatus.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp_DeletedObjectResult_OperationStatus.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core + .Map<$core.int, DeleteResp_DeletedObjectResult_OperationStatus_OperStatus> + _DeleteResp_DeletedObjectResult_OperationStatus_OperStatusByTag = { + 1: DeleteResp_DeletedObjectResult_OperationStatus_OperStatus.operFailure, + 2: DeleteResp_DeletedObjectResult_OperationStatus_OperStatus.operSuccess, + 0: DeleteResp_DeletedObjectResult_OperationStatus_OperStatus.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeleteResp.DeletedObjectResult.OperationStatus', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2]) + ..aOM( + 1, _omitFieldNames ? '' : 'operFailure', + subBuilder: + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + .create) + ..aOM( + 2, _omitFieldNames ? '' : 'operSuccess', + subBuilder: + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + .create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult_OperationStatus copyWith( + void Function(DeleteResp_DeletedObjectResult_OperationStatus) + updates) => + super.copyWith((message) => updates( + message as DeleteResp_DeletedObjectResult_OperationStatus)) + as DeleteResp_DeletedObjectResult_OperationStatus; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus create() => + DeleteResp_DeletedObjectResult_OperationStatus._(); + @$core.override + DeleteResp_DeletedObjectResult_OperationStatus createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult_OperationStatus getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeleteResp_DeletedObjectResult_OperationStatus>(create); + static DeleteResp_DeletedObjectResult_OperationStatus? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + DeleteResp_DeletedObjectResult_OperationStatus_OperStatus whichOperStatus() => + _DeleteResp_DeletedObjectResult_OperationStatus_OperStatusByTag[ + $_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + get operFailure => $_getN(0); + @$pb.TagNumber(1) + set operFailure( + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + value) => + $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasOperFailure() => $_has(0); + @$pb.TagNumber(1) + void clearOperFailure() => $_clearField(1); + @$pb.TagNumber(1) + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure + ensureOperFailure() => $_ensure(0); + + @$pb.TagNumber(2) + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + get operSuccess => $_getN(1); + @$pb.TagNumber(2) + set operSuccess( + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperSuccess() => $_has(1); + @$pb.TagNumber(2) + void clearOperSuccess() => $_clearField(2); + @$pb.TagNumber(2) + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess + ensureOperSuccess() => $_ensure(1); +} + +class DeleteResp_DeletedObjectResult extends $pb.GeneratedMessage { + factory DeleteResp_DeletedObjectResult({ + $core.String? requestedPath, + DeleteResp_DeletedObjectResult_OperationStatus? operStatus, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (operStatus != null) result.operStatus = operStatus; + return result; + } + + DeleteResp_DeletedObjectResult._(); + + factory DeleteResp_DeletedObjectResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp_DeletedObjectResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeleteResp.DeletedObjectResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aOM( + 2, _omitFieldNames ? '' : 'operStatus', + subBuilder: DeleteResp_DeletedObjectResult_OperationStatus.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_DeletedObjectResult copyWith( + void Function(DeleteResp_DeletedObjectResult) updates) => + super.copyWith( + (message) => updates(message as DeleteResp_DeletedObjectResult)) + as DeleteResp_DeletedObjectResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult create() => + DeleteResp_DeletedObjectResult._(); + @$core.override + DeleteResp_DeletedObjectResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeleteResp_DeletedObjectResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DeleteResp_DeletedObjectResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + DeleteResp_DeletedObjectResult_OperationStatus get operStatus => $_getN(1); + @$pb.TagNumber(2) + set operStatus(DeleteResp_DeletedObjectResult_OperationStatus value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperStatus() => $_has(1); + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField(2); + @$pb.TagNumber(2) + DeleteResp_DeletedObjectResult_OperationStatus ensureOperStatus() => + $_ensure(1); +} + +class DeleteResp_UnaffectedPathError extends $pb.GeneratedMessage { + factory DeleteResp_UnaffectedPathError({ + $core.String? unaffectedPath, + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (unaffectedPath != null) result.unaffectedPath = unaffectedPath; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + DeleteResp_UnaffectedPathError._(); + + factory DeleteResp_UnaffectedPathError.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp_UnaffectedPathError.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeleteResp.UnaffectedPathError', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'unaffectedPath') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_UnaffectedPathError clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp_UnaffectedPathError copyWith( + void Function(DeleteResp_UnaffectedPathError) updates) => + super.copyWith( + (message) => updates(message as DeleteResp_UnaffectedPathError)) + as DeleteResp_UnaffectedPathError; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp_UnaffectedPathError create() => + DeleteResp_UnaffectedPathError._(); + @$core.override + DeleteResp_UnaffectedPathError createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeleteResp_UnaffectedPathError getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DeleteResp_UnaffectedPathError? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get unaffectedPath => $_getSZ(0); + @$pb.TagNumber(1) + set unaffectedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasUnaffectedPath() => $_has(0); + @$pb.TagNumber(1) + void clearUnaffectedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); +} + +class DeleteResp extends $pb.GeneratedMessage { + factory DeleteResp({ + $core.Iterable? deletedObjResults, + }) { + final result = create(); + if (deletedObjResults != null) + result.deletedObjResults.addAll(deletedObjResults); + return result; + } + + DeleteResp._(); + + factory DeleteResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeleteResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeleteResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'deletedObjResults', + subBuilder: DeleteResp_DeletedObjectResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeleteResp copyWith(void Function(DeleteResp) updates) => + super.copyWith((message) => updates(message as DeleteResp)) as DeleteResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeleteResp create() => DeleteResp._(); + @$core.override + DeleteResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeleteResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DeleteResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get deletedObjResults => + $_getList(0); +} + +class Set_UpdateObject extends $pb.GeneratedMessage { + factory Set_UpdateObject({ + $core.String? objPath, + $core.Iterable? paramSettings, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + if (paramSettings != null) result.paramSettings.addAll(paramSettings); + return result; + } + + Set_UpdateObject._(); + + factory Set_UpdateObject.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Set_UpdateObject.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Set.UpdateObject', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..pPM(2, _omitFieldNames ? '' : 'paramSettings', + subBuilder: Set_UpdateParamSetting.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set_UpdateObject clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set_UpdateObject copyWith(void Function(Set_UpdateObject) updates) => + super.copyWith((message) => updates(message as Set_UpdateObject)) + as Set_UpdateObject; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Set_UpdateObject create() => Set_UpdateObject._(); + @$core.override + Set_UpdateObject createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Set_UpdateObject getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Set_UpdateObject? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get paramSettings => $_getList(1); +} + +class Set_UpdateParamSetting extends $pb.GeneratedMessage { + factory Set_UpdateParamSetting({ + $core.String? param, + $core.String? value, + $core.bool? required, + }) { + final result = create(); + if (param != null) result.param = param; + if (value != null) result.value = value; + if (required != null) result.required = required; + return result; + } + + Set_UpdateParamSetting._(); + + factory Set_UpdateParamSetting.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Set_UpdateParamSetting.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Set.UpdateParamSetting', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'param') + ..aOS(2, _omitFieldNames ? '' : 'value') + ..aOB(3, _omitFieldNames ? '' : 'required') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set_UpdateParamSetting clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set_UpdateParamSetting copyWith( + void Function(Set_UpdateParamSetting) updates) => + super.copyWith((message) => updates(message as Set_UpdateParamSetting)) + as Set_UpdateParamSetting; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Set_UpdateParamSetting create() => Set_UpdateParamSetting._(); + @$core.override + Set_UpdateParamSetting createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Set_UpdateParamSetting getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Set_UpdateParamSetting? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get param => $_getSZ(0); + @$pb.TagNumber(1) + set param($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParam() => $_has(0); + @$pb.TagNumber(1) + void clearParam() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get value => $_getSZ(1); + @$pb.TagNumber(2) + set value($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasValue() => $_has(1); + @$pb.TagNumber(2) + void clearValue() => $_clearField(2); + + @$pb.TagNumber(3) + $core.bool get required => $_getBF(2); + @$pb.TagNumber(3) + set required($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasRequired() => $_has(2); + @$pb.TagNumber(3) + void clearRequired() => $_clearField(3); +} + +class Set extends $pb.GeneratedMessage { + factory Set({ + $core.bool? allowPartial, + $core.Iterable? updateObjs, + }) { + final result = create(); + if (allowPartial != null) result.allowPartial = allowPartial; + if (updateObjs != null) result.updateObjs.addAll(updateObjs); + return result; + } + + Set._(); + + factory Set.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Set.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Set', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'allowPartial') + ..pPM(2, _omitFieldNames ? '' : 'updateObjs', + subBuilder: Set_UpdateObject.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Set copyWith(void Function(Set) updates) => + super.copyWith((message) => updates(message as Set)) as Set; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Set create() => Set._(); + @$core.override + Set createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Set getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Set? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get allowPartial => $_getBF(0); + @$pb.TagNumber(1) + set allowPartial($core.bool value) => $_setBool(0, value); + @$pb.TagNumber(1) + $core.bool hasAllowPartial() => $_has(0); + @$pb.TagNumber(1) + void clearAllowPartial() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get updateObjs => $_getList(1); +} + +class SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + extends $pb.GeneratedMessage { + factory SetResp_UpdatedObjectResult_OperationStatus_OperationFailure({ + $core.int? errCode, + $core.String? errMsg, + $core.Iterable? updatedInstFailures, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + if (updatedInstFailures != null) + result.updatedInstFailures.addAll(updatedInstFailures); + return result; + } + + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure._(); + + factory SetResp_UpdatedObjectResult_OperationStatus_OperationFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedObjectResult_OperationStatus_OperationFailure.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'SetResp.UpdatedObjectResult.OperationStatus.OperationFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..pPM( + 3, _omitFieldNames ? '' : 'updatedInstFailures', + subBuilder: SetResp_UpdatedInstanceFailure.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure copyWith( + void Function( + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure) + updates) => + super.copyWith((message) => updates(message + as SetResp_UpdatedObjectResult_OperationStatus_OperationFailure)) + as SetResp_UpdatedObjectResult_OperationStatus_OperationFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + create() => + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure._(); + @$core.override + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure>(create); + static SetResp_UpdatedObjectResult_OperationStatus_OperationFailure? + _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); + + @$pb.TagNumber(3) + $pb.PbList get updatedInstFailures => + $_getList(2); +} + +class SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + extends $pb.GeneratedMessage { + factory SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess({ + $core.Iterable? updatedInstResults, + }) { + final result = create(); + if (updatedInstResults != null) + result.updatedInstResults.addAll(updatedInstResults); + return result; + } + + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess._(); + + factory SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'SetResp.UpdatedObjectResult.OperationStatus.OperationSuccess', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'updatedInstResults', + subBuilder: SetResp_UpdatedInstanceResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess copyWith( + void Function( + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess) + updates) => + super.copyWith((message) => updates(message + as SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess)) + as SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + create() => + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess._(); + @$core.override + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess>(create); + static SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess? + _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get updatedInstResults => + $_getList(0); +} + +enum SetResp_UpdatedObjectResult_OperationStatus_OperStatus { + operFailure, + operSuccess, + notSet +} + +class SetResp_UpdatedObjectResult_OperationStatus extends $pb.GeneratedMessage { + factory SetResp_UpdatedObjectResult_OperationStatus({ + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure? operFailure, + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess? operSuccess, + }) { + final result = create(); + if (operFailure != null) result.operFailure = operFailure; + if (operSuccess != null) result.operSuccess = operSuccess; + return result; + } + + SetResp_UpdatedObjectResult_OperationStatus._(); + + factory SetResp_UpdatedObjectResult_OperationStatus.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedObjectResult_OperationStatus.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core + .Map<$core.int, SetResp_UpdatedObjectResult_OperationStatus_OperStatus> + _SetResp_UpdatedObjectResult_OperationStatus_OperStatusByTag = { + 1: SetResp_UpdatedObjectResult_OperationStatus_OperStatus.operFailure, + 2: SetResp_UpdatedObjectResult_OperationStatus_OperStatus.operSuccess, + 0: SetResp_UpdatedObjectResult_OperationStatus_OperStatus.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp.UpdatedObjectResult.OperationStatus', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2]) + ..aOM( + 1, _omitFieldNames ? '' : 'operFailure', + subBuilder: + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure.create) + ..aOM( + 2, _omitFieldNames ? '' : 'operSuccess', + subBuilder: + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult_OperationStatus copyWith( + void Function(SetResp_UpdatedObjectResult_OperationStatus) updates) => + super.copyWith((message) => + updates(message as SetResp_UpdatedObjectResult_OperationStatus)) + as SetResp_UpdatedObjectResult_OperationStatus; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus create() => + SetResp_UpdatedObjectResult_OperationStatus._(); + @$core.override + SetResp_UpdatedObjectResult_OperationStatus createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult_OperationStatus getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + SetResp_UpdatedObjectResult_OperationStatus>(create); + static SetResp_UpdatedObjectResult_OperationStatus? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + SetResp_UpdatedObjectResult_OperationStatus_OperStatus whichOperStatus() => + _SetResp_UpdatedObjectResult_OperationStatus_OperStatusByTag[ + $_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + get operFailure => $_getN(0); + @$pb.TagNumber(1) + set operFailure( + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure value) => + $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasOperFailure() => $_has(0); + @$pb.TagNumber(1) + void clearOperFailure() => $_clearField(1); + @$pb.TagNumber(1) + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure + ensureOperFailure() => $_ensure(0); + + @$pb.TagNumber(2) + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + get operSuccess => $_getN(1); + @$pb.TagNumber(2) + set operSuccess( + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperSuccess() => $_has(1); + @$pb.TagNumber(2) + void clearOperSuccess() => $_clearField(2); + @$pb.TagNumber(2) + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess + ensureOperSuccess() => $_ensure(1); +} + +class SetResp_UpdatedObjectResult extends $pb.GeneratedMessage { + factory SetResp_UpdatedObjectResult({ + $core.String? requestedPath, + SetResp_UpdatedObjectResult_OperationStatus? operStatus, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (operStatus != null) result.operStatus = operStatus; + return result; + } + + SetResp_UpdatedObjectResult._(); + + factory SetResp_UpdatedObjectResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedObjectResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp.UpdatedObjectResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aOM( + 2, _omitFieldNames ? '' : 'operStatus', + subBuilder: SetResp_UpdatedObjectResult_OperationStatus.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedObjectResult copyWith( + void Function(SetResp_UpdatedObjectResult) updates) => + super.copyWith( + (message) => updates(message as SetResp_UpdatedObjectResult)) + as SetResp_UpdatedObjectResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult create() => + SetResp_UpdatedObjectResult._(); + @$core.override + SetResp_UpdatedObjectResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedObjectResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SetResp_UpdatedObjectResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + SetResp_UpdatedObjectResult_OperationStatus get operStatus => $_getN(1); + @$pb.TagNumber(2) + set operStatus(SetResp_UpdatedObjectResult_OperationStatus value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperStatus() => $_has(1); + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField(2); + @$pb.TagNumber(2) + SetResp_UpdatedObjectResult_OperationStatus ensureOperStatus() => $_ensure(1); +} + +class SetResp_UpdatedInstanceFailure extends $pb.GeneratedMessage { + factory SetResp_UpdatedInstanceFailure({ + $core.String? affectedPath, + $core.Iterable? paramErrs, + }) { + final result = create(); + if (affectedPath != null) result.affectedPath = affectedPath; + if (paramErrs != null) result.paramErrs.addAll(paramErrs); + return result; + } + + SetResp_UpdatedInstanceFailure._(); + + factory SetResp_UpdatedInstanceFailure.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedInstanceFailure.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp.UpdatedInstanceFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'affectedPath') + ..pPM(2, _omitFieldNames ? '' : 'paramErrs', + subBuilder: SetResp_ParameterError.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedInstanceFailure clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedInstanceFailure copyWith( + void Function(SetResp_UpdatedInstanceFailure) updates) => + super.copyWith( + (message) => updates(message as SetResp_UpdatedInstanceFailure)) + as SetResp_UpdatedInstanceFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedInstanceFailure create() => + SetResp_UpdatedInstanceFailure._(); + @$core.override + SetResp_UpdatedInstanceFailure createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedInstanceFailure getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SetResp_UpdatedInstanceFailure? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get affectedPath => $_getSZ(0); + @$pb.TagNumber(1) + set affectedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasAffectedPath() => $_has(0); + @$pb.TagNumber(1) + void clearAffectedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get paramErrs => $_getList(1); +} + +class SetResp_UpdatedInstanceResult extends $pb.GeneratedMessage { + factory SetResp_UpdatedInstanceResult({ + $core.String? affectedPath, + $core.Iterable? paramErrs, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? updatedParams, + }) { + final result = create(); + if (affectedPath != null) result.affectedPath = affectedPath; + if (paramErrs != null) result.paramErrs.addAll(paramErrs); + if (updatedParams != null) result.updatedParams.addEntries(updatedParams); + return result; + } + + SetResp_UpdatedInstanceResult._(); + + factory SetResp_UpdatedInstanceResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_UpdatedInstanceResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp.UpdatedInstanceResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'affectedPath') + ..pPM(2, _omitFieldNames ? '' : 'paramErrs', + subBuilder: SetResp_ParameterError.create) + ..m<$core.String, $core.String>(3, _omitFieldNames ? '' : 'updatedParams', + entryClassName: 'SetResp.UpdatedInstanceResult.UpdatedParamsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedInstanceResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_UpdatedInstanceResult copyWith( + void Function(SetResp_UpdatedInstanceResult) updates) => + super.copyWith( + (message) => updates(message as SetResp_UpdatedInstanceResult)) + as SetResp_UpdatedInstanceResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedInstanceResult create() => + SetResp_UpdatedInstanceResult._(); + @$core.override + SetResp_UpdatedInstanceResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_UpdatedInstanceResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SetResp_UpdatedInstanceResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get affectedPath => $_getSZ(0); + @$pb.TagNumber(1) + set affectedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasAffectedPath() => $_has(0); + @$pb.TagNumber(1) + void clearAffectedPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get paramErrs => $_getList(1); + + @$pb.TagNumber(3) + $pb.PbMap<$core.String, $core.String> get updatedParams => $_getMap(2); +} + +class SetResp_ParameterError extends $pb.GeneratedMessage { + factory SetResp_ParameterError({ + $core.String? param, + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (param != null) result.param = param; + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + SetResp_ParameterError._(); + + factory SetResp_ParameterError.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp_ParameterError.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp.ParameterError', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'param') + ..aI(2, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(3, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_ParameterError clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp_ParameterError copyWith( + void Function(SetResp_ParameterError) updates) => + super.copyWith((message) => updates(message as SetResp_ParameterError)) + as SetResp_ParameterError; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp_ParameterError create() => SetResp_ParameterError._(); + @$core.override + SetResp_ParameterError createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp_ParameterError getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SetResp_ParameterError? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get param => $_getSZ(0); + @$pb.TagNumber(1) + set param($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParam() => $_has(0); + @$pb.TagNumber(1) + void clearParam() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get errCode => $_getIZ(1); + @$pb.TagNumber(2) + set errCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasErrCode() => $_has(1); + @$pb.TagNumber(2) + void clearErrCode() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get errMsg => $_getSZ(2); + @$pb.TagNumber(3) + set errMsg($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasErrMsg() => $_has(2); + @$pb.TagNumber(3) + void clearErrMsg() => $_clearField(3); +} + +class SetResp extends $pb.GeneratedMessage { + factory SetResp({ + $core.Iterable? updatedObjResults, + }) { + final result = create(); + if (updatedObjResults != null) + result.updatedObjResults.addAll(updatedObjResults); + return result; + } + + SetResp._(); + + factory SetResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SetResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SetResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'updatedObjResults', + subBuilder: SetResp_UpdatedObjectResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SetResp copyWith(void Function(SetResp) updates) => + super.copyWith((message) => updates(message as SetResp)) as SetResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SetResp create() => SetResp._(); + @$core.override + SetResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SetResp getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static SetResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get updatedObjResults => $_getList(0); +} + +class Operate extends $pb.GeneratedMessage { + factory Operate({ + $core.String? command, + $core.String? commandKey, + $core.bool? sendResp, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? inputArgs, + }) { + final result = create(); + if (command != null) result.command = command; + if (commandKey != null) result.commandKey = commandKey; + if (sendResp != null) result.sendResp = sendResp; + if (inputArgs != null) result.inputArgs.addEntries(inputArgs); + return result; + } + + Operate._(); + + factory Operate.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Operate.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Operate', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'command') + ..aOS(2, _omitFieldNames ? '' : 'commandKey') + ..aOB(3, _omitFieldNames ? '' : 'sendResp') + ..m<$core.String, $core.String>(4, _omitFieldNames ? '' : 'inputArgs', + entryClassName: 'Operate.InputArgsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Operate clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Operate copyWith(void Function(Operate) updates) => + super.copyWith((message) => updates(message as Operate)) as Operate; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Operate create() => Operate._(); + @$core.override + Operate createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Operate getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Operate? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get command => $_getSZ(0); + @$pb.TagNumber(1) + set command($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasCommand() => $_has(0); + @$pb.TagNumber(1) + void clearCommand() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get commandKey => $_getSZ(1); + @$pb.TagNumber(2) + set commandKey($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasCommandKey() => $_has(1); + @$pb.TagNumber(2) + void clearCommandKey() => $_clearField(2); + + @$pb.TagNumber(3) + $core.bool get sendResp => $_getBF(2); + @$pb.TagNumber(3) + set sendResp($core.bool value) => $_setBool(2, value); + @$pb.TagNumber(3) + $core.bool hasSendResp() => $_has(2); + @$pb.TagNumber(3) + void clearSendResp() => $_clearField(3); + + @$pb.TagNumber(4) + $pb.PbMap<$core.String, $core.String> get inputArgs => $_getMap(3); +} + +class OperateResp_OperationResult_OutputArgs extends $pb.GeneratedMessage { + factory OperateResp_OperationResult_OutputArgs({ + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? outputArgs, + }) { + final result = create(); + if (outputArgs != null) result.outputArgs.addEntries(outputArgs); + return result; + } + + OperateResp_OperationResult_OutputArgs._(); + + factory OperateResp_OperationResult_OutputArgs.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory OperateResp_OperationResult_OutputArgs.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'OperateResp.OperationResult.OutputArgs', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..m<$core.String, $core.String>(1, _omitFieldNames ? '' : 'outputArgs', + entryClassName: + 'OperateResp.OperationResult.OutputArgs.OutputArgsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult_OutputArgs clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult_OutputArgs copyWith( + void Function(OperateResp_OperationResult_OutputArgs) updates) => + super.copyWith((message) => + updates(message as OperateResp_OperationResult_OutputArgs)) + as OperateResp_OperationResult_OutputArgs; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult_OutputArgs create() => + OperateResp_OperationResult_OutputArgs._(); + @$core.override + OperateResp_OperationResult_OutputArgs createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult_OutputArgs getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + OperateResp_OperationResult_OutputArgs>(create); + static OperateResp_OperationResult_OutputArgs? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbMap<$core.String, $core.String> get outputArgs => $_getMap(0); +} + +class OperateResp_OperationResult_CommandFailure extends $pb.GeneratedMessage { + factory OperateResp_OperationResult_CommandFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + OperateResp_OperationResult_CommandFailure._(); + + factory OperateResp_OperationResult_CommandFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory OperateResp_OperationResult_CommandFailure.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'OperateResp.OperationResult.CommandFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult_CommandFailure clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult_CommandFailure copyWith( + void Function(OperateResp_OperationResult_CommandFailure) updates) => + super.copyWith((message) => + updates(message as OperateResp_OperationResult_CommandFailure)) + as OperateResp_OperationResult_CommandFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult_CommandFailure create() => + OperateResp_OperationResult_CommandFailure._(); + @$core.override + OperateResp_OperationResult_CommandFailure createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult_CommandFailure getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + OperateResp_OperationResult_CommandFailure>(create); + static OperateResp_OperationResult_CommandFailure? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +enum OperateResp_OperationResult_OperationResp { + reqObjPath, + reqOutputArgs, + cmdFailure, + notSet +} + +class OperateResp_OperationResult extends $pb.GeneratedMessage { + factory OperateResp_OperationResult({ + $core.String? executedCommand, + $core.String? reqObjPath, + OperateResp_OperationResult_OutputArgs? reqOutputArgs, + OperateResp_OperationResult_CommandFailure? cmdFailure, + }) { + final result = create(); + if (executedCommand != null) result.executedCommand = executedCommand; + if (reqObjPath != null) result.reqObjPath = reqObjPath; + if (reqOutputArgs != null) result.reqOutputArgs = reqOutputArgs; + if (cmdFailure != null) result.cmdFailure = cmdFailure; + return result; + } + + OperateResp_OperationResult._(); + + factory OperateResp_OperationResult.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory OperateResp_OperationResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, OperateResp_OperationResult_OperationResp> + _OperateResp_OperationResult_OperationRespByTag = { + 2: OperateResp_OperationResult_OperationResp.reqObjPath, + 3: OperateResp_OperationResult_OperationResp.reqOutputArgs, + 4: OperateResp_OperationResult_OperationResp.cmdFailure, + 0: OperateResp_OperationResult_OperationResp.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'OperateResp.OperationResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [2, 3, 4]) + ..aOS(1, _omitFieldNames ? '' : 'executedCommand') + ..aOS(2, _omitFieldNames ? '' : 'reqObjPath') + ..aOM( + 3, _omitFieldNames ? '' : 'reqOutputArgs', + subBuilder: OperateResp_OperationResult_OutputArgs.create) + ..aOM( + 4, _omitFieldNames ? '' : 'cmdFailure', + subBuilder: OperateResp_OperationResult_CommandFailure.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp_OperationResult copyWith( + void Function(OperateResp_OperationResult) updates) => + super.copyWith( + (message) => updates(message as OperateResp_OperationResult)) + as OperateResp_OperationResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult create() => + OperateResp_OperationResult._(); + @$core.override + OperateResp_OperationResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static OperateResp_OperationResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static OperateResp_OperationResult? _defaultInstance; + + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + OperateResp_OperationResult_OperationResp whichOperationResp() => + _OperateResp_OperationResult_OperationRespByTag[$_whichOneof(0)]!; + @$pb.TagNumber(2) + @$pb.TagNumber(3) + @$pb.TagNumber(4) + void clearOperationResp() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get executedCommand => $_getSZ(0); + @$pb.TagNumber(1) + set executedCommand($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasExecutedCommand() => $_has(0); + @$pb.TagNumber(1) + void clearExecutedCommand() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get reqObjPath => $_getSZ(1); + @$pb.TagNumber(2) + set reqObjPath($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasReqObjPath() => $_has(1); + @$pb.TagNumber(2) + void clearReqObjPath() => $_clearField(2); + + @$pb.TagNumber(3) + OperateResp_OperationResult_OutputArgs get reqOutputArgs => $_getN(2); + @$pb.TagNumber(3) + set reqOutputArgs(OperateResp_OperationResult_OutputArgs value) => + $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasReqOutputArgs() => $_has(2); + @$pb.TagNumber(3) + void clearReqOutputArgs() => $_clearField(3); + @$pb.TagNumber(3) + OperateResp_OperationResult_OutputArgs ensureReqOutputArgs() => $_ensure(2); + + @$pb.TagNumber(4) + OperateResp_OperationResult_CommandFailure get cmdFailure => $_getN(3); + @$pb.TagNumber(4) + set cmdFailure(OperateResp_OperationResult_CommandFailure value) => + $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasCmdFailure() => $_has(3); + @$pb.TagNumber(4) + void clearCmdFailure() => $_clearField(4); + @$pb.TagNumber(4) + OperateResp_OperationResult_CommandFailure ensureCmdFailure() => $_ensure(3); +} + +class OperateResp extends $pb.GeneratedMessage { + factory OperateResp({ + $core.Iterable? operationResults, + }) { + final result = create(); + if (operationResults != null) + result.operationResults.addAll(operationResults); + return result; + } + + OperateResp._(); + + factory OperateResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory OperateResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'OperateResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'operationResults', + subBuilder: OperateResp_OperationResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + OperateResp copyWith(void Function(OperateResp) updates) => + super.copyWith((message) => updates(message as OperateResp)) + as OperateResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static OperateResp create() => OperateResp._(); + @$core.override + OperateResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static OperateResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static OperateResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get operationResults => $_getList(0); +} + +class Notify_Event extends $pb.GeneratedMessage { + factory Notify_Event({ + $core.String? objPath, + $core.String? eventName, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? params, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + if (eventName != null) result.eventName = eventName; + if (params != null) result.params.addEntries(params); + return result; + } + + Notify_Event._(); + + factory Notify_Event.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_Event.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.Event', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..aOS(2, _omitFieldNames ? '' : 'eventName') + ..m<$core.String, $core.String>(3, _omitFieldNames ? '' : 'params', + entryClassName: 'Notify.Event.ParamsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_Event clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_Event copyWith(void Function(Notify_Event) updates) => + super.copyWith((message) => updates(message as Notify_Event)) + as Notify_Event; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_Event create() => Notify_Event._(); + @$core.override + Notify_Event createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_Event getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_Event? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get eventName => $_getSZ(1); + @$pb.TagNumber(2) + set eventName($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasEventName() => $_has(1); + @$pb.TagNumber(2) + void clearEventName() => $_clearField(2); + + @$pb.TagNumber(3) + $pb.PbMap<$core.String, $core.String> get params => $_getMap(2); +} + +class Notify_ValueChange extends $pb.GeneratedMessage { + factory Notify_ValueChange({ + $core.String? paramPath, + $core.String? paramValue, + }) { + final result = create(); + if (paramPath != null) result.paramPath = paramPath; + if (paramValue != null) result.paramValue = paramValue; + return result; + } + + Notify_ValueChange._(); + + factory Notify_ValueChange.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_ValueChange.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.ValueChange', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'paramPath') + ..aOS(2, _omitFieldNames ? '' : 'paramValue') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ValueChange clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ValueChange copyWith(void Function(Notify_ValueChange) updates) => + super.copyWith((message) => updates(message as Notify_ValueChange)) + as Notify_ValueChange; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_ValueChange create() => Notify_ValueChange._(); + @$core.override + Notify_ValueChange createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_ValueChange getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_ValueChange? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get paramPath => $_getSZ(0); + @$pb.TagNumber(1) + set paramPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasParamPath() => $_has(0); + @$pb.TagNumber(1) + void clearParamPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get paramValue => $_getSZ(1); + @$pb.TagNumber(2) + set paramValue($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasParamValue() => $_has(1); + @$pb.TagNumber(2) + void clearParamValue() => $_clearField(2); +} + +class Notify_ObjectCreation extends $pb.GeneratedMessage { + factory Notify_ObjectCreation({ + $core.String? objPath, + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? uniqueKeys, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + if (uniqueKeys != null) result.uniqueKeys.addEntries(uniqueKeys); + return result; + } + + Notify_ObjectCreation._(); + + factory Notify_ObjectCreation.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_ObjectCreation.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.ObjectCreation', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'uniqueKeys', + entryClassName: 'Notify.ObjectCreation.UniqueKeysEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ObjectCreation clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ObjectCreation copyWith( + void Function(Notify_ObjectCreation) updates) => + super.copyWith((message) => updates(message as Notify_ObjectCreation)) + as Notify_ObjectCreation; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_ObjectCreation create() => Notify_ObjectCreation._(); + @$core.override + Notify_ObjectCreation createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_ObjectCreation getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_ObjectCreation? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbMap<$core.String, $core.String> get uniqueKeys => $_getMap(1); +} + +class Notify_ObjectDeletion extends $pb.GeneratedMessage { + factory Notify_ObjectDeletion({ + $core.String? objPath, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + return result; + } + + Notify_ObjectDeletion._(); + + factory Notify_ObjectDeletion.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_ObjectDeletion.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.ObjectDeletion', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ObjectDeletion clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_ObjectDeletion copyWith( + void Function(Notify_ObjectDeletion) updates) => + super.copyWith((message) => updates(message as Notify_ObjectDeletion)) + as Notify_ObjectDeletion; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_ObjectDeletion create() => Notify_ObjectDeletion._(); + @$core.override + Notify_ObjectDeletion createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_ObjectDeletion getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_ObjectDeletion? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); +} + +class Notify_OperationComplete_OutputArgs extends $pb.GeneratedMessage { + factory Notify_OperationComplete_OutputArgs({ + $core.Iterable<$core.MapEntry<$core.String, $core.String>>? outputArgs, + }) { + final result = create(); + if (outputArgs != null) result.outputArgs.addEntries(outputArgs); + return result; + } + + Notify_OperationComplete_OutputArgs._(); + + factory Notify_OperationComplete_OutputArgs.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_OperationComplete_OutputArgs.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.OperationComplete.OutputArgs', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..m<$core.String, $core.String>(1, _omitFieldNames ? '' : 'outputArgs', + entryClassName: 'Notify.OperationComplete.OutputArgs.OutputArgsEntry', + keyFieldType: $pb.PbFieldType.OS, + valueFieldType: $pb.PbFieldType.OS, + packageName: const $pb.PackageName('usp')) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete_OutputArgs clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete_OutputArgs copyWith( + void Function(Notify_OperationComplete_OutputArgs) updates) => + super.copyWith((message) => + updates(message as Notify_OperationComplete_OutputArgs)) + as Notify_OperationComplete_OutputArgs; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete_OutputArgs create() => + Notify_OperationComplete_OutputArgs._(); + @$core.override + Notify_OperationComplete_OutputArgs createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete_OutputArgs getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Notify_OperationComplete_OutputArgs>(create); + static Notify_OperationComplete_OutputArgs? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbMap<$core.String, $core.String> get outputArgs => $_getMap(0); +} + +class Notify_OperationComplete_CommandFailure extends $pb.GeneratedMessage { + factory Notify_OperationComplete_CommandFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + Notify_OperationComplete_CommandFailure._(); + + factory Notify_OperationComplete_CommandFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_OperationComplete_CommandFailure.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.OperationComplete.CommandFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete_CommandFailure clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete_CommandFailure copyWith( + void Function(Notify_OperationComplete_CommandFailure) updates) => + super.copyWith((message) => + updates(message as Notify_OperationComplete_CommandFailure)) + as Notify_OperationComplete_CommandFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete_CommandFailure create() => + Notify_OperationComplete_CommandFailure._(); + @$core.override + Notify_OperationComplete_CommandFailure createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete_CommandFailure getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + Notify_OperationComplete_CommandFailure>(create); + static Notify_OperationComplete_CommandFailure? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +enum Notify_OperationComplete_OperationResp { + reqOutputArgs, + cmdFailure, + notSet +} + +class Notify_OperationComplete extends $pb.GeneratedMessage { + factory Notify_OperationComplete({ + $core.String? objPath, + $core.String? commandName, + $core.String? commandKey, + Notify_OperationComplete_OutputArgs? reqOutputArgs, + Notify_OperationComplete_CommandFailure? cmdFailure, + }) { + final result = create(); + if (objPath != null) result.objPath = objPath; + if (commandName != null) result.commandName = commandName; + if (commandKey != null) result.commandKey = commandKey; + if (reqOutputArgs != null) result.reqOutputArgs = reqOutputArgs; + if (cmdFailure != null) result.cmdFailure = cmdFailure; + return result; + } + + Notify_OperationComplete._(); + + factory Notify_OperationComplete.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_OperationComplete.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Notify_OperationComplete_OperationResp> + _Notify_OperationComplete_OperationRespByTag = { + 4: Notify_OperationComplete_OperationResp.reqOutputArgs, + 5: Notify_OperationComplete_OperationResp.cmdFailure, + 0: Notify_OperationComplete_OperationResp.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.OperationComplete', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [4, 5]) + ..aOS(1, _omitFieldNames ? '' : 'objPath') + ..aOS(2, _omitFieldNames ? '' : 'commandName') + ..aOS(3, _omitFieldNames ? '' : 'commandKey') + ..aOM( + 4, _omitFieldNames ? '' : 'reqOutputArgs', + subBuilder: Notify_OperationComplete_OutputArgs.create) + ..aOM( + 5, _omitFieldNames ? '' : 'cmdFailure', + subBuilder: Notify_OperationComplete_CommandFailure.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OperationComplete copyWith( + void Function(Notify_OperationComplete) updates) => + super.copyWith((message) => updates(message as Notify_OperationComplete)) + as Notify_OperationComplete; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete create() => Notify_OperationComplete._(); + @$core.override + Notify_OperationComplete createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_OperationComplete getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_OperationComplete? _defaultInstance; + + @$pb.TagNumber(4) + @$pb.TagNumber(5) + Notify_OperationComplete_OperationResp whichOperationResp() => + _Notify_OperationComplete_OperationRespByTag[$_whichOneof(0)]!; + @$pb.TagNumber(4) + @$pb.TagNumber(5) + void clearOperationResp() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get objPath => $_getSZ(0); + @$pb.TagNumber(1) + set objPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasObjPath() => $_has(0); + @$pb.TagNumber(1) + void clearObjPath() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get commandName => $_getSZ(1); + @$pb.TagNumber(2) + set commandName($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasCommandName() => $_has(1); + @$pb.TagNumber(2) + void clearCommandName() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get commandKey => $_getSZ(2); + @$pb.TagNumber(3) + set commandKey($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasCommandKey() => $_has(2); + @$pb.TagNumber(3) + void clearCommandKey() => $_clearField(3); + + @$pb.TagNumber(4) + Notify_OperationComplete_OutputArgs get reqOutputArgs => $_getN(3); + @$pb.TagNumber(4) + set reqOutputArgs(Notify_OperationComplete_OutputArgs value) => + $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasReqOutputArgs() => $_has(3); + @$pb.TagNumber(4) + void clearReqOutputArgs() => $_clearField(4); + @$pb.TagNumber(4) + Notify_OperationComplete_OutputArgs ensureReqOutputArgs() => $_ensure(3); + + @$pb.TagNumber(5) + Notify_OperationComplete_CommandFailure get cmdFailure => $_getN(4); + @$pb.TagNumber(5) + set cmdFailure(Notify_OperationComplete_CommandFailure value) => + $_setField(5, value); + @$pb.TagNumber(5) + $core.bool hasCmdFailure() => $_has(4); + @$pb.TagNumber(5) + void clearCmdFailure() => $_clearField(5); + @$pb.TagNumber(5) + Notify_OperationComplete_CommandFailure ensureCmdFailure() => $_ensure(4); +} + +class Notify_OnBoardRequest extends $pb.GeneratedMessage { + factory Notify_OnBoardRequest({ + $core.String? oui, + $core.String? productClass, + $core.String? serialNumber, + $core.String? agentSupportedProtocolVersions, + }) { + final result = create(); + if (oui != null) result.oui = oui; + if (productClass != null) result.productClass = productClass; + if (serialNumber != null) result.serialNumber = serialNumber; + if (agentSupportedProtocolVersions != null) + result.agentSupportedProtocolVersions = agentSupportedProtocolVersions; + return result; + } + + Notify_OnBoardRequest._(); + + factory Notify_OnBoardRequest.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify_OnBoardRequest.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify.OnBoardRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'oui') + ..aOS(2, _omitFieldNames ? '' : 'productClass') + ..aOS(3, _omitFieldNames ? '' : 'serialNumber') + ..aOS(4, _omitFieldNames ? '' : 'agentSupportedProtocolVersions') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OnBoardRequest clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify_OnBoardRequest copyWith( + void Function(Notify_OnBoardRequest) updates) => + super.copyWith((message) => updates(message as Notify_OnBoardRequest)) + as Notify_OnBoardRequest; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify_OnBoardRequest create() => Notify_OnBoardRequest._(); + @$core.override + Notify_OnBoardRequest createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify_OnBoardRequest getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Notify_OnBoardRequest? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get oui => $_getSZ(0); + @$pb.TagNumber(1) + set oui($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasOui() => $_has(0); + @$pb.TagNumber(1) + void clearOui() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get productClass => $_getSZ(1); + @$pb.TagNumber(2) + set productClass($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasProductClass() => $_has(1); + @$pb.TagNumber(2) + void clearProductClass() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get serialNumber => $_getSZ(2); + @$pb.TagNumber(3) + set serialNumber($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasSerialNumber() => $_has(2); + @$pb.TagNumber(3) + void clearSerialNumber() => $_clearField(3); + + @$pb.TagNumber(4) + $core.String get agentSupportedProtocolVersions => $_getSZ(3); + @$pb.TagNumber(4) + set agentSupportedProtocolVersions($core.String value) => + $_setString(3, value); + @$pb.TagNumber(4) + $core.bool hasAgentSupportedProtocolVersions() => $_has(3); + @$pb.TagNumber(4) + void clearAgentSupportedProtocolVersions() => $_clearField(4); +} + +enum Notify_Notification { + event, + valueChange, + objCreation, + objDeletion, + operComplete, + onBoardReq, + notSet +} + +class Notify extends $pb.GeneratedMessage { + factory Notify({ + $core.String? subscriptionId, + $core.bool? sendResp, + Notify_Event? event, + Notify_ValueChange? valueChange, + Notify_ObjectCreation? objCreation, + Notify_ObjectDeletion? objDeletion, + Notify_OperationComplete? operComplete, + Notify_OnBoardRequest? onBoardReq, + }) { + final result = create(); + if (subscriptionId != null) result.subscriptionId = subscriptionId; + if (sendResp != null) result.sendResp = sendResp; + if (event != null) result.event = event; + if (valueChange != null) result.valueChange = valueChange; + if (objCreation != null) result.objCreation = objCreation; + if (objDeletion != null) result.objDeletion = objDeletion; + if (operComplete != null) result.operComplete = operComplete; + if (onBoardReq != null) result.onBoardReq = onBoardReq; + return result; + } + + Notify._(); + + factory Notify.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Notify.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Notify_Notification> + _Notify_NotificationByTag = { + 3: Notify_Notification.event, + 4: Notify_Notification.valueChange, + 5: Notify_Notification.objCreation, + 6: Notify_Notification.objDeletion, + 7: Notify_Notification.operComplete, + 8: Notify_Notification.onBoardReq, + 0: Notify_Notification.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Notify', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [3, 4, 5, 6, 7, 8]) + ..aOS(1, _omitFieldNames ? '' : 'subscriptionId') + ..aOB(2, _omitFieldNames ? '' : 'sendResp') + ..aOM(3, _omitFieldNames ? '' : 'event', + subBuilder: Notify_Event.create) + ..aOM(4, _omitFieldNames ? '' : 'valueChange', + subBuilder: Notify_ValueChange.create) + ..aOM(5, _omitFieldNames ? '' : 'objCreation', + subBuilder: Notify_ObjectCreation.create) + ..aOM(6, _omitFieldNames ? '' : 'objDeletion', + subBuilder: Notify_ObjectDeletion.create) + ..aOM(7, _omitFieldNames ? '' : 'operComplete', + subBuilder: Notify_OperationComplete.create) + ..aOM(8, _omitFieldNames ? '' : 'onBoardReq', + subBuilder: Notify_OnBoardRequest.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Notify copyWith(void Function(Notify) updates) => + super.copyWith((message) => updates(message as Notify)) as Notify; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Notify create() => Notify._(); + @$core.override + Notify createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Notify getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Notify? _defaultInstance; + + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + Notify_Notification whichNotification() => + _Notify_NotificationByTag[$_whichOneof(0)]!; + @$pb.TagNumber(3) + @$pb.TagNumber(4) + @$pb.TagNumber(5) + @$pb.TagNumber(6) + @$pb.TagNumber(7) + @$pb.TagNumber(8) + void clearNotification() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get subscriptionId => $_getSZ(0); + @$pb.TagNumber(1) + set subscriptionId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasSubscriptionId() => $_has(0); + @$pb.TagNumber(1) + void clearSubscriptionId() => $_clearField(1); + + @$pb.TagNumber(2) + $core.bool get sendResp => $_getBF(1); + @$pb.TagNumber(2) + set sendResp($core.bool value) => $_setBool(1, value); + @$pb.TagNumber(2) + $core.bool hasSendResp() => $_has(1); + @$pb.TagNumber(2) + void clearSendResp() => $_clearField(2); + + @$pb.TagNumber(3) + Notify_Event get event => $_getN(2); + @$pb.TagNumber(3) + set event(Notify_Event value) => $_setField(3, value); + @$pb.TagNumber(3) + $core.bool hasEvent() => $_has(2); + @$pb.TagNumber(3) + void clearEvent() => $_clearField(3); + @$pb.TagNumber(3) + Notify_Event ensureEvent() => $_ensure(2); + + @$pb.TagNumber(4) + Notify_ValueChange get valueChange => $_getN(3); + @$pb.TagNumber(4) + set valueChange(Notify_ValueChange value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasValueChange() => $_has(3); + @$pb.TagNumber(4) + void clearValueChange() => $_clearField(4); + @$pb.TagNumber(4) + Notify_ValueChange ensureValueChange() => $_ensure(3); + + @$pb.TagNumber(5) + Notify_ObjectCreation get objCreation => $_getN(4); + @$pb.TagNumber(5) + set objCreation(Notify_ObjectCreation value) => $_setField(5, value); + @$pb.TagNumber(5) + $core.bool hasObjCreation() => $_has(4); + @$pb.TagNumber(5) + void clearObjCreation() => $_clearField(5); + @$pb.TagNumber(5) + Notify_ObjectCreation ensureObjCreation() => $_ensure(4); + + @$pb.TagNumber(6) + Notify_ObjectDeletion get objDeletion => $_getN(5); + @$pb.TagNumber(6) + set objDeletion(Notify_ObjectDeletion value) => $_setField(6, value); + @$pb.TagNumber(6) + $core.bool hasObjDeletion() => $_has(5); + @$pb.TagNumber(6) + void clearObjDeletion() => $_clearField(6); + @$pb.TagNumber(6) + Notify_ObjectDeletion ensureObjDeletion() => $_ensure(5); + + @$pb.TagNumber(7) + Notify_OperationComplete get operComplete => $_getN(6); + @$pb.TagNumber(7) + set operComplete(Notify_OperationComplete value) => $_setField(7, value); + @$pb.TagNumber(7) + $core.bool hasOperComplete() => $_has(6); + @$pb.TagNumber(7) + void clearOperComplete() => $_clearField(7); + @$pb.TagNumber(7) + Notify_OperationComplete ensureOperComplete() => $_ensure(6); + + @$pb.TagNumber(8) + Notify_OnBoardRequest get onBoardReq => $_getN(7); + @$pb.TagNumber(8) + set onBoardReq(Notify_OnBoardRequest value) => $_setField(8, value); + @$pb.TagNumber(8) + $core.bool hasOnBoardReq() => $_has(7); + @$pb.TagNumber(8) + void clearOnBoardReq() => $_clearField(8); + @$pb.TagNumber(8) + Notify_OnBoardRequest ensureOnBoardReq() => $_ensure(7); +} + +class NotifyResp extends $pb.GeneratedMessage { + factory NotifyResp({ + $core.String? subscriptionId, + }) { + final result = create(); + if (subscriptionId != null) result.subscriptionId = subscriptionId; + return result; + } + + NotifyResp._(); + + factory NotifyResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory NotifyResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'NotifyResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'subscriptionId') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + NotifyResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + NotifyResp copyWith(void Function(NotifyResp) updates) => + super.copyWith((message) => updates(message as NotifyResp)) as NotifyResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static NotifyResp create() => NotifyResp._(); + @$core.override + NotifyResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static NotifyResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static NotifyResp? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get subscriptionId => $_getSZ(0); + @$pb.TagNumber(1) + set subscriptionId($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasSubscriptionId() => $_has(0); + @$pb.TagNumber(1) + void clearSubscriptionId() => $_clearField(1); +} + +class Register_RegistrationPath extends $pb.GeneratedMessage { + factory Register_RegistrationPath({ + $core.String? path, + }) { + final result = create(); + if (path != null) result.path = path; + return result; + } + + Register_RegistrationPath._(); + + factory Register_RegistrationPath.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Register_RegistrationPath.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Register.RegistrationPath', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'path') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Register_RegistrationPath clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Register_RegistrationPath copyWith( + void Function(Register_RegistrationPath) updates) => + super.copyWith((message) => updates(message as Register_RegistrationPath)) + as Register_RegistrationPath; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Register_RegistrationPath create() => Register_RegistrationPath._(); + @$core.override + Register_RegistrationPath createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Register_RegistrationPath getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Register_RegistrationPath? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get path => $_getSZ(0); + @$pb.TagNumber(1) + set path($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasPath() => $_has(0); + @$pb.TagNumber(1) + void clearPath() => $_clearField(1); +} + +class Register extends $pb.GeneratedMessage { + factory Register({ + $core.bool? allowPartial, + $core.Iterable? regPaths, + }) { + final result = create(); + if (allowPartial != null) result.allowPartial = allowPartial; + if (regPaths != null) result.regPaths.addAll(regPaths); + return result; + } + + Register._(); + + factory Register.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Register.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Register', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'allowPartial') + ..pPM(2, _omitFieldNames ? '' : 'regPaths', + subBuilder: Register_RegistrationPath.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Register clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Register copyWith(void Function(Register) updates) => + super.copyWith((message) => updates(message as Register)) as Register; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Register create() => Register._(); + @$core.override + Register createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Register getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Register? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get allowPartial => $_getBF(0); + @$pb.TagNumber(1) + set allowPartial($core.bool value) => $_setBool(0, value); + @$pb.TagNumber(1) + $core.bool hasAllowPartial() => $_has(0); + @$pb.TagNumber(1) + void clearAllowPartial() => $_clearField(1); + + @$pb.TagNumber(2) + $pb.PbList get regPaths => $_getList(1); +} + +class RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + extends $pb.GeneratedMessage { + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure._(); + + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'RegisterResp.RegisteredPathResult.OperationStatus.OperationFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure copyWith( + void Function( + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure) + updates) => + super.copyWith((message) => updates(message + as RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure)) + as RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + create() => + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + ._(); + @$core.override + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure>( + create); + static RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure? + _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +class RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + extends $pb.GeneratedMessage { + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess({ + $core.String? registeredPath, + }) { + final result = create(); + if (registeredPath != null) result.registeredPath = registeredPath; + return result; + } + + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess._(); + + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'RegisterResp.RegisteredPathResult.OperationStatus.OperationSuccess', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'registeredPath') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess clone() => + deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess copyWith( + void Function( + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess) + updates) => + super.copyWith((message) => updates(message + as RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess)) + as RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + create() => + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + ._(); + @$core.override + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess>( + create); + static RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess? + _defaultInstance; + + @$pb.TagNumber(1) + $core.String get registeredPath => $_getSZ(0); + @$pb.TagNumber(1) + set registeredPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRegisteredPath() => $_has(0); + @$pb.TagNumber(1) + void clearRegisteredPath() => $_clearField(1); +} + +enum RegisterResp_RegisteredPathResult_OperationStatus_OperStatus { + operFailure, + operSuccess, + notSet +} + +class RegisterResp_RegisteredPathResult_OperationStatus + extends $pb.GeneratedMessage { + factory RegisterResp_RegisteredPathResult_OperationStatus({ + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure? + operFailure, + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess? + operSuccess, + }) { + final result = create(); + if (operFailure != null) result.operFailure = operFailure; + if (operSuccess != null) result.operSuccess = operSuccess; + return result; + } + + RegisterResp_RegisteredPathResult_OperationStatus._(); + + factory RegisterResp_RegisteredPathResult_OperationStatus.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RegisterResp_RegisteredPathResult_OperationStatus.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, + RegisterResp_RegisteredPathResult_OperationStatus_OperStatus> + _RegisterResp_RegisteredPathResult_OperationStatus_OperStatusByTag = { + 1: RegisterResp_RegisteredPathResult_OperationStatus_OperStatus.operFailure, + 2: RegisterResp_RegisteredPathResult_OperationStatus_OperStatus.operSuccess, + 0: RegisterResp_RegisteredPathResult_OperationStatus_OperStatus.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'RegisterResp.RegisteredPathResult.OperationStatus', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2]) + ..aOM( + 1, _omitFieldNames ? '' : 'operFailure', + subBuilder: + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + .create) + ..aOM( + 2, _omitFieldNames ? '' : 'operSuccess', + subBuilder: + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + .create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult_OperationStatus copyWith( + void Function(RegisterResp_RegisteredPathResult_OperationStatus) + updates) => + super.copyWith((message) => updates( + message as RegisterResp_RegisteredPathResult_OperationStatus)) + as RegisterResp_RegisteredPathResult_OperationStatus; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus create() => + RegisterResp_RegisteredPathResult_OperationStatus._(); + @$core.override + RegisterResp_RegisteredPathResult_OperationStatus createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult_OperationStatus getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + RegisterResp_RegisteredPathResult_OperationStatus>(create); + static RegisterResp_RegisteredPathResult_OperationStatus? _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + RegisterResp_RegisteredPathResult_OperationStatus_OperStatus + whichOperStatus() => + _RegisterResp_RegisteredPathResult_OperationStatus_OperStatusByTag[ + $_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + get operFailure => $_getN(0); + @$pb.TagNumber(1) + set operFailure( + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + value) => + $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasOperFailure() => $_has(0); + @$pb.TagNumber(1) + void clearOperFailure() => $_clearField(1); + @$pb.TagNumber(1) + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure + ensureOperFailure() => $_ensure(0); + + @$pb.TagNumber(2) + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + get operSuccess => $_getN(1); + @$pb.TagNumber(2) + set operSuccess( + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperSuccess() => $_has(1); + @$pb.TagNumber(2) + void clearOperSuccess() => $_clearField(2); + @$pb.TagNumber(2) + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess + ensureOperSuccess() => $_ensure(1); +} + +class RegisterResp_RegisteredPathResult extends $pb.GeneratedMessage { + factory RegisterResp_RegisteredPathResult({ + $core.String? requestedPath, + RegisterResp_RegisteredPathResult_OperationStatus? operStatus, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (operStatus != null) result.operStatus = operStatus; + return result; + } + + RegisterResp_RegisteredPathResult._(); + + factory RegisterResp_RegisteredPathResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RegisterResp_RegisteredPathResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RegisterResp.RegisteredPathResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aOM( + 2, _omitFieldNames ? '' : 'operStatus', + subBuilder: RegisterResp_RegisteredPathResult_OperationStatus.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp_RegisteredPathResult copyWith( + void Function(RegisterResp_RegisteredPathResult) updates) => + super.copyWith((message) => + updates(message as RegisterResp_RegisteredPathResult)) + as RegisterResp_RegisteredPathResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult create() => + RegisterResp_RegisteredPathResult._(); + @$core.override + RegisterResp_RegisteredPathResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RegisterResp_RegisteredPathResult getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor( + create); + static RegisterResp_RegisteredPathResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + RegisterResp_RegisteredPathResult_OperationStatus get operStatus => $_getN(1); + @$pb.TagNumber(2) + set operStatus(RegisterResp_RegisteredPathResult_OperationStatus value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperStatus() => $_has(1); + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField(2); + @$pb.TagNumber(2) + RegisterResp_RegisteredPathResult_OperationStatus ensureOperStatus() => + $_ensure(1); +} + +class RegisterResp extends $pb.GeneratedMessage { + factory RegisterResp({ + $core.Iterable? registeredPathResults, + }) { + final result = create(); + if (registeredPathResults != null) + result.registeredPathResults.addAll(registeredPathResults); + return result; + } + + RegisterResp._(); + + factory RegisterResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory RegisterResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'RegisterResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'registeredPathResults', + subBuilder: RegisterResp_RegisteredPathResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + RegisterResp copyWith(void Function(RegisterResp) updates) => + super.copyWith((message) => updates(message as RegisterResp)) + as RegisterResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RegisterResp create() => RegisterResp._(); + @$core.override + RegisterResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static RegisterResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static RegisterResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList get registeredPathResults => + $_getList(0); +} + +class Deregister extends $pb.GeneratedMessage { + factory Deregister({ + $core.Iterable<$core.String>? paths, + }) { + final result = create(); + if (paths != null) result.paths.addAll(paths); + return result; + } + + Deregister._(); + + factory Deregister.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Deregister.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Deregister', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'paths') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Deregister clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Deregister copyWith(void Function(Deregister) updates) => + super.copyWith((message) => updates(message as Deregister)) as Deregister; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Deregister create() => Deregister._(); + @$core.override + Deregister createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Deregister getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static Deregister? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get paths => $_getList(0); +} + +class DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + extends $pb.GeneratedMessage { + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure({ + $core.int? errCode, + $core.String? errMsg, + }) { + final result = create(); + if (errCode != null) result.errCode = errCode; + if (errMsg != null) result.errMsg = errMsg; + return result; + } + + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure._(); + + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'DeregisterResp.DeregisteredPathResult.OperationStatus.OperationFailure', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aI(1, _omitFieldNames ? '' : 'errCode', fieldType: $pb.PbFieldType.OF3) + ..aOS(2, _omitFieldNames ? '' : 'errMsg') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure copyWith( + void Function( + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure) + updates) => + super.copyWith((message) => updates(message + as DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure)) + as DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + create() => + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + ._(); + @$core.override + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure>( + create); + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure? + _defaultInstance; + + @$pb.TagNumber(1) + $core.int get errCode => $_getIZ(0); + @$pb.TagNumber(1) + set errCode($core.int value) => $_setUnsignedInt32(0, value); + @$pb.TagNumber(1) + $core.bool hasErrCode() => $_has(0); + @$pb.TagNumber(1) + void clearErrCode() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get errMsg => $_getSZ(1); + @$pb.TagNumber(2) + set errMsg($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasErrMsg() => $_has(1); + @$pb.TagNumber(2) + void clearErrMsg() => $_clearField(2); +} + +class DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + extends $pb.GeneratedMessage { + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess({ + $core.Iterable<$core.String>? deregisteredPath, + }) { + final result = create(); + if (deregisteredPath != null) + result.deregisteredPath.addAll(deregisteredPath); + return result; + } + + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess._(); + + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'DeregisterResp.DeregisteredPathResult.OperationStatus.OperationSuccess', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPS(1, _omitFieldNames ? '' : 'deregisteredPath') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess copyWith( + void Function( + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess) + updates) => + super.copyWith((message) => updates(message + as DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess)) + as DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + create() => + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + ._(); + @$core.override + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess>( + create); + static DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess? + _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList<$core.String> get deregisteredPath => $_getList(0); +} + +enum DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus { + operFailure, + operSuccess, + notSet +} + +class DeregisterResp_DeregisteredPathResult_OperationStatus + extends $pb.GeneratedMessage { + factory DeregisterResp_DeregisteredPathResult_OperationStatus({ + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure? + operFailure, + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess? + operSuccess, + }) { + final result = create(); + if (operFailure != null) result.operFailure = operFailure; + if (operSuccess != null) result.operSuccess = operSuccess; + return result; + } + + DeregisterResp_DeregisteredPathResult_OperationStatus._(); + + factory DeregisterResp_DeregisteredPathResult_OperationStatus.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeregisterResp_DeregisteredPathResult_OperationStatus.fromJson( + $core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, + DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus> + _DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatusByTag = { + 1: DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus + .operFailure, + 2: DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus + .operSuccess, + 0: DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames + ? '' + : 'DeregisterResp.DeregisteredPathResult.OperationStatus', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..oo(0, [1, 2]) + ..aOM( + 1, _omitFieldNames ? '' : 'operFailure', + subBuilder: + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + .create) + ..aOM( + 2, _omitFieldNames ? '' : 'operSuccess', + subBuilder: + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + .create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult_OperationStatus copyWith( + void Function(DeregisterResp_DeregisteredPathResult_OperationStatus) + updates) => + super.copyWith((message) => updates( + message as DeregisterResp_DeregisteredPathResult_OperationStatus)) + as DeregisterResp_DeregisteredPathResult_OperationStatus; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus create() => + DeregisterResp_DeregisteredPathResult_OperationStatus._(); + @$core.override + DeregisterResp_DeregisteredPathResult_OperationStatus createEmptyInstance() => + create(); + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult_OperationStatus getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeregisterResp_DeregisteredPathResult_OperationStatus>(create); + static DeregisterResp_DeregisteredPathResult_OperationStatus? + _defaultInstance; + + @$pb.TagNumber(1) + @$pb.TagNumber(2) + DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatus + whichOperStatus() => + _DeregisterResp_DeregisteredPathResult_OperationStatus_OperStatusByTag[ + $_whichOneof(0)]!; + @$pb.TagNumber(1) + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + get operFailure => $_getN(0); + @$pb.TagNumber(1) + set operFailure( + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + value) => + $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasOperFailure() => $_has(0); + @$pb.TagNumber(1) + void clearOperFailure() => $_clearField(1); + @$pb.TagNumber(1) + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure + ensureOperFailure() => $_ensure(0); + + @$pb.TagNumber(2) + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + get operSuccess => $_getN(1); + @$pb.TagNumber(2) + set operSuccess( + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperSuccess() => $_has(1); + @$pb.TagNumber(2) + void clearOperSuccess() => $_clearField(2); + @$pb.TagNumber(2) + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess + ensureOperSuccess() => $_ensure(1); +} + +class DeregisterResp_DeregisteredPathResult extends $pb.GeneratedMessage { + factory DeregisterResp_DeregisteredPathResult({ + $core.String? requestedPath, + DeregisterResp_DeregisteredPathResult_OperationStatus? operStatus, + }) { + final result = create(); + if (requestedPath != null) result.requestedPath = requestedPath; + if (operStatus != null) result.operStatus = operStatus; + return result; + } + + DeregisterResp_DeregisteredPathResult._(); + + factory DeregisterResp_DeregisteredPathResult.fromBuffer( + $core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeregisterResp_DeregisteredPathResult.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeregisterResp.DeregisteredPathResult', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'requestedPath') + ..aOM( + 2, _omitFieldNames ? '' : 'operStatus', + subBuilder: + DeregisterResp_DeregisteredPathResult_OperationStatus.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp_DeregisteredPathResult copyWith( + void Function(DeregisterResp_DeregisteredPathResult) updates) => + super.copyWith((message) => + updates(message as DeregisterResp_DeregisteredPathResult)) + as DeregisterResp_DeregisteredPathResult; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult create() => + DeregisterResp_DeregisteredPathResult._(); + @$core.override + DeregisterResp_DeregisteredPathResult createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeregisterResp_DeregisteredPathResult getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor< + DeregisterResp_DeregisteredPathResult>(create); + static DeregisterResp_DeregisteredPathResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get requestedPath => $_getSZ(0); + @$pb.TagNumber(1) + set requestedPath($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasRequestedPath() => $_has(0); + @$pb.TagNumber(1) + void clearRequestedPath() => $_clearField(1); + + @$pb.TagNumber(2) + DeregisterResp_DeregisteredPathResult_OperationStatus get operStatus => + $_getN(1); + @$pb.TagNumber(2) + set operStatus(DeregisterResp_DeregisteredPathResult_OperationStatus value) => + $_setField(2, value); + @$pb.TagNumber(2) + $core.bool hasOperStatus() => $_has(1); + @$pb.TagNumber(2) + void clearOperStatus() => $_clearField(2); + @$pb.TagNumber(2) + DeregisterResp_DeregisteredPathResult_OperationStatus ensureOperStatus() => + $_ensure(1); +} + +class DeregisterResp extends $pb.GeneratedMessage { + factory DeregisterResp({ + $core.Iterable? + deregisteredPathResults, + }) { + final result = create(); + if (deregisteredPathResults != null) + result.deregisteredPathResults.addAll(deregisteredPathResults); + return result; + } + + DeregisterResp._(); + + factory DeregisterResp.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DeregisterResp.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DeregisterResp', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp'), + createEmptyInstance: create) + ..pPM( + 1, _omitFieldNames ? '' : 'deregisteredPathResults', + subBuilder: DeregisterResp_DeregisteredPathResult.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DeregisterResp copyWith(void Function(DeregisterResp) updates) => + super.copyWith((message) => updates(message as DeregisterResp)) + as DeregisterResp; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DeregisterResp create() => DeregisterResp._(); + @$core.override + DeregisterResp createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DeregisterResp getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DeregisterResp? _defaultInstance; + + @$pb.TagNumber(1) + $pb.PbList + get deregisteredPathResults => $_getList(0); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_msg.pbenum.dart b/packages/usp_protocol_common/lib/src/generated/usp_msg.pbenum.dart new file mode 100644 index 000000000..1e7a81664 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_msg.pbenum.dart @@ -0,0 +1,262 @@ +// This is a generated file - do not edit. +// +// Generated from usp_msg.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class Header_MsgType extends $pb.ProtobufEnum { + static const Header_MsgType ERROR = + Header_MsgType._(0, _omitEnumNames ? '' : 'ERROR'); + static const Header_MsgType GET = + Header_MsgType._(1, _omitEnumNames ? '' : 'GET'); + static const Header_MsgType GET_RESP = + Header_MsgType._(2, _omitEnumNames ? '' : 'GET_RESP'); + static const Header_MsgType NOTIFY = + Header_MsgType._(3, _omitEnumNames ? '' : 'NOTIFY'); + static const Header_MsgType SET = + Header_MsgType._(4, _omitEnumNames ? '' : 'SET'); + static const Header_MsgType SET_RESP = + Header_MsgType._(5, _omitEnumNames ? '' : 'SET_RESP'); + static const Header_MsgType OPERATE = + Header_MsgType._(6, _omitEnumNames ? '' : 'OPERATE'); + static const Header_MsgType OPERATE_RESP = + Header_MsgType._(7, _omitEnumNames ? '' : 'OPERATE_RESP'); + static const Header_MsgType ADD = + Header_MsgType._(8, _omitEnumNames ? '' : 'ADD'); + static const Header_MsgType ADD_RESP = + Header_MsgType._(9, _omitEnumNames ? '' : 'ADD_RESP'); + static const Header_MsgType DELETE = + Header_MsgType._(10, _omitEnumNames ? '' : 'DELETE'); + static const Header_MsgType DELETE_RESP = + Header_MsgType._(11, _omitEnumNames ? '' : 'DELETE_RESP'); + static const Header_MsgType GET_SUPPORTED_DM = + Header_MsgType._(12, _omitEnumNames ? '' : 'GET_SUPPORTED_DM'); + static const Header_MsgType GET_SUPPORTED_DM_RESP = + Header_MsgType._(13, _omitEnumNames ? '' : 'GET_SUPPORTED_DM_RESP'); + static const Header_MsgType GET_INSTANCES = + Header_MsgType._(14, _omitEnumNames ? '' : 'GET_INSTANCES'); + static const Header_MsgType GET_INSTANCES_RESP = + Header_MsgType._(15, _omitEnumNames ? '' : 'GET_INSTANCES_RESP'); + static const Header_MsgType NOTIFY_RESP = + Header_MsgType._(16, _omitEnumNames ? '' : 'NOTIFY_RESP'); + static const Header_MsgType GET_SUPPORTED_PROTO = + Header_MsgType._(17, _omitEnumNames ? '' : 'GET_SUPPORTED_PROTO'); + static const Header_MsgType GET_SUPPORTED_PROTO_RESP = + Header_MsgType._(18, _omitEnumNames ? '' : 'GET_SUPPORTED_PROTO_RESP'); + static const Header_MsgType REGISTER = + Header_MsgType._(19, _omitEnumNames ? '' : 'REGISTER'); + static const Header_MsgType REGISTER_RESP = + Header_MsgType._(20, _omitEnumNames ? '' : 'REGISTER_RESP'); + static const Header_MsgType DEREGISTER = + Header_MsgType._(21, _omitEnumNames ? '' : 'DEREGISTER'); + static const Header_MsgType DEREGISTER_RESP = + Header_MsgType._(22, _omitEnumNames ? '' : 'DEREGISTER_RESP'); + + static const $core.List values = [ + ERROR, + GET, + GET_RESP, + NOTIFY, + SET, + SET_RESP, + OPERATE, + OPERATE_RESP, + ADD, + ADD_RESP, + DELETE, + DELETE_RESP, + GET_SUPPORTED_DM, + GET_SUPPORTED_DM_RESP, + GET_INSTANCES, + GET_INSTANCES_RESP, + NOTIFY_RESP, + GET_SUPPORTED_PROTO, + GET_SUPPORTED_PROTO_RESP, + REGISTER, + REGISTER_RESP, + DEREGISTER, + DEREGISTER_RESP, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 22); + static Header_MsgType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const Header_MsgType._(super.value, super.name); +} + +class GetSupportedDMResp_ParamAccessType extends $pb.ProtobufEnum { + static const GetSupportedDMResp_ParamAccessType PARAM_READ_ONLY = + GetSupportedDMResp_ParamAccessType._( + 0, _omitEnumNames ? '' : 'PARAM_READ_ONLY'); + static const GetSupportedDMResp_ParamAccessType PARAM_READ_WRITE = + GetSupportedDMResp_ParamAccessType._( + 1, _omitEnumNames ? '' : 'PARAM_READ_WRITE'); + static const GetSupportedDMResp_ParamAccessType PARAM_WRITE_ONLY = + GetSupportedDMResp_ParamAccessType._( + 2, _omitEnumNames ? '' : 'PARAM_WRITE_ONLY'); + + static const $core.List values = + [ + PARAM_READ_ONLY, + PARAM_READ_WRITE, + PARAM_WRITE_ONLY, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 2); + static GetSupportedDMResp_ParamAccessType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const GetSupportedDMResp_ParamAccessType._(super.value, super.name); +} + +class GetSupportedDMResp_ObjAccessType extends $pb.ProtobufEnum { + static const GetSupportedDMResp_ObjAccessType OBJ_READ_ONLY = + GetSupportedDMResp_ObjAccessType._( + 0, _omitEnumNames ? '' : 'OBJ_READ_ONLY'); + static const GetSupportedDMResp_ObjAccessType OBJ_ADD_DELETE = + GetSupportedDMResp_ObjAccessType._( + 1, _omitEnumNames ? '' : 'OBJ_ADD_DELETE'); + static const GetSupportedDMResp_ObjAccessType OBJ_ADD_ONLY = + GetSupportedDMResp_ObjAccessType._( + 2, _omitEnumNames ? '' : 'OBJ_ADD_ONLY'); + static const GetSupportedDMResp_ObjAccessType OBJ_DELETE_ONLY = + GetSupportedDMResp_ObjAccessType._( + 3, _omitEnumNames ? '' : 'OBJ_DELETE_ONLY'); + + static const $core.List values = + [ + OBJ_READ_ONLY, + OBJ_ADD_DELETE, + OBJ_ADD_ONLY, + OBJ_DELETE_ONLY, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 3); + static GetSupportedDMResp_ObjAccessType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const GetSupportedDMResp_ObjAccessType._(super.value, super.name); +} + +class GetSupportedDMResp_ParamValueType extends $pb.ProtobufEnum { + static const GetSupportedDMResp_ParamValueType PARAM_UNKNOWN = + GetSupportedDMResp_ParamValueType._( + 0, _omitEnumNames ? '' : 'PARAM_UNKNOWN'); + static const GetSupportedDMResp_ParamValueType PARAM_BASE_64 = + GetSupportedDMResp_ParamValueType._( + 1, _omitEnumNames ? '' : 'PARAM_BASE_64'); + static const GetSupportedDMResp_ParamValueType PARAM_BOOLEAN = + GetSupportedDMResp_ParamValueType._( + 2, _omitEnumNames ? '' : 'PARAM_BOOLEAN'); + static const GetSupportedDMResp_ParamValueType PARAM_DATE_TIME = + GetSupportedDMResp_ParamValueType._( + 3, _omitEnumNames ? '' : 'PARAM_DATE_TIME'); + static const GetSupportedDMResp_ParamValueType PARAM_DECIMAL = + GetSupportedDMResp_ParamValueType._( + 4, _omitEnumNames ? '' : 'PARAM_DECIMAL'); + static const GetSupportedDMResp_ParamValueType PARAM_HEX_BINARY = + GetSupportedDMResp_ParamValueType._( + 5, _omitEnumNames ? '' : 'PARAM_HEX_BINARY'); + static const GetSupportedDMResp_ParamValueType PARAM_INT = + GetSupportedDMResp_ParamValueType._(6, _omitEnumNames ? '' : 'PARAM_INT'); + static const GetSupportedDMResp_ParamValueType PARAM_LONG = + GetSupportedDMResp_ParamValueType._( + 7, _omitEnumNames ? '' : 'PARAM_LONG'); + static const GetSupportedDMResp_ParamValueType PARAM_STRING = + GetSupportedDMResp_ParamValueType._( + 8, _omitEnumNames ? '' : 'PARAM_STRING'); + static const GetSupportedDMResp_ParamValueType PARAM_UNSIGNED_INT = + GetSupportedDMResp_ParamValueType._( + 9, _omitEnumNames ? '' : 'PARAM_UNSIGNED_INT'); + static const GetSupportedDMResp_ParamValueType PARAM_UNSIGNED_LONG = + GetSupportedDMResp_ParamValueType._( + 10, _omitEnumNames ? '' : 'PARAM_UNSIGNED_LONG'); + + static const $core.List values = + [ + PARAM_UNKNOWN, + PARAM_BASE_64, + PARAM_BOOLEAN, + PARAM_DATE_TIME, + PARAM_DECIMAL, + PARAM_HEX_BINARY, + PARAM_INT, + PARAM_LONG, + PARAM_STRING, + PARAM_UNSIGNED_INT, + PARAM_UNSIGNED_LONG, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 10); + static GetSupportedDMResp_ParamValueType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const GetSupportedDMResp_ParamValueType._(super.value, super.name); +} + +class GetSupportedDMResp_ValueChangeType extends $pb.ProtobufEnum { + static const GetSupportedDMResp_ValueChangeType VALUE_CHANGE_UNKNOWN = + GetSupportedDMResp_ValueChangeType._( + 0, _omitEnumNames ? '' : 'VALUE_CHANGE_UNKNOWN'); + static const GetSupportedDMResp_ValueChangeType VALUE_CHANGE_ALLOWED = + GetSupportedDMResp_ValueChangeType._( + 1, _omitEnumNames ? '' : 'VALUE_CHANGE_ALLOWED'); + static const GetSupportedDMResp_ValueChangeType VALUE_CHANGE_WILL_IGNORE = + GetSupportedDMResp_ValueChangeType._( + 2, _omitEnumNames ? '' : 'VALUE_CHANGE_WILL_IGNORE'); + + static const $core.List values = + [ + VALUE_CHANGE_UNKNOWN, + VALUE_CHANGE_ALLOWED, + VALUE_CHANGE_WILL_IGNORE, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 2); + static GetSupportedDMResp_ValueChangeType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const GetSupportedDMResp_ValueChangeType._(super.value, super.name); +} + +class GetSupportedDMResp_CmdType extends $pb.ProtobufEnum { + static const GetSupportedDMResp_CmdType CMD_UNKNOWN = + GetSupportedDMResp_CmdType._(0, _omitEnumNames ? '' : 'CMD_UNKNOWN'); + static const GetSupportedDMResp_CmdType CMD_SYNC = + GetSupportedDMResp_CmdType._(1, _omitEnumNames ? '' : 'CMD_SYNC'); + static const GetSupportedDMResp_CmdType CMD_ASYNC = + GetSupportedDMResp_CmdType._(2, _omitEnumNames ? '' : 'CMD_ASYNC'); + + static const $core.List values = + [ + CMD_UNKNOWN, + CMD_SYNC, + CMD_ASYNC, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 2); + static GetSupportedDMResp_CmdType? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const GetSupportedDMResp_CmdType._(super.value, super.name); +} + +const $core.bool _omitEnumNames = + $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_msg.pbjson.dart b/packages/usp_protocol_common/lib/src/generated/usp_msg.pbjson.dart new file mode 100644 index 000000000..2954944a4 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_msg.pbjson.dart @@ -0,0 +1,2172 @@ +// This is a generated file - do not edit. +// +// Generated from usp_msg.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use msgDescriptor instead') +const Msg$json = { + '1': 'Msg', + '2': [ + { + '1': 'header', + '3': 1, + '4': 1, + '5': 11, + '6': '.usp.Header', + '10': 'header' + }, + {'1': 'body', '3': 2, '4': 1, '5': 11, '6': '.usp.Body', '10': 'body'}, + ], +}; + +/// Descriptor for `Msg`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List msgDescriptor = $convert.base64Decode( + 'CgNNc2cSIwoGaGVhZGVyGAEgASgLMgsudXNwLkhlYWRlclIGaGVhZGVyEh0KBGJvZHkYAiABKA' + 'syCS51c3AuQm9keVIEYm9keQ=='); + +@$core.Deprecated('Use headerDescriptor instead') +const Header$json = { + '1': 'Header', + '2': [ + {'1': 'msg_id', '3': 1, '4': 1, '5': 9, '10': 'msgId'}, + { + '1': 'msg_type', + '3': 2, + '4': 1, + '5': 14, + '6': '.usp.Header.MsgType', + '10': 'msgType' + }, + ], + '4': [Header_MsgType$json], +}; + +@$core.Deprecated('Use headerDescriptor instead') +const Header_MsgType$json = { + '1': 'MsgType', + '2': [ + {'1': 'ERROR', '2': 0}, + {'1': 'GET', '2': 1}, + {'1': 'GET_RESP', '2': 2}, + {'1': 'NOTIFY', '2': 3}, + {'1': 'SET', '2': 4}, + {'1': 'SET_RESP', '2': 5}, + {'1': 'OPERATE', '2': 6}, + {'1': 'OPERATE_RESP', '2': 7}, + {'1': 'ADD', '2': 8}, + {'1': 'ADD_RESP', '2': 9}, + {'1': 'DELETE', '2': 10}, + {'1': 'DELETE_RESP', '2': 11}, + {'1': 'GET_SUPPORTED_DM', '2': 12}, + {'1': 'GET_SUPPORTED_DM_RESP', '2': 13}, + {'1': 'GET_INSTANCES', '2': 14}, + {'1': 'GET_INSTANCES_RESP', '2': 15}, + {'1': 'NOTIFY_RESP', '2': 16}, + {'1': 'GET_SUPPORTED_PROTO', '2': 17}, + {'1': 'GET_SUPPORTED_PROTO_RESP', '2': 18}, + {'1': 'REGISTER', '2': 19}, + {'1': 'REGISTER_RESP', '2': 20}, + {'1': 'DEREGISTER', '2': 21}, + {'1': 'DEREGISTER_RESP', '2': 22}, + ], +}; + +/// Descriptor for `Header`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List headerDescriptor = $convert.base64Decode( + 'CgZIZWFkZXISFQoGbXNnX2lkGAEgASgJUgVtc2dJZBIuCghtc2dfdHlwZRgCIAEoDjITLnVzcC' + '5IZWFkZXIuTXNnVHlwZVIHbXNnVHlwZSKLAwoHTXNnVHlwZRIJCgVFUlJPUhAAEgcKA0dFVBAB' + 'EgwKCEdFVF9SRVNQEAISCgoGTk9USUZZEAMSBwoDU0VUEAQSDAoIU0VUX1JFU1AQBRILCgdPUE' + 'VSQVRFEAYSEAoMT1BFUkFURV9SRVNQEAcSBwoDQUREEAgSDAoIQUREX1JFU1AQCRIKCgZERUxF' + 'VEUQChIPCgtERUxFVEVfUkVTUBALEhQKEEdFVF9TVVBQT1JURURfRE0QDBIZChVHRVRfU1VQUE' + '9SVEVEX0RNX1JFU1AQDRIRCg1HRVRfSU5TVEFOQ0VTEA4SFgoSR0VUX0lOU1RBTkNFU19SRVNQ' + 'EA8SDwoLTk9USUZZX1JFU1AQEBIXChNHRVRfU1VQUE9SVEVEX1BST1RPEBESHAoYR0VUX1NVUF' + 'BPUlRFRF9QUk9UT19SRVNQEBISDAoIUkVHSVNURVIQExIRCg1SRUdJU1RFUl9SRVNQEBQSDgoK' + 'REVSRUdJU1RFUhAVEhMKD0RFUkVHSVNURVJfUkVTUBAW'); + +@$core.Deprecated('Use bodyDescriptor instead') +const Body$json = { + '1': 'Body', + '2': [ + { + '1': 'request', + '3': 1, + '4': 1, + '5': 11, + '6': '.usp.Request', + '9': 0, + '10': 'request' + }, + { + '1': 'response', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.Response', + '9': 0, + '10': 'response' + }, + { + '1': 'error', + '3': 3, + '4': 1, + '5': 11, + '6': '.usp.Error', + '9': 0, + '10': 'error' + }, + ], + '8': [ + {'1': 'msg_body'}, + ], +}; + +/// Descriptor for `Body`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List bodyDescriptor = $convert.base64Decode( + 'CgRCb2R5EigKB3JlcXVlc3QYASABKAsyDC51c3AuUmVxdWVzdEgAUgdyZXF1ZXN0EisKCHJlc3' + 'BvbnNlGAIgASgLMg0udXNwLlJlc3BvbnNlSABSCHJlc3BvbnNlEiIKBWVycm9yGAMgASgLMgou' + 'dXNwLkVycm9ySABSBWVycm9yQgoKCG1zZ19ib2R5'); + +@$core.Deprecated('Use requestDescriptor instead') +const Request$json = { + '1': 'Request', + '2': [ + {'1': 'get', '3': 1, '4': 1, '5': 11, '6': '.usp.Get', '9': 0, '10': 'get'}, + { + '1': 'get_supported_dm', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.GetSupportedDM', + '9': 0, + '10': 'getSupportedDm' + }, + { + '1': 'get_instances', + '3': 3, + '4': 1, + '5': 11, + '6': '.usp.GetInstances', + '9': 0, + '10': 'getInstances' + }, + {'1': 'set', '3': 4, '4': 1, '5': 11, '6': '.usp.Set', '9': 0, '10': 'set'}, + {'1': 'add', '3': 5, '4': 1, '5': 11, '6': '.usp.Add', '9': 0, '10': 'add'}, + { + '1': 'delete', + '3': 6, + '4': 1, + '5': 11, + '6': '.usp.Delete', + '9': 0, + '10': 'delete' + }, + { + '1': 'operate', + '3': 7, + '4': 1, + '5': 11, + '6': '.usp.Operate', + '9': 0, + '10': 'operate' + }, + { + '1': 'notify', + '3': 8, + '4': 1, + '5': 11, + '6': '.usp.Notify', + '9': 0, + '10': 'notify' + }, + { + '1': 'get_supported_protocol', + '3': 9, + '4': 1, + '5': 11, + '6': '.usp.GetSupportedProtocol', + '9': 0, + '10': 'getSupportedProtocol' + }, + { + '1': 'register', + '3': 10, + '4': 1, + '5': 11, + '6': '.usp.Register', + '9': 0, + '10': 'register' + }, + { + '1': 'deregister', + '3': 11, + '4': 1, + '5': 11, + '6': '.usp.Deregister', + '9': 0, + '10': 'deregister' + }, + ], + '8': [ + {'1': 'req_type'}, + ], +}; + +/// Descriptor for `Request`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List requestDescriptor = $convert.base64Decode( + 'CgdSZXF1ZXN0EhwKA2dldBgBIAEoCzIILnVzcC5HZXRIAFIDZ2V0Ej8KEGdldF9zdXBwb3J0ZW' + 'RfZG0YAiABKAsyEy51c3AuR2V0U3VwcG9ydGVkRE1IAFIOZ2V0U3VwcG9ydGVkRG0SOAoNZ2V0' + 'X2luc3RhbmNlcxgDIAEoCzIRLnVzcC5HZXRJbnN0YW5jZXNIAFIMZ2V0SW5zdGFuY2VzEhwKA3' + 'NldBgEIAEoCzIILnVzcC5TZXRIAFIDc2V0EhwKA2FkZBgFIAEoCzIILnVzcC5BZGRIAFIDYWRk' + 'EiUKBmRlbGV0ZRgGIAEoCzILLnVzcC5EZWxldGVIAFIGZGVsZXRlEigKB29wZXJhdGUYByABKA' + 'syDC51c3AuT3BlcmF0ZUgAUgdvcGVyYXRlEiUKBm5vdGlmeRgIIAEoCzILLnVzcC5Ob3RpZnlI' + 'AFIGbm90aWZ5ElEKFmdldF9zdXBwb3J0ZWRfcHJvdG9jb2wYCSABKAsyGS51c3AuR2V0U3VwcG' + '9ydGVkUHJvdG9jb2xIAFIUZ2V0U3VwcG9ydGVkUHJvdG9jb2wSKwoIcmVnaXN0ZXIYCiABKAsy' + 'DS51c3AuUmVnaXN0ZXJIAFIIcmVnaXN0ZXISMQoKZGVyZWdpc3RlchgLIAEoCzIPLnVzcC5EZX' + 'JlZ2lzdGVySABSCmRlcmVnaXN0ZXJCCgoIcmVxX3R5cGU='); + +@$core.Deprecated('Use responseDescriptor instead') +const Response$json = { + '1': 'Response', + '2': [ + { + '1': 'get_resp', + '3': 1, + '4': 1, + '5': 11, + '6': '.usp.GetResp', + '9': 0, + '10': 'getResp' + }, + { + '1': 'get_supported_dm_resp', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.GetSupportedDMResp', + '9': 0, + '10': 'getSupportedDmResp' + }, + { + '1': 'get_instances_resp', + '3': 3, + '4': 1, + '5': 11, + '6': '.usp.GetInstancesResp', + '9': 0, + '10': 'getInstancesResp' + }, + { + '1': 'set_resp', + '3': 4, + '4': 1, + '5': 11, + '6': '.usp.SetResp', + '9': 0, + '10': 'setResp' + }, + { + '1': 'add_resp', + '3': 5, + '4': 1, + '5': 11, + '6': '.usp.AddResp', + '9': 0, + '10': 'addResp' + }, + { + '1': 'delete_resp', + '3': 6, + '4': 1, + '5': 11, + '6': '.usp.DeleteResp', + '9': 0, + '10': 'deleteResp' + }, + { + '1': 'operate_resp', + '3': 7, + '4': 1, + '5': 11, + '6': '.usp.OperateResp', + '9': 0, + '10': 'operateResp' + }, + { + '1': 'notify_resp', + '3': 8, + '4': 1, + '5': 11, + '6': '.usp.NotifyResp', + '9': 0, + '10': 'notifyResp' + }, + { + '1': 'get_supported_protocol_resp', + '3': 9, + '4': 1, + '5': 11, + '6': '.usp.GetSupportedProtocolResp', + '9': 0, + '10': 'getSupportedProtocolResp' + }, + { + '1': 'register_resp', + '3': 10, + '4': 1, + '5': 11, + '6': '.usp.RegisterResp', + '9': 0, + '10': 'registerResp' + }, + { + '1': 'deregister_resp', + '3': 11, + '4': 1, + '5': 11, + '6': '.usp.DeregisterResp', + '9': 0, + '10': 'deregisterResp' + }, + ], + '8': [ + {'1': 'resp_type'}, + ], +}; + +/// Descriptor for `Response`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List responseDescriptor = $convert.base64Decode( + 'CghSZXNwb25zZRIpCghnZXRfcmVzcBgBIAEoCzIMLnVzcC5HZXRSZXNwSABSB2dldFJlc3ASTA' + 'oVZ2V0X3N1cHBvcnRlZF9kbV9yZXNwGAIgASgLMhcudXNwLkdldFN1cHBvcnRlZERNUmVzcEgA' + 'UhJnZXRTdXBwb3J0ZWREbVJlc3ASRQoSZ2V0X2luc3RhbmNlc19yZXNwGAMgASgLMhUudXNwLk' + 'dldEluc3RhbmNlc1Jlc3BIAFIQZ2V0SW5zdGFuY2VzUmVzcBIpCghzZXRfcmVzcBgEIAEoCzIM' + 'LnVzcC5TZXRSZXNwSABSB3NldFJlc3ASKQoIYWRkX3Jlc3AYBSABKAsyDC51c3AuQWRkUmVzcE' + 'gAUgdhZGRSZXNwEjIKC2RlbGV0ZV9yZXNwGAYgASgLMg8udXNwLkRlbGV0ZVJlc3BIAFIKZGVs' + 'ZXRlUmVzcBI1CgxvcGVyYXRlX3Jlc3AYByABKAsyEC51c3AuT3BlcmF0ZVJlc3BIAFILb3Blcm' + 'F0ZVJlc3ASMgoLbm90aWZ5X3Jlc3AYCCABKAsyDy51c3AuTm90aWZ5UmVzcEgAUgpub3RpZnlS' + 'ZXNwEl4KG2dldF9zdXBwb3J0ZWRfcHJvdG9jb2xfcmVzcBgJIAEoCzIdLnVzcC5HZXRTdXBwb3' + 'J0ZWRQcm90b2NvbFJlc3BIAFIYZ2V0U3VwcG9ydGVkUHJvdG9jb2xSZXNwEjgKDXJlZ2lzdGVy' + 'X3Jlc3AYCiABKAsyES51c3AuUmVnaXN0ZXJSZXNwSABSDHJlZ2lzdGVyUmVzcBI+Cg9kZXJlZ2' + 'lzdGVyX3Jlc3AYCyABKAsyEy51c3AuRGVyZWdpc3RlclJlc3BIAFIOZGVyZWdpc3RlclJlc3BC' + 'CwoJcmVzcF90eXBl'); + +@$core.Deprecated('Use errorDescriptor instead') +const Error$json = { + '1': 'Error', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + { + '1': 'param_errs', + '3': 3, + '4': 3, + '5': 11, + '6': '.usp.Error.ParamError', + '10': 'paramErrs' + }, + ], + '3': [Error_ParamError$json], +}; + +@$core.Deprecated('Use errorDescriptor instead') +const Error_ParamError$json = { + '1': 'ParamError', + '2': [ + {'1': 'param_path', '3': 1, '4': 1, '5': 9, '10': 'paramPath'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +/// Descriptor for `Error`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List errorDescriptor = $convert.base64Decode( + 'CgVFcnJvchIZCghlcnJfY29kZRgBIAEoB1IHZXJyQ29kZRIXCgdlcnJfbXNnGAIgASgJUgZlcn' + 'JNc2cSNAoKcGFyYW1fZXJycxgDIAMoCzIVLnVzcC5FcnJvci5QYXJhbUVycm9yUglwYXJhbUVy' + 'cnMaXwoKUGFyYW1FcnJvchIdCgpwYXJhbV9wYXRoGAEgASgJUglwYXJhbVBhdGgSGQoIZXJyX2' + 'NvZGUYAiABKAdSB2VyckNvZGUSFwoHZXJyX21zZxgDIAEoCVIGZXJyTXNn'); + +@$core.Deprecated('Use getDescriptor instead') +const Get$json = { + '1': 'Get', + '2': [ + {'1': 'param_paths', '3': 1, '4': 3, '5': 9, '10': 'paramPaths'}, + {'1': 'max_depth', '3': 2, '4': 1, '5': 7, '10': 'maxDepth'}, + ], +}; + +/// Descriptor for `Get`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getDescriptor = $convert.base64Decode( + 'CgNHZXQSHwoLcGFyYW1fcGF0aHMYASADKAlSCnBhcmFtUGF0aHMSGwoJbWF4X2RlcHRoGAIgAS' + 'gHUghtYXhEZXB0aA=='); + +@$core.Deprecated('Use getRespDescriptor instead') +const GetResp$json = { + '1': 'GetResp', + '2': [ + { + '1': 'req_path_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.GetResp.RequestedPathResult', + '10': 'reqPathResults' + }, + ], + '3': [GetResp_RequestedPathResult$json, GetResp_ResolvedPathResult$json], +}; + +@$core.Deprecated('Use getRespDescriptor instead') +const GetResp_RequestedPathResult$json = { + '1': 'RequestedPathResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + { + '1': 'resolved_path_results', + '3': 4, + '4': 3, + '5': 11, + '6': '.usp.GetResp.ResolvedPathResult', + '10': 'resolvedPathResults' + }, + ], +}; + +@$core.Deprecated('Use getRespDescriptor instead') +const GetResp_ResolvedPathResult$json = { + '1': 'ResolvedPathResult', + '2': [ + {'1': 'resolved_path', '3': 1, '4': 1, '5': 9, '10': 'resolvedPath'}, + { + '1': 'result_params', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.GetResp.ResolvedPathResult.ResultParamsEntry', + '10': 'resultParams' + }, + ], + '3': [GetResp_ResolvedPathResult_ResultParamsEntry$json], +}; + +@$core.Deprecated('Use getRespDescriptor instead') +const GetResp_ResolvedPathResult_ResultParamsEntry$json = { + '1': 'ResultParamsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +/// Descriptor for `GetResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getRespDescriptor = $convert.base64Decode( + 'CgdHZXRSZXNwEkoKEHJlcV9wYXRoX3Jlc3VsdHMYASADKAsyIC51c3AuR2V0UmVzcC5SZXF1ZX' + 'N0ZWRQYXRoUmVzdWx0Ug5yZXFQYXRoUmVzdWx0cxrFAQoTUmVxdWVzdGVkUGF0aFJlc3VsdBIl' + 'Cg5yZXF1ZXN0ZWRfcGF0aBgBIAEoCVINcmVxdWVzdGVkUGF0aBIZCghlcnJfY29kZRgCIAEoB1' + 'IHZXJyQ29kZRIXCgdlcnJfbXNnGAMgASgJUgZlcnJNc2cSUwoVcmVzb2x2ZWRfcGF0aF9yZXN1' + 'bHRzGAQgAygLMh8udXNwLkdldFJlc3AuUmVzb2x2ZWRQYXRoUmVzdWx0UhNyZXNvbHZlZFBhdG' + 'hSZXN1bHRzGtIBChJSZXNvbHZlZFBhdGhSZXN1bHQSIwoNcmVzb2x2ZWRfcGF0aBgBIAEoCVIM' + 'cmVzb2x2ZWRQYXRoElYKDXJlc3VsdF9wYXJhbXMYAiADKAsyMS51c3AuR2V0UmVzcC5SZXNvbH' + 'ZlZFBhdGhSZXN1bHQuUmVzdWx0UGFyYW1zRW50cnlSDHJlc3VsdFBhcmFtcxo/ChFSZXN1bHRQ' + 'YXJhbXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgB'); + +@$core.Deprecated('Use getSupportedDMDescriptor instead') +const GetSupportedDM$json = { + '1': 'GetSupportedDM', + '2': [ + {'1': 'obj_paths', '3': 1, '4': 3, '5': 9, '10': 'objPaths'}, + {'1': 'first_level_only', '3': 2, '4': 1, '5': 8, '10': 'firstLevelOnly'}, + {'1': 'return_commands', '3': 3, '4': 1, '5': 8, '10': 'returnCommands'}, + {'1': 'return_events', '3': 4, '4': 1, '5': 8, '10': 'returnEvents'}, + {'1': 'return_params', '3': 5, '4': 1, '5': 8, '10': 'returnParams'}, + { + '1': 'return_unique_key_sets', + '3': 6, + '4': 1, + '5': 8, + '10': 'returnUniqueKeySets' + }, + ], +}; + +/// Descriptor for `GetSupportedDM`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getSupportedDMDescriptor = $convert.base64Decode( + 'Cg5HZXRTdXBwb3J0ZWRETRIbCglvYmpfcGF0aHMYASADKAlSCG9ialBhdGhzEigKEGZpcnN0X2' + 'xldmVsX29ubHkYAiABKAhSDmZpcnN0TGV2ZWxPbmx5EicKD3JldHVybl9jb21tYW5kcxgDIAEo' + 'CFIOcmV0dXJuQ29tbWFuZHMSIwoNcmV0dXJuX2V2ZW50cxgEIAEoCFIMcmV0dXJuRXZlbnRzEi' + 'MKDXJldHVybl9wYXJhbXMYBSABKAhSDHJldHVyblBhcmFtcxIzChZyZXR1cm5fdW5pcXVlX2tl' + 'eV9zZXRzGAYgASgIUhNyZXR1cm5VbmlxdWVLZXlTZXRz'); + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp$json = { + '1': 'GetSupportedDMResp', + '2': [ + { + '1': 'req_obj_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.RequestedObjectResult', + '10': 'reqObjResults' + }, + ], + '3': [ + GetSupportedDMResp_RequestedObjectResult$json, + GetSupportedDMResp_SupportedObjectResult$json, + GetSupportedDMResp_SupportedParamResult$json, + GetSupportedDMResp_SupportedCommandResult$json, + GetSupportedDMResp_SupportedEventResult$json, + GetSupportedDMResp_SupportedUniqueKeySet$json + ], + '4': [ + GetSupportedDMResp_ParamAccessType$json, + GetSupportedDMResp_ObjAccessType$json, + GetSupportedDMResp_ParamValueType$json, + GetSupportedDMResp_ValueChangeType$json, + GetSupportedDMResp_CmdType$json + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_RequestedObjectResult$json = { + '1': 'RequestedObjectResult', + '2': [ + {'1': 'req_obj_path', '3': 1, '4': 1, '5': 9, '10': 'reqObjPath'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + { + '1': 'data_model_inst_uri', + '3': 4, + '4': 1, + '5': 9, + '10': 'dataModelInstUri' + }, + { + '1': 'supported_objs', + '3': 5, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.SupportedObjectResult', + '10': 'supportedObjs' + }, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_SupportedObjectResult$json = { + '1': 'SupportedObjectResult', + '2': [ + { + '1': 'supported_obj_path', + '3': 1, + '4': 1, + '5': 9, + '10': 'supportedObjPath' + }, + { + '1': 'access', + '3': 2, + '4': 1, + '5': 14, + '6': '.usp.GetSupportedDMResp.ObjAccessType', + '10': 'access' + }, + {'1': 'is_multi_instance', '3': 3, '4': 1, '5': 8, '10': 'isMultiInstance'}, + { + '1': 'supported_commands', + '3': 4, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.SupportedCommandResult', + '10': 'supportedCommands' + }, + { + '1': 'supported_events', + '3': 5, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.SupportedEventResult', + '10': 'supportedEvents' + }, + { + '1': 'supported_params', + '3': 6, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.SupportedParamResult', + '10': 'supportedParams' + }, + {'1': 'divergent_paths', '3': 7, '4': 3, '5': 9, '10': 'divergentPaths'}, + { + '1': 'unique_key_sets', + '3': 8, + '4': 3, + '5': 11, + '6': '.usp.GetSupportedDMResp.SupportedUniqueKeySet', + '10': 'uniqueKeySets' + }, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_SupportedParamResult$json = { + '1': 'SupportedParamResult', + '2': [ + {'1': 'param_name', '3': 1, '4': 1, '5': 9, '10': 'paramName'}, + { + '1': 'access', + '3': 2, + '4': 1, + '5': 14, + '6': '.usp.GetSupportedDMResp.ParamAccessType', + '10': 'access' + }, + { + '1': 'value_type', + '3': 3, + '4': 1, + '5': 14, + '6': '.usp.GetSupportedDMResp.ParamValueType', + '10': 'valueType' + }, + { + '1': 'value_change', + '3': 4, + '4': 1, + '5': 14, + '6': '.usp.GetSupportedDMResp.ValueChangeType', + '10': 'valueChange' + }, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_SupportedCommandResult$json = { + '1': 'SupportedCommandResult', + '2': [ + {'1': 'command_name', '3': 1, '4': 1, '5': 9, '10': 'commandName'}, + {'1': 'input_arg_names', '3': 2, '4': 3, '5': 9, '10': 'inputArgNames'}, + {'1': 'output_arg_names', '3': 3, '4': 3, '5': 9, '10': 'outputArgNames'}, + { + '1': 'command_type', + '3': 4, + '4': 1, + '5': 14, + '6': '.usp.GetSupportedDMResp.CmdType', + '10': 'commandType' + }, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_SupportedEventResult$json = { + '1': 'SupportedEventResult', + '2': [ + {'1': 'event_name', '3': 1, '4': 1, '5': 9, '10': 'eventName'}, + {'1': 'arg_names', '3': 2, '4': 3, '5': 9, '10': 'argNames'}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_SupportedUniqueKeySet$json = { + '1': 'SupportedUniqueKeySet', + '2': [ + {'1': 'key_names', '3': 1, '4': 3, '5': 9, '10': 'keyNames'}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_ParamAccessType$json = { + '1': 'ParamAccessType', + '2': [ + {'1': 'PARAM_READ_ONLY', '2': 0}, + {'1': 'PARAM_READ_WRITE', '2': 1}, + {'1': 'PARAM_WRITE_ONLY', '2': 2}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_ObjAccessType$json = { + '1': 'ObjAccessType', + '2': [ + {'1': 'OBJ_READ_ONLY', '2': 0}, + {'1': 'OBJ_ADD_DELETE', '2': 1}, + {'1': 'OBJ_ADD_ONLY', '2': 2}, + {'1': 'OBJ_DELETE_ONLY', '2': 3}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_ParamValueType$json = { + '1': 'ParamValueType', + '2': [ + {'1': 'PARAM_UNKNOWN', '2': 0}, + {'1': 'PARAM_BASE_64', '2': 1}, + {'1': 'PARAM_BOOLEAN', '2': 2}, + {'1': 'PARAM_DATE_TIME', '2': 3}, + {'1': 'PARAM_DECIMAL', '2': 4}, + {'1': 'PARAM_HEX_BINARY', '2': 5}, + {'1': 'PARAM_INT', '2': 6}, + {'1': 'PARAM_LONG', '2': 7}, + {'1': 'PARAM_STRING', '2': 8}, + {'1': 'PARAM_UNSIGNED_INT', '2': 9}, + {'1': 'PARAM_UNSIGNED_LONG', '2': 10}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_ValueChangeType$json = { + '1': 'ValueChangeType', + '2': [ + {'1': 'VALUE_CHANGE_UNKNOWN', '2': 0}, + {'1': 'VALUE_CHANGE_ALLOWED', '2': 1}, + {'1': 'VALUE_CHANGE_WILL_IGNORE', '2': 2}, + ], +}; + +@$core.Deprecated('Use getSupportedDMRespDescriptor instead') +const GetSupportedDMResp_CmdType$json = { + '1': 'CmdType', + '2': [ + {'1': 'CMD_UNKNOWN', '2': 0}, + {'1': 'CMD_SYNC', '2': 1}, + {'1': 'CMD_ASYNC', '2': 2}, + ], +}; + +/// Descriptor for `GetSupportedDMResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getSupportedDMRespDescriptor = $convert.base64Decode( + 'ChJHZXRTdXBwb3J0ZWRETVJlc3ASVQoPcmVxX29ial9yZXN1bHRzGAEgAygLMi0udXNwLkdldF' + 'N1cHBvcnRlZERNUmVzcC5SZXF1ZXN0ZWRPYmplY3RSZXN1bHRSDXJlcU9ialJlc3VsdHMa8gEK' + 'FVJlcXVlc3RlZE9iamVjdFJlc3VsdBIgCgxyZXFfb2JqX3BhdGgYASABKAlSCnJlcU9ialBhdG' + 'gSGQoIZXJyX2NvZGUYAiABKAdSB2VyckNvZGUSFwoHZXJyX21zZxgDIAEoCVIGZXJyTXNnEi0K' + 'E2RhdGFfbW9kZWxfaW5zdF91cmkYBCABKAlSEGRhdGFNb2RlbEluc3RVcmkSVAoOc3VwcG9ydG' + 'VkX29ianMYBSADKAsyLS51c3AuR2V0U3VwcG9ydGVkRE1SZXNwLlN1cHBvcnRlZE9iamVjdFJl' + 'c3VsdFINc3VwcG9ydGVkT2JqcxrBBAoVU3VwcG9ydGVkT2JqZWN0UmVzdWx0EiwKEnN1cHBvcn' + 'RlZF9vYmpfcGF0aBgBIAEoCVIQc3VwcG9ydGVkT2JqUGF0aBI9CgZhY2Nlc3MYAiABKA4yJS51' + 'c3AuR2V0U3VwcG9ydGVkRE1SZXNwLk9iakFjY2Vzc1R5cGVSBmFjY2VzcxIqChFpc19tdWx0aV' + '9pbnN0YW5jZRgDIAEoCFIPaXNNdWx0aUluc3RhbmNlEl0KEnN1cHBvcnRlZF9jb21tYW5kcxgE' + 'IAMoCzIuLnVzcC5HZXRTdXBwb3J0ZWRETVJlc3AuU3VwcG9ydGVkQ29tbWFuZFJlc3VsdFIRc3' + 'VwcG9ydGVkQ29tbWFuZHMSVwoQc3VwcG9ydGVkX2V2ZW50cxgFIAMoCzIsLnVzcC5HZXRTdXBw' + 'b3J0ZWRETVJlc3AuU3VwcG9ydGVkRXZlbnRSZXN1bHRSD3N1cHBvcnRlZEV2ZW50cxJXChBzdX' + 'Bwb3J0ZWRfcGFyYW1zGAYgAygLMiwudXNwLkdldFN1cHBvcnRlZERNUmVzcC5TdXBwb3J0ZWRQ' + 'YXJhbVJlc3VsdFIPc3VwcG9ydGVkUGFyYW1zEicKD2RpdmVyZ2VudF9wYXRocxgHIAMoCVIOZG' + 'l2ZXJnZW50UGF0aHMSVQoPdW5pcXVlX2tleV9zZXRzGAggAygLMi0udXNwLkdldFN1cHBvcnRl' + 'ZERNUmVzcC5TdXBwb3J0ZWRVbmlxdWVLZXlTZXRSDXVuaXF1ZUtleVNldHMaiQIKFFN1cHBvcn' + 'RlZFBhcmFtUmVzdWx0Eh0KCnBhcmFtX25hbWUYASABKAlSCXBhcmFtTmFtZRI/CgZhY2Nlc3MY' + 'AiABKA4yJy51c3AuR2V0U3VwcG9ydGVkRE1SZXNwLlBhcmFtQWNjZXNzVHlwZVIGYWNjZXNzEk' + 'UKCnZhbHVlX3R5cGUYAyABKA4yJi51c3AuR2V0U3VwcG9ydGVkRE1SZXNwLlBhcmFtVmFsdWVU' + 'eXBlUgl2YWx1ZVR5cGUSSgoMdmFsdWVfY2hhbmdlGAQgASgOMicudXNwLkdldFN1cHBvcnRlZE' + 'RNUmVzcC5WYWx1ZUNoYW5nZVR5cGVSC3ZhbHVlQ2hhbmdlGtEBChZTdXBwb3J0ZWRDb21tYW5k' + 'UmVzdWx0EiEKDGNvbW1hbmRfbmFtZRgBIAEoCVILY29tbWFuZE5hbWUSJgoPaW5wdXRfYXJnX2' + '5hbWVzGAIgAygJUg1pbnB1dEFyZ05hbWVzEigKEG91dHB1dF9hcmdfbmFtZXMYAyADKAlSDm91' + 'dHB1dEFyZ05hbWVzEkIKDGNvbW1hbmRfdHlwZRgEIAEoDjIfLnVzcC5HZXRTdXBwb3J0ZWRETV' + 'Jlc3AuQ21kVHlwZVILY29tbWFuZFR5cGUaUgoUU3VwcG9ydGVkRXZlbnRSZXN1bHQSHQoKZXZl' + 'bnRfbmFtZRgBIAEoCVIJZXZlbnROYW1lEhsKCWFyZ19uYW1lcxgCIAMoCVIIYXJnTmFtZXMaNA' + 'oVU3VwcG9ydGVkVW5pcXVlS2V5U2V0EhsKCWtleV9uYW1lcxgBIAMoCVIIa2V5TmFtZXMiUgoP' + 'UGFyYW1BY2Nlc3NUeXBlEhMKD1BBUkFNX1JFQURfT05MWRAAEhQKEFBBUkFNX1JFQURfV1JJVE' + 'UQARIUChBQQVJBTV9XUklURV9PTkxZEAIiXQoNT2JqQWNjZXNzVHlwZRIRCg1PQkpfUkVBRF9P' + 'TkxZEAASEgoOT0JKX0FERF9ERUxFVEUQARIQCgxPQkpfQUREX09OTFkQAhITCg9PQkpfREVMRV' + 'RFX09OTFkQAyLpAQoOUGFyYW1WYWx1ZVR5cGUSEQoNUEFSQU1fVU5LTk9XThAAEhEKDVBBUkFN' + 'X0JBU0VfNjQQARIRCg1QQVJBTV9CT09MRUFOEAISEwoPUEFSQU1fREFURV9USU1FEAMSEQoNUE' + 'FSQU1fREVDSU1BTBAEEhQKEFBBUkFNX0hFWF9CSU5BUlkQBRINCglQQVJBTV9JTlQQBhIOCgpQ' + 'QVJBTV9MT05HEAcSEAoMUEFSQU1fU1RSSU5HEAgSFgoSUEFSQU1fVU5TSUdORURfSU5UEAkSFw' + 'oTUEFSQU1fVU5TSUdORURfTE9ORxAKImMKD1ZhbHVlQ2hhbmdlVHlwZRIYChRWQUxVRV9DSEFO' + 'R0VfVU5LTk9XThAAEhgKFFZBTFVFX0NIQU5HRV9BTExPV0VEEAESHAoYVkFMVUVfQ0hBTkdFX1' + 'dJTExfSUdOT1JFEAIiNwoHQ21kVHlwZRIPCgtDTURfVU5LTk9XThAAEgwKCENNRF9TWU5DEAES' + 'DQoJQ01EX0FTWU5DEAI='); + +@$core.Deprecated('Use getInstancesDescriptor instead') +const GetInstances$json = { + '1': 'GetInstances', + '2': [ + {'1': 'obj_paths', '3': 1, '4': 3, '5': 9, '10': 'objPaths'}, + {'1': 'first_level_only', '3': 2, '4': 1, '5': 8, '10': 'firstLevelOnly'}, + ], +}; + +/// Descriptor for `GetInstances`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getInstancesDescriptor = $convert.base64Decode( + 'CgxHZXRJbnN0YW5jZXMSGwoJb2JqX3BhdGhzGAEgAygJUghvYmpQYXRocxIoChBmaXJzdF9sZX' + 'ZlbF9vbmx5GAIgASgIUg5maXJzdExldmVsT25seQ=='); + +@$core.Deprecated('Use getInstancesRespDescriptor instead') +const GetInstancesResp$json = { + '1': 'GetInstancesResp', + '2': [ + { + '1': 'req_path_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.GetInstancesResp.RequestedPathResult', + '10': 'reqPathResults' + }, + ], + '3': [ + GetInstancesResp_RequestedPathResult$json, + GetInstancesResp_CurrInstance$json + ], +}; + +@$core.Deprecated('Use getInstancesRespDescriptor instead') +const GetInstancesResp_RequestedPathResult$json = { + '1': 'RequestedPathResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + { + '1': 'curr_insts', + '3': 4, + '4': 3, + '5': 11, + '6': '.usp.GetInstancesResp.CurrInstance', + '10': 'currInsts' + }, + ], +}; + +@$core.Deprecated('Use getInstancesRespDescriptor instead') +const GetInstancesResp_CurrInstance$json = { + '1': 'CurrInstance', + '2': [ + { + '1': 'instantiated_obj_path', + '3': 1, + '4': 1, + '5': 9, + '10': 'instantiatedObjPath' + }, + { + '1': 'unique_keys', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.GetInstancesResp.CurrInstance.UniqueKeysEntry', + '10': 'uniqueKeys' + }, + ], + '3': [GetInstancesResp_CurrInstance_UniqueKeysEntry$json], +}; + +@$core.Deprecated('Use getInstancesRespDescriptor instead') +const GetInstancesResp_CurrInstance_UniqueKeysEntry$json = { + '1': 'UniqueKeysEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +/// Descriptor for `GetInstancesResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getInstancesRespDescriptor = $convert.base64Decode( + 'ChBHZXRJbnN0YW5jZXNSZXNwElMKEHJlcV9wYXRoX3Jlc3VsdHMYASADKAsyKS51c3AuR2V0SW' + '5zdGFuY2VzUmVzcC5SZXF1ZXN0ZWRQYXRoUmVzdWx0Ug5yZXFQYXRoUmVzdWx0cxqzAQoTUmVx' + 'dWVzdGVkUGF0aFJlc3VsdBIlCg5yZXF1ZXN0ZWRfcGF0aBgBIAEoCVINcmVxdWVzdGVkUGF0aB' + 'IZCghlcnJfY29kZRgCIAEoB1IHZXJyQ29kZRIXCgdlcnJfbXNnGAMgASgJUgZlcnJNc2cSQQoK' + 'Y3Vycl9pbnN0cxgEIAMoCzIiLnVzcC5HZXRJbnN0YW5jZXNSZXNwLkN1cnJJbnN0YW5jZVIJY3' + 'Vyckluc3RzGtYBCgxDdXJySW5zdGFuY2USMgoVaW5zdGFudGlhdGVkX29ial9wYXRoGAEgASgJ' + 'UhNpbnN0YW50aWF0ZWRPYmpQYXRoElMKC3VuaXF1ZV9rZXlzGAIgAygLMjIudXNwLkdldEluc3' + 'RhbmNlc1Jlc3AuQ3Vyckluc3RhbmNlLlVuaXF1ZUtleXNFbnRyeVIKdW5pcXVlS2V5cxo9Cg9V' + 'bmlxdWVLZXlzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbHVlOg' + 'I4AQ=='); + +@$core.Deprecated('Use getSupportedProtocolDescriptor instead') +const GetSupportedProtocol$json = { + '1': 'GetSupportedProtocol', + '2': [ + { + '1': 'controller_supported_protocol_versions', + '3': 1, + '4': 1, + '5': 9, + '10': 'controllerSupportedProtocolVersions' + }, + ], +}; + +/// Descriptor for `GetSupportedProtocol`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getSupportedProtocolDescriptor = $convert.base64Decode( + 'ChRHZXRTdXBwb3J0ZWRQcm90b2NvbBJTCiZjb250cm9sbGVyX3N1cHBvcnRlZF9wcm90b2NvbF' + '92ZXJzaW9ucxgBIAEoCVIjY29udHJvbGxlclN1cHBvcnRlZFByb3RvY29sVmVyc2lvbnM='); + +@$core.Deprecated('Use getSupportedProtocolRespDescriptor instead') +const GetSupportedProtocolResp$json = { + '1': 'GetSupportedProtocolResp', + '2': [ + { + '1': 'agent_supported_protocol_versions', + '3': 1, + '4': 1, + '5': 9, + '10': 'agentSupportedProtocolVersions' + }, + ], +}; + +/// Descriptor for `GetSupportedProtocolResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List getSupportedProtocolRespDescriptor = + $convert.base64Decode( + 'ChhHZXRTdXBwb3J0ZWRQcm90b2NvbFJlc3ASSQohYWdlbnRfc3VwcG9ydGVkX3Byb3RvY29sX3' + 'ZlcnNpb25zGAEgASgJUh5hZ2VudFN1cHBvcnRlZFByb3RvY29sVmVyc2lvbnM='); + +@$core.Deprecated('Use addDescriptor instead') +const Add$json = { + '1': 'Add', + '2': [ + {'1': 'allow_partial', '3': 1, '4': 1, '5': 8, '10': 'allowPartial'}, + { + '1': 'create_objs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Add.CreateObject', + '10': 'createObjs' + }, + ], + '3': [Add_CreateObject$json, Add_CreateParamSetting$json], +}; + +@$core.Deprecated('Use addDescriptor instead') +const Add_CreateObject$json = { + '1': 'CreateObject', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + { + '1': 'param_settings', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Add.CreateParamSetting', + '10': 'paramSettings' + }, + ], +}; + +@$core.Deprecated('Use addDescriptor instead') +const Add_CreateParamSetting$json = { + '1': 'CreateParamSetting', + '2': [ + {'1': 'param', '3': 1, '4': 1, '5': 9, '10': 'param'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + {'1': 'required', '3': 3, '4': 1, '5': 8, '10': 'required'}, + ], +}; + +/// Descriptor for `Add`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List addDescriptor = $convert.base64Decode( + 'CgNBZGQSIwoNYWxsb3dfcGFydGlhbBgBIAEoCFIMYWxsb3dQYXJ0aWFsEjYKC2NyZWF0ZV9vYm' + 'pzGAIgAygLMhUudXNwLkFkZC5DcmVhdGVPYmplY3RSCmNyZWF0ZU9ianMabQoMQ3JlYXRlT2Jq' + 'ZWN0EhkKCG9ial9wYXRoGAEgASgJUgdvYmpQYXRoEkIKDnBhcmFtX3NldHRpbmdzGAIgAygLMh' + 'sudXNwLkFkZC5DcmVhdGVQYXJhbVNldHRpbmdSDXBhcmFtU2V0dGluZ3MaXAoSQ3JlYXRlUGFy' + 'YW1TZXR0aW5nEhQKBXBhcmFtGAEgASgJUgVwYXJhbRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWUSGg' + 'oIcmVxdWlyZWQYAyABKAhSCHJlcXVpcmVk'); + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp$json = { + '1': 'AddResp', + '2': [ + { + '1': 'created_obj_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.AddResp.CreatedObjectResult', + '10': 'createdObjResults' + }, + ], + '3': [AddResp_CreatedObjectResult$json, AddResp_ParameterError$json], +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_CreatedObjectResult$json = { + '1': 'CreatedObjectResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + { + '1': 'oper_status', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.AddResp.CreatedObjectResult.OperationStatus', + '10': 'operStatus' + }, + ], + '3': [AddResp_CreatedObjectResult_OperationStatus$json], +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_CreatedObjectResult_OperationStatus$json = { + '1': 'OperationStatus', + '2': [ + { + '1': 'oper_failure', + '3': 1, + '4': 1, + '5': 11, + '6': '.usp.AddResp.CreatedObjectResult.OperationStatus.OperationFailure', + '9': 0, + '10': 'operFailure' + }, + { + '1': 'oper_success', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.AddResp.CreatedObjectResult.OperationStatus.OperationSuccess', + '9': 0, + '10': 'operSuccess' + }, + ], + '3': [ + AddResp_CreatedObjectResult_OperationStatus_OperationFailure$json, + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess$json + ], + '8': [ + {'1': 'oper_status'}, + ], +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_CreatedObjectResult_OperationStatus_OperationFailure$json = { + '1': 'OperationFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_CreatedObjectResult_OperationStatus_OperationSuccess$json = { + '1': 'OperationSuccess', + '2': [ + { + '1': 'instantiated_path', + '3': 1, + '4': 1, + '5': 9, + '10': 'instantiatedPath' + }, + { + '1': 'param_errs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.AddResp.ParameterError', + '10': 'paramErrs' + }, + { + '1': 'unique_keys', + '3': 3, + '4': 3, + '5': 11, + '6': + '.usp.AddResp.CreatedObjectResult.OperationStatus.OperationSuccess.UniqueKeysEntry', + '10': 'uniqueKeys' + }, + ], + '3': [ + AddResp_CreatedObjectResult_OperationStatus_OperationSuccess_UniqueKeysEntry$json + ], +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_CreatedObjectResult_OperationStatus_OperationSuccess_UniqueKeysEntry$json = + { + '1': 'UniqueKeysEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use addRespDescriptor instead') +const AddResp_ParameterError$json = { + '1': 'ParameterError', + '2': [ + {'1': 'param', '3': 1, '4': 1, '5': 9, '10': 'param'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +/// Descriptor for `AddResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List addRespDescriptor = $convert.base64Decode( + 'CgdBZGRSZXNwElAKE2NyZWF0ZWRfb2JqX3Jlc3VsdHMYASADKAsyIC51c3AuQWRkUmVzcC5Dcm' + 'VhdGVkT2JqZWN0UmVzdWx0UhFjcmVhdGVkT2JqUmVzdWx0cxr7BQoTQ3JlYXRlZE9iamVjdFJl' + 'c3VsdBIlCg5yZXF1ZXN0ZWRfcGF0aBgBIAEoCVINcmVxdWVzdGVkUGF0aBJRCgtvcGVyX3N0YX' + 'R1cxgCIAEoCzIwLnVzcC5BZGRSZXNwLkNyZWF0ZWRPYmplY3RSZXN1bHQuT3BlcmF0aW9uU3Rh' + 'dHVzUgpvcGVyU3RhdHVzGukECg9PcGVyYXRpb25TdGF0dXMSZgoMb3Blcl9mYWlsdXJlGAEgAS' + 'gLMkEudXNwLkFkZFJlc3AuQ3JlYXRlZE9iamVjdFJlc3VsdC5PcGVyYXRpb25TdGF0dXMuT3Bl' + 'cmF0aW9uRmFpbHVyZUgAUgtvcGVyRmFpbHVyZRJmCgxvcGVyX3N1Y2Nlc3MYAiABKAsyQS51c3' + 'AuQWRkUmVzcC5DcmVhdGVkT2JqZWN0UmVzdWx0Lk9wZXJhdGlvblN0YXR1cy5PcGVyYXRpb25T' + 'dWNjZXNzSABSC29wZXJTdWNjZXNzGkYKEE9wZXJhdGlvbkZhaWx1cmUSGQoIZXJyX2NvZGUYAS' + 'ABKAdSB2VyckNvZGUSFwoHZXJyX21zZxgCIAEoCVIGZXJyTXNnGq4CChBPcGVyYXRpb25TdWNj' + 'ZXNzEisKEWluc3RhbnRpYXRlZF9wYXRoGAEgASgJUhBpbnN0YW50aWF0ZWRQYXRoEjoKCnBhcm' + 'FtX2VycnMYAiADKAsyGy51c3AuQWRkUmVzcC5QYXJhbWV0ZXJFcnJvclIJcGFyYW1FcnJzEnIK' + 'C3VuaXF1ZV9rZXlzGAMgAygLMlEudXNwLkFkZFJlc3AuQ3JlYXRlZE9iamVjdFJlc3VsdC5PcG' + 'VyYXRpb25TdGF0dXMuT3BlcmF0aW9uU3VjY2Vzcy5VbmlxdWVLZXlzRW50cnlSCnVuaXF1ZUtl' + 'eXMaPQoPVW5pcXVlS2V5c0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUg' + 'V2YWx1ZToCOAFCDQoLb3Blcl9zdGF0dXMaWgoOUGFyYW1ldGVyRXJyb3ISFAoFcGFyYW0YASAB' + 'KAlSBXBhcmFtEhkKCGVycl9jb2RlGAIgASgHUgdlcnJDb2RlEhcKB2Vycl9tc2cYAyABKAlSBm' + 'Vyck1zZw=='); + +@$core.Deprecated('Use deleteDescriptor instead') +const Delete$json = { + '1': 'Delete', + '2': [ + {'1': 'allow_partial', '3': 1, '4': 1, '5': 8, '10': 'allowPartial'}, + {'1': 'obj_paths', '3': 2, '4': 3, '5': 9, '10': 'objPaths'}, + ], +}; + +/// Descriptor for `Delete`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deleteDescriptor = $convert.base64Decode( + 'CgZEZWxldGUSIwoNYWxsb3dfcGFydGlhbBgBIAEoCFIMYWxsb3dQYXJ0aWFsEhsKCW9ial9wYX' + 'RocxgCIAMoCVIIb2JqUGF0aHM='); + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp$json = { + '1': 'DeleteResp', + '2': [ + { + '1': 'deleted_obj_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.DeleteResp.DeletedObjectResult', + '10': 'deletedObjResults' + }, + ], + '3': [ + DeleteResp_DeletedObjectResult$json, + DeleteResp_UnaffectedPathError$json + ], +}; + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp_DeletedObjectResult$json = { + '1': 'DeletedObjectResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + { + '1': 'oper_status', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.DeleteResp.DeletedObjectResult.OperationStatus', + '10': 'operStatus' + }, + ], + '3': [DeleteResp_DeletedObjectResult_OperationStatus$json], +}; + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp_DeletedObjectResult_OperationStatus$json = { + '1': 'OperationStatus', + '2': [ + { + '1': 'oper_failure', + '3': 1, + '4': 1, + '5': 11, + '6': + '.usp.DeleteResp.DeletedObjectResult.OperationStatus.OperationFailure', + '9': 0, + '10': 'operFailure' + }, + { + '1': 'oper_success', + '3': 2, + '4': 1, + '5': 11, + '6': + '.usp.DeleteResp.DeletedObjectResult.OperationStatus.OperationSuccess', + '9': 0, + '10': 'operSuccess' + }, + ], + '3': [ + DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure$json, + DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess$json + ], + '8': [ + {'1': 'oper_status'}, + ], +}; + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp_DeletedObjectResult_OperationStatus_OperationFailure$json = { + '1': 'OperationFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp_DeletedObjectResult_OperationStatus_OperationSuccess$json = { + '1': 'OperationSuccess', + '2': [ + {'1': 'affected_paths', '3': 1, '4': 3, '5': 9, '10': 'affectedPaths'}, + { + '1': 'unaffected_path_errs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.DeleteResp.UnaffectedPathError', + '10': 'unaffectedPathErrs' + }, + ], +}; + +@$core.Deprecated('Use deleteRespDescriptor instead') +const DeleteResp_UnaffectedPathError$json = { + '1': 'UnaffectedPathError', + '2': [ + {'1': 'unaffected_path', '3': 1, '4': 1, '5': 9, '10': 'unaffectedPath'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +/// Descriptor for `DeleteResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deleteRespDescriptor = $convert.base64Decode( + 'CgpEZWxldGVSZXNwElMKE2RlbGV0ZWRfb2JqX3Jlc3VsdHMYASADKAsyIy51c3AuRGVsZXRlUm' + 'VzcC5EZWxldGVkT2JqZWN0UmVzdWx0UhFkZWxldGVkT2JqUmVzdWx0cxrmBAoTRGVsZXRlZE9i' + 'amVjdFJlc3VsdBIlCg5yZXF1ZXN0ZWRfcGF0aBgBIAEoCVINcmVxdWVzdGVkUGF0aBJUCgtvcG' + 'VyX3N0YXR1cxgCIAEoCzIzLnVzcC5EZWxldGVSZXNwLkRlbGV0ZWRPYmplY3RSZXN1bHQuT3Bl' + 'cmF0aW9uU3RhdHVzUgpvcGVyU3RhdHVzGtEDCg9PcGVyYXRpb25TdGF0dXMSaQoMb3Blcl9mYW' + 'lsdXJlGAEgASgLMkQudXNwLkRlbGV0ZVJlc3AuRGVsZXRlZE9iamVjdFJlc3VsdC5PcGVyYXRp' + 'b25TdGF0dXMuT3BlcmF0aW9uRmFpbHVyZUgAUgtvcGVyRmFpbHVyZRJpCgxvcGVyX3N1Y2Nlc3' + 'MYAiABKAsyRC51c3AuRGVsZXRlUmVzcC5EZWxldGVkT2JqZWN0UmVzdWx0Lk9wZXJhdGlvblN0' + 'YXR1cy5PcGVyYXRpb25TdWNjZXNzSABSC29wZXJTdWNjZXNzGkYKEE9wZXJhdGlvbkZhaWx1cm' + 'USGQoIZXJyX2NvZGUYASABKAdSB2VyckNvZGUSFwoHZXJyX21zZxgCIAEoCVIGZXJyTXNnGpAB' + 'ChBPcGVyYXRpb25TdWNjZXNzEiUKDmFmZmVjdGVkX3BhdGhzGAEgAygJUg1hZmZlY3RlZFBhdG' + 'hzElUKFHVuYWZmZWN0ZWRfcGF0aF9lcnJzGAIgAygLMiMudXNwLkRlbGV0ZVJlc3AuVW5hZmZl' + 'Y3RlZFBhdGhFcnJvclISdW5hZmZlY3RlZFBhdGhFcnJzQg0KC29wZXJfc3RhdHVzGnIKE1VuYW' + 'ZmZWN0ZWRQYXRoRXJyb3ISJwoPdW5hZmZlY3RlZF9wYXRoGAEgASgJUg51bmFmZmVjdGVkUGF0' + 'aBIZCghlcnJfY29kZRgCIAEoB1IHZXJyQ29kZRIXCgdlcnJfbXNnGAMgASgJUgZlcnJNc2c='); + +@$core.Deprecated('Use setDescriptor instead') +const Set$json = { + '1': 'Set', + '2': [ + {'1': 'allow_partial', '3': 1, '4': 1, '5': 8, '10': 'allowPartial'}, + { + '1': 'update_objs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Set.UpdateObject', + '10': 'updateObjs' + }, + ], + '3': [Set_UpdateObject$json, Set_UpdateParamSetting$json], +}; + +@$core.Deprecated('Use setDescriptor instead') +const Set_UpdateObject$json = { + '1': 'UpdateObject', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + { + '1': 'param_settings', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Set.UpdateParamSetting', + '10': 'paramSettings' + }, + ], +}; + +@$core.Deprecated('Use setDescriptor instead') +const Set_UpdateParamSetting$json = { + '1': 'UpdateParamSetting', + '2': [ + {'1': 'param', '3': 1, '4': 1, '5': 9, '10': 'param'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + {'1': 'required', '3': 3, '4': 1, '5': 8, '10': 'required'}, + ], +}; + +/// Descriptor for `Set`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List setDescriptor = $convert.base64Decode( + 'CgNTZXQSIwoNYWxsb3dfcGFydGlhbBgBIAEoCFIMYWxsb3dQYXJ0aWFsEjYKC3VwZGF0ZV9vYm' + 'pzGAIgAygLMhUudXNwLlNldC5VcGRhdGVPYmplY3RSCnVwZGF0ZU9ianMabQoMVXBkYXRlT2Jq' + 'ZWN0EhkKCG9ial9wYXRoGAEgASgJUgdvYmpQYXRoEkIKDnBhcmFtX3NldHRpbmdzGAIgAygLMh' + 'sudXNwLlNldC5VcGRhdGVQYXJhbVNldHRpbmdSDXBhcmFtU2V0dGluZ3MaXAoSVXBkYXRlUGFy' + 'YW1TZXR0aW5nEhQKBXBhcmFtGAEgASgJUgVwYXJhbRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWUSGg' + 'oIcmVxdWlyZWQYAyABKAhSCHJlcXVpcmVk'); + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp$json = { + '1': 'SetResp', + '2': [ + { + '1': 'updated_obj_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.SetResp.UpdatedObjectResult', + '10': 'updatedObjResults' + }, + ], + '3': [ + SetResp_UpdatedObjectResult$json, + SetResp_UpdatedInstanceFailure$json, + SetResp_UpdatedInstanceResult$json, + SetResp_ParameterError$json + ], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedObjectResult$json = { + '1': 'UpdatedObjectResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + { + '1': 'oper_status', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.SetResp.UpdatedObjectResult.OperationStatus', + '10': 'operStatus' + }, + ], + '3': [SetResp_UpdatedObjectResult_OperationStatus$json], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedObjectResult_OperationStatus$json = { + '1': 'OperationStatus', + '2': [ + { + '1': 'oper_failure', + '3': 1, + '4': 1, + '5': 11, + '6': '.usp.SetResp.UpdatedObjectResult.OperationStatus.OperationFailure', + '9': 0, + '10': 'operFailure' + }, + { + '1': 'oper_success', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.SetResp.UpdatedObjectResult.OperationStatus.OperationSuccess', + '9': 0, + '10': 'operSuccess' + }, + ], + '3': [ + SetResp_UpdatedObjectResult_OperationStatus_OperationFailure$json, + SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess$json + ], + '8': [ + {'1': 'oper_status'}, + ], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedObjectResult_OperationStatus_OperationFailure$json = { + '1': 'OperationFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + { + '1': 'updated_inst_failures', + '3': 3, + '4': 3, + '5': 11, + '6': '.usp.SetResp.UpdatedInstanceFailure', + '10': 'updatedInstFailures' + }, + ], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedObjectResult_OperationStatus_OperationSuccess$json = { + '1': 'OperationSuccess', + '2': [ + { + '1': 'updated_inst_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.SetResp.UpdatedInstanceResult', + '10': 'updatedInstResults' + }, + ], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedInstanceFailure$json = { + '1': 'UpdatedInstanceFailure', + '2': [ + {'1': 'affected_path', '3': 1, '4': 1, '5': 9, '10': 'affectedPath'}, + { + '1': 'param_errs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.SetResp.ParameterError', + '10': 'paramErrs' + }, + ], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedInstanceResult$json = { + '1': 'UpdatedInstanceResult', + '2': [ + {'1': 'affected_path', '3': 1, '4': 1, '5': 9, '10': 'affectedPath'}, + { + '1': 'param_errs', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.SetResp.ParameterError', + '10': 'paramErrs' + }, + { + '1': 'updated_params', + '3': 3, + '4': 3, + '5': 11, + '6': '.usp.SetResp.UpdatedInstanceResult.UpdatedParamsEntry', + '10': 'updatedParams' + }, + ], + '3': [SetResp_UpdatedInstanceResult_UpdatedParamsEntry$json], +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_UpdatedInstanceResult_UpdatedParamsEntry$json = { + '1': 'UpdatedParamsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use setRespDescriptor instead') +const SetResp_ParameterError$json = { + '1': 'ParameterError', + '2': [ + {'1': 'param', '3': 1, '4': 1, '5': 9, '10': 'param'}, + {'1': 'err_code', '3': 2, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 3, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +/// Descriptor for `SetResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List setRespDescriptor = $convert.base64Decode( + 'CgdTZXRSZXNwElAKE3VwZGF0ZWRfb2JqX3Jlc3VsdHMYASADKAsyIC51c3AuU2V0UmVzcC5VcG' + 'RhdGVkT2JqZWN0UmVzdWx0UhF1cGRhdGVkT2JqUmVzdWx0cxqOBQoTVXBkYXRlZE9iamVjdFJl' + 'c3VsdBIlCg5yZXF1ZXN0ZWRfcGF0aBgBIAEoCVINcmVxdWVzdGVkUGF0aBJRCgtvcGVyX3N0YX' + 'R1cxgCIAEoCzIwLnVzcC5TZXRSZXNwLlVwZGF0ZWRPYmplY3RSZXN1bHQuT3BlcmF0aW9uU3Rh' + 'dHVzUgpvcGVyU3RhdHVzGvwDCg9PcGVyYXRpb25TdGF0dXMSZgoMb3Blcl9mYWlsdXJlGAEgAS' + 'gLMkEudXNwLlNldFJlc3AuVXBkYXRlZE9iamVjdFJlc3VsdC5PcGVyYXRpb25TdGF0dXMuT3Bl' + 'cmF0aW9uRmFpbHVyZUgAUgtvcGVyRmFpbHVyZRJmCgxvcGVyX3N1Y2Nlc3MYAiABKAsyQS51c3' + 'AuU2V0UmVzcC5VcGRhdGVkT2JqZWN0UmVzdWx0Lk9wZXJhdGlvblN0YXR1cy5PcGVyYXRpb25T' + 'dWNjZXNzSABSC29wZXJTdWNjZXNzGp8BChBPcGVyYXRpb25GYWlsdXJlEhkKCGVycl9jb2RlGA' + 'EgASgHUgdlcnJDb2RlEhcKB2Vycl9tc2cYAiABKAlSBmVyck1zZxJXChV1cGRhdGVkX2luc3Rf' + 'ZmFpbHVyZXMYAyADKAsyIy51c3AuU2V0UmVzcC5VcGRhdGVkSW5zdGFuY2VGYWlsdXJlUhN1cG' + 'RhdGVkSW5zdEZhaWx1cmVzGmgKEE9wZXJhdGlvblN1Y2Nlc3MSVAoUdXBkYXRlZF9pbnN0X3Jl' + 'c3VsdHMYASADKAsyIi51c3AuU2V0UmVzcC5VcGRhdGVkSW5zdGFuY2VSZXN1bHRSEnVwZGF0ZW' + 'RJbnN0UmVzdWx0c0INCgtvcGVyX3N0YXR1cxp5ChZVcGRhdGVkSW5zdGFuY2VGYWlsdXJlEiMK' + 'DWFmZmVjdGVkX3BhdGgYASABKAlSDGFmZmVjdGVkUGF0aBI6CgpwYXJhbV9lcnJzGAIgAygLMh' + 'sudXNwLlNldFJlc3AuUGFyYW1ldGVyRXJyb3JSCXBhcmFtRXJycxqYAgoVVXBkYXRlZEluc3Rh' + 'bmNlUmVzdWx0EiMKDWFmZmVjdGVkX3BhdGgYASABKAlSDGFmZmVjdGVkUGF0aBI6CgpwYXJhbV' + '9lcnJzGAIgAygLMhsudXNwLlNldFJlc3AuUGFyYW1ldGVyRXJyb3JSCXBhcmFtRXJycxJcCg51' + 'cGRhdGVkX3BhcmFtcxgDIAMoCzI1LnVzcC5TZXRSZXNwLlVwZGF0ZWRJbnN0YW5jZVJlc3VsdC' + '5VcGRhdGVkUGFyYW1zRW50cnlSDXVwZGF0ZWRQYXJhbXMaQAoSVXBkYXRlZFBhcmFtc0VudHJ5' + 'EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEaWgoOUGFyYW1ldG' + 'VyRXJyb3ISFAoFcGFyYW0YASABKAlSBXBhcmFtEhkKCGVycl9jb2RlGAIgASgHUgdlcnJDb2Rl' + 'EhcKB2Vycl9tc2cYAyABKAlSBmVyck1zZw=='); + +@$core.Deprecated('Use operateDescriptor instead') +const Operate$json = { + '1': 'Operate', + '2': [ + {'1': 'command', '3': 1, '4': 1, '5': 9, '10': 'command'}, + {'1': 'command_key', '3': 2, '4': 1, '5': 9, '10': 'commandKey'}, + {'1': 'send_resp', '3': 3, '4': 1, '5': 8, '10': 'sendResp'}, + { + '1': 'input_args', + '3': 4, + '4': 3, + '5': 11, + '6': '.usp.Operate.InputArgsEntry', + '10': 'inputArgs' + }, + ], + '3': [Operate_InputArgsEntry$json], +}; + +@$core.Deprecated('Use operateDescriptor instead') +const Operate_InputArgsEntry$json = { + '1': 'InputArgsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +/// Descriptor for `Operate`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List operateDescriptor = $convert.base64Decode( + 'CgdPcGVyYXRlEhgKB2NvbW1hbmQYASABKAlSB2NvbW1hbmQSHwoLY29tbWFuZF9rZXkYAiABKA' + 'lSCmNvbW1hbmRLZXkSGwoJc2VuZF9yZXNwGAMgASgIUghzZW5kUmVzcBI6CgppbnB1dF9hcmdz' + 'GAQgAygLMhsudXNwLk9wZXJhdGUuSW5wdXRBcmdzRW50cnlSCWlucHV0QXJncxo8Cg5JbnB1dE' + 'FyZ3NFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgB'); + +@$core.Deprecated('Use operateRespDescriptor instead') +const OperateResp$json = { + '1': 'OperateResp', + '2': [ + { + '1': 'operation_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.OperateResp.OperationResult', + '10': 'operationResults' + }, + ], + '3': [OperateResp_OperationResult$json], +}; + +@$core.Deprecated('Use operateRespDescriptor instead') +const OperateResp_OperationResult$json = { + '1': 'OperationResult', + '2': [ + {'1': 'executed_command', '3': 1, '4': 1, '5': 9, '10': 'executedCommand'}, + {'1': 'req_obj_path', '3': 2, '4': 1, '5': 9, '9': 0, '10': 'reqObjPath'}, + { + '1': 'req_output_args', + '3': 3, + '4': 1, + '5': 11, + '6': '.usp.OperateResp.OperationResult.OutputArgs', + '9': 0, + '10': 'reqOutputArgs' + }, + { + '1': 'cmd_failure', + '3': 4, + '4': 1, + '5': 11, + '6': '.usp.OperateResp.OperationResult.CommandFailure', + '9': 0, + '10': 'cmdFailure' + }, + ], + '3': [ + OperateResp_OperationResult_OutputArgs$json, + OperateResp_OperationResult_CommandFailure$json + ], + '8': [ + {'1': 'operation_resp'}, + ], +}; + +@$core.Deprecated('Use operateRespDescriptor instead') +const OperateResp_OperationResult_OutputArgs$json = { + '1': 'OutputArgs', + '2': [ + { + '1': 'output_args', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.OperateResp.OperationResult.OutputArgs.OutputArgsEntry', + '10': 'outputArgs' + }, + ], + '3': [OperateResp_OperationResult_OutputArgs_OutputArgsEntry$json], +}; + +@$core.Deprecated('Use operateRespDescriptor instead') +const OperateResp_OperationResult_OutputArgs_OutputArgsEntry$json = { + '1': 'OutputArgsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use operateRespDescriptor instead') +const OperateResp_OperationResult_CommandFailure$json = { + '1': 'CommandFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +/// Descriptor for `OperateResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List operateRespDescriptor = $convert.base64Decode( + 'CgtPcGVyYXRlUmVzcBJNChFvcGVyYXRpb25fcmVzdWx0cxgBIAMoCzIgLnVzcC5PcGVyYXRlUm' + 'VzcC5PcGVyYXRpb25SZXN1bHRSEG9wZXJhdGlvblJlc3VsdHMajwQKD09wZXJhdGlvblJlc3Vs' + 'dBIpChBleGVjdXRlZF9jb21tYW5kGAEgASgJUg9leGVjdXRlZENvbW1hbmQSIgoMcmVxX29ial' + '9wYXRoGAIgASgJSABSCnJlcU9ialBhdGgSVQoPcmVxX291dHB1dF9hcmdzGAMgASgLMisudXNw' + 'Lk9wZXJhdGVSZXNwLk9wZXJhdGlvblJlc3VsdC5PdXRwdXRBcmdzSABSDXJlcU91dHB1dEFyZ3' + 'MSUgoLY21kX2ZhaWx1cmUYBCABKAsyLy51c3AuT3BlcmF0ZVJlc3AuT3BlcmF0aW9uUmVzdWx0' + 'LkNvbW1hbmRGYWlsdXJlSABSCmNtZEZhaWx1cmUaqQEKCk91dHB1dEFyZ3MSXAoLb3V0cHV0X2' + 'FyZ3MYASADKAsyOy51c3AuT3BlcmF0ZVJlc3AuT3BlcmF0aW9uUmVzdWx0Lk91dHB1dEFyZ3Mu' + 'T3V0cHV0QXJnc0VudHJ5UgpvdXRwdXRBcmdzGj0KD091dHB1dEFyZ3NFbnRyeRIQCgNrZXkYAS' + 'ABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBGkQKDkNvbW1hbmRGYWlsdXJlEhkK' + 'CGVycl9jb2RlGAEgASgHUgdlcnJDb2RlEhcKB2Vycl9tc2cYAiABKAlSBmVyck1zZ0IQCg5vcG' + 'VyYXRpb25fcmVzcA=='); + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify$json = { + '1': 'Notify', + '2': [ + {'1': 'subscription_id', '3': 1, '4': 1, '5': 9, '10': 'subscriptionId'}, + {'1': 'send_resp', '3': 2, '4': 1, '5': 8, '10': 'sendResp'}, + { + '1': 'event', + '3': 3, + '4': 1, + '5': 11, + '6': '.usp.Notify.Event', + '9': 0, + '10': 'event' + }, + { + '1': 'value_change', + '3': 4, + '4': 1, + '5': 11, + '6': '.usp.Notify.ValueChange', + '9': 0, + '10': 'valueChange' + }, + { + '1': 'obj_creation', + '3': 5, + '4': 1, + '5': 11, + '6': '.usp.Notify.ObjectCreation', + '9': 0, + '10': 'objCreation' + }, + { + '1': 'obj_deletion', + '3': 6, + '4': 1, + '5': 11, + '6': '.usp.Notify.ObjectDeletion', + '9': 0, + '10': 'objDeletion' + }, + { + '1': 'oper_complete', + '3': 7, + '4': 1, + '5': 11, + '6': '.usp.Notify.OperationComplete', + '9': 0, + '10': 'operComplete' + }, + { + '1': 'on_board_req', + '3': 8, + '4': 1, + '5': 11, + '6': '.usp.Notify.OnBoardRequest', + '9': 0, + '10': 'onBoardReq' + }, + ], + '3': [ + Notify_Event$json, + Notify_ValueChange$json, + Notify_ObjectCreation$json, + Notify_ObjectDeletion$json, + Notify_OperationComplete$json, + Notify_OnBoardRequest$json + ], + '8': [ + {'1': 'notification'}, + ], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_Event$json = { + '1': 'Event', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + {'1': 'event_name', '3': 2, '4': 1, '5': 9, '10': 'eventName'}, + { + '1': 'params', + '3': 3, + '4': 3, + '5': 11, + '6': '.usp.Notify.Event.ParamsEntry', + '10': 'params' + }, + ], + '3': [Notify_Event_ParamsEntry$json], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_Event_ParamsEntry$json = { + '1': 'ParamsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_ValueChange$json = { + '1': 'ValueChange', + '2': [ + {'1': 'param_path', '3': 1, '4': 1, '5': 9, '10': 'paramPath'}, + {'1': 'param_value', '3': 2, '4': 1, '5': 9, '10': 'paramValue'}, + ], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_ObjectCreation$json = { + '1': 'ObjectCreation', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + { + '1': 'unique_keys', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Notify.ObjectCreation.UniqueKeysEntry', + '10': 'uniqueKeys' + }, + ], + '3': [Notify_ObjectCreation_UniqueKeysEntry$json], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_ObjectCreation_UniqueKeysEntry$json = { + '1': 'UniqueKeysEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_ObjectDeletion$json = { + '1': 'ObjectDeletion', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + ], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_OperationComplete$json = { + '1': 'OperationComplete', + '2': [ + {'1': 'obj_path', '3': 1, '4': 1, '5': 9, '10': 'objPath'}, + {'1': 'command_name', '3': 2, '4': 1, '5': 9, '10': 'commandName'}, + {'1': 'command_key', '3': 3, '4': 1, '5': 9, '10': 'commandKey'}, + { + '1': 'req_output_args', + '3': 4, + '4': 1, + '5': 11, + '6': '.usp.Notify.OperationComplete.OutputArgs', + '9': 0, + '10': 'reqOutputArgs' + }, + { + '1': 'cmd_failure', + '3': 5, + '4': 1, + '5': 11, + '6': '.usp.Notify.OperationComplete.CommandFailure', + '9': 0, + '10': 'cmdFailure' + }, + ], + '3': [ + Notify_OperationComplete_OutputArgs$json, + Notify_OperationComplete_CommandFailure$json + ], + '8': [ + {'1': 'operation_resp'}, + ], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_OperationComplete_OutputArgs$json = { + '1': 'OutputArgs', + '2': [ + { + '1': 'output_args', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.Notify.OperationComplete.OutputArgs.OutputArgsEntry', + '10': 'outputArgs' + }, + ], + '3': [Notify_OperationComplete_OutputArgs_OutputArgsEntry$json], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_OperationComplete_OutputArgs_OutputArgsEntry$json = { + '1': 'OutputArgsEntry', + '2': [ + {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], + '7': {'7': true}, +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_OperationComplete_CommandFailure$json = { + '1': 'CommandFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +@$core.Deprecated('Use notifyDescriptor instead') +const Notify_OnBoardRequest$json = { + '1': 'OnBoardRequest', + '2': [ + {'1': 'oui', '3': 1, '4': 1, '5': 9, '10': 'oui'}, + {'1': 'product_class', '3': 2, '4': 1, '5': 9, '10': 'productClass'}, + {'1': 'serial_number', '3': 3, '4': 1, '5': 9, '10': 'serialNumber'}, + { + '1': 'agent_supported_protocol_versions', + '3': 4, + '4': 1, + '5': 9, + '10': 'agentSupportedProtocolVersions' + }, + ], +}; + +/// Descriptor for `Notify`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List notifyDescriptor = $convert.base64Decode( + 'CgZOb3RpZnkSJwoPc3Vic2NyaXB0aW9uX2lkGAEgASgJUg5zdWJzY3JpcHRpb25JZBIbCglzZW' + '5kX3Jlc3AYAiABKAhSCHNlbmRSZXNwEikKBWV2ZW50GAMgASgLMhEudXNwLk5vdGlmeS5FdmVu' + 'dEgAUgVldmVudBI8Cgx2YWx1ZV9jaGFuZ2UYBCABKAsyFy51c3AuTm90aWZ5LlZhbHVlQ2hhbm' + 'dlSABSC3ZhbHVlQ2hhbmdlEj8KDG9ial9jcmVhdGlvbhgFIAEoCzIaLnVzcC5Ob3RpZnkuT2Jq' + 'ZWN0Q3JlYXRpb25IAFILb2JqQ3JlYXRpb24SPwoMb2JqX2RlbGV0aW9uGAYgASgLMhoudXNwLk' + '5vdGlmeS5PYmplY3REZWxldGlvbkgAUgtvYmpEZWxldGlvbhJECg1vcGVyX2NvbXBsZXRlGAcg' + 'ASgLMh0udXNwLk5vdGlmeS5PcGVyYXRpb25Db21wbGV0ZUgAUgxvcGVyQ29tcGxldGUSPgoMb2' + '5fYm9hcmRfcmVxGAggASgLMhoudXNwLk5vdGlmeS5PbkJvYXJkUmVxdWVzdEgAUgpvbkJvYXJk' + 'UmVxGrMBCgVFdmVudBIZCghvYmpfcGF0aBgBIAEoCVIHb2JqUGF0aBIdCgpldmVudF9uYW1lGA' + 'IgASgJUglldmVudE5hbWUSNQoGcGFyYW1zGAMgAygLMh0udXNwLk5vdGlmeS5FdmVudC5QYXJh' + 'bXNFbnRyeVIGcGFyYW1zGjkKC1BhcmFtc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbH' + 'VlGAIgASgJUgV2YWx1ZToCOAEaTQoLVmFsdWVDaGFuZ2USHQoKcGFyYW1fcGF0aBgBIAEoCVIJ' + 'cGFyYW1QYXRoEh8KC3BhcmFtX3ZhbHVlGAIgASgJUgpwYXJhbVZhbHVlGrcBCg5PYmplY3RDcm' + 'VhdGlvbhIZCghvYmpfcGF0aBgBIAEoCVIHb2JqUGF0aBJLCgt1bmlxdWVfa2V5cxgCIAMoCzIq' + 'LnVzcC5Ob3RpZnkuT2JqZWN0Q3JlYXRpb24uVW5pcXVlS2V5c0VudHJ5Ugp1bmlxdWVLZXlzGj' + '0KD1VuaXF1ZUtleXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFs' + 'dWU6AjgBGisKDk9iamVjdERlbGV0aW9uEhkKCG9ial9wYXRoGAEgASgJUgdvYmpQYXRoGpgECh' + 'FPcGVyYXRpb25Db21wbGV0ZRIZCghvYmpfcGF0aBgBIAEoCVIHb2JqUGF0aBIhCgxjb21tYW5k' + 'X25hbWUYAiABKAlSC2NvbW1hbmROYW1lEh8KC2NvbW1hbmRfa2V5GAMgASgJUgpjb21tYW5kS2' + 'V5ElIKD3JlcV9vdXRwdXRfYXJncxgEIAEoCzIoLnVzcC5Ob3RpZnkuT3BlcmF0aW9uQ29tcGxl' + 'dGUuT3V0cHV0QXJnc0gAUg1yZXFPdXRwdXRBcmdzEk8KC2NtZF9mYWlsdXJlGAUgASgLMiwudX' + 'NwLk5vdGlmeS5PcGVyYXRpb25Db21wbGV0ZS5Db21tYW5kRmFpbHVyZUgAUgpjbWRGYWlsdXJl' + 'GqYBCgpPdXRwdXRBcmdzElkKC291dHB1dF9hcmdzGAEgAygLMjgudXNwLk5vdGlmeS5PcGVyYX' + 'Rpb25Db21wbGV0ZS5PdXRwdXRBcmdzLk91dHB1dEFyZ3NFbnRyeVIKb3V0cHV0QXJncxo9Cg9P' + 'dXRwdXRBcmdzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbHVlOg' + 'I4ARpECg5Db21tYW5kRmFpbHVyZRIZCghlcnJfY29kZRgBIAEoB1IHZXJyQ29kZRIXCgdlcnJf' + 'bXNnGAIgASgJUgZlcnJNc2dCEAoOb3BlcmF0aW9uX3Jlc3AatwEKDk9uQm9hcmRSZXF1ZXN0Eh' + 'AKA291aRgBIAEoCVIDb3VpEiMKDXByb2R1Y3RfY2xhc3MYAiABKAlSDHByb2R1Y3RDbGFzcxIj' + 'Cg1zZXJpYWxfbnVtYmVyGAMgASgJUgxzZXJpYWxOdW1iZXISSQohYWdlbnRfc3VwcG9ydGVkX3' + 'Byb3RvY29sX3ZlcnNpb25zGAQgASgJUh5hZ2VudFN1cHBvcnRlZFByb3RvY29sVmVyc2lvbnNC' + 'DgoMbm90aWZpY2F0aW9u'); + +@$core.Deprecated('Use notifyRespDescriptor instead') +const NotifyResp$json = { + '1': 'NotifyResp', + '2': [ + {'1': 'subscription_id', '3': 1, '4': 1, '5': 9, '10': 'subscriptionId'}, + ], +}; + +/// Descriptor for `NotifyResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List notifyRespDescriptor = $convert.base64Decode( + 'CgpOb3RpZnlSZXNwEicKD3N1YnNjcmlwdGlvbl9pZBgBIAEoCVIOc3Vic2NyaXB0aW9uSWQ='); + +@$core.Deprecated('Use registerDescriptor instead') +const Register$json = { + '1': 'Register', + '2': [ + {'1': 'allow_partial', '3': 1, '4': 1, '5': 8, '10': 'allowPartial'}, + { + '1': 'reg_paths', + '3': 2, + '4': 3, + '5': 11, + '6': '.usp.Register.RegistrationPath', + '10': 'regPaths' + }, + ], + '3': [Register_RegistrationPath$json], +}; + +@$core.Deprecated('Use registerDescriptor instead') +const Register_RegistrationPath$json = { + '1': 'RegistrationPath', + '2': [ + {'1': 'path', '3': 1, '4': 1, '5': 9, '10': 'path'}, + ], +}; + +/// Descriptor for `Register`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List registerDescriptor = $convert.base64Decode( + 'CghSZWdpc3RlchIjCg1hbGxvd19wYXJ0aWFsGAEgASgIUgxhbGxvd1BhcnRpYWwSOwoJcmVnX3' + 'BhdGhzGAIgAygLMh4udXNwLlJlZ2lzdGVyLlJlZ2lzdHJhdGlvblBhdGhSCHJlZ1BhdGhzGiYK' + 'EFJlZ2lzdHJhdGlvblBhdGgSEgoEcGF0aBgBIAEoCVIEcGF0aA=='); + +@$core.Deprecated('Use registerRespDescriptor instead') +const RegisterResp$json = { + '1': 'RegisterResp', + '2': [ + { + '1': 'registered_path_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.RegisterResp.RegisteredPathResult', + '10': 'registeredPathResults' + }, + ], + '3': [RegisterResp_RegisteredPathResult$json], +}; + +@$core.Deprecated('Use registerRespDescriptor instead') +const RegisterResp_RegisteredPathResult$json = { + '1': 'RegisteredPathResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + { + '1': 'oper_status', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.RegisterResp.RegisteredPathResult.OperationStatus', + '10': 'operStatus' + }, + ], + '3': [RegisterResp_RegisteredPathResult_OperationStatus$json], +}; + +@$core.Deprecated('Use registerRespDescriptor instead') +const RegisterResp_RegisteredPathResult_OperationStatus$json = { + '1': 'OperationStatus', + '2': [ + { + '1': 'oper_failure', + '3': 1, + '4': 1, + '5': 11, + '6': + '.usp.RegisterResp.RegisteredPathResult.OperationStatus.OperationFailure', + '9': 0, + '10': 'operFailure' + }, + { + '1': 'oper_success', + '3': 2, + '4': 1, + '5': 11, + '6': + '.usp.RegisterResp.RegisteredPathResult.OperationStatus.OperationSuccess', + '9': 0, + '10': 'operSuccess' + }, + ], + '3': [ + RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure$json, + RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess$json + ], + '8': [ + {'1': 'oper_status'}, + ], +}; + +@$core.Deprecated('Use registerRespDescriptor instead') +const RegisterResp_RegisteredPathResult_OperationStatus_OperationFailure$json = + { + '1': 'OperationFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +@$core.Deprecated('Use registerRespDescriptor instead') +const RegisterResp_RegisteredPathResult_OperationStatus_OperationSuccess$json = + { + '1': 'OperationSuccess', + '2': [ + {'1': 'registered_path', '3': 1, '4': 1, '5': 9, '10': 'registeredPath'}, + ], +}; + +/// Descriptor for `RegisterResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List registerRespDescriptor = $convert.base64Decode( + 'CgxSZWdpc3RlclJlc3ASXgoXcmVnaXN0ZXJlZF9wYXRoX3Jlc3VsdHMYASADKAsyJi51c3AuUm' + 'VnaXN0ZXJSZXNwLlJlZ2lzdGVyZWRQYXRoUmVzdWx0UhVyZWdpc3RlcmVkUGF0aFJlc3VsdHMa' + 'mgQKFFJlZ2lzdGVyZWRQYXRoUmVzdWx0EiUKDnJlcXVlc3RlZF9wYXRoGAEgASgJUg1yZXF1ZX' + 'N0ZWRQYXRoElcKC29wZXJfc3RhdHVzGAIgASgLMjYudXNwLlJlZ2lzdGVyUmVzcC5SZWdpc3Rl' + 'cmVkUGF0aFJlc3VsdC5PcGVyYXRpb25TdGF0dXNSCm9wZXJTdGF0dXMagQMKD09wZXJhdGlvbl' + 'N0YXR1cxJsCgxvcGVyX2ZhaWx1cmUYASABKAsyRy51c3AuUmVnaXN0ZXJSZXNwLlJlZ2lzdGVy' + 'ZWRQYXRoUmVzdWx0Lk9wZXJhdGlvblN0YXR1cy5PcGVyYXRpb25GYWlsdXJlSABSC29wZXJGYW' + 'lsdXJlEmwKDG9wZXJfc3VjY2VzcxgCIAEoCzJHLnVzcC5SZWdpc3RlclJlc3AuUmVnaXN0ZXJl' + 'ZFBhdGhSZXN1bHQuT3BlcmF0aW9uU3RhdHVzLk9wZXJhdGlvblN1Y2Nlc3NIAFILb3BlclN1Y2' + 'Nlc3MaRgoQT3BlcmF0aW9uRmFpbHVyZRIZCghlcnJfY29kZRgBIAEoB1IHZXJyQ29kZRIXCgdl' + 'cnJfbXNnGAIgASgJUgZlcnJNc2caOwoQT3BlcmF0aW9uU3VjY2VzcxInCg9yZWdpc3RlcmVkX3' + 'BhdGgYASABKAlSDnJlZ2lzdGVyZWRQYXRoQg0KC29wZXJfc3RhdHVz'); + +@$core.Deprecated('Use deregisterDescriptor instead') +const Deregister$json = { + '1': 'Deregister', + '2': [ + {'1': 'paths', '3': 1, '4': 3, '5': 9, '10': 'paths'}, + ], +}; + +/// Descriptor for `Deregister`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deregisterDescriptor = + $convert.base64Decode('CgpEZXJlZ2lzdGVyEhQKBXBhdGhzGAEgAygJUgVwYXRocw=='); + +@$core.Deprecated('Use deregisterRespDescriptor instead') +const DeregisterResp$json = { + '1': 'DeregisterResp', + '2': [ + { + '1': 'deregistered_path_results', + '3': 1, + '4': 3, + '5': 11, + '6': '.usp.DeregisterResp.DeregisteredPathResult', + '10': 'deregisteredPathResults' + }, + ], + '3': [DeregisterResp_DeregisteredPathResult$json], +}; + +@$core.Deprecated('Use deregisterRespDescriptor instead') +const DeregisterResp_DeregisteredPathResult$json = { + '1': 'DeregisteredPathResult', + '2': [ + {'1': 'requested_path', '3': 1, '4': 1, '5': 9, '10': 'requestedPath'}, + { + '1': 'oper_status', + '3': 2, + '4': 1, + '5': 11, + '6': '.usp.DeregisterResp.DeregisteredPathResult.OperationStatus', + '10': 'operStatus' + }, + ], + '3': [DeregisterResp_DeregisteredPathResult_OperationStatus$json], +}; + +@$core.Deprecated('Use deregisterRespDescriptor instead') +const DeregisterResp_DeregisteredPathResult_OperationStatus$json = { + '1': 'OperationStatus', + '2': [ + { + '1': 'oper_failure', + '3': 1, + '4': 1, + '5': 11, + '6': + '.usp.DeregisterResp.DeregisteredPathResult.OperationStatus.OperationFailure', + '9': 0, + '10': 'operFailure' + }, + { + '1': 'oper_success', + '3': 2, + '4': 1, + '5': 11, + '6': + '.usp.DeregisterResp.DeregisteredPathResult.OperationStatus.OperationSuccess', + '9': 0, + '10': 'operSuccess' + }, + ], + '3': [ + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure$json, + DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess$json + ], + '8': [ + {'1': 'oper_status'}, + ], +}; + +@$core.Deprecated('Use deregisterRespDescriptor instead') +const DeregisterResp_DeregisteredPathResult_OperationStatus_OperationFailure$json = + { + '1': 'OperationFailure', + '2': [ + {'1': 'err_code', '3': 1, '4': 1, '5': 7, '10': 'errCode'}, + {'1': 'err_msg', '3': 2, '4': 1, '5': 9, '10': 'errMsg'}, + ], +}; + +@$core.Deprecated('Use deregisterRespDescriptor instead') +const DeregisterResp_DeregisteredPathResult_OperationStatus_OperationSuccess$json = + { + '1': 'OperationSuccess', + '2': [ + { + '1': 'deregistered_path', + '3': 1, + '4': 3, + '5': 9, + '10': 'deregisteredPath' + }, + ], +}; + +/// Descriptor for `DeregisterResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List deregisterRespDescriptor = $convert.base64Decode( + 'Cg5EZXJlZ2lzdGVyUmVzcBJmChlkZXJlZ2lzdGVyZWRfcGF0aF9yZXN1bHRzGAEgAygLMioudX' + 'NwLkRlcmVnaXN0ZXJSZXNwLkRlcmVnaXN0ZXJlZFBhdGhSZXN1bHRSF2RlcmVnaXN0ZXJlZFBh' + 'dGhSZXN1bHRzGqwEChZEZXJlZ2lzdGVyZWRQYXRoUmVzdWx0EiUKDnJlcXVlc3RlZF9wYXRoGA' + 'EgASgJUg1yZXF1ZXN0ZWRQYXRoElsKC29wZXJfc3RhdHVzGAIgASgLMjoudXNwLkRlcmVnaXN0' + 'ZXJSZXNwLkRlcmVnaXN0ZXJlZFBhdGhSZXN1bHQuT3BlcmF0aW9uU3RhdHVzUgpvcGVyU3RhdH' + 'VzGo0DCg9PcGVyYXRpb25TdGF0dXMScAoMb3Blcl9mYWlsdXJlGAEgASgLMksudXNwLkRlcmVn' + 'aXN0ZXJSZXNwLkRlcmVnaXN0ZXJlZFBhdGhSZXN1bHQuT3BlcmF0aW9uU3RhdHVzLk9wZXJhdG' + 'lvbkZhaWx1cmVIAFILb3BlckZhaWx1cmUScAoMb3Blcl9zdWNjZXNzGAIgASgLMksudXNwLkRl' + 'cmVnaXN0ZXJSZXNwLkRlcmVnaXN0ZXJlZFBhdGhSZXN1bHQuT3BlcmF0aW9uU3RhdHVzLk9wZX' + 'JhdGlvblN1Y2Nlc3NIAFILb3BlclN1Y2Nlc3MaRgoQT3BlcmF0aW9uRmFpbHVyZRIZCghlcnJf' + 'Y29kZRgBIAEoB1IHZXJyQ29kZRIXCgdlcnJfbXNnGAIgASgJUgZlcnJNc2caPwoQT3BlcmF0aW' + '9uU3VjY2VzcxIrChFkZXJlZ2lzdGVyZWRfcGF0aBgBIAMoCVIQZGVyZWdpc3RlcmVkUGF0aEIN' + 'CgtvcGVyX3N0YXR1cw=='); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_record.pb.dart b/packages/usp_protocol_common/lib/src/generated/usp_record.pb.dart new file mode 100644 index 000000000..8acd93205 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_record.pb.dart @@ -0,0 +1,759 @@ +// This is a generated file - do not edit. +// +// Generated from usp_record.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:core' as $core; + +import 'package:fixnum/fixnum.dart' as $fixnum; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'usp_record.pbenum.dart'; + +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + +export 'usp_record.pbenum.dart'; + +enum Record_RecordType { + noSessionContext, + sessionContext, + websocketConnect, + mqttConnect, + stompConnect, + disconnect, + udsConnect, + notSet +} + +class Record extends $pb.GeneratedMessage { + factory Record({ + $core.String? version, + $core.String? toId, + $core.String? fromId, + Record_PayloadSecurity? payloadSecurity, + $core.List<$core.int>? macSignature, + $core.List<$core.int>? senderCert, + NoSessionContextRecord? noSessionContext, + SessionContextRecord? sessionContext, + WebSocketConnectRecord? websocketConnect, + MQTTConnectRecord? mqttConnect, + STOMPConnectRecord? stompConnect, + DisconnectRecord? disconnect, + UDSConnectRecord? udsConnect, + }) { + final result = create(); + if (version != null) result.version = version; + if (toId != null) result.toId = toId; + if (fromId != null) result.fromId = fromId; + if (payloadSecurity != null) result.payloadSecurity = payloadSecurity; + if (macSignature != null) result.macSignature = macSignature; + if (senderCert != null) result.senderCert = senderCert; + if (noSessionContext != null) result.noSessionContext = noSessionContext; + if (sessionContext != null) result.sessionContext = sessionContext; + if (websocketConnect != null) result.websocketConnect = websocketConnect; + if (mqttConnect != null) result.mqttConnect = mqttConnect; + if (stompConnect != null) result.stompConnect = stompConnect; + if (disconnect != null) result.disconnect = disconnect; + if (udsConnect != null) result.udsConnect = udsConnect; + return result; + } + + Record._(); + + factory Record.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory Record.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static const $core.Map<$core.int, Record_RecordType> _Record_RecordTypeByTag = + { + 7: Record_RecordType.noSessionContext, + 8: Record_RecordType.sessionContext, + 9: Record_RecordType.websocketConnect, + 10: Record_RecordType.mqttConnect, + 11: Record_RecordType.stompConnect, + 12: Record_RecordType.disconnect, + 13: Record_RecordType.udsConnect, + 0: Record_RecordType.notSet + }; + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'Record', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..oo(0, [7, 8, 9, 10, 11, 12, 13]) + ..aOS(1, _omitFieldNames ? '' : 'version') + ..aOS(2, _omitFieldNames ? '' : 'toId') + ..aOS(3, _omitFieldNames ? '' : 'fromId') + ..aE(4, _omitFieldNames ? '' : 'payloadSecurity', + enumValues: Record_PayloadSecurity.values) + ..a<$core.List<$core.int>>( + 5, _omitFieldNames ? '' : 'macSignature', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>( + 6, _omitFieldNames ? '' : 'senderCert', $pb.PbFieldType.OY) + ..aOM(7, _omitFieldNames ? '' : 'noSessionContext', + subBuilder: NoSessionContextRecord.create) + ..aOM(8, _omitFieldNames ? '' : 'sessionContext', + subBuilder: SessionContextRecord.create) + ..aOM(9, _omitFieldNames ? '' : 'websocketConnect', + subBuilder: WebSocketConnectRecord.create) + ..aOM(10, _omitFieldNames ? '' : 'mqttConnect', + subBuilder: MQTTConnectRecord.create) + ..aOM(11, _omitFieldNames ? '' : 'stompConnect', + subBuilder: STOMPConnectRecord.create) + ..aOM(12, _omitFieldNames ? '' : 'disconnect', + subBuilder: DisconnectRecord.create) + ..aOM(13, _omitFieldNames ? '' : 'udsConnect', + subBuilder: UDSConnectRecord.create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Record clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + Record copyWith(void Function(Record) updates) => + super.copyWith((message) => updates(message as Record)) as Record; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Record create() => Record._(); + @$core.override + Record createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static Record getDefault() => + _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Record? _defaultInstance; + + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + @$pb.TagNumber(12) + @$pb.TagNumber(13) + Record_RecordType whichRecordType() => + _Record_RecordTypeByTag[$_whichOneof(0)]!; + @$pb.TagNumber(7) + @$pb.TagNumber(8) + @$pb.TagNumber(9) + @$pb.TagNumber(10) + @$pb.TagNumber(11) + @$pb.TagNumber(12) + @$pb.TagNumber(13) + void clearRecordType() => $_clearField($_whichOneof(0)); + + @$pb.TagNumber(1) + $core.String get version => $_getSZ(0); + @$pb.TagNumber(1) + set version($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasVersion() => $_has(0); + @$pb.TagNumber(1) + void clearVersion() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get toId => $_getSZ(1); + @$pb.TagNumber(2) + set toId($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasToId() => $_has(1); + @$pb.TagNumber(2) + void clearToId() => $_clearField(2); + + @$pb.TagNumber(3) + $core.String get fromId => $_getSZ(2); + @$pb.TagNumber(3) + set fromId($core.String value) => $_setString(2, value); + @$pb.TagNumber(3) + $core.bool hasFromId() => $_has(2); + @$pb.TagNumber(3) + void clearFromId() => $_clearField(3); + + @$pb.TagNumber(4) + Record_PayloadSecurity get payloadSecurity => $_getN(3); + @$pb.TagNumber(4) + set payloadSecurity(Record_PayloadSecurity value) => $_setField(4, value); + @$pb.TagNumber(4) + $core.bool hasPayloadSecurity() => $_has(3); + @$pb.TagNumber(4) + void clearPayloadSecurity() => $_clearField(4); + + @$pb.TagNumber(5) + $core.List<$core.int> get macSignature => $_getN(4); + @$pb.TagNumber(5) + set macSignature($core.List<$core.int> value) => $_setBytes(4, value); + @$pb.TagNumber(5) + $core.bool hasMacSignature() => $_has(4); + @$pb.TagNumber(5) + void clearMacSignature() => $_clearField(5); + + @$pb.TagNumber(6) + $core.List<$core.int> get senderCert => $_getN(5); + @$pb.TagNumber(6) + set senderCert($core.List<$core.int> value) => $_setBytes(5, value); + @$pb.TagNumber(6) + $core.bool hasSenderCert() => $_has(5); + @$pb.TagNumber(6) + void clearSenderCert() => $_clearField(6); + + @$pb.TagNumber(7) + NoSessionContextRecord get noSessionContext => $_getN(6); + @$pb.TagNumber(7) + set noSessionContext(NoSessionContextRecord value) => $_setField(7, value); + @$pb.TagNumber(7) + $core.bool hasNoSessionContext() => $_has(6); + @$pb.TagNumber(7) + void clearNoSessionContext() => $_clearField(7); + @$pb.TagNumber(7) + NoSessionContextRecord ensureNoSessionContext() => $_ensure(6); + + @$pb.TagNumber(8) + SessionContextRecord get sessionContext => $_getN(7); + @$pb.TagNumber(8) + set sessionContext(SessionContextRecord value) => $_setField(8, value); + @$pb.TagNumber(8) + $core.bool hasSessionContext() => $_has(7); + @$pb.TagNumber(8) + void clearSessionContext() => $_clearField(8); + @$pb.TagNumber(8) + SessionContextRecord ensureSessionContext() => $_ensure(7); + + @$pb.TagNumber(9) + WebSocketConnectRecord get websocketConnect => $_getN(8); + @$pb.TagNumber(9) + set websocketConnect(WebSocketConnectRecord value) => $_setField(9, value); + @$pb.TagNumber(9) + $core.bool hasWebsocketConnect() => $_has(8); + @$pb.TagNumber(9) + void clearWebsocketConnect() => $_clearField(9); + @$pb.TagNumber(9) + WebSocketConnectRecord ensureWebsocketConnect() => $_ensure(8); + + @$pb.TagNumber(10) + MQTTConnectRecord get mqttConnect => $_getN(9); + @$pb.TagNumber(10) + set mqttConnect(MQTTConnectRecord value) => $_setField(10, value); + @$pb.TagNumber(10) + $core.bool hasMqttConnect() => $_has(9); + @$pb.TagNumber(10) + void clearMqttConnect() => $_clearField(10); + @$pb.TagNumber(10) + MQTTConnectRecord ensureMqttConnect() => $_ensure(9); + + @$pb.TagNumber(11) + STOMPConnectRecord get stompConnect => $_getN(10); + @$pb.TagNumber(11) + set stompConnect(STOMPConnectRecord value) => $_setField(11, value); + @$pb.TagNumber(11) + $core.bool hasStompConnect() => $_has(10); + @$pb.TagNumber(11) + void clearStompConnect() => $_clearField(11); + @$pb.TagNumber(11) + STOMPConnectRecord ensureStompConnect() => $_ensure(10); + + @$pb.TagNumber(12) + DisconnectRecord get disconnect => $_getN(11); + @$pb.TagNumber(12) + set disconnect(DisconnectRecord value) => $_setField(12, value); + @$pb.TagNumber(12) + $core.bool hasDisconnect() => $_has(11); + @$pb.TagNumber(12) + void clearDisconnect() => $_clearField(12); + @$pb.TagNumber(12) + DisconnectRecord ensureDisconnect() => $_ensure(11); + + @$pb.TagNumber(13) + UDSConnectRecord get udsConnect => $_getN(12); + @$pb.TagNumber(13) + set udsConnect(UDSConnectRecord value) => $_setField(13, value); + @$pb.TagNumber(13) + $core.bool hasUdsConnect() => $_has(12); + @$pb.TagNumber(13) + void clearUdsConnect() => $_clearField(13); + @$pb.TagNumber(13) + UDSConnectRecord ensureUdsConnect() => $_ensure(12); +} + +class NoSessionContextRecord extends $pb.GeneratedMessage { + factory NoSessionContextRecord({ + $core.List<$core.int>? payload, + }) { + final result = create(); + if (payload != null) result.payload = payload; + return result; + } + + NoSessionContextRecord._(); + + factory NoSessionContextRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory NoSessionContextRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'NoSessionContextRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 2, _omitFieldNames ? '' : 'payload', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + NoSessionContextRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + NoSessionContextRecord copyWith( + void Function(NoSessionContextRecord) updates) => + super.copyWith((message) => updates(message as NoSessionContextRecord)) + as NoSessionContextRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static NoSessionContextRecord create() => NoSessionContextRecord._(); + @$core.override + NoSessionContextRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static NoSessionContextRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static NoSessionContextRecord? _defaultInstance; + + @$pb.TagNumber(2) + $core.List<$core.int> get payload => $_getN(0); + @$pb.TagNumber(2) + set payload($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(2) + $core.bool hasPayload() => $_has(0); + @$pb.TagNumber(2) + void clearPayload() => $_clearField(2); +} + +class SessionContextRecord extends $pb.GeneratedMessage { + factory SessionContextRecord({ + $fixnum.Int64? sessionId, + $fixnum.Int64? sequenceId, + $fixnum.Int64? expectedId, + $fixnum.Int64? retransmitId, + SessionContextRecord_PayloadSARState? payloadSarState, + SessionContextRecord_PayloadSARState? payloadrecSarState, + $core.Iterable<$core.List<$core.int>>? payload, + }) { + final result = create(); + if (sessionId != null) result.sessionId = sessionId; + if (sequenceId != null) result.sequenceId = sequenceId; + if (expectedId != null) result.expectedId = expectedId; + if (retransmitId != null) result.retransmitId = retransmitId; + if (payloadSarState != null) result.payloadSarState = payloadSarState; + if (payloadrecSarState != null) + result.payloadrecSarState = payloadrecSarState; + if (payload != null) result.payload.addAll(payload); + return result; + } + + SessionContextRecord._(); + + factory SessionContextRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory SessionContextRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'SessionContextRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..a<$fixnum.Int64>( + 1, _omitFieldNames ? '' : 'sessionId', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>( + 2, _omitFieldNames ? '' : 'sequenceId', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>( + 3, _omitFieldNames ? '' : 'expectedId', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..a<$fixnum.Int64>( + 4, _omitFieldNames ? '' : 'retransmitId', $pb.PbFieldType.OU6, + defaultOrMaker: $fixnum.Int64.ZERO) + ..aE( + 5, _omitFieldNames ? '' : 'payloadSarState', + enumValues: SessionContextRecord_PayloadSARState.values) + ..aE( + 6, _omitFieldNames ? '' : 'payloadrecSarState', + enumValues: SessionContextRecord_PayloadSARState.values) + ..p<$core.List<$core.int>>( + 7, _omitFieldNames ? '' : 'payload', $pb.PbFieldType.PY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SessionContextRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + SessionContextRecord copyWith(void Function(SessionContextRecord) updates) => + super.copyWith((message) => updates(message as SessionContextRecord)) + as SessionContextRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static SessionContextRecord create() => SessionContextRecord._(); + @$core.override + SessionContextRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static SessionContextRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static SessionContextRecord? _defaultInstance; + + @$pb.TagNumber(1) + $fixnum.Int64 get sessionId => $_getI64(0); + @$pb.TagNumber(1) + set sessionId($fixnum.Int64 value) => $_setInt64(0, value); + @$pb.TagNumber(1) + $core.bool hasSessionId() => $_has(0); + @$pb.TagNumber(1) + void clearSessionId() => $_clearField(1); + + @$pb.TagNumber(2) + $fixnum.Int64 get sequenceId => $_getI64(1); + @$pb.TagNumber(2) + set sequenceId($fixnum.Int64 value) => $_setInt64(1, value); + @$pb.TagNumber(2) + $core.bool hasSequenceId() => $_has(1); + @$pb.TagNumber(2) + void clearSequenceId() => $_clearField(2); + + @$pb.TagNumber(3) + $fixnum.Int64 get expectedId => $_getI64(2); + @$pb.TagNumber(3) + set expectedId($fixnum.Int64 value) => $_setInt64(2, value); + @$pb.TagNumber(3) + $core.bool hasExpectedId() => $_has(2); + @$pb.TagNumber(3) + void clearExpectedId() => $_clearField(3); + + @$pb.TagNumber(4) + $fixnum.Int64 get retransmitId => $_getI64(3); + @$pb.TagNumber(4) + set retransmitId($fixnum.Int64 value) => $_setInt64(3, value); + @$pb.TagNumber(4) + $core.bool hasRetransmitId() => $_has(3); + @$pb.TagNumber(4) + void clearRetransmitId() => $_clearField(4); + + @$pb.TagNumber(5) + SessionContextRecord_PayloadSARState get payloadSarState => $_getN(4); + @$pb.TagNumber(5) + set payloadSarState(SessionContextRecord_PayloadSARState value) => + $_setField(5, value); + @$pb.TagNumber(5) + $core.bool hasPayloadSarState() => $_has(4); + @$pb.TagNumber(5) + void clearPayloadSarState() => $_clearField(5); + + @$pb.TagNumber(6) + SessionContextRecord_PayloadSARState get payloadrecSarState => $_getN(5); + @$pb.TagNumber(6) + set payloadrecSarState(SessionContextRecord_PayloadSARState value) => + $_setField(6, value); + @$pb.TagNumber(6) + $core.bool hasPayloadrecSarState() => $_has(5); + @$pb.TagNumber(6) + void clearPayloadrecSarState() => $_clearField(6); + + @$pb.TagNumber(7) + $pb.PbList<$core.List<$core.int>> get payload => $_getList(6); +} + +class WebSocketConnectRecord extends $pb.GeneratedMessage { + factory WebSocketConnectRecord() => create(); + + WebSocketConnectRecord._(); + + factory WebSocketConnectRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory WebSocketConnectRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'WebSocketConnectRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + WebSocketConnectRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + WebSocketConnectRecord copyWith( + void Function(WebSocketConnectRecord) updates) => + super.copyWith((message) => updates(message as WebSocketConnectRecord)) + as WebSocketConnectRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static WebSocketConnectRecord create() => WebSocketConnectRecord._(); + @$core.override + WebSocketConnectRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static WebSocketConnectRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static WebSocketConnectRecord? _defaultInstance; +} + +class MQTTConnectRecord extends $pb.GeneratedMessage { + factory MQTTConnectRecord({ + MQTTConnectRecord_MQTTVersion? version, + $core.String? subscribedTopic, + }) { + final result = create(); + if (version != null) result.version = version; + if (subscribedTopic != null) result.subscribedTopic = subscribedTopic; + return result; + } + + MQTTConnectRecord._(); + + factory MQTTConnectRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory MQTTConnectRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'MQTTConnectRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..aE(1, _omitFieldNames ? '' : 'version', + enumValues: MQTTConnectRecord_MQTTVersion.values) + ..aOS(2, _omitFieldNames ? '' : 'subscribedTopic') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + MQTTConnectRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + MQTTConnectRecord copyWith(void Function(MQTTConnectRecord) updates) => + super.copyWith((message) => updates(message as MQTTConnectRecord)) + as MQTTConnectRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static MQTTConnectRecord create() => MQTTConnectRecord._(); + @$core.override + MQTTConnectRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static MQTTConnectRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static MQTTConnectRecord? _defaultInstance; + + @$pb.TagNumber(1) + MQTTConnectRecord_MQTTVersion get version => $_getN(0); + @$pb.TagNumber(1) + set version(MQTTConnectRecord_MQTTVersion value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasVersion() => $_has(0); + @$pb.TagNumber(1) + void clearVersion() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get subscribedTopic => $_getSZ(1); + @$pb.TagNumber(2) + set subscribedTopic($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasSubscribedTopic() => $_has(1); + @$pb.TagNumber(2) + void clearSubscribedTopic() => $_clearField(2); +} + +class STOMPConnectRecord extends $pb.GeneratedMessage { + factory STOMPConnectRecord({ + STOMPConnectRecord_STOMPVersion? version, + $core.String? subscribedDestination, + }) { + final result = create(); + if (version != null) result.version = version; + if (subscribedDestination != null) + result.subscribedDestination = subscribedDestination; + return result; + } + + STOMPConnectRecord._(); + + factory STOMPConnectRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory STOMPConnectRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'STOMPConnectRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..aE(1, _omitFieldNames ? '' : 'version', + enumValues: STOMPConnectRecord_STOMPVersion.values) + ..aOS(2, _omitFieldNames ? '' : 'subscribedDestination') + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + STOMPConnectRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + STOMPConnectRecord copyWith(void Function(STOMPConnectRecord) updates) => + super.copyWith((message) => updates(message as STOMPConnectRecord)) + as STOMPConnectRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static STOMPConnectRecord create() => STOMPConnectRecord._(); + @$core.override + STOMPConnectRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static STOMPConnectRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static STOMPConnectRecord? _defaultInstance; + + @$pb.TagNumber(1) + STOMPConnectRecord_STOMPVersion get version => $_getN(0); + @$pb.TagNumber(1) + set version(STOMPConnectRecord_STOMPVersion value) => $_setField(1, value); + @$pb.TagNumber(1) + $core.bool hasVersion() => $_has(0); + @$pb.TagNumber(1) + void clearVersion() => $_clearField(1); + + @$pb.TagNumber(2) + $core.String get subscribedDestination => $_getSZ(1); + @$pb.TagNumber(2) + set subscribedDestination($core.String value) => $_setString(1, value); + @$pb.TagNumber(2) + $core.bool hasSubscribedDestination() => $_has(1); + @$pb.TagNumber(2) + void clearSubscribedDestination() => $_clearField(2); +} + +class UDSConnectRecord extends $pb.GeneratedMessage { + factory UDSConnectRecord() => create(); + + UDSConnectRecord._(); + + factory UDSConnectRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory UDSConnectRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'UDSConnectRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UDSConnectRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UDSConnectRecord copyWith(void Function(UDSConnectRecord) updates) => + super.copyWith((message) => updates(message as UDSConnectRecord)) + as UDSConnectRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UDSConnectRecord create() => UDSConnectRecord._(); + @$core.override + UDSConnectRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static UDSConnectRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static UDSConnectRecord? _defaultInstance; +} + +class DisconnectRecord extends $pb.GeneratedMessage { + factory DisconnectRecord({ + $core.String? reason, + $core.int? reasonCode, + }) { + final result = create(); + if (reason != null) result.reason = reason; + if (reasonCode != null) result.reasonCode = reasonCode; + return result; + } + + DisconnectRecord._(); + + factory DisconnectRecord.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory DisconnectRecord.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'DisconnectRecord', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_record'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'reason') + ..aI(2, _omitFieldNames ? '' : 'reasonCode', fieldType: $pb.PbFieldType.OF3) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DisconnectRecord clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + DisconnectRecord copyWith(void Function(DisconnectRecord) updates) => + super.copyWith((message) => updates(message as DisconnectRecord)) + as DisconnectRecord; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static DisconnectRecord create() => DisconnectRecord._(); + @$core.override + DisconnectRecord createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static DisconnectRecord getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static DisconnectRecord? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get reason => $_getSZ(0); + @$pb.TagNumber(1) + set reason($core.String value) => $_setString(0, value); + @$pb.TagNumber(1) + $core.bool hasReason() => $_has(0); + @$pb.TagNumber(1) + void clearReason() => $_clearField(1); + + @$pb.TagNumber(2) + $core.int get reasonCode => $_getIZ(1); + @$pb.TagNumber(2) + set reasonCode($core.int value) => $_setUnsignedInt32(1, value); + @$pb.TagNumber(2) + $core.bool hasReasonCode() => $_has(1); + @$pb.TagNumber(2) + void clearReasonCode() => $_clearField(2); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_record.pbenum.dart b/packages/usp_protocol_common/lib/src/generated/usp_record.pbenum.dart new file mode 100644 index 000000000..0bbd39cf1 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_record.pbenum.dart @@ -0,0 +1,103 @@ +// This is a generated file - do not edit. +// +// Generated from usp_record.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class Record_PayloadSecurity extends $pb.ProtobufEnum { + static const Record_PayloadSecurity PLAINTEXT = + Record_PayloadSecurity._(0, _omitEnumNames ? '' : 'PLAINTEXT'); + static const Record_PayloadSecurity TLS12 = + Record_PayloadSecurity._(1, _omitEnumNames ? '' : 'TLS12'); + + static const $core.List values = + [ + PLAINTEXT, + TLS12, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 1); + static Record_PayloadSecurity? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const Record_PayloadSecurity._(super.value, super.name); +} + +class SessionContextRecord_PayloadSARState extends $pb.ProtobufEnum { + static const SessionContextRecord_PayloadSARState NONE = + SessionContextRecord_PayloadSARState._(0, _omitEnumNames ? '' : 'NONE'); + static const SessionContextRecord_PayloadSARState BEGIN = + SessionContextRecord_PayloadSARState._(1, _omitEnumNames ? '' : 'BEGIN'); + static const SessionContextRecord_PayloadSARState INPROCESS = + SessionContextRecord_PayloadSARState._( + 2, _omitEnumNames ? '' : 'INPROCESS'); + static const SessionContextRecord_PayloadSARState COMPLETE = + SessionContextRecord_PayloadSARState._( + 3, _omitEnumNames ? '' : 'COMPLETE'); + + static const $core.List values = + [ + NONE, + BEGIN, + INPROCESS, + COMPLETE, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 3); + static SessionContextRecord_PayloadSARState? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const SessionContextRecord_PayloadSARState._(super.value, super.name); +} + +class MQTTConnectRecord_MQTTVersion extends $pb.ProtobufEnum { + static const MQTTConnectRecord_MQTTVersion V3_1_1 = + MQTTConnectRecord_MQTTVersion._(0, _omitEnumNames ? '' : 'V3_1_1'); + static const MQTTConnectRecord_MQTTVersion V5 = + MQTTConnectRecord_MQTTVersion._(1, _omitEnumNames ? '' : 'V5'); + + static const $core.List values = + [ + V3_1_1, + V5, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 1); + static MQTTConnectRecord_MQTTVersion? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const MQTTConnectRecord_MQTTVersion._(super.value, super.name); +} + +class STOMPConnectRecord_STOMPVersion extends $pb.ProtobufEnum { + static const STOMPConnectRecord_STOMPVersion V1_2 = + STOMPConnectRecord_STOMPVersion._(0, _omitEnumNames ? '' : 'V1_2'); + + static const $core.List values = + [ + V1_2, + ]; + + static final $core.List _byValue = + $pb.ProtobufEnum.$_initByValueList(values, 0); + static STOMPConnectRecord_STOMPVersion? valueOf($core.int value) => + value < 0 || value >= _byValue.length ? null : _byValue[value]; + + const STOMPConnectRecord_STOMPVersion._(super.value, super.name); +} + +const $core.bool _omitEnumNames = + $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_record.pbjson.dart b/packages/usp_protocol_common/lib/src/generated/usp_record.pbjson.dart new file mode 100644 index 000000000..db91bc430 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_record.pbjson.dart @@ -0,0 +1,297 @@ +// This is a generated file - do not edit. +// +// Generated from usp_record.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use recordDescriptor instead') +const Record$json = { + '1': 'Record', + '2': [ + {'1': 'version', '3': 1, '4': 1, '5': 9, '10': 'version'}, + {'1': 'to_id', '3': 2, '4': 1, '5': 9, '10': 'toId'}, + {'1': 'from_id', '3': 3, '4': 1, '5': 9, '10': 'fromId'}, + { + '1': 'payload_security', + '3': 4, + '4': 1, + '5': 14, + '6': '.usp_record.Record.PayloadSecurity', + '10': 'payloadSecurity' + }, + {'1': 'mac_signature', '3': 5, '4': 1, '5': 12, '10': 'macSignature'}, + {'1': 'sender_cert', '3': 6, '4': 1, '5': 12, '10': 'senderCert'}, + { + '1': 'no_session_context', + '3': 7, + '4': 1, + '5': 11, + '6': '.usp_record.NoSessionContextRecord', + '9': 0, + '10': 'noSessionContext' + }, + { + '1': 'session_context', + '3': 8, + '4': 1, + '5': 11, + '6': '.usp_record.SessionContextRecord', + '9': 0, + '10': 'sessionContext' + }, + { + '1': 'websocket_connect', + '3': 9, + '4': 1, + '5': 11, + '6': '.usp_record.WebSocketConnectRecord', + '9': 0, + '10': 'websocketConnect' + }, + { + '1': 'mqtt_connect', + '3': 10, + '4': 1, + '5': 11, + '6': '.usp_record.MQTTConnectRecord', + '9': 0, + '10': 'mqttConnect' + }, + { + '1': 'stomp_connect', + '3': 11, + '4': 1, + '5': 11, + '6': '.usp_record.STOMPConnectRecord', + '9': 0, + '10': 'stompConnect' + }, + { + '1': 'disconnect', + '3': 12, + '4': 1, + '5': 11, + '6': '.usp_record.DisconnectRecord', + '9': 0, + '10': 'disconnect' + }, + { + '1': 'uds_connect', + '3': 13, + '4': 1, + '5': 11, + '6': '.usp_record.UDSConnectRecord', + '9': 0, + '10': 'udsConnect' + }, + ], + '4': [Record_PayloadSecurity$json], + '8': [ + {'1': 'record_type'}, + ], +}; + +@$core.Deprecated('Use recordDescriptor instead') +const Record_PayloadSecurity$json = { + '1': 'PayloadSecurity', + '2': [ + {'1': 'PLAINTEXT', '2': 0}, + {'1': 'TLS12', '2': 1}, + ], +}; + +/// Descriptor for `Record`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recordDescriptor = $convert.base64Decode( + 'CgZSZWNvcmQSGAoHdmVyc2lvbhgBIAEoCVIHdmVyc2lvbhITCgV0b19pZBgCIAEoCVIEdG9JZB' + 'IXCgdmcm9tX2lkGAMgASgJUgZmcm9tSWQSTQoQcGF5bG9hZF9zZWN1cml0eRgEIAEoDjIiLnVz' + 'cF9yZWNvcmQuUmVjb3JkLlBheWxvYWRTZWN1cml0eVIPcGF5bG9hZFNlY3VyaXR5EiMKDW1hY1' + '9zaWduYXR1cmUYBSABKAxSDG1hY1NpZ25hdHVyZRIfCgtzZW5kZXJfY2VydBgGIAEoDFIKc2Vu' + 'ZGVyQ2VydBJSChJub19zZXNzaW9uX2NvbnRleHQYByABKAsyIi51c3BfcmVjb3JkLk5vU2Vzc2' + 'lvbkNvbnRleHRSZWNvcmRIAFIQbm9TZXNzaW9uQ29udGV4dBJLCg9zZXNzaW9uX2NvbnRleHQY' + 'CCABKAsyIC51c3BfcmVjb3JkLlNlc3Npb25Db250ZXh0UmVjb3JkSABSDnNlc3Npb25Db250ZX' + 'h0ElEKEXdlYnNvY2tldF9jb25uZWN0GAkgASgLMiIudXNwX3JlY29yZC5XZWJTb2NrZXRDb25u' + 'ZWN0UmVjb3JkSABSEHdlYnNvY2tldENvbm5lY3QSQgoMbXF0dF9jb25uZWN0GAogASgLMh0udX' + 'NwX3JlY29yZC5NUVRUQ29ubmVjdFJlY29yZEgAUgttcXR0Q29ubmVjdBJFCg1zdG9tcF9jb25u' + 'ZWN0GAsgASgLMh4udXNwX3JlY29yZC5TVE9NUENvbm5lY3RSZWNvcmRIAFIMc3RvbXBDb25uZW' + 'N0Ej4KCmRpc2Nvbm5lY3QYDCABKAsyHC51c3BfcmVjb3JkLkRpc2Nvbm5lY3RSZWNvcmRIAFIK' + 'ZGlzY29ubmVjdBI/Cgt1ZHNfY29ubmVjdBgNIAEoCzIcLnVzcF9yZWNvcmQuVURTQ29ubmVjdF' + 'JlY29yZEgAUgp1ZHNDb25uZWN0IisKD1BheWxvYWRTZWN1cml0eRINCglQTEFJTlRFWFQQABIJ' + 'CgVUTFMxMhABQg0KC3JlY29yZF90eXBl'); + +@$core.Deprecated('Use noSessionContextRecordDescriptor instead') +const NoSessionContextRecord$json = { + '1': 'NoSessionContextRecord', + '2': [ + {'1': 'payload', '3': 2, '4': 1, '5': 12, '10': 'payload'}, + ], +}; + +/// Descriptor for `NoSessionContextRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List noSessionContextRecordDescriptor = + $convert.base64Decode( + 'ChZOb1Nlc3Npb25Db250ZXh0UmVjb3JkEhgKB3BheWxvYWQYAiABKAxSB3BheWxvYWQ='); + +@$core.Deprecated('Use sessionContextRecordDescriptor instead') +const SessionContextRecord$json = { + '1': 'SessionContextRecord', + '2': [ + {'1': 'session_id', '3': 1, '4': 1, '5': 4, '10': 'sessionId'}, + {'1': 'sequence_id', '3': 2, '4': 1, '5': 4, '10': 'sequenceId'}, + {'1': 'expected_id', '3': 3, '4': 1, '5': 4, '10': 'expectedId'}, + {'1': 'retransmit_id', '3': 4, '4': 1, '5': 4, '10': 'retransmitId'}, + { + '1': 'payload_sar_state', + '3': 5, + '4': 1, + '5': 14, + '6': '.usp_record.SessionContextRecord.PayloadSARState', + '10': 'payloadSarState' + }, + { + '1': 'payloadrec_sar_state', + '3': 6, + '4': 1, + '5': 14, + '6': '.usp_record.SessionContextRecord.PayloadSARState', + '10': 'payloadrecSarState' + }, + {'1': 'payload', '3': 7, '4': 3, '5': 12, '10': 'payload'}, + ], + '4': [SessionContextRecord_PayloadSARState$json], +}; + +@$core.Deprecated('Use sessionContextRecordDescriptor instead') +const SessionContextRecord_PayloadSARState$json = { + '1': 'PayloadSARState', + '2': [ + {'1': 'NONE', '2': 0}, + {'1': 'BEGIN', '2': 1}, + {'1': 'INPROCESS', '2': 2}, + {'1': 'COMPLETE', '2': 3}, + ], +}; + +/// Descriptor for `SessionContextRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List sessionContextRecordDescriptor = $convert.base64Decode( + 'ChRTZXNzaW9uQ29udGV4dFJlY29yZBIdCgpzZXNzaW9uX2lkGAEgASgEUglzZXNzaW9uSWQSHw' + 'oLc2VxdWVuY2VfaWQYAiABKARSCnNlcXVlbmNlSWQSHwoLZXhwZWN0ZWRfaWQYAyABKARSCmV4' + 'cGVjdGVkSWQSIwoNcmV0cmFuc21pdF9pZBgEIAEoBFIMcmV0cmFuc21pdElkElwKEXBheWxvYW' + 'Rfc2FyX3N0YXRlGAUgASgOMjAudXNwX3JlY29yZC5TZXNzaW9uQ29udGV4dFJlY29yZC5QYXls' + 'b2FkU0FSU3RhdGVSD3BheWxvYWRTYXJTdGF0ZRJiChRwYXlsb2FkcmVjX3Nhcl9zdGF0ZRgGIA' + 'EoDjIwLnVzcF9yZWNvcmQuU2Vzc2lvbkNvbnRleHRSZWNvcmQuUGF5bG9hZFNBUlN0YXRlUhJw' + 'YXlsb2FkcmVjU2FyU3RhdGUSGAoHcGF5bG9hZBgHIAMoDFIHcGF5bG9hZCJDCg9QYXlsb2FkU0' + 'FSU3RhdGUSCAoETk9ORRAAEgkKBUJFR0lOEAESDQoJSU5QUk9DRVNTEAISDAoIQ09NUExFVEUQ' + 'Aw=='); + +@$core.Deprecated('Use webSocketConnectRecordDescriptor instead') +const WebSocketConnectRecord$json = { + '1': 'WebSocketConnectRecord', +}; + +/// Descriptor for `WebSocketConnectRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List webSocketConnectRecordDescriptor = + $convert.base64Decode('ChZXZWJTb2NrZXRDb25uZWN0UmVjb3Jk'); + +@$core.Deprecated('Use mQTTConnectRecordDescriptor instead') +const MQTTConnectRecord$json = { + '1': 'MQTTConnectRecord', + '2': [ + { + '1': 'version', + '3': 1, + '4': 1, + '5': 14, + '6': '.usp_record.MQTTConnectRecord.MQTTVersion', + '10': 'version' + }, + {'1': 'subscribed_topic', '3': 2, '4': 1, '5': 9, '10': 'subscribedTopic'}, + ], + '4': [MQTTConnectRecord_MQTTVersion$json], +}; + +@$core.Deprecated('Use mQTTConnectRecordDescriptor instead') +const MQTTConnectRecord_MQTTVersion$json = { + '1': 'MQTTVersion', + '2': [ + {'1': 'V3_1_1', '2': 0}, + {'1': 'V5', '2': 1}, + ], +}; + +/// Descriptor for `MQTTConnectRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List mQTTConnectRecordDescriptor = $convert.base64Decode( + 'ChFNUVRUQ29ubmVjdFJlY29yZBJDCgd2ZXJzaW9uGAEgASgOMikudXNwX3JlY29yZC5NUVRUQ2' + '9ubmVjdFJlY29yZC5NUVRUVmVyc2lvblIHdmVyc2lvbhIpChBzdWJzY3JpYmVkX3RvcGljGAIg' + 'ASgJUg9zdWJzY3JpYmVkVG9waWMiIQoLTVFUVFZlcnNpb24SCgoGVjNfMV8xEAASBgoCVjUQAQ' + '=='); + +@$core.Deprecated('Use sTOMPConnectRecordDescriptor instead') +const STOMPConnectRecord$json = { + '1': 'STOMPConnectRecord', + '2': [ + { + '1': 'version', + '3': 1, + '4': 1, + '5': 14, + '6': '.usp_record.STOMPConnectRecord.STOMPVersion', + '10': 'version' + }, + { + '1': 'subscribed_destination', + '3': 2, + '4': 1, + '5': 9, + '10': 'subscribedDestination' + }, + ], + '4': [STOMPConnectRecord_STOMPVersion$json], +}; + +@$core.Deprecated('Use sTOMPConnectRecordDescriptor instead') +const STOMPConnectRecord_STOMPVersion$json = { + '1': 'STOMPVersion', + '2': [ + {'1': 'V1_2', '2': 0}, + ], +}; + +/// Descriptor for `STOMPConnectRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List sTOMPConnectRecordDescriptor = $convert.base64Decode( + 'ChJTVE9NUENvbm5lY3RSZWNvcmQSRQoHdmVyc2lvbhgBIAEoDjIrLnVzcF9yZWNvcmQuU1RPTV' + 'BDb25uZWN0UmVjb3JkLlNUT01QVmVyc2lvblIHdmVyc2lvbhI1ChZzdWJzY3JpYmVkX2Rlc3Rp' + 'bmF0aW9uGAIgASgJUhVzdWJzY3JpYmVkRGVzdGluYXRpb24iGAoMU1RPTVBWZXJzaW9uEggKBF' + 'YxXzIQAA=='); + +@$core.Deprecated('Use uDSConnectRecordDescriptor instead') +const UDSConnectRecord$json = { + '1': 'UDSConnectRecord', +}; + +/// Descriptor for `UDSConnectRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List uDSConnectRecordDescriptor = + $convert.base64Decode('ChBVRFNDb25uZWN0UmVjb3Jk'); + +@$core.Deprecated('Use disconnectRecordDescriptor instead') +const DisconnectRecord$json = { + '1': 'DisconnectRecord', + '2': [ + {'1': 'reason', '3': 1, '4': 1, '5': 9, '10': 'reason'}, + {'1': 'reason_code', '3': 2, '4': 1, '5': 7, '10': 'reasonCode'}, + ], +}; + +/// Descriptor for `DisconnectRecord`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List disconnectRecordDescriptor = $convert.base64Decode( + 'ChBEaXNjb25uZWN0UmVjb3JkEhYKBnJlYXNvbhgBIAEoCVIGcmVhc29uEh8KC3JlYXNvbl9jb2' + 'RlGAIgASgHUgpyZWFzb25Db2Rl'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_transport.pb.dart b/packages/usp_protocol_common/lib/src/generated/usp_transport.pb.dart new file mode 100644 index 000000000..dc0471f81 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_transport.pb.dart @@ -0,0 +1,136 @@ +// This is a generated file - do not edit. +// +// Generated from usp_transport.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions; + +/// This is the request sent by the Client to the Router Gateway/Proxy +class UspTransportRequest extends $pb.GeneratedMessage { + factory UspTransportRequest({ + $core.List<$core.int>? uspRecordPayload, + }) { + final result = create(); + if (uspRecordPayload != null) result.uspRecordPayload = uspRecordPayload; + return result; + } + + UspTransportRequest._(); + + factory UspTransportRequest.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory UspTransportRequest.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'UspTransportRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_transport'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'uspRecordPayload', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UspTransportRequest clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UspTransportRequest copyWith(void Function(UspTransportRequest) updates) => + super.copyWith((message) => updates(message as UspTransportRequest)) + as UspTransportRequest; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UspTransportRequest create() => UspTransportRequest._(); + @$core.override + UspTransportRequest createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static UspTransportRequest getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static UspTransportRequest? _defaultInstance; + + /// The serialized USP Record binary data + @$pb.TagNumber(1) + $core.List<$core.int> get uspRecordPayload => $_getN(0); + @$pb.TagNumber(1) + set uspRecordPayload($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasUspRecordPayload() => $_has(0); + @$pb.TagNumber(1) + void clearUspRecordPayload() => $_clearField(1); +} + +/// This is the response returned by the Proxy to the Client +class UspTransportResponse extends $pb.GeneratedMessage { + factory UspTransportResponse({ + $core.List<$core.int>? uspRecordResponse, + }) { + final result = create(); + if (uspRecordResponse != null) result.uspRecordResponse = uspRecordResponse; + return result; + } + + UspTransportResponse._(); + + factory UspTransportResponse.fromBuffer($core.List<$core.int> data, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(data, registry); + factory UspTransportResponse.fromJson($core.String json, + [$pb.ExtensionRegistry registry = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(json, registry); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'UspTransportResponse', + package: const $pb.PackageName(_omitMessageNames ? '' : 'usp_transport'), + createEmptyInstance: create) + ..a<$core.List<$core.int>>( + 1, _omitFieldNames ? '' : 'uspRecordResponse', $pb.PbFieldType.OY) + ..hasRequiredFields = false; + + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UspTransportResponse clone() => deepCopy(); + @$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.') + UspTransportResponse copyWith(void Function(UspTransportResponse) updates) => + super.copyWith((message) => updates(message as UspTransportResponse)) + as UspTransportResponse; + + @$core.override + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static UspTransportResponse create() => UspTransportResponse._(); + @$core.override + UspTransportResponse createEmptyInstance() => create(); + @$core.pragma('dart2js:noInline') + static UspTransportResponse getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static UspTransportResponse? _defaultInstance; + + /// The USP Record binary data returned after Agent processing + @$pb.TagNumber(1) + $core.List<$core.int> get uspRecordResponse => $_getN(0); + @$pb.TagNumber(1) + set uspRecordResponse($core.List<$core.int> value) => $_setBytes(0, value); + @$pb.TagNumber(1) + $core.bool hasUspRecordResponse() => $_has(0); + @$pb.TagNumber(1) + void clearUspRecordResponse() => $_clearField(1); +} + +const $core.bool _omitFieldNames = + $core.bool.fromEnvironment('protobuf.omit_field_names'); +const $core.bool _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/usp_protocol_common/lib/src/generated/usp_transport.pbenum.dart b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbenum.dart new file mode 100644 index 000000000..cd523345b --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbenum.dart @@ -0,0 +1,11 @@ +// This is a generated file - do not edit. +// +// Generated from usp_transport.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names diff --git a/packages/usp_protocol_common/lib/src/generated/usp_transport.pbgrpc.dart b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbgrpc.dart new file mode 100644 index 000000000..3179dcf1e --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbgrpc.dart @@ -0,0 +1,78 @@ +// This is a generated file - do not edit. +// +// Generated from usp_transport.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:grpc/service_api.dart' as $grpc; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'usp_transport.pb.dart' as $0; + +export 'usp_transport.pb.dart'; + +/// Defines the gRPC service called by the Client App +@$pb.GrpcServiceName('usp_transport.UspTransportService') +class UspTransportServiceClient extends $grpc.Client { + /// The hostname for this service. + static const $core.String defaultHost = ''; + + /// OAuth scopes needed for the client. + static const $core.List<$core.String> oauthScopes = [ + '', + ]; + + UspTransportServiceClient(super.channel, {super.options, super.interceptors}); + + /// RPC method: Used to send a USP message and await a synchronous response. + /// The Proxy forwards this message to the internal MQTT broker. + $grpc.ResponseFuture<$0.UspTransportResponse> sendUspMessage( + $0.UspTransportRequest request, { + $grpc.CallOptions? options, + }) { + return $createUnaryCall(_$sendUspMessage, request, options: options); + } + + // method descriptors + + static final _$sendUspMessage = + $grpc.ClientMethod<$0.UspTransportRequest, $0.UspTransportResponse>( + '/usp_transport.UspTransportService/SendUspMessage', + ($0.UspTransportRequest value) => value.writeToBuffer(), + $0.UspTransportResponse.fromBuffer); +} + +@$pb.GrpcServiceName('usp_transport.UspTransportService') +abstract class UspTransportServiceBase extends $grpc.Service { + $core.String get $name => 'usp_transport.UspTransportService'; + + UspTransportServiceBase() { + $addMethod( + $grpc.ServiceMethod<$0.UspTransportRequest, $0.UspTransportResponse>( + 'SendUspMessage', + sendUspMessage_Pre, + false, + false, + ($core.List<$core.int> value) => + $0.UspTransportRequest.fromBuffer(value), + ($0.UspTransportResponse value) => value.writeToBuffer())); + } + + $async.Future<$0.UspTransportResponse> sendUspMessage_Pre( + $grpc.ServiceCall $call, + $async.Future<$0.UspTransportRequest> $request) async { + return sendUspMessage($call, await $request); + } + + $async.Future<$0.UspTransportResponse> sendUspMessage( + $grpc.ServiceCall call, $0.UspTransportRequest request); +} diff --git a/packages/usp_protocol_common/lib/src/generated/usp_transport.pbjson.dart b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbjson.dart new file mode 100644 index 000000000..2a55fb240 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/generated/usp_transport.pbjson.dart @@ -0,0 +1,53 @@ +// This is a generated file - do not edit. +// +// Generated from usp_transport.proto. + +// @dart = 3.3 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: curly_braces_in_flow_control_structures +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use uspTransportRequestDescriptor instead') +const UspTransportRequest$json = { + '1': 'UspTransportRequest', + '2': [ + { + '1': 'usp_record_payload', + '3': 1, + '4': 1, + '5': 12, + '10': 'uspRecordPayload' + }, + ], +}; + +/// Descriptor for `UspTransportRequest`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List uspTransportRequestDescriptor = $convert.base64Decode( + 'ChNVc3BUcmFuc3BvcnRSZXF1ZXN0EiwKEnVzcF9yZWNvcmRfcGF5bG9hZBgBIAEoDFIQdXNwUm' + 'Vjb3JkUGF5bG9hZA=='); + +@$core.Deprecated('Use uspTransportResponseDescriptor instead') +const UspTransportResponse$json = { + '1': 'UspTransportResponse', + '2': [ + { + '1': 'usp_record_response', + '3': 1, + '4': 1, + '5': 12, + '10': 'uspRecordResponse' + }, + ], +}; + +/// Descriptor for `UspTransportResponse`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List uspTransportResponseDescriptor = $convert.base64Decode( + 'ChRVc3BUcmFuc3BvcnRSZXNwb25zZRIuChN1c3BfcmVjb3JkX3Jlc3BvbnNlGAEgASgMUhF1c3' + 'BSZWNvcmRSZXNwb25zZQ=='); diff --git a/packages/usp_protocol_common/lib/src/interfaces/i_traversable_node.dart b/packages/usp_protocol_common/lib/src/interfaces/i_traversable_node.dart new file mode 100644 index 000000000..9c9d4807b --- /dev/null +++ b/packages/usp_protocol_common/lib/src/interfaces/i_traversable_node.dart @@ -0,0 +1,18 @@ +/// An interface for a node that can be traversed by [PathResolver]. +/// +/// [T] is the concrete implementation type (e.g., `UspNode` on the server-side, +/// or `LiteNode` on the client-side). +abstract class ITraversableNode> { + /// The name of the node, used for matching path segments. + String get name; + + /// Gets a child node by name (for exact matching). + /// + /// Returns null if this is not a container node or the child is not found. + T? getChild(String name); + + /// Gets all child nodes (for wildcard matching). + /// + /// Returns an empty iterable if this is not a container node. + Iterable getAllChildren(); +} diff --git a/packages/usp_protocol_common/lib/src/metadata/usp_dm_definitions.dart b/packages/usp_protocol_common/lib/src/metadata/usp_dm_definitions.dart new file mode 100644 index 000000000..d7dd99cf2 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/metadata/usp_dm_definitions.dart @@ -0,0 +1,113 @@ +import 'package:equatable/equatable.dart'; +import '../value_objects/usp_value_type.dart'; + +/// -------------------------------------------------------------------------- +/// Metadata Type Alias (供 Repository 介面使用) +/// -------------------------------------------------------------------------- + +/// The top-level map of the entire Data Model's Supported Capabilities. +/// Key is the requested path, Value is the object definition at that path. +typedef UspSupportedDMObject = Map; + +/// -------------------------------------------------------------------------- +/// Shared Metadata Structures (Used for GetSupportedDM) +/// -------------------------------------------------------------------------- + +/// Describes the "capabilities" of an Object +class UspObjectDefinition extends Equatable { + final String path; + final bool isMultiInstance; + final String access; // "ReadOnly", "ReadWrite", etc. + final Map supportedParams; + final Map supportedCommands; + // For Add/Delete, this is the basis (e.g. MAC address may be a unique key) + final List> uniqueKeySets; + // For complex XML, this is used to declare structural differences + final List divergentPaths; + + const UspObjectDefinition({ + required this.path, + this.isMultiInstance = false, + this.access = "ReadOnly", + this.supportedParams = const {}, + this.supportedCommands = const {}, + this.uniqueKeySets = const [], + this.divergentPaths = const [], + }); + + @override + List get props => [ + path, + isMultiInstance, + access, + supportedParams, + supportedCommands, + uniqueKeySets, + divergentPaths, + ]; +} + +/// Describes the "capabilities" of a Parameter +class UspParamDefinition extends Equatable { + final String name; + final UspValueType type; + final bool isWritable; + final UspParamConstraints constraints; + + const UspParamDefinition({ + required this.name, + required this.type, + required this.isWritable, + required this.constraints, + }); + + @override + List get props => [name, type, isWritable, constraints]; +} + +/// Describes the "capabilities" of a Command's Input/Output Arguments +class UspArgumentDefinition extends Equatable { + final String name; + final UspValueType type; + + const UspArgumentDefinition({required this.name, required this.type}); + + @override + List get props => [name, type]; +} + +/// Describes the "capabilities" of a Command +class UspCommandDefinition extends Equatable { + final String name; + final Map inputArgs; + final Map outputArgs; + final bool isAsync; + + const UspCommandDefinition({ + required this.name, + this.inputArgs = const {}, + this.outputArgs = const {}, + this.isAsync = false, + }); + + @override + List get props => [name, inputArgs, outputArgs, isAsync]; +} + +class UspParamConstraints extends Equatable { + final int? min; // min (From XML ) + final int? max; // max (From XML ) + final int? maxLength; // maxLength (From XML ) + final List + enumeration; // enumeration (From XML ) + + const UspParamConstraints({ + this.min, + this.max, + this.maxLength, + this.enumeration = const [], + }); + + @override + List get props => [min, max, maxLength, enumeration]; +} diff --git a/packages/usp_protocol_common/lib/src/record/usp_record_helper.dart b/packages/usp_protocol_common/lib/src/record/usp_record_helper.dart new file mode 100644 index 000000000..23ae017aa --- /dev/null +++ b/packages/usp_protocol_common/lib/src/record/usp_record_helper.dart @@ -0,0 +1,76 @@ +import '../generated/usp_msg.pb.dart' as pb_msg; +import '../generated/usp_record.pb.dart' as pb_record; +import '../exceptions/usp_exception.dart'; + +/// A utility class for wrapping and unwrapping USP messages in USP records. +class UspRecordHelper { + /// Wraps a [pb_msg.Msg] in a [pb_record.Record]. + pb_record.Record wrap( + pb_msg.Msg msg, { + required String fromId, + required String toId, + String version = "1.0", + }) { + // 1. Serialize the inner Msg + final payloadBytes = msg.writeToBuffer(); + + // 2. Construct the outer Record + // Note: Since this is PLAINTEXT, NoSessionContextRecord must be used + final noSessionRecord = pb_record.NoSessionContextRecord() + ..payload = payloadBytes; + + return pb_record.Record() + ..version = version + ..fromId = fromId + ..toId = toId + ..payloadSecurity = pb_record.Record_PayloadSecurity.PLAINTEXT + ..noSessionContext = noSessionRecord; + } + + /// Unwraps a [pb_msg.Msg] from a binary [pb_record.Record]. + pb_msg.Msg unwrap(List recordBytes) { + try { + // 1. Deserialize the outer Record + final record = pb_record.Record.fromBuffer(recordBytes); + + // 2. Check security and extract the payload + List payloadBytes; + + if (record.payloadSecurity == + pb_record.Record_PayloadSecurity.PLAINTEXT) { + if (!record.hasNoSessionContext()) { + throw UspException( + 7002, + "Record has PLAINTEXT security but noSessionContext is missing", + ); + } + payloadBytes = record.noSessionContext.payload; + } else if (record.payloadSecurity == + pb_record.Record_PayloadSecurity.TLS12) { + // TODO: Implement E2E decryption logic + // if (!record.hasSessionContext()) ... + throw UspException(7000, "E2E Encryption not supported yet"); + } else { + throw UspException( + 7000, + "Unsupported security type: ${record.payloadSecurity}", + ); + } + + // 3. Deserialize the inner Msg + if (payloadBytes.isEmpty) { + throw UspException(7002, "Record payload is empty"); + } + + return pb_msg.Msg.fromBuffer(payloadBytes); + } catch (e) { + if (e is UspException) rethrow; + throw UspException(7000, "Failed to parse USP Record: $e"); + } + } + + /// Peeks at the header of a binary [pb_record.Record] without unwrapping the payload. + pb_record.Record peekHeader(List recordBytes) { + return pb_record.Record.fromBuffer(recordBytes); + } +} diff --git a/packages/usp_protocol_common/lib/src/services/path_resolver.dart b/packages/usp_protocol_common/lib/src/services/path_resolver.dart new file mode 100644 index 000000000..a2dec05b8 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/services/path_resolver.dart @@ -0,0 +1,53 @@ +import '../interfaces/i_traversable_node.dart'; +import '../value_objects/usp_path.dart'; + +/// A utility class for resolving USP paths in a tree-like data structure. +class PathResolver { + /// Resolves a [UspPath] in a data structure that implements [ITraversableNode]. + /// + /// [root] is the starting point of the traversal. + /// [path] is the target path. + List resolve>(T root, UspPath path) { + final segments = path.segments; + final results = []; + + // Recursive traversal function + void traverse(T node, List currentSegments) { + // Base Case: Path traversal complete, target found + if (currentSegments.isEmpty) { + results.add(node); + return; + } + + final currentSegment = currentSegments.first; + final remainingSegments = currentSegments.sublist(1); + + // --- Case A: Wildcard (*) --- + if (currentSegment == '*') { + // Use the interface method getAllChildren() + for (final child in node.getAllChildren()) { + traverse(child, remainingSegments); + } + } + // --- Case B: Exact Match --- + else { + // Use the interface method getChild() + final child = node.getChild(currentSegment); + if (child != null) { + traverse(child, remainingSegments); + } + } + } + + // Startup logic: Handle the Root Name (e.g., "Device") + // If the path starts with the Root's name, consume the first segment and start searching from the next + if (segments.isNotEmpty && segments.first == root.name) { + traverse(root, segments.sublist(1)); + } else { + // Fault tolerance: If the path doesn't start with "Device", start searching directly from the Root + traverse(root, segments); + } + + return results; + } +} diff --git a/packages/usp_protocol_common/lib/src/value_objects/instance_id.dart b/packages/usp_protocol_common/lib/src/value_objects/instance_id.dart new file mode 100644 index 000000000..9e087bb07 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/instance_id.dart @@ -0,0 +1,22 @@ +import 'package:usp_protocol_common/src/exceptions/usp_exception.dart'; + +class InstanceId { + final int id; + + InstanceId(this.id) { + if (id < 0) { + throw UspException(7003, 'InstanceId cannot be negative'); + } + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is InstanceId && runtimeType == other.runtimeType && id == other.id; + + @override + int get hashCode => id.hashCode; + + @override + String toString() => 'InstanceId($id)'; +} diff --git a/packages/usp_protocol_common/lib/src/value_objects/usp_path.dart b/packages/usp_protocol_common/lib/src/value_objects/usp_path.dart new file mode 100644 index 000000000..15be072fb --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/usp_path.dart @@ -0,0 +1,110 @@ +import 'package:collection/collection.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'usp_path.g.dart'; + +/// A representation of a USP (User Services Platform) path. +/// +/// This class provides utilities for parsing, validating, and normalizing USP paths. +@JsonSerializable() +class UspPath { + /// The segments of the USP path. + final List segments; + + /// Whether the path contains a wildcard (`*`). + final bool hasWildcard; + + /// A map of alias filters for the path. + final Map aliasFilter; + + /// Creates a new [UspPath]. + UspPath( + this.segments, { + this.hasWildcard = false, + Map? aliasFilter, + }) : aliasFilter = aliasFilter ?? const {}; + + /// Creates a new [UspPath] from a JSON map. + factory UspPath.fromJson(Map json) => + _$UspPathFromJson(json); + + /// Converts this [UspPath] to a JSON map. + Map toJson() => _$UspPathToJson(this); + + /// Parses a raw path string into a [UspPath] object. + factory UspPath.parse(String rawPath) { + if (rawPath.isEmpty) { + return UspPath([]); + } + + final cleanedPath = rawPath.trim(); + final List parsedSegments = []; + bool wildcardFound = false; + Map parsedAliasFilter = {}; + + // Simple handling for Alias [Key=="Value"] + final aliasRegex = RegExp(r'\[(.*?)\]'); + final aliasMatch = aliasRegex.firstMatch(cleanedPath); + + if (aliasMatch != null) { + final filter = aliasMatch.group(1)!; + final parts = filter.split('=='); + if (parts.length == 2) { + parsedAliasFilter[parts[0]] = parts[1].replaceAll('"', ''); + } + } + + final pathWithoutAlias = cleanedPath.replaceAll(aliasRegex, ''); + // Handle trailing dots like in "Device." + final parts = pathWithoutAlias.split('.'); + + for (var part in parts) { + if (part == '*') { + wildcardFound = true; + } + if (part.isNotEmpty) { + parsedSegments.add(part); + } + } + + return UspPath( + parsedSegments, + hasWildcard: wildcardFound, + aliasFilter: parsedAliasFilter, + ); + } + + /// The full path string. + String get fullPath => segments.join('.'); + + /// The name of the path, which is the last segment. + String get name => segments.isNotEmpty ? segments.last : ''; + + /// The parent path of this path. + /// + /// Returns null if this path is the root. + UspPath? get parent { + if (segments.isEmpty) { + return null; // Root has no parent + } + return UspPath(segments.sublist(0, segments.length - 1)); + } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UspPath && + runtimeType == other.runtimeType && + const ListEquality().equals(segments, other.segments) && + hasWildcard == other.hasWildcard && + const MapEquality().equals(aliasFilter, other.aliasFilter); + + @override + int get hashCode => + const ListEquality().hash(segments) ^ + hasWildcard.hashCode ^ + const MapEquality().hash(aliasFilter); + + @override + String toString() => 'UspPath($fullPath)'; +} diff --git a/packages/usp_protocol_common/lib/src/value_objects/usp_path.g.dart b/packages/usp_protocol_common/lib/src/value_objects/usp_path.g.dart new file mode 100644 index 000000000..245946d39 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/usp_path.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'usp_path.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UspPath _$UspPathFromJson(Map json) => UspPath( + (json['segments'] as List).map((e) => e as String).toList(), + hasWildcard: json['hasWildcard'] as bool? ?? false, + aliasFilter: (json['aliasFilter'] as Map?)?.map( + (k, e) => MapEntry(k as String, e as String), + ), +); + +Map _$UspPathToJson(UspPath instance) => { + 'segments': instance.segments, + 'hasWildcard': instance.hasWildcard, + 'aliasFilter': instance.aliasFilter, +}; diff --git a/packages/usp_protocol_common/lib/src/value_objects/usp_value.dart b/packages/usp_protocol_common/lib/src/value_objects/usp_value.dart new file mode 100644 index 000000000..9f8f628b5 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/usp_value.dart @@ -0,0 +1,43 @@ +import 'package:usp_protocol_common/src/value_objects/usp_value_type.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'usp_value.g.dart'; + +/// A representation of a USP (User Services Platform) value. +/// +/// This class encapsulates a value of a specific USP data type. +@JsonSerializable(genericArgumentFactories: true) +class UspValue { + /// The actual value. + final T value; + + /// The USP data type of the value. + final UspValueType type; + + /// Creates a new [UspValue]. + UspValue(this.value, this.type); + + /// Creates a new [UspValue] from a JSON map. + factory UspValue.fromJson( + Map json, + T Function(Object? json) fromJsonT, + ) => _$UspValueFromJson(json, fromJsonT); + + /// Converts this [UspValue] to a JSON map. + Map toJson(Object? Function(T value) toJsonT) => + _$UspValueToJson(this, toJsonT); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is UspValue && + runtimeType == other.runtimeType && + value == other.value && + type == other.type; + + @override + int get hashCode => value.hashCode ^ type.hashCode; + + @override + String toString() => 'UspValue{value: $value, type: $type}'; +} diff --git a/packages/usp_protocol_common/lib/src/value_objects/usp_value.g.dart b/packages/usp_protocol_common/lib/src/value_objects/usp_value.g.dart new file mode 100644 index 000000000..7bc0eb2cb --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/usp_value.g.dart @@ -0,0 +1,35 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'usp_value.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +UspValue _$UspValueFromJson( + Map json, + T Function(Object? json) fromJsonT, +) => UspValue( + fromJsonT(json['value']), + $enumDecode(_$UspValueTypeEnumMap, json['type']), +); + +Map _$UspValueToJson( + UspValue instance, + Object? Function(T value) toJsonT, +) => { + 'value': toJsonT(instance.value), + 'type': _$UspValueTypeEnumMap[instance.type]!, +}; + +const _$UspValueTypeEnumMap = { + UspValueType.string: 'string', + UspValueType.int: 'int', + UspValueType.unsignedInt: 'unsignedInt', + UspValueType.long: 'long', + UspValueType.unsignedLong: 'unsignedLong', + UspValueType.boolean: 'boolean', + UspValueType.dateTime: 'dateTime', + UspValueType.base64: 'base64', + UspValueType.hexBinary: 'hexBinary', +}; diff --git a/packages/usp_protocol_common/lib/src/value_objects/usp_value_type.dart b/packages/usp_protocol_common/lib/src/value_objects/usp_value_type.dart new file mode 100644 index 000000000..818cfebc0 --- /dev/null +++ b/packages/usp_protocol_common/lib/src/value_objects/usp_value_type.dart @@ -0,0 +1,29 @@ +/// An enumeration of USP (User Services Platform) data types. +enum UspValueType { + /// A string value. + string, + + /// An integer value. + int, + + /// An unsigned integer value. + unsignedInt, + + /// A long integer value. + long, + + /// An unsigned long integer value. + unsignedLong, + + /// A boolean value. + boolean, + + /// A date and time value. + dateTime, + + /// A base64-encoded binary value. + base64, + + /// A hexadecimal binary value. + hexBinary, +} diff --git a/packages/usp_protocol_common/lib/usp_protocol_common.dart b/packages/usp_protocol_common/lib/usp_protocol_common.dart new file mode 100644 index 000000000..1539449d2 --- /dev/null +++ b/packages/usp_protocol_common/lib/usp_protocol_common.dart @@ -0,0 +1,34 @@ +/// A library for working with the USP (User Services Platform) protocol. +library usp_protocol_common; + +// Generated Protobuf Contracts +export 'src/generated/usp_msg.pb.dart'; +export 'src/generated/usp_record.pb.dart'; + +// DTOs +export 'src/dtos/base_dto.dart'; +export 'src/dtos/requests/usp_requests.dart'; +export 'src/dtos/responses/usp_responses.dart'; + +// Value Objects +export 'src/value_objects/usp_path.dart'; +export 'src/value_objects/usp_value.dart'; +export 'src/value_objects/usp_value_type.dart'; + +// Exceptions +export 'src/exceptions/usp_exception.dart'; + +// Services +export 'src/converter/usp_protobuf_converter.dart'; +export 'src/record/usp_record_helper.dart'; +export 'src/services/path_resolver.dart'; + +// Interfaces +export 'src/interfaces/i_traversable_node.dart'; + +// Export gRPC Transport related contracts +export 'src/generated/usp_transport.pb.dart'; +export 'src/generated/usp_transport.pbgrpc.dart'; + +// Metadata +export 'src/metadata/usp_dm_definitions.dart'; diff --git a/packages/usp_protocol_common/proto/usp_msg.proto b/packages/usp_protocol_common/proto/usp_msg.proto new file mode 100644 index 000000000..634a7c921 --- /dev/null +++ b/packages/usp_protocol_common/proto/usp_msg.proto @@ -0,0 +1,600 @@ +syntax = "proto3"; + +//************************************************************************** +// TR-369 USP Message Protocol Buffer Schema +// +// Copyright (c) 2017-2023, Broadband Forum +// +// The undersigned members have elected to grant the copyright to +// their contributed material used in this software: +// Copyright (c) 2017-2018 ARRIS Enterprises, LLC. +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the following +// conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The above license is used as a license under copyright only. +// Please reference the Forum IPR Policy for patent licensing terms +// . +// +// Any moral rights which are necessary to exercise under the above +// license grant are also deemed granted under this license. +// +// BBF software release registry: +// https://www.broadband-forum.org/software-releases +//************************************************************************** + +package usp; + +message Msg { + Header header = 1; // Make required in the protocol + Body body = 2; // Make required in the protocol +} + + +message Header { + string msg_id = 1; // Make required in the protocol + MsgType msg_type = 2; // Make required in the protocol + + enum MsgType { + ERROR = 0; + GET = 1; + GET_RESP = 2; + NOTIFY = 3; + SET = 4; + SET_RESP = 5; + OPERATE = 6; + OPERATE_RESP = 7; + ADD = 8; + ADD_RESP = 9; + DELETE = 10; + DELETE_RESP = 11; + GET_SUPPORTED_DM = 12; + GET_SUPPORTED_DM_RESP = 13; + GET_INSTANCES = 14; + GET_INSTANCES_RESP = 15; + NOTIFY_RESP = 16; + GET_SUPPORTED_PROTO = 17; + GET_SUPPORTED_PROTO_RESP = 18; + REGISTER = 19; + REGISTER_RESP = 20; + DEREGISTER = 21; + DEREGISTER_RESP = 22; + } +} + + +message Body { + oneof msg_body { + Request request = 1; + Response response = 2; + Error error = 3; + } +} + + +message Request { + oneof req_type { + Get get = 1; + GetSupportedDM get_supported_dm = 2; + GetInstances get_instances = 3; + Set set = 4; + Add add = 5; + Delete delete = 6; + Operate operate = 7; + Notify notify = 8; + GetSupportedProtocol get_supported_protocol = 9; + Register register = 10; + Deregister deregister = 11; + } +} + + +message Response { + oneof resp_type { + GetResp get_resp = 1; + GetSupportedDMResp get_supported_dm_resp = 2; + GetInstancesResp get_instances_resp = 3; + SetResp set_resp = 4; + AddResp add_resp = 5; + DeleteResp delete_resp = 6; + OperateResp operate_resp = 7; + NotifyResp notify_resp = 8; + GetSupportedProtocolResp get_supported_protocol_resp = 9; + RegisterResp register_resp = 10; + DeregisterResp deregister_resp = 11; + } +} + + +message Error { + fixed32 err_code = 1; + string err_msg = 2; + repeated ParamError param_errs = 3; + + message ParamError { + string param_path = 1; + fixed32 err_code = 2; + string err_msg = 3; + } +} + + +message Get { + repeated string param_paths = 1; + fixed32 max_depth = 2; +} + +message GetResp { + repeated RequestedPathResult req_path_results = 1; + + message RequestedPathResult { + string requested_path = 1; + fixed32 err_code = 2; + string err_msg = 3; + repeated ResolvedPathResult resolved_path_results = 4; + } + + message ResolvedPathResult { + string resolved_path = 1; + map result_params = 2; + } +} + + + +message GetSupportedDM { + repeated string obj_paths = 1; + bool first_level_only = 2; + bool return_commands = 3; + bool return_events = 4; + bool return_params = 5; + bool return_unique_key_sets = 6; +} + +message GetSupportedDMResp { + repeated RequestedObjectResult req_obj_results = 1; + + message RequestedObjectResult { + string req_obj_path = 1; + fixed32 err_code = 2; + string err_msg = 3; + string data_model_inst_uri = 4; + repeated SupportedObjectResult supported_objs = 5; + } + + message SupportedObjectResult { + string supported_obj_path = 1; + ObjAccessType access = 2; + bool is_multi_instance = 3; + repeated SupportedCommandResult supported_commands = 4; + repeated SupportedEventResult supported_events = 5; + repeated SupportedParamResult supported_params = 6; + repeated string divergent_paths = 7; + repeated SupportedUniqueKeySet unique_key_sets = 8; + } + + message SupportedParamResult { + string param_name = 1; + ParamAccessType access = 2; + ParamValueType value_type = 3; + ValueChangeType value_change = 4; + } + + message SupportedCommandResult { + string command_name = 1; + repeated string input_arg_names = 2; + repeated string output_arg_names = 3; + CmdType command_type = 4; + } + + message SupportedEventResult { + string event_name = 1; + repeated string arg_names = 2; + } + + message SupportedUniqueKeySet { + repeated string key_names = 1; + } + + enum ParamAccessType { + PARAM_READ_ONLY = 0; + PARAM_READ_WRITE = 1; + PARAM_WRITE_ONLY = 2; + } + + enum ObjAccessType { + OBJ_READ_ONLY = 0; + OBJ_ADD_DELETE = 1; + OBJ_ADD_ONLY = 2; + OBJ_DELETE_ONLY = 3; + } + + enum ParamValueType { + PARAM_UNKNOWN = 0; + PARAM_BASE_64 = 1; + PARAM_BOOLEAN = 2; + PARAM_DATE_TIME = 3; + PARAM_DECIMAL = 4; + PARAM_HEX_BINARY = 5; + PARAM_INT = 6; + PARAM_LONG = 7; + PARAM_STRING = 8; + PARAM_UNSIGNED_INT = 9; + PARAM_UNSIGNED_LONG = 10; + } + + enum ValueChangeType { + VALUE_CHANGE_UNKNOWN = 0; + VALUE_CHANGE_ALLOWED = 1; + VALUE_CHANGE_WILL_IGNORE = 2; + } + + enum CmdType { + CMD_UNKNOWN = 0; + CMD_SYNC = 1; + CMD_ASYNC = 2; + } +} + + +message GetInstances { + repeated string obj_paths = 1; + bool first_level_only = 2; +} + +message GetInstancesResp { + repeated RequestedPathResult req_path_results = 1; + + message RequestedPathResult { + string requested_path = 1; + fixed32 err_code = 2; + string err_msg = 3; + repeated CurrInstance curr_insts = 4; + } + + + message CurrInstance { + string instantiated_obj_path = 1; + map unique_keys = 2; + } +} + + +message GetSupportedProtocol { + string controller_supported_protocol_versions = 1; +} + +message GetSupportedProtocolResp { + string agent_supported_protocol_versions = 1; +} + + +message Add { + bool allow_partial = 1; + repeated CreateObject create_objs = 2; + + message CreateObject { + string obj_path = 1; + repeated CreateParamSetting param_settings = 2; + } + + message CreateParamSetting { + string param = 1; + string value = 2; + bool required = 3; + } +} + +message AddResp { + repeated CreatedObjectResult created_obj_results = 1; + + message CreatedObjectResult { + string requested_path = 1; + OperationStatus oper_status = 2; + + message OperationStatus { + oneof oper_status { + OperationFailure oper_failure = 1; + OperationSuccess oper_success = 2; + } + + message OperationFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + + message OperationSuccess { + string instantiated_path = 1; + repeated ParameterError param_errs = 2; + map unique_keys = 3; + } + } + } + + message ParameterError { + string param = 1; + fixed32 err_code = 2; + string err_msg = 3; + } +} + + +message Delete { + bool allow_partial = 1; + repeated string obj_paths = 2; +} + +message DeleteResp { + repeated DeletedObjectResult deleted_obj_results = 1; + + message DeletedObjectResult { + string requested_path = 1; + OperationStatus oper_status = 2; + + message OperationStatus { + oneof oper_status { + OperationFailure oper_failure = 1; + OperationSuccess oper_success = 2; + } + + message OperationFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + + message OperationSuccess { + repeated string affected_paths = 1; + repeated UnaffectedPathError unaffected_path_errs = 2; + } + } + } + + message UnaffectedPathError { + string unaffected_path = 1; + fixed32 err_code = 2; + string err_msg = 3; + } +} + + +message Set { + bool allow_partial = 1; + repeated UpdateObject update_objs = 2; + + message UpdateObject { + string obj_path = 1; + repeated UpdateParamSetting param_settings = 2; + } + + message UpdateParamSetting { + string param = 1; + string value = 2; + bool required = 3; + } +} + +message SetResp { + repeated UpdatedObjectResult updated_obj_results = 1; + + message UpdatedObjectResult { + string requested_path = 1; + OperationStatus oper_status = 2; + + message OperationStatus { + oneof oper_status { + OperationFailure oper_failure = 1; + OperationSuccess oper_success = 2; + } + + message OperationFailure { + fixed32 err_code = 1; + string err_msg = 2; + repeated UpdatedInstanceFailure updated_inst_failures = 3; + } + + message OperationSuccess { + repeated UpdatedInstanceResult updated_inst_results = 1; + } + } + } + + message UpdatedInstanceFailure { + string affected_path = 1; + repeated ParameterError param_errs = 2; + } + + message UpdatedInstanceResult { + string affected_path = 1; + repeated ParameterError param_errs = 2; + map updated_params = 3; + } + + message ParameterError { + string param = 1; + fixed32 err_code = 2; + string err_msg = 3; + } +} + +message Operate { + string command = 1; + string command_key = 2; + bool send_resp = 3; + map input_args = 4; +} + +message OperateResp { + repeated OperationResult operation_results = 1; + + message OperationResult { + string executed_command = 1; + oneof operation_resp { + string req_obj_path = 2; + OutputArgs req_output_args = 3; + CommandFailure cmd_failure = 4; + } + + message OutputArgs { + map output_args = 1; + } + + message CommandFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + } +} + +message Notify { + string subscription_id = 1; + bool send_resp = 2; + oneof notification { + Event event = 3; + ValueChange value_change = 4; + ObjectCreation obj_creation = 5; + ObjectDeletion obj_deletion = 6; + OperationComplete oper_complete = 7; + OnBoardRequest on_board_req = 8; + } + + message Event { + string obj_path = 1; + string event_name = 2; + map params = 3; + } + + message ValueChange { + string param_path = 1; + string param_value = 2; + } + + message ObjectCreation { + string obj_path = 1; + map unique_keys = 2; + } + + message ObjectDeletion { + string obj_path = 1; + } + + message OperationComplete { + string obj_path = 1; + string command_name = 2; + string command_key = 3; + oneof operation_resp { + OutputArgs req_output_args = 4; + CommandFailure cmd_failure = 5; + } + + message OutputArgs { + map output_args = 1; + } + + message CommandFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + } + + message OnBoardRequest { + string oui = 1; + string product_class = 2; + string serial_number = 3; + string agent_supported_protocol_versions = 4; + } +} + +message NotifyResp { + string subscription_id = 1; +} + +message Register { + bool allow_partial = 1; + repeated RegistrationPath reg_paths = 2; + + message RegistrationPath { + string path = 1; + } +} + +message RegisterResp { + repeated RegisteredPathResult registered_path_results = 1; + + message RegisteredPathResult { + string requested_path = 1; + OperationStatus oper_status = 2; + + message OperationStatus { + oneof oper_status { + OperationFailure oper_failure = 1; + OperationSuccess oper_success = 2; + } + + message OperationFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + + message OperationSuccess { + string registered_path = 1; + } + } + } +} + +message Deregister { + repeated string paths = 1; +} + +message DeregisterResp { + repeated DeregisteredPathResult deregistered_path_results = 1; + + message DeregisteredPathResult { + string requested_path = 1; + OperationStatus oper_status = 2; + + message OperationStatus { + oneof oper_status { + OperationFailure oper_failure = 1; + OperationSuccess oper_success = 2; + } + + message OperationFailure { + fixed32 err_code = 1; + string err_msg = 2; + } + + message OperationSuccess { + repeated string deregistered_path = 1; + } + } + } +} diff --git a/packages/usp_protocol_common/proto/usp_record.proto b/packages/usp_protocol_common/proto/usp_record.proto new file mode 100644 index 000000000..97ffe5e2e --- /dev/null +++ b/packages/usp_protocol_common/proto/usp_record.proto @@ -0,0 +1,131 @@ +syntax = "proto3"; + +//************************************************************************** +// TR-369 USP Record Protocol Buffer Schema +// +// Copyright (c) 2017-2023, Broadband Forum +// +// The undersigned members have elected to grant the copyright to +// their contributed material used in this software: +// Copyright (c) 2017-2022 ARRIS Enterprises, LLC. +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the following +// conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials +// provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products +// derived from this software without specific prior written +// permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +// CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// The above license is used as a license under copyright only. +// Please reference the Forum IPR Policy for patent licensing terms +// . +// +// Any moral rights which are necessary to exercise under the above +// license grant are also deemed granted under this license. +// +// BBF software release registry: +// https://www.broadband-forum.org/software-releases +//************************************************************************** + +package usp_record; + +message Record { + string version = 1; + string to_id = 2; + string from_id = 3; + PayloadSecurity payload_security = 4; + bytes mac_signature = 5; //MAC or Signature + bytes sender_cert = 6; + + oneof record_type { + NoSessionContextRecord no_session_context = 7; + SessionContextRecord session_context = 8; + WebSocketConnectRecord websocket_connect = 9; + MQTTConnectRecord mqtt_connect = 10; + STOMPConnectRecord stomp_connect = 11; + DisconnectRecord disconnect = 12; + UDSConnectRecord uds_connect = 13; + } + + enum PayloadSecurity { + PLAINTEXT = 0; + TLS12 = 1; + } +} + +message NoSessionContextRecord { + bytes payload = 2; +} + +message SessionContextRecord { + uint64 session_id = 1; + uint64 sequence_id = 2; + uint64 expected_id = 3; + uint64 retransmit_id = 4; + PayloadSARState payload_sar_state = 5; + PayloadSARState payloadrec_sar_state = 6; + repeated bytes payload = 7; + + enum PayloadSARState { + NONE = 0; //No segmentation + BEGIN = 1; //Begin segmentation + INPROCESS = 2; //Segmentation in process + COMPLETE = 3; //Segmentation is complete + } +} + +message WebSocketConnectRecord { + // An empty message +} + +message MQTTConnectRecord { + MQTTVersion version = 1; + string subscribed_topic = 2; + + enum MQTTVersion { + V3_1_1 = 0; // Represents MQTT v3.1.1, a.k.a. v4 in the MQTT Spec + V5 = 1; + } +} + +message STOMPConnectRecord { + STOMPVersion version = 1; + string subscribed_destination = 2; + + enum STOMPVersion { + V1_2 = 0; + } +} + +message UDSConnectRecord { + // An empty message +} + +message DisconnectRecord { + string reason = 1; + fixed32 reason_code = 2; +} diff --git a/packages/usp_protocol_common/proto/usp_transport.proto b/packages/usp_protocol_common/proto/usp_transport.proto new file mode 100644 index 000000000..458ae2d19 --- /dev/null +++ b/packages/usp_protocol_common/proto/usp_transport.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package usp_transport; + + +// --- DTOs for gRPC Payload --- + +// This is the request sent by the Client to the Router Gateway/Proxy +message UspTransportRequest { + // The serialized USP Record binary data + bytes usp_record_payload = 1; +} + +// This is the response returned by the Proxy to the Client +message UspTransportResponse { + // The USP Record binary data returned after Agent processing + bytes usp_record_response = 1; +} + +// --- gRPC Service Definition --- + +// Defines the gRPC service called by the Client App +service UspTransportService { + // RPC method: Used to send a USP message and await a synchronous response. + // The Proxy forwards this message to the internal MQTT broker. + rpc SendUspMessage (UspTransportRequest) returns (UspTransportResponse); +} \ No newline at end of file diff --git a/packages/usp_protocol_common/pubspec.lock b/packages/usp_protocol_common/pubspec.lock new file mode 100644 index 000000000..d2a3a9243 --- /dev/null +++ b/packages/usp_protocol_common/pubspec.lock @@ -0,0 +1,589 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "5b7468c326d2f8a4f630056404ca0d291ade42918f4a3c6233618e724f39da8e" + url: "https://pub.dev" + source: hosted + version: "92.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "70e4b1ef8003c64793a9e268a551a82869a8a96f39deb73dea28084b0e8bf75e" + url: "https://pub.dev" + source: hosted + version: "9.0.0" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 + url: "https://pub.dev" + source: hosted + version: "4.0.3" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 + url: "https://pub.dev" + source: hosted + version: "4.1.1" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" + url: "https://pub.dev" + source: hosted + version: "2.10.4" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" + url: "https://pub.dev" + source: hosted + version: "8.12.1" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + url: "https://pub.dev" + source: hosted + version: "4.11.0" + collection: + dependency: "direct main" + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: "direct dev" + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b + url: "https://pub.dev" + source: hosted + version: "3.1.3" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" + url: "https://pub.dev" + source: hosted + version: "2.0.8" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: "direct main" + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + google_identity_services_web: + dependency: transitive + description: + name: google_identity_services_web + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" + url: "https://pub.dev" + source: hosted + version: "0.3.3+1" + googleapis_auth: + dependency: transitive + description: + name: googleapis_auth + sha256: b81fe352cc4a330b3710d2b7ad258d9bcef6f909bb759b306bf42973a7d046db + url: "https://pub.dev" + source: hosted + version: "2.0.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + grpc: + dependency: "direct main" + description: + name: grpc + sha256: d5711432e7fcb41d23f88461651d503db98661af73ac7df211ace9c92fd03753 + url: "https://pub.dev" + source: hosted + version: "5.0.0" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http2: + dependency: transitive + description: + name: http2 + sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: "6b253f7851cf1626a05c8b49c792e04a14897349798c03798137f2b5f7e0b5b1" + url: "https://pub.dev" + source: hosted + version: "6.11.3" + lints: + dependency: "direct dev" + description: + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" + url: "https://pub.dev" + source: hosted + version: "0.12.18" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + protobuf: + dependency: "direct main" + description: + name: protobuf + sha256: "2fcc8a202ca7ec17dab7c97d6b6d91cf03aa07fe6f65f8afbb6dfa52cc5bd902" + url: "https://pub.dev" + source: hosted + version: "5.1.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: e82b1996c63da42aa3e6a34cc1ec17427728a1baf72ed017717a5669a7123f0d + url: "https://pub.dev" + source: hosted + version: "1.3.9" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test: + dependency: "direct dev" + description: + name: test + sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae" + url: "https://pub.dev" + source: hosted + version: "1.28.0" + test_api: + dependency: transitive + description: + name: test_api + sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8" + url: "https://pub.dev" + source: hosted + version: "0.7.8" + test_core: + dependency: transitive + description: + name: test_core + sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4 + url: "https://pub.dev" + source: hosted + version: "0.6.14" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" + url: "https://pub.dev" + source: hosted + version: "15.0.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 + url: "https://pub.dev" + source: hosted + version: "1.2.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" +sdks: + dart: ">=3.9.0 <4.0.0" diff --git a/packages/usp_protocol_common/pubspec.yaml b/packages/usp_protocol_common/pubspec.yaml new file mode 100644 index 000000000..195d9a255 --- /dev/null +++ b/packages/usp_protocol_common/pubspec.yaml @@ -0,0 +1,23 @@ +name: usp_protocol_common +description: "A pure Dart library for working with the USP (User Services Platform / TR-369) protocol." +version: 1.0.0 +homepage: + +environment: + sdk: ^3.8.1 + +dependencies: + protobuf: ^5.1.0 + equatable: ^2.0.5 + json_annotation: ^4.8.2 + fixnum: ^1.1.0 + collection: ^1.18.0 + grpc: ^5.0.0 + + +dev_dependencies: + test: ^1.24.0 + lints: ^3.0.0 + build_runner: ^2.4.0 + json_serializable: ^6.9.5 + coverage: ^1.7.2 \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 416fdbcf9..330a04a80 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -50,23 +50,30 @@ dependencies: path_provider: ^2.1.0 share_plus: ^7.1.0 package_info_plus: ^4.1.0 - phone_numbers_parser: ^8.3.0 uuid: ^4.4.2 flutter_inappwebview: ^5.7.0 shared_preferences: ^2.0.15 qr_flutter: ^4.0.0 url_launcher: ^6.1.5 - numberpicker: ^2.1.2 - graphview: ^1.2.0 local_auth: ^2.1.7 image: ^4.1.0 collection: ^1.18.0 flutter_secure_storage: ^9.2.2 + usp_client_core: + path: packages/usp_client_core + usp_protocol_common: + path: packages/usp_protocol_common + ui_kit_library: git: url: https://github.com/linksys/privacyGUI-UI-kit.git - ref: v2.3.2 + ref: v2.10.1 + generative_ui: + git: + url: https://github.com/linksys/privacyGUI-UI-kit.git + ref: v2.10.1 + path: generative_ui flutter_blue_plus: ^1.4.0 crypto: ^3.0.2 encrypt: ^5.0.3 @@ -77,15 +84,15 @@ dependencies: animated_list_plus: ^0.5.2 http_parser: ^4.0.2 meta: ^1.11.0 - flutter_fancy_tree_view: ^1.6.0 printing: ^5.13.1 get_it: ^7.7.0 pdf: ^3.11.1 - web: ^0.5.1 + web: ^1.1.1 file_picker: ^8.0.7 flutter_staggered_grid_view: ^0.7.0 async: ^2.11.0 path: ^1.9.0 + flutter_dotenv: ^5.0.2 dev_dependencies: flutter_test: @@ -122,6 +129,8 @@ flutter: # To add assets to your application, add an assets section, like this: assets: + - assets/.env + - env.template - assets/icons/ - assets/resources/ # An image asset can refer to one or more resolution-specific "variants", see diff --git a/run_generate_loc_snapshots.sh b/run_generate_loc_snapshots.sh index b4bb545e2..87622d99d 100755 --- a/run_generate_loc_snapshots.sh +++ b/run_generate_loc_snapshots.sh @@ -38,7 +38,7 @@ if [ -z "$file" ]; then part=( "${LOCS[@]:i:g}" ) locale=$(IFS=, ; echo "${part[*]}") echo "Start run screenshot testing with screen: $screens, locales: $locale" - flutter test --file-reporter json:snapshots/tests.json --tags=loc --update-goldens --dart-define=locales="$locale" --dart-define=screens="$screens" + flutter test --file-reporter json:snapshots/tests.json --tags=loc --update-goldens --dart-define=locales="$locale" --dart-define=screens="$screens" --dart-define=visualEffects=0 dart test_scripts/test_result_parser.dart snapshots/tests.json "$locale" "$screenStr" rm snapshots/tests.json done @@ -47,7 +47,7 @@ if [ -z "$file" ]; then dart test_scripts/combine_results.dart snapshots "$version" else echo "Target file: $file" - flutter test $file --tags=loc --update-goldens --dart-define=locales="$locales" --dart-define=screens="$screens" --dart-define=overlay="$overlay" + flutter test $file --tags=loc --update-goldens --dart-define=locales="$locales" --dart-define=screens="$screens" --dart-define=overlay="$overlay" --dart-define=visualEffects=0 exit $? # Exit with the status of the last command (flutter test) fi echo 'Generating Localization snapshots Finished!******************************************' diff --git a/test/common/testable_router.dart b/test/common/testable_router.dart index 58d3cb1a6..f47ca18a9 100644 --- a/test/common/testable_router.dart +++ b/test/common/testable_router.dart @@ -4,10 +4,11 @@ import 'package:go_router/go_router.dart'; import 'package:privacy_gui/page/dashboard/views/dashboard_shell.dart'; import 'package:privacy_gui/route/route_model.dart'; import 'package:privacy_gui/route/router_provider.dart'; -import 'package:privacy_gui/theme/theme_json_config.dart'; import 'package:privacy_gui/l10n/gen/app_localizations.dart'; import 'package:ui_kit_library/ui_kit.dart'; +import 'theme_data.dart'; + Widget testableRouter({ required GoRouter router, ProviderContainer? provider, @@ -19,8 +20,8 @@ Widget testableRouter({ bool disableAnimations = true, }) { // Use ThemeJsonConfig for consistent rendering with DI-registered themes - final appLightTheme = ThemeJsonConfig.defaultConfig().createLightTheme(); - final appDarkTheme = ThemeJsonConfig.defaultConfig().createDarkTheme(); + final appLightTheme = mockLightThemeData; + final appDarkTheme = mockDarkThemeData; Widget result = ProviderScope( overrides: overrides, diff --git a/test/common/theme_data.dart b/test/common/theme_data.dart index 6a412876d..69539096d 100644 --- a/test/common/theme_data.dart +++ b/test/common/theme_data.dart @@ -7,12 +7,22 @@ import 'package:golden_toolkit/golden_toolkit.dart'; import 'package:flutter/material.dart'; import 'package:privacy_gui/theme/theme_json_config.dart'; -final mockLightThemeData = ThemeJsonConfig.defaultConfig() - .createLightTheme() - .copyWith(textTheme: mockLinksysDarkTextTheme); -final mockDarkThemeData = ThemeJsonConfig.defaultConfig() - .createDarkTheme() - .copyWith(textTheme: mockLinksysLightTextTheme); +/// +/// Use this theme to generate snapshots with localizations +/// To avoid animation in snapshots, set visualEffects to 0 +/// Flat has no animation and global effects +/// +final mockLightThemeData = ThemeJsonConfig.fromJson({ + 'style': 'flat', + 'visualEffects': 0, + 'brightness': 'light', +}).createLightTheme(); + +final mockDarkThemeData = ThemeJsonConfig.fromJson({ + 'style': 'flat', + 'visualEffects': 0, + 'brightness': 'dark', +}).createDarkTheme(); /// /// This is used to help to generate snapshots with localizations diff --git a/test/page/advanced_settings/static_routing/views/localizations/static_routing_view_test.dart b/test/page/advanced_settings/static_routing/views/localizations/static_routing_view_test.dart index 3141010b1..4894ecc88 100644 --- a/test/page/advanced_settings/static_routing/views/localizations/static_routing_view_test.dart +++ b/test/page/advanced_settings/static_routing/views/localizations/static_routing_view_test.dart @@ -234,19 +234,19 @@ void main() { expect(nameController?.text, isEmpty); final destinationIp = tester - .widget(find.byKey(const Key('destinationIP'))) + .widget(find.byKey(const Key('destinationIP'))) .controller ?.text; expect(destinationIp, isEmpty); final subnetMask = tester - .widget(find.byKey(const Key('subnetMask'))) + .widget(find.byKey(const Key('subnetMask'))) .controller ?.text; expect(subnetMask, '255.255.255.0'); final gatewayIp = tester - .widget(find.byKey(const Key('gateway'))) + .widget(find.byKey(const Key('gateway'))) .controller ?.text; expect(gatewayIp, isEmpty); @@ -284,19 +284,19 @@ void main() { expect(nameController?.text, isEmpty); final destinationIp = tester - .widget(find.byKey(const Key('destinationIP'))) + .widget(find.byKey(const Key('destinationIP'))) .controller ?.text; expect(destinationIp, isEmpty); final subnetMask = tester - .widget(find.byKey(const Key('subnetMask'))) + .widget(find.byKey(const Key('subnetMask'))) .controller ?.text; expect(subnetMask, '255.255.255.0'); final gatewayIp = tester - .widget(find.byKey(const Key('gateway'))) + .widget(find.byKey(const Key('gateway'))) .controller ?.text; expect(gatewayIp, isEmpty); @@ -336,17 +336,17 @@ void main() { expect(nameController?.text, rule.name); final destinationIp = tester - .widget(find.byKey(const Key('destinationIP'))) + .widget(find.byKey(const Key('destinationIP'))) .controller ?.text; expect(destinationIp, rule.destinationIP); final subnetMask = tester - .widget(find.byKey(const Key('subnetMask'))) + .widget(find.byKey(const Key('subnetMask'))) .controller ?.text; expect(subnetMask, rule.subnetMask); final gatewayIp = tester - .widget(find.byKey(const Key('gateway'))) + .widget(find.byKey(const Key('gateway'))) .controller ?.text; expect(gatewayIp, rule.gateway); diff --git a/test/page/components/localizations/snack_bar_test.dart b/test/page/components/localizations/snack_bar_test.dart index 58aa8febd..dc3d59892 100644 --- a/test/page/components/localizations/snack_bar_test.dart +++ b/test/page/components/localizations/snack_bar_test.dart @@ -51,11 +51,7 @@ void main() { final label = successButtons[i]; final buttonFinder = find.text(label); - await tester.scrollUntilVisible( - buttonFinder, - 100, - scrollable: find.byType(Scrollable).last, - ); + await tester.ensureVisible(buttonFinder); await tester.tap(buttonFinder); await tester.pump(); // Start animation await tester @@ -119,11 +115,7 @@ void main() { final buttonIndex = startIndex + i; final buttonFinder = find.byType(AppButton).at(buttonIndex); - await tester.scrollUntilVisible( - buttonFinder, - 100, - scrollable: find.byType(Scrollable).last, - ); + await tester.ensureVisible(buttonFinder); await tester.tap(buttonFinder); await tester.pump(); diff --git a/test/page/dashboard/views/components/loading_tile_test.dart b/test/page/dashboard/views/components/loading_tile_test.dart new file mode 100644 index 000000000..ca9a47078 --- /dev/null +++ b/test/page/dashboard/views/components/loading_tile_test.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:privacy_gui/page/dashboard/views/components/loading_tile.dart'; +import 'package:ui_kit_library/ui_kit.dart'; +import 'package:mockito/mockito.dart'; + +void main() { + // Use a simple test wrapper that provides the AppDesignTheme + Widget createTestWidget(Widget child) { + return MaterialApp( + theme: ThemeData( + extensions: [ + // Use concrete GlassDesignTheme for testing + GlassDesignTheme.light(), + ], + ), + home: Scaffold( + body: child, + ), + ); + } + + group('LoadingTile', () { + testWidgets('renders child when isLoading is false', (tester) async { + await tester.pumpWidget(createTestWidget( + const LoadingTile( + isLoading: false, + child: Text('Content'), + ), + )); + + expect(find.text('Content'), findsOneWidget); + expect(find.byType(AppSkeleton), findsNothing); + }); + + testWidgets( + 'renders AppSkeleton structure when isLoading is true with simple child', + (tester) async { + await tester.pumpWidget(createTestWidget( + const LoadingTile( + isLoading: true, + child: Text('Content'), + ), + )); + + // Should find AppSkeleton instead of Text + expect(find.text('Content'), findsNothing); + expect(find.byType(AppSkeleton), findsOneWidget); + }); + + testWidgets('renders recursive structure when isLoading is true (Row)', + (tester) async { + await tester.pumpWidget(createTestWidget( + const LoadingTile( + isLoading: true, + child: Row( + children: [ + Text('Item 1'), + Icon(Icons.home), + ], + ), + ), + )); + + expect(find.text('Item 1'), findsNothing); + expect(find.byIcon(Icons.home), findsNothing); + + // Should preserve the Row + expect(find.byType(Row), findsOneWidget); + // Both Text and Icon should be converted to AppSkeleton + expect(find.byType(AppSkeleton), findsNWidgets(2)); + }); + + testWidgets('renders default skeleton when child is null', (tester) async { + await tester.pumpWidget(createTestWidget( + const LoadingTile(isLoading: true), + )); + + // The default skeleton has 3 AppSkeleton items in a Column + expect(find.byType(AppSkeleton), findsNWidgets(3)); + }); + }); +}