├── linux ├── .gitignore ├── main.cc ├── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugins.cmake │ ├── generated_plugin_registrant.cc │ └── CMakeLists.txt ├── my_application.h ├── my_application.cc └── CMakeLists.txt ├── CHANGELOG.md ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ │ └── LaunchBackground.imageset │ │ │ ├── background.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist └── .gitignore ├── macos ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── Runner │ ├── Configs │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ ├── Warnings.xcconfig │ │ └── AppInfo.xcconfig │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_64.png │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_512.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Release.entitlements │ ├── DebugProfile.entitlements │ ├── MainFlutterWindow.swift │ └── Info.plist ├── .gitignore ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner.xcodeproj │ ├── project.xcworkspace │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ └── xcschemes │ └── Runner.xcscheme ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── manifest.json └── index.html ├── images ├── desktop.png ├── mobile.jpg └── webview.jpg ├── fonts ├── Nunito-Bold.ttf └── Nunito-Regular.ttf ├── lib ├── features │ ├── device_add │ │ ├── device_add.dart │ │ ├── bloc │ │ │ ├── device_add_state.dart │ │ │ ├── device_add_event.dart │ │ │ └── device_add_bloc.dart │ │ ├── widgets │ │ │ ├── add_list_item.dart │ │ │ └── background_shape.dart │ │ └── views │ │ │ └── device_add_view.dart │ ├── device_list │ │ ├── device_list.dart │ │ ├── bloc │ │ │ ├── device_list_state.dart │ │ │ ├── device_list_event.dart │ │ │ └── device_list_bloc.dart │ │ ├── data │ │ │ ├── local_device_discovery.dart │ │ │ └── mdns_device_discovery.dart │ │ ├── domain │ │ │ └── device_fetch_repository.dart │ │ ├── widgets │ │ │ ├── device_list_switch.dart │ │ │ ├── device_list_grid_delegate.dart │ │ │ └── device_list_item.dart │ │ └── views │ │ │ └── device_list_view.dart │ ├── device_control │ │ ├── device_control.dart │ │ ├── bloc │ │ │ ├── device_control_event.dart │ │ │ ├── device_control_state.dart │ │ │ └── device_control_bloc.dart │ │ └── views │ │ │ └── device_control_view.dart │ ├── features.dart │ └── wled_device │ │ ├── wled_device.dart │ │ ├── utils │ │ ├── converters.dart │ │ └── extensions.dart │ │ ├── data │ │ └── http_connection.dart │ │ ├── models │ │ ├── wled_device.g.dart │ │ ├── wled_device.dart │ │ ├── info.g.dart │ │ ├── state.g.dart │ │ └── info.dart │ │ └── domain │ │ └── device_update_repository.dart ├── core │ ├── l10n │ │ ├── intl_nl.arb │ │ ├── intl_en.arb │ │ └── generated │ │ │ ├── intl │ │ │ ├── messages_nl.dart │ │ │ ├── messages_en.dart │ │ │ └── messages_all.dart │ │ │ └── l10n.dart │ ├── app │ │ ├── app_router.dart │ │ ├── app_injector.dart │ │ ├── app_bloc_observer.dart │ │ ├── app.dart │ │ ├── app_injector.config.dart │ │ └── app_router.gr.dart │ ├── core.dart │ ├── widgets │ │ ├── loading_widget.dart │ │ ├── round_icon_button.dart │ │ └── desktop_frame.dart │ └── utils │ │ ├── bloc_transformers.dart │ │ ├── consts.dart │ │ ├── overlays.dart │ │ ├── type_extensions.dart │ │ └── context_extensions.dart └── main.dart ├── assets ├── rive │ └── loading.riv └── images │ ├── fallback.png │ ├── splash.png │ ├── background.png │ └── foreground.png ├── android ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── drawable │ │ │ │ │ ├── background.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-hdpi │ │ │ │ │ ├── splash.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ ├── splash.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ ├── splash.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ ├── splash.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-v21 │ │ │ │ │ ├── background.png │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ ├── splash.png │ │ │ │ │ ├── ic_launcher_background.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── values │ │ │ │ │ └── styles.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── casvanluijtelaar │ │ │ │ │ └── wled_app │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle └── build.gradle ├── windows ├── runner │ ├── resources │ │ ├── app_icon.ico │ │ ├── app_icon_128.ico │ │ ├── app_icon_256.ico │ │ ├── app_icon_32.ico │ │ ├── app_icon_512.ico │ │ ├── app_icon_64.ico │ │ └── app_icon_1024.ico │ ├── CMakeLists.txt │ ├── resource.h │ ├── utils.h │ ├── runner.exe.manifest │ ├── flutter_window.h │ ├── main.cpp │ ├── utils.cpp │ ├── flutter_window.cpp │ ├── win32_window.h │ └── Runner.rc ├── .gitignore ├── flutter │ ├── generated_plugin_registrant.h │ ├── generated_plugin_registrant.cc │ ├── generated_plugins.cmake │ └── CMakeLists.txt └── CMakeLists.txt ├── .metadata ├── test └── core │ └── utils │ ├── string_extensions_test.dart │ └── num_extensions_test.dart ├── .github └── workflows │ └── build.yml ├── README.md ├── analysis_options.yaml ├── LICENSE ├── pubspec.yaml └── .gitignore /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | * Initial Relase -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/web/favicon.png -------------------------------------------------------------------------------- /images/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/images/desktop.png -------------------------------------------------------------------------------- /images/mobile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/images/mobile.jpg -------------------------------------------------------------------------------- /images/webview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/images/webview.jpg -------------------------------------------------------------------------------- /fonts/Nunito-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/fonts/Nunito-Bold.ttf -------------------------------------------------------------------------------- /lib/features/device_add/device_add.dart: -------------------------------------------------------------------------------- 1 | export 'views/device_add_view.dart' show DeviceAddView; 2 | -------------------------------------------------------------------------------- /lib/features/device_list/device_list.dart: -------------------------------------------------------------------------------- 1 | export 'views/device_list_view.dart' show DeviceListView; 2 | -------------------------------------------------------------------------------- /assets/rive/loading.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/assets/rive/loading.riv -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/web/icons/Icon-512.png -------------------------------------------------------------------------------- /assets/images/fallback.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/assets/images/fallback.png -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/assets/images/splash.png -------------------------------------------------------------------------------- /fonts/Nunito-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/fonts/Nunito-Regular.ttf -------------------------------------------------------------------------------- /assets/images/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/assets/images/background.png -------------------------------------------------------------------------------- /assets/images/foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/assets/images/foreground.png -------------------------------------------------------------------------------- /lib/features/device_control/device_control.dart: -------------------------------------------------------------------------------- 1 | export 'views/device_control_view.dart' show DeviceControlView; 2 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /lib/core/l10n/intl_nl.arb: -------------------------------------------------------------------------------- 1 | { 2 | "deviceListPowerOff": "Uit", 3 | "deviceListEmpty": "Geen apparaten gevonden" 4 | } -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /lib/core/l10n/intl_en.arb: -------------------------------------------------------------------------------- 1 | { 2 | "deviceListPowerOff": "off", 3 | "deviceListEmpty": "No devices found, Lets add some" 4 | } -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_128.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_128.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_256.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_256.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_32.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_32.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_512.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_512.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_64.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_64.ico -------------------------------------------------------------------------------- /windows/runner/resources/app_icon_1024.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/windows/runner/resources/app_icon_1024.ico -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-hdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-mdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-v21/background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxxhdpi/splash.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/casvanluijtelaar/WLED-App/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/features/features.dart: -------------------------------------------------------------------------------- 1 | export 'device_add/device_add.dart'; 2 | export 'device_control/device_control.dart'; 3 | export 'device_list/device_list.dart'; 4 | export 'wled_device/wled_device.dart'; 5 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/features/device_add/bloc/device_add_state.dart: -------------------------------------------------------------------------------- 1 | part of 'device_add_bloc.dart'; 2 | 3 | @freezed 4 | class DeviceAddState with _$DeviceAddState { 5 | const factory DeviceAddState() = _DeviceAddState; 6 | 7 | 8 | } 9 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/casvanluijtelaar/wled_app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.casvanluijtelaar.wled_app 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/features/wled_device/wled_device.dart: -------------------------------------------------------------------------------- 1 | export 'domain/device_update_repository.dart'; 2 | export 'models/state.dart'; 3 | export 'models/wled_device.dart'; 4 | export 'utils/converters.dart'; 5 | export 'utils/extensions.dart'; 6 | -------------------------------------------------------------------------------- /lib/features/device_add/bloc/device_add_event.dart: -------------------------------------------------------------------------------- 1 | part of 'device_add_bloc.dart'; 2 | 3 | @freezed 4 | class DeviceAddEvent with _$DeviceAddEvent { 5 | 6 | const factory DeviceAddEvent.initial(WledDevice? device) = Initial; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/features/device_control/bloc/device_control_event.dart: -------------------------------------------------------------------------------- 1 | part of 'device_control_bloc.dart'; 2 | 3 | @freezed 4 | class DeviceControlEvent with _$DeviceControlEvent { 5 | const factory DeviceControlEvent.started() = Started; 6 | 7 | const factory DeviceControlEvent.back() = Back; 8 | } 9 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: f4abaa0735eba4dfd8f33f73363911d63931fe03 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /lib/features/device_list/bloc/device_list_state.dart: -------------------------------------------------------------------------------- 1 | part of 'device_list_bloc.dart'; 2 | 3 | @freezed 4 | class DeviceListState with _$DeviceListState { 5 | const factory DeviceListState.loading() = Loading; 6 | 7 | const factory DeviceListState.found(List devices) = Found; 8 | 9 | const factory DeviceListState.empty() = Empty; 10 | } 11 | -------------------------------------------------------------------------------- /test/core/utils/string_extensions_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:wled/core/core.dart'; 4 | 5 | void main() { 6 | group('test string extensions', () { 7 | test('convert a string to a bool', () { 8 | const bool = 'true'; 9 | expect(bool.parseBool(), true); 10 | }); 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /lib/features/device_control/bloc/device_control_state.dart: -------------------------------------------------------------------------------- 1 | part of 'device_control_bloc.dart'; 2 | 3 | @freezed 4 | class DeviceControlState with _$DeviceControlState { 5 | const factory DeviceControlState.initial() = Initial; 6 | 7 | const factory DeviceControlState.loaded({ 8 | required String name, 9 | required String address, 10 | }) = Loaded; 11 | } 12 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "background.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/core/app/app_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:auto_route/auto_route.dart'; 2 | import 'package:wled/features/features.dart'; 3 | 4 | @MaterialAutoRouter( 5 | replaceInRouteName: 'View,Route', 6 | routes: [ 7 | AutoRoute( 8 | page: DeviceListView, 9 | path: 'devices', 10 | initial: true, 11 | ), 12 | AutoRoute( 13 | page: DeviceControlView, 14 | path: 'controls', 15 | ), 16 | ], 17 | ) 18 | class $AppRouter {} 19 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "LaunchImage.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "LaunchImage@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "LaunchImage@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/core/app/app_injector.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | import 'package:injectable/injectable.dart'; 3 | import 'package:wled/features/features.dart'; 4 | 5 | import 'app_injector.config.dart'; 6 | import 'app_router.gr.dart'; 7 | 8 | final getIt = GetIt.instance; 9 | 10 | @InjectableInit( 11 | generateForDir: ['lib'], 12 | ignoreUnregisteredTypes: [AppRouter, DeviceUpdateRepository], 13 | ) 14 | void configureDependencies() => $initGetIt(getIt); 15 | 16 | @module 17 | abstract class RegisterModule { 18 | AppRouter get appRouter => AppRouter(); 19 | } 20 | -------------------------------------------------------------------------------- /lib/core/core.dart: -------------------------------------------------------------------------------- 1 | /// app 2 | export 'app/app.dart'; 3 | export 'app/app_bloc_observer.dart'; 4 | export 'app/app_injector.dart'; 5 | export 'app/app_router.gr.dart'; 6 | export 'app/app_theme.dart'; 7 | /// l10n 8 | export 'l10n/generated/l10n.dart'; 9 | /// utils 10 | export 'utils/bloc_transformers.dart'; 11 | export 'utils/consts.dart'; 12 | export 'utils/context_extensions.dart'; 13 | export 'utils/overlays.dart'; 14 | export 'utils/type_extensions.dart'; 15 | /// widgets 16 | export 'widgets/desktop_frame.dart'; 17 | export 'widgets/loading_widget.dart'; 18 | export 'widgets/round_icon_button.dart'; 19 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_linux 7 | url_launcher_linux 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void RegisterPlugins(flutter::PluginRegistry* registry) { 13 | BitsdojoWindowPluginRegisterWithRegistrar( 14 | registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); 15 | UrlLauncherWindowsRegisterWithRegistrar( 16 | registry->GetRegistrarForPlugin("UrlLauncherWindows")); 17 | } 18 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_windows 7 | url_launcher_windows 8 | ) 9 | 10 | set(PLUGIN_BUNDLED_LIBRARIES) 11 | 12 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 13 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 14 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 15 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 17 | endforeach(plugin) 18 | -------------------------------------------------------------------------------- /lib/features/wled_device/utils/converters.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/rendering.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | class ColorConverter implements JsonConverter, List>> { 5 | const ColorConverter(); 6 | 7 | static const defaultColor = Color(0xFF303636); 8 | 9 | @override 10 | List fromJson(List> json) { 11 | return json.map((i) => Color.fromRGBO(i[0], i[1], i[2], 1)).toList(); 12 | } 13 | 14 | @override 15 | List> toJson(List colors) { 16 | return colors.map((c) => [c.red, c.green, c.blue]).toList(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/core/widgets/loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:rive/rive.dart'; 4 | 5 | import 'package:wled/core/core.dart'; 6 | 7 | class LoadingWidget extends StatelessWidget { 8 | const LoadingWidget({Key? key, this.size = 50}) : super(key: key); 9 | 10 | final double size; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return SizedBox( 15 | width: size, 16 | height: size, 17 | child: const RiveAnimation.asset( 18 | Kasset.loading, 19 | antialiasing: false, 20 | ), 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/features/device_add/bloc/device_add_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | 5 | import 'package:wled/core/core.dart'; 6 | import 'package:wled/features/wled_device/wled_device.dart'; 7 | 8 | part 'device_add_bloc.freezed.dart'; 9 | part 'device_add_event.dart'; 10 | part 'device_add_state.dart'; 11 | 12 | @injectable 13 | class DeviceAddBloc extends Bloc { 14 | DeviceAddBloc( 15 | this._router, 16 | ) : super(const DeviceAddState()); 17 | 18 | final AppRouter _router; 19 | } 20 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v1 8 | - uses: actions/setup-java@v1 9 | with: 10 | java-version: '12.x' 11 | - uses: subosito/flutter-action@v1 12 | with: 13 | channel: 'beta' 14 | 15 | - name: get packages 16 | run: flutter pub get 17 | 18 | - name: run code generation 19 | run: flutter pub run build_runner build --delete-conflicting-outputs 20 | 21 | - name: run analyzer 22 | run: flutter analyze 23 | 24 | - name: Run tests 25 | run: flutter test -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import bitsdojo_window_macos 4 | 5 | class MainFlutterWindow: BitsdojoWindow { 6 | 7 | override func bitsdojo_window_configure() -> UInt { 8 | return BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP 9 | } 10 | 11 | override func awakeFromNib() { 12 | let flutterViewController = FlutterViewController.init() 13 | let windowFrame = self.frame 14 | self.contentViewController = flutterViewController 15 | self.setFrame(windowFrame, display: true) 16 | 17 | RegisterGeneratedPlugins(registry: flutterViewController) 18 | 19 | super.awakeFromNib() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /lib/features/device_list/bloc/device_list_event.dart: -------------------------------------------------------------------------------- 1 | part of 'device_list_bloc.dart'; 2 | 3 | // ignore_for_file: lines_longer_than_80_chars 4 | 5 | @freezed 6 | class DeviceListEvent with _$DeviceListEvent { 7 | 8 | const factory DeviceListEvent.initial() = Initial; 9 | const factory DeviceListEvent.devicePressed(WledDevice device) = DevicePressed; 10 | const factory DeviceListEvent.devicePower(WledDevice device) = DevicePower; 11 | const factory DeviceListEvent.deviceSlider(WledDevice device, int value) = DeviceSlider; 12 | 13 | const factory DeviceListEvent.deviceAdd(BuildContext context) = DeviceAdd; 14 | 15 | const factory DeviceListEvent.listPeriodic() = ListPeriodic; 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WLED App 2 | 3 | this project aims to build a cross platform version of the well known [WLED Android & IOS app](https://github.com/Aircoookie/WLED-App) made by Aircookie. 4 | 5 | This application is build from the ground up to natively support Android, IOS, Windows, MacOS and Linux. 6 | 7 | 8 |
9 | Example 10 | Example 11 |
12 | Example -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = wled_app 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.casvanluijtelaar.wledApp 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.casvanluijtelaar. All rights reserved. 15 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | #define IDI_APP_ICON_32 102 7 | #define IDI_APP_ICON_64 103 8 | #define IDI_APP_ICON_128 104 9 | #define IDI_APP_ICON_256 105 10 | #define IDI_APP_ICON_512 106 11 | #define IDI_APP_ICON_1024 107 12 | 13 | // Next default values for new objects 14 | // 15 | #ifdef APSTUDIO_INVOKED 16 | #ifndef APSTUDIO_READONLY_SYMBOLS 17 | #define _APS_NEXT_RESOURCE_VALUE 102 18 | #define _APS_NEXT_COMMAND_VALUE 40001 19 | #define _APS_NEXT_CONTROL_VALUE 1001 20 | #define _APS_NEXT_SYMED_VALUE 101 21 | #endif 22 | #endif 23 | -------------------------------------------------------------------------------- /lib/core/app/app_bloc_observer.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | 5 | // ignore_for_file: avoid_dynamic_calls 6 | class AppBlocObserver extends BlocObserver { 7 | @override 8 | void onTransition(Bloc bloc, Transition transition) { 9 | log( 10 | '${bloc.runtimeType}: ' 11 | 'from ${transition.currentState.runtimeType} ' 12 | 'to ${transition.nextState.runtimeType}', 13 | ); 14 | super.onTransition(bloc, transition); 15 | } 16 | 17 | @override 18 | void onError(BlocBase bloc, Object error, StackTrace stackTrace) { 19 | log(error.toString(), stackTrace: stackTrace); 20 | super.onError(bloc, error, stackTrace); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /lib/features/device_list/data/local_device_discovery.dart: -------------------------------------------------------------------------------- 1 | import 'package:injectable/injectable.dart'; 2 | import 'package:wled/features/wled_device/wled_device.dart'; 3 | 4 | @injectable 5 | class LocalDeviceDiscovery { 6 | LocalDeviceDiscovery(); 7 | 8 | 9 | 10 | /// fetch all the locally saved WledDevices 11 | List getWledDevice() { 12 | return []; 13 | } 14 | 15 | /// adds a Wled device to the local storage only if it isn't saved already 16 | void saveWledDevice(WledDevice device) { 17 | 18 | } 19 | 20 | /// attempts to remove WledDevice from the local storage 21 | void removeWledDevice(WledDevice d) { 22 | 23 | } 24 | 25 | void updateWledDevice(WledDevice d) { 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:very_good_analysis/analysis_options.yaml 2 | 3 | linter: 4 | rules: 5 | public_member_api_docs: false 6 | always_use_package_imports: false 7 | sort_constructors_first: false 8 | implicit_dynamic_type: false 9 | use_full_hex_values_for_flutter_colors: false 10 | unawaited_futures: false 11 | require_trailing_commas: false 12 | library_private_types_in_public_api: false 13 | prefer_asserts_with_message: false 14 | 15 | analyzer: 16 | exclude: 17 | - lib/core/app/app_injector.config.dart 18 | - lib/core/l10n/** 19 | - lib/generated_plugin_registrant.dart 20 | - "**/*.g.dart" 21 | - "**/*.gr.dart" 22 | - "**/*.freezed.dart" 23 | errors: 24 | invalid_annotation_target: ignore -------------------------------------------------------------------------------- /lib/core/utils/bloc_transformers.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:rxdart/rxdart.dart'; 3 | import 'package:wled/core/core.dart'; 4 | 5 | abstract class BlocTransformers { 6 | /// bloc transformer that's a debounce time to prevent bloc spamming 7 | static Stream debounce( 8 | Stream events, 9 | Stream Function(T) transitionFn, 10 | ) { 11 | return events 12 | .debounceTime(const Duration(milliseconds: 250)) 13 | .switchMap(transitionFn); 14 | } 15 | } 16 | 17 | mixin AutoResetLazySingleton on Bloc { 18 | @override 19 | Future close() { 20 | if (getIt.isRegistered>(instance: this)) { 21 | getIt.resetLazySingleton>(instance: this); 22 | } 23 | return super.close(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/core/utils/consts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | abstract class Kpadding { 4 | static const large = 32.0; 5 | static const medium = 20.0; 6 | static const small = 8.0; 7 | static const extraSmall = 4.0; 8 | } 9 | 10 | abstract class Kduration { 11 | static const xLarge = Duration(milliseconds: 1000); 12 | static const large = Duration(milliseconds: 800); 13 | static const medium = Duration(milliseconds: 400); 14 | static const short = Duration(milliseconds: 200); 15 | } 16 | 17 | abstract class Kspacer { 18 | static const small = SizedBox(height: 4); 19 | static const medium = SizedBox(height: 8); 20 | static const large = SizedBox(height: 16); 21 | } 22 | 23 | abstract class Kasset { 24 | static const loading = 'assets/rive/loading.riv'; 25 | static const devicesBox = 'devices'; 26 | } 27 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | 12 | void fl_register_plugins(FlPluginRegistry* registry) { 13 | g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = 14 | fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); 15 | bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); 16 | g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = 17 | fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); 18 | url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); 19 | } 20 | -------------------------------------------------------------------------------- /lib/features/wled_device/data/http_connection.dart: -------------------------------------------------------------------------------- 1 | import 'package:http/http.dart' as http; 2 | 3 | class HttpConnectionException implements Exception {} 4 | class HttpStatusException implements Exception {} 5 | 6 | /// class wrapping all the HTTP interactions 7 | class HttpConnection { 8 | 9 | /// sends [data] to the provided [url], can throw 10 | /// [HttpConnectionException] or [HttpStatusException] 11 | Future sendApiCall(String url, [String? data]) async { 12 | try { 13 | 14 | final result = await http 15 | .post(Uri.parse('$url/json'), body: data) 16 | .timeout(const Duration(seconds: 4)); 17 | 18 | if (result.statusCode != 200) throw HttpStatusException(); 19 | 20 | return result.body; 21 | } catch (e) { 22 | throw HttpConnectionException(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/core/utils/num_extensions_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_test/flutter_test.dart'; 2 | 3 | import 'package:wled/core/core.dart'; 4 | 5 | void main() { 6 | group('number extension tests', () { 7 | test('map an integer to a new range', () { 8 | const old = 10; 9 | const oldMin = 0; 10 | const oldMax = 100; 11 | 12 | const neww = 1; 13 | const newMin = 0; 14 | const newMax = 10; 15 | 16 | final updated = old.map(oldMin, oldMax, newMin, newMax); 17 | 18 | expect(updated, neww); 19 | }); 20 | 21 | test('map a double to a new range', () { 22 | const old = 10.0; 23 | const oldMin = 0.0; 24 | const oldMax = 100.0; 25 | 26 | const neww = 1.0; 27 | const newMin = 0.0; 28 | const newMax = 10.0; 29 | 30 | final updated = old.map(oldMin, oldMax, newMin, newMax); 31 | 32 | expect(updated, neww); 33 | }); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /lib/core/utils/overlays.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wled/core/core.dart'; 3 | import 'package:wled/core/utils/consts.dart'; 4 | 5 | /// holder class for overlay related implementations 6 | abstract class Overlays { 7 | /// shows a basic that animates in from the bottom of the screen 8 | static Future showDialog(BuildContext context, Widget child) { 9 | return showGeneralDialog( 10 | barrierLabel: 'add', 11 | barrierDismissible: true, 12 | barrierColor: Colors.black.withOpacity(0.5), 13 | transitionDuration: Kduration.medium, 14 | context: context, 15 | pageBuilder: (context, anim1, anim2) => SafeArea(child: child), 16 | transitionBuilder: (context, anim1, anim2, child) { 17 | return FadeTransition( 18 | opacity: Tween(begin: 0, end: 1).animate(anim1), 19 | child: child, 20 | ); 21 | }, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /lib/features/wled_device/models/wled_device.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'wled_device.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_WledDevice _$$_WledDeviceFromJson(Map json) => 10 | _$_WledDevice( 11 | state: DeviceState.fromJson(json['state'] as Map), 12 | info: DeviceInfo.fromJson(json['info'] as Map), 13 | effects: 14 | (json['effects'] as List).map((e) => e as String).toList(), 15 | palettes: 16 | (json['palettes'] as List).map((e) => e as String).toList(), 17 | ); 18 | 19 | Map _$$_WledDeviceToJson(_$_WledDevice instance) => 20 | { 21 | 'state': instance.state, 22 | 'info': instance.info, 23 | 'effects': instance.effects, 24 | 'palettes': instance.palettes, 25 | }; 26 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wled_app", 3 | "short_name": "wled_app", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /lib/core/widgets/round_icon_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RoundIconButton extends StatelessWidget { 4 | const RoundIconButton({ 5 | Key? key, 6 | required this.icon, 7 | this.size = 32, 8 | this.onPressed, 9 | this.background = const Color.fromRGBO(0, 0, 0, 0.1544), 10 | }) : super(key: key); 11 | 12 | final double size; 13 | final VoidCallback? onPressed; 14 | final IconData icon; 15 | final Color background; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return InkWell( 20 | customBorder: RoundedRectangleBorder( 21 | borderRadius: BorderRadius.circular(double.maxFinite), 22 | ), 23 | onTap: onPressed, 24 | child: SizedBox.square( 25 | dimension: size, 26 | child: DecoratedBox( 27 | decoration: BoxDecoration( 28 | color: background, 29 | borderRadius: BorderRadius.all(Radius.circular(size / 2)), 30 | ), 31 | child: Icon(icon, size: 24), 32 | ), 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Cas van Luijtelaar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 16 | 19 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/core/app/app.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_localizations/flutter_localizations.dart'; 5 | 6 | import 'package:wled/core/core.dart'; 7 | 8 | class App extends StatelessWidget { 9 | const App({Key? key}) : super(key: key); 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | final _appRouter = getIt(); 14 | 15 | return MaterialApp.router( 16 | title: 'WLED', 17 | theme: AppTheme.theme, 18 | darkTheme: AppTheme.theme, 19 | debugShowCheckedModeBanner: false, 20 | routeInformationParser: _appRouter.defaultRouteParser(), 21 | routerDelegate: _appRouter.delegate(), 22 | localizationsDelegates: const [ 23 | GlobalMaterialLocalizations.delegate, 24 | GlobalCupertinoLocalizations.delegate, 25 | GlobalWidgetsLocalizations.delegate, 26 | Localization.delegate, 27 | ], 28 | supportedLocales: Localization.delegate.supportedLocales, 29 | builder: (context, child) => 30 | Platform.isWindows || Platform.isMacOS || Platform.isLinux 31 | ? DesktopFrame(child: child!) 32 | : child!, 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/features/device_control/bloc/device_control_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:injectable/injectable.dart'; 4 | 5 | import 'package:wled/core/core.dart'; 6 | 7 | part 'device_control_event.dart'; 8 | part 'device_control_state.dart'; 9 | part 'device_control_bloc.freezed.dart'; 10 | 11 | @injectable 12 | class DeviceControlBloc extends Bloc { 13 | DeviceControlBloc(this._router) : super(const Initial()) { 14 | on(_onStarted); 15 | on(_onBack); 16 | } 17 | 18 | final AppRouter _router; 19 | 20 | String get _name => 21 | _router.current.argsAs().deviceName; 22 | 23 | String get _address => 24 | _router.current.argsAs().deviceAddress; 25 | 26 | /// when have fetched all data from the router we can display the controls 27 | void _onStarted(Started event, Emitter emit) { 28 | emit(Loaded(name: _name, address: _address)); 29 | } 30 | 31 | /// pop this route from the stack to go back to the previous page 32 | void _onBack(Back event, Emitter emit) { 33 | _router.pop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer'; 3 | import 'dart:io'; 4 | 5 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 6 | import 'package:bloc/bloc.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | import 'package:wled/core/core.dart'; 9 | import 'package:wled/features/features.dart'; 10 | import 'package:wled/features/wled_device/data/http_connection.dart'; 11 | 12 | Future main() async { 13 | WidgetsFlutterBinding.ensureInitialized(); 14 | 15 | // temp because injectable is broken 16 | getIt 17 | ..registerSingleton(HttpConnection()) 18 | ..registerSingleton(DeviceUpdateRepository(getIt())); 19 | 20 | /// setup GetIt injections 21 | configureDependencies(); 22 | 23 | /// run main application 24 | runZonedGuarded( 25 | () => BlocOverrides.runZoned(() => runApp(const App()), 26 | blocObserver: AppBlocObserver()), 27 | (e, s) => log(e.toString(), stackTrace: s), 28 | ); 29 | 30 | /// setup custom desktop frames 31 | if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { 32 | doWhenWindowReady(() => appWindow 33 | ..minSize = const Size(400, 300) 34 | ..size = const Size(1280, 720) 35 | ..alignment = Alignment.center 36 | ..title = 'WLED' 37 | ..show()); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/features/device_add/widgets/add_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wled/core/core.dart'; 3 | 4 | class AddListItem extends StatelessWidget { 5 | const AddListItem({ 6 | Key? key, 7 | required this.text, 8 | this.size = 50, 9 | this.selected = false, 10 | this.padding = 5, 11 | }) : super(key: key); 12 | 13 | final double size; 14 | final bool selected; 15 | final String text; 16 | final double padding; 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Padding( 21 | padding: EdgeInsets.all(padding), 22 | child: DecoratedBox( 23 | decoration: BoxDecoration( 24 | 25 | borderRadius: BorderRadius.circular(10), 26 | ), 27 | child: Padding( 28 | padding: const EdgeInsets.all(Kpadding.small), 29 | child: Column( 30 | crossAxisAlignment: CrossAxisAlignment.start, 31 | mainAxisSize: MainAxisSize.min, 32 | children: [ 33 | Text( 34 | 'WLED', 35 | style: context.theme.textTheme.bodyText2, 36 | ), 37 | Text( 38 | '192.168.0.191', 39 | style: context.theme.textTheme.bodyText1, 40 | ), 41 | ], 42 | ), 43 | ), 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/features/wled_device/models/wled_device.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | import 'info.dart'; 4 | import 'state.dart'; 5 | 6 | part 'wled_device.freezed.dart'; 7 | part 'wled_device.g.dart'; 8 | 9 | enum DeviceStatus { 10 | unreachable, 11 | error, 12 | functional, 13 | } 14 | 15 | @freezed 16 | class WledDevice with _$WledDevice { 17 | const factory WledDevice({ 18 | // state contains the current state of the light. All values may be 19 | // modified by the client 20 | @JsonKey(name: 'state') required DeviceState state, 21 | // info contains general information about the device. No value can 22 | // be modified using this API 23 | @JsonKey(name: 'info') required DeviceInfo info, 24 | // effects contains an array of the effect mode names 25 | @JsonKey(name: 'effects') required List effects, 26 | // palettes contains an array of the palette names 27 | @JsonKey(name: 'palettes') required List palettes, 28 | // Whether this device is saved to the local system 29 | @Default(false) @JsonKey(ignore: true) bool isSaved, 30 | // current status of the functionality of this device 31 | @Default(DeviceStatus.unreachable) 32 | @JsonKey(ignore: true) 33 | DeviceStatus status, 34 | }) = _WledDevice; 35 | 36 | factory WledDevice.fromJson(Map json) => 37 | _$WledDeviceFromJson(json); 38 | } 39 | -------------------------------------------------------------------------------- /lib/core/l10n/generated/intl/messages_nl.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a nl locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'nl'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "deviceListEmpty": 26 | MessageLookupByLibrary.simpleMessage("Geen apparaten gevonden"), 27 | "deviceListPowerOff": MessageLookupByLibrary.simpleMessage("Uit") 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /lib/features/wled_device/utils/extensions.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:wled/features/wled_device/wled_device.dart'; 3 | 4 | /// custom extensions specifically for working with [WledDevice] lists 5 | extension WledListExtension on List { 6 | /// contains operation only comparing the WledDevice.address properties 7 | bool containsWithAddress(WledDevice? device) { 8 | if (device == null) return false; 9 | 10 | for (final e in this) { 11 | if (e.info.ipAddress == device.info.ipAddress) return true; 12 | } 13 | return false; 14 | } 15 | 16 | bool notContainsWithAddress(WledDevice? d) => !containsWithAddress(d); 17 | 18 | /// only add device to list if a device with the same ip isn't 19 | /// allready in the list 20 | void addIfNotContains(WledDevice device) { 21 | if (!containsWithAddress(device)) add(device); 22 | } 23 | 24 | void addAllIfNotContains(List devices) { 25 | for (final device in devices) { 26 | if (!containsWithAddress(device)) add(device); 27 | } 28 | } 29 | 30 | /// compares device against list, if it exists replace it, otherwise add it 31 | void addOrReplace(WledDevice d) { 32 | final index = indexWhere((i) => i.info.ipAddress == d.info.ipAddress); 33 | index == -1 ? add(d) : this[index] = d; 34 | } 35 | 36 | /// check if any of the device in the list are currently enabled 37 | bool get anyOn => any((e) => e.state.isEnabled); 38 | } 39 | -------------------------------------------------------------------------------- /lib/core/l10n/generated/intl/messages_en.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that provides messages for a en locale. All the 3 | // messages from the main program should be duplicated here with the same 4 | // function name. 5 | 6 | // Ignore issues from commonly used lints in this file. 7 | // ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new 8 | // ignore_for_file:prefer_single_quotes,comment_references, directives_ordering 9 | // ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases 10 | // ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes 11 | // ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes 12 | 13 | import 'package:intl/intl.dart'; 14 | import 'package:intl/message_lookup_by_library.dart'; 15 | 16 | final messages = new MessageLookup(); 17 | 18 | typedef String MessageIfAbsent(String messageStr, List args); 19 | 20 | class MessageLookup extends MessageLookupByLibrary { 21 | String get localeName => 'en'; 22 | 23 | final messages = _notInlinedMessages(_notInlinedMessages); 24 | static Map _notInlinedMessages(_) => { 25 | "deviceListEmpty": MessageLookupByLibrary.simpleMessage( 26 | "No devices found, Lets add some"), 27 | "deviceListPowerOff": MessageLookupByLibrary.simpleMessage("off") 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /lib/features/wled_device/domain/device_update_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import '../data/http_connection.dart'; 4 | import '../models/wled_device.dart'; 5 | 6 | class DeviceUpdateRepository { 7 | 8 | DeviceUpdateRepository(this._http); 9 | 10 | final HttpConnection _http; 11 | 12 | Future updateFromDevice(WledDevice device) async { 13 | final url = 'http://${device.info.ipAddress}/'; 14 | 15 | try { 16 | final response = await _http.sendApiCall(url, device.toString()); 17 | final data = json.decode(response) as Map; 18 | final result = WledDevice.fromJson(data); 19 | 20 | return result.copyWith(status: DeviceStatus.functional); 21 | } on HttpConnectionException { 22 | return device.copyWith(status: DeviceStatus.unreachable); 23 | } on HttpStatusException { 24 | return device.copyWith(status: DeviceStatus.error); 25 | } 26 | } 27 | 28 | Future updateFromIp(String ip) async { 29 | final url = 'http://$ip/'; 30 | 31 | try { 32 | final response = await _http.sendApiCall(url); 33 | final data = json.decode(response) as Map; 34 | final result = WledDevice.fromJson(data); 35 | 36 | return result.copyWith(status: DeviceStatus.functional); 37 | } on HttpConnectionException { 38 | return null; 39 | } on HttpStatusException { 40 | return null; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | #include 9 | auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); 10 | 11 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 12 | _In_ wchar_t *command_line, _In_ int show_command) { 13 | // Attach to console when present (e.g., 'flutter run') or create a 14 | // new console when running with a debugger. 15 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 16 | CreateAndAttachConsole(); 17 | } 18 | 19 | // Initialize COM, so that it is available for use in the library and/or 20 | // plugins. 21 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 22 | 23 | flutter::DartProject project(L"data"); 24 | 25 | std::vector command_line_arguments = 26 | GetCommandLineArguments(); 27 | 28 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 29 | 30 | FlutterWindow window(project); 31 | Win32Window::Point origin(10, 10); 32 | Win32Window::Size size(1280, 720); 33 | if (!window.CreateAndShow(L"wled_app", origin, size)) { 34 | return EXIT_FAILURE; 35 | } 36 | window.SetQuitOnClose(true); 37 | 38 | ::MSG msg; 39 | while (::GetMessage(&msg, nullptr, 0, 0)) { 40 | ::TranslateMessage(&msg); 41 | ::DispatchMessage(&msg); 42 | } 43 | 44 | ::CoUninitialize(); 45 | return EXIT_SUCCESS; 46 | } 47 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/core/widgets/desktop_frame.dart: -------------------------------------------------------------------------------- 1 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class DesktopFrame extends StatelessWidget { 5 | const DesktopFrame({ 6 | Key? key, 7 | required this.child, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | final buttonColors = WindowButtonColors( 15 | iconNormal: Colors.white, 16 | mouseOver: Colors.white, 17 | mouseDown: Colors.white, 18 | iconMouseOver: const Color(0xFF1D1D1D), 19 | iconMouseDown: const Color(0xFF1D1D1D), 20 | ); 21 | 22 | final closeButtonColors = WindowButtonColors( 23 | mouseOver: const Color(0xFFD32F2F), 24 | mouseDown: const Color(0xFFB71C1C), 25 | iconNormal: Colors.white, 26 | iconMouseOver: Colors.white, 27 | ); 28 | 29 | return Scaffold( 30 | body: WindowBorder( 31 | color: const Color(0xFF1D1D1D), 32 | width: 1, 33 | child: Column( 34 | children: [ 35 | DecoratedBox( 36 | decoration: const BoxDecoration(color: Color(0xFF191919)), 37 | child: WindowTitleBarBox( 38 | child: Row( 39 | children: [ 40 | Expanded(child: MoveWindow()), 41 | MinimizeWindowButton(colors: buttonColors), 42 | MaximizeWindowButton( 43 | colors: buttonColors, 44 | onPressed: appWindow.maximizeOrRestore, 45 | ), 46 | CloseWindowButton(colors: closeButtonColors), 47 | ], 48 | ), 49 | ), 50 | ), 51 | Expanded(child: child), 52 | ], 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/core/utils/type_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/painting.dart'; 2 | 3 | extension NumExtension on num { 4 | /// maps a number from an old range to a new range 5 | double map(T oldStart, T oldEnd, T newStart, T newEnd) { 6 | final slope = (newEnd - newStart) / (oldEnd - oldStart); 7 | return newStart + slope * (this - oldStart); 8 | } 9 | } 10 | 11 | extension StringExtension on String { 12 | bool parseBool() => toLowerCase() == 'true'; 13 | } 14 | 15 | extension ColorExtension on Color { 16 | /// darkens the color by an [amount] where 1.0 is min brightness and 0.0 is 17 | /// no changes 18 | Color darken([double amount = .1]) { 19 | assert(amount >= 0 && amount <= 1, 'must be between 0 and 1'); 20 | 21 | final hsl = HSLColor.fromColor(this); 22 | final hslDark = hsl.withLightness( 23 | (hsl.lightness - amount).clamp(0.0, 1.0), 24 | ); 25 | 26 | return hslDark.toColor(); 27 | } 28 | 29 | /// brightens the color by an [amount] where 1.0 is max brightness and 0.0 is 30 | /// no changes 31 | Color lighten([double amount = .1]) { 32 | assert(amount >= 0 && amount <= 1, 'must be between 0 and 1'); 33 | 34 | final hsl = HSLColor.fromColor(this); 35 | final hslLight = hsl.withLightness( 36 | (hsl.lightness + amount).clamp(0.0, 1.0), 37 | ); 38 | 39 | return hslLight.toColor(); 40 | } 41 | 42 | /// clamps a color's brightness values between a [min] and [max] were both 43 | /// range between 0 and 1 44 | Color clamp(double min, double max) { 45 | assert(min >= 0 && min <= 1, 'must be between 0 and 1'); 46 | assert(max >= 0 && max <= 1, 'must be between 0 and 1'); 47 | assert(min < max, 'min must be smaller than max'); 48 | 49 | final l = computeLuminance(); 50 | if (l < min) return lighten(min - l); 51 | if (l > max) return darken(l - max); 52 | 53 | return this; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | WLED 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | CFBundleLocalizations 45 | 46 | en 47 | nl 48 | 49 | UIStatusBarHidden 50 | 51 | 52 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /lib/features/device_list/data/mdns_device_discovery.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:injectable/injectable.dart'; 4 | import 'package:multicast_dns/multicast_dns.dart'; 5 | 6 | /// class handling listening for multi_dns devices 7 | @injectable 8 | class MdnsDeviceDiscovery { 9 | /// mDNS default wled address 10 | static const _name = '_wled._tcp'; 11 | 12 | /// creates the MDnsClient 13 | final _client = MDnsClient(rawDatagramSocketFactory: _socketFactory); 14 | 15 | /// we have to overwrite the reusePort because it's 16 | /// currently not supported on windows 17 | /// https://github.com/flutter/flutter/issues/55173 18 | static Future _socketFactory( 19 | dynamic host, 20 | int port, { 21 | bool? reuseAddress, 22 | bool? reusePort, 23 | int? ttl, 24 | }) { 25 | return RawDatagramSocket.bind(InternetAddress.anyIPv4, port, ttl: ttl!); 26 | } 27 | 28 | 29 | /// receive an instance of the stream containing the service records for 30 | /// listening to. 31 | Stream get stream async* { 32 | _client.stop(); 33 | await _client.start(); 34 | 35 | final ptrQuery = ResourceRecordQuery.serverPointer(_name); 36 | final ptrRecord = _client.lookup(ptrQuery); 37 | 38 | await for (final ptr in ptrRecord) { 39 | final srvQuery = ResourceRecordQuery.service(ptr.domainName); 40 | final srvRecord = _client.lookup(srvQuery); 41 | 42 | final srv = await srvRecord.first; // only use the first service record 43 | 44 | final ipQuery = ResourceRecordQuery.addressIPv4(srv.target); 45 | final ipRecord = _client.lookup(ipQuery); 46 | 47 | final ip = await ipRecord.first; // only use the first ip record 48 | yield MDNSrecord(ptr, srv, ip); 49 | } 50 | } 51 | } 52 | 53 | class MDNSrecord { 54 | MDNSrecord(this.ptr, this.srv, this.ip); 55 | 56 | final PtrResourceRecord ptr; 57 | final SrvResourceRecord srv; 58 | final IPAddressResourceRecord ip; 59 | } 60 | -------------------------------------------------------------------------------- /lib/features/device_control/views/device_control_view.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_bloc/flutter_bloc.dart'; 5 | import 'package:webview_flutter/webview_flutter.dart'; 6 | import 'package:wled/core/core.dart'; 7 | 8 | import '../bloc/device_control_bloc.dart'; 9 | 10 | class DeviceControlView extends StatelessWidget { 11 | const DeviceControlView( 12 | this.deviceAddress, 13 | this.deviceName, { 14 | Key? key, 15 | }) : super(key: key); 16 | 17 | final String deviceName; 18 | final String deviceAddress; 19 | 20 | @override 21 | Widget build(BuildContext context) => BlocProvider( 22 | create: (BuildContext context) => 23 | getIt()..add(const Started()), 24 | child: const DeviceControl(), 25 | ); 26 | } 27 | 28 | class DeviceControl extends StatelessWidget { 29 | const DeviceControl({Key? key}) : super(key: key); 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | final bloc = context.read(); 34 | 35 | return BlocBuilder( 36 | builder: (context, state) { 37 | if (state is Initial) { 38 | const SizedBox.expand( 39 | child: Center( 40 | child: LoadingWidget(), 41 | ), 42 | ); 43 | } else if (state is Loaded) { 44 | return Scaffold( 45 | appBar: AppBar( 46 | leading: BackButton(onPressed: () => bloc.add(const Back())), 47 | centerTitle: context.isPhone, 48 | title: Text(state.name), 49 | ), 50 | body: Platform.isAndroid || Platform.isIOS 51 | ? WebView( 52 | initialUrl: state.address, 53 | javascriptMode: JavascriptMode.unrestricted, 54 | ) 55 | : const SizedBox.shrink(), 56 | ); 57 | } 58 | return const SizedBox(); 59 | }, 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/features/device_add/widgets/background_shape.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: lines_longer_than_80_chars 2 | import 'package:flutter/rendering.dart'; 3 | 4 | class BackgroundShape extends CustomPainter { 5 | const BackgroundShape({ 6 | required this.backgroundColor, 7 | this.borderRadius = 20, 8 | this.buttonSize = 54, 9 | this.buttonGap = 10, 10 | }); 11 | 12 | final double borderRadius; 13 | final double buttonSize; 14 | final double buttonGap; 15 | 16 | final Color backgroundColor; 17 | 18 | @override 19 | void paint(Canvas canvas, Size size) { 20 | 21 | const topLeft = Offset.zero; 22 | final topRight = Offset(size.width, 0); 23 | final middleRight = Offset(size.width, size.height - buttonSize / 2 - buttonGap); 24 | final middle = Offset(size.width - buttonSize - buttonGap, middleRight.dy); 25 | final bottomMiddle = Offset(middle.dx, size.height); 26 | final bottomLeft = Offset(0, size.height); 27 | 28 | final shape = Path() 29 | ..moveTo(topLeft.dx + borderRadius, topLeft.dy) 30 | ..moveTo(topRight.dx - borderRadius, topRight.dy) 31 | ..conicTo(topRight.dx, topRight.dy, topRight.dx, topRight.dy + borderRadius, 1) 32 | ..moveTo(middleRight.dx, middle.dy - borderRadius) 33 | ..conicTo(middleRight.dx, middleRight.dy, middleRight.dx - borderRadius, middleRight.dy, 1) 34 | ..conicTo(middle.dx, middle.dy, bottomMiddle.dx, bottomMiddle.dy - borderRadius, 1) 35 | ..conicTo(bottomMiddle.dx, bottomMiddle.dy, bottomMiddle.dx - borderRadius, bottomMiddle.dy, 1) 36 | ..moveTo(bottomLeft.dx + borderRadius, bottomLeft.dy) 37 | ..conicTo(bottomLeft.dx, bottomLeft.dy, bottomLeft.dx, bottomLeft.dy - borderRadius, 1) 38 | ..moveTo(topLeft.dx, topLeft.dy + borderRadius) 39 | ..conicTo(topLeft.dx, topLeft.dy, topLeft.dx + borderRadius, topLeft.dy, 1); 40 | 41 | final paint = Paint()..color = backgroundColor..style = PaintingStyle.fill; 42 | canvas.drawPath(shape, paint); 43 | } 44 | 45 | @override 46 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.casvanluijtelaar.wled_app" 47 | minSdkVersion 19 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: wled 2 | description: A cross-platform application for interacting with WLED devices 3 | 4 | version: 1.1.0 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | auto_route: ^3.2.1 11 | bitsdojo_window: ^0.1.1+1 12 | bloc: ^8.0.2 13 | flutter: 14 | sdk: flutter 15 | flutter_bloc: ^8.0.1 16 | flutter_feather_icons: ^2.0.0+1 17 | flutter_localizations: 18 | sdk: flutter 19 | freezed_annotation: ^1.1.0 20 | get_it: ^7.2.0 21 | http: ^0.13.3 22 | injectable: ^1.5.3 23 | json_annotation: ^4.4.0 24 | multicast_dns: ^0.3.1 25 | rive: ^0.8.1 26 | rxdart: ^0.27.2 27 | url_launcher: ^6.0.18 28 | webview_flutter: ^3.0.0 29 | 30 | dev_dependencies: 31 | auto_route_generator: ^3.2.1 32 | build_runner: ^2.1.7 33 | flutter_app_name: ^0.1.0 34 | flutter_launcher_icons: 35 | git: https://github.com/dirkbo/flutter_launcher_icons.git 36 | flutter_native_splash: ^1.2.3 37 | flutter_test: 38 | sdk: flutter 39 | freezed: ^1.1.1 40 | injectable_generator: ^1.5.3 41 | json_serializable: ^6.1.3 42 | very_good_analysis: ^2.3.0 43 | 44 | # assets 45 | flutter: 46 | uses-material-design: true 47 | assets: 48 | - assets/rive/ 49 | fonts: 50 | - family: Nunito 51 | fonts: 52 | - asset: fonts/Nunito-Bold.ttf 53 | weight: 700 54 | - asset: fonts/Nunito-Regular.ttf 55 | weight: 400 56 | 57 | # internationalization settings 58 | flutter_intl: 59 | enabled: true 60 | class_name: Localization 61 | arb_dir: lib/core/l10n 62 | output_dir: lib/core/l10n/generated 63 | 64 | # set flutter splash screen 65 | flutter_native_splash: 66 | color: "#1D1D1D" 67 | image: assets/images/splash.png 68 | fullscreen: true 69 | android12: false 70 | 71 | # set flutter app icon 72 | flutter_icons: 73 | ios: true 74 | android: true 75 | windows: true 76 | macos: true 77 | image_path: "assets/images/fallback.png" 78 | image_path_ios: "assets/images/fallback.png" 79 | image_path_android: "assets/images/fallback.png" 80 | adaptive_icon_background: "assets/images/background.png" 81 | adaptive_icon_foreground: "assets/images/foreground.png" 82 | 83 | # set flutter app name 84 | flutter_app_name: 85 | name: "WLED" 86 | -------------------------------------------------------------------------------- /lib/core/app/app_injector.config.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | // ************************************************************************** 4 | // InjectableConfigGenerator 5 | // ************************************************************************** 6 | 7 | import 'package:get_it/get_it.dart' as _i1; 8 | import 'package:injectable/injectable.dart' as _i2; 9 | 10 | import '../../features/device_add/bloc/device_add_bloc.dart' as _i4; 11 | import '../../features/device_control/bloc/device_control_bloc.dart' as _i6; 12 | import '../../features/device_list/bloc/device_list_bloc.dart' as _i7; 13 | import '../../features/device_list/data/local_device_discovery.dart' as _i9; 14 | import '../../features/device_list/data/mdns_device_discovery.dart' as _i10; 15 | import '../../features/device_list/domain/device_fetch_repository.dart' as _i11; 16 | import '../../features/features.dart' as _i8; 17 | import '../core.dart' as _i5; 18 | import 'app_injector.dart' as _i12; 19 | import 'app_router.gr.dart' as _i3; // ignore_for_file: unnecessary_lambdas 20 | 21 | // ignore_for_file: lines_longer_than_80_chars 22 | /// initializes the registration of provided dependencies inside of [GetIt] 23 | _i1.GetIt $initGetIt(_i1.GetIt get, 24 | {String? environment, _i2.EnvironmentFilter? environmentFilter}) { 25 | final gh = _i2.GetItHelper(get, environment, environmentFilter); 26 | final registerModule = _$RegisterModule(); 27 | gh.factory<_i3.AppRouter>(() => registerModule.appRouter); 28 | gh.factory<_i4.DeviceAddBloc>(() => _i4.DeviceAddBloc(get<_i5.AppRouter>())); 29 | gh.factory<_i6.DeviceControlBloc>( 30 | () => _i6.DeviceControlBloc(get<_i5.AppRouter>())); 31 | gh.factory<_i7.DeviceListBloc>(() => _i7.DeviceListBloc( 32 | get<_i8.DeviceUpdateRepository>(), get<_i5.AppRouter>())); 33 | gh.factory<_i9.LocalDeviceDiscovery>(() => _i9.LocalDeviceDiscovery()); 34 | gh.factory<_i10.MdnsDeviceDiscovery>(() => _i10.MdnsDeviceDiscovery()); 35 | gh.singleton<_i11.DeviceFetchRepository>(_i11.DeviceFetchRepository( 36 | get<_i10.MdnsDeviceDiscovery>(), 37 | get<_i9.LocalDeviceDiscovery>(), 38 | get<_i8.DeviceUpdateRepository>())); 39 | return get; 40 | } 41 | 42 | class _$RegisterModule extends _i12.RegisterModule {} 43 | -------------------------------------------------------------------------------- /lib/features/device_list/domain/device_fetch_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:injectable/injectable.dart'; 5 | import 'package:wled/features/features.dart'; 6 | 7 | import '../data/local_device_discovery.dart'; 8 | import '../data/mdns_device_discovery.dart'; 9 | 10 | @singleton 11 | class DeviceFetchRepository { 12 | DeviceFetchRepository( 13 | this._remote, 14 | this._local, 15 | this._update, 16 | ); 17 | 18 | final MdnsDeviceDiscovery _remote; 19 | final LocalDeviceDiscovery _local; 20 | final DeviceUpdateRepository _update; 21 | 22 | Stream> get() async* { 23 | yield await getLocal(); 24 | // mdns lookup currently doesn't work on Windows, so end the stream here 25 | if (Platform.isWindows) return; 26 | yield* getRemote(); 27 | } 28 | 29 | /// retrieve locally saved WledDevices and return an updated version of each 30 | @visibleForTesting 31 | Future> getLocal() { 32 | // first fetch the locally saved devices 33 | final localDevices = _local.getWledDevice(); 34 | // update devices by fetching new data 35 | final futures = localDevices.map(_update.updateFromDevice); 36 | // yield the local devices first 37 | return Future.wait(futures); 38 | } 39 | 40 | /// retrieve a stream of potential WledDevices on this network 41 | @visibleForTesting 42 | Stream> getRemote() { 43 | final remoteDevices = _remote.stream.asyncMap((mdns) async { 44 | // create a new WledDevice from mdns lookup 45 | final address = mdns.ip.address.address; 46 | 47 | // attempt to fetch data for this WledDevice 48 | return _update.updateFromIp(address); 49 | // only yield actual WledDevices by testing if the data fetch completed 50 | // succesfully 51 | }).where((i) => i != null && i.status != DeviceStatus.unreachable); 52 | 53 | return remoteDevices.map((i) => [i!]); 54 | } 55 | 56 | void deleteLocal(WledDevice device) { 57 | _local.removeWledDevice(device); 58 | } 59 | 60 | void saveLocal(WledDevice device) { 61 | _local.saveWledDevice(device); 62 | } 63 | 64 | void updateLocal(WledDevice device) { 65 | _local.saveWledDevice(device); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/core/l10n/generated/intl/messages_all.dart: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This is code generated via package:intl/generate_localized.dart 2 | // This is a library that looks up messages for specific locales by 3 | // delegating to the appropriate library. 4 | 5 | // Ignore issues from commonly used lints in this file. 6 | // ignore_for_file:implementation_imports, file_names, unnecessary_new 7 | // ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering 8 | // ignore_for_file:argument_type_not_assignable, invalid_assignment 9 | // ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases 10 | // ignore_for_file:comment_references 11 | 12 | import 'dart:async'; 13 | 14 | import 'package:intl/intl.dart'; 15 | import 'package:intl/message_lookup_by_library.dart'; 16 | import 'package:intl/src/intl_helpers.dart'; 17 | 18 | import 'messages_en.dart' as messages_en; 19 | import 'messages_nl.dart' as messages_nl; 20 | 21 | typedef Future LibraryLoader(); 22 | Map _deferredLibraries = { 23 | 'en': () => new Future.value(null), 24 | 'nl': () => new Future.value(null), 25 | }; 26 | 27 | MessageLookupByLibrary? _findExact(String localeName) { 28 | switch (localeName) { 29 | case 'en': 30 | return messages_en.messages; 31 | case 'nl': 32 | return messages_nl.messages; 33 | default: 34 | return null; 35 | } 36 | } 37 | 38 | /// User programs should call this before using [localeName] for messages. 39 | Future initializeMessages(String localeName) async { 40 | var availableLocale = Intl.verifiedLocale( 41 | localeName, (locale) => _deferredLibraries[locale] != null, 42 | onFailure: (_) => null); 43 | if (availableLocale == null) { 44 | return new Future.value(false); 45 | } 46 | var lib = _deferredLibraries[availableLocale]; 47 | await (lib == null ? new Future.value(false) : lib()); 48 | initializeInternalMessageLookup(() => new CompositeMessageLookup()); 49 | messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); 50 | return new Future.value(true); 51 | } 52 | 53 | bool _messagesExistFor(String locale) { 54 | try { 55 | return _findExact(locale) != null; 56 | } catch (e) { 57 | return false; 58 | } 59 | } 60 | 61 | MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) { 62 | var actualLocale = 63 | Intl.verifiedLocale(locale, _messagesExistFor, onFailure: (_) => null); 64 | if (actualLocale == null) return null; 65 | return _findExact(actualLocale); 66 | } 67 | -------------------------------------------------------------------------------- /lib/features/device_add/views/device_add_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wled/core/core.dart'; 4 | import 'package:wled/features/device_add/widgets/background_shape.dart'; 5 | 6 | import '../bloc/device_add_bloc.dart'; 7 | 8 | class DeviceAddView extends StatelessWidget { 9 | const DeviceAddView({ 10 | Key? key, 11 | required this.buttonKey, 12 | }) : super(key: key); 13 | 14 | final GlobalKey buttonKey; 15 | 16 | @override 17 | Widget build(BuildContext context) => BlocProvider( 18 | create: (BuildContext context) => getIt(), 19 | child: DeviceAdd(buttonKey: buttonKey), 20 | ); 21 | } 22 | 23 | class DeviceAdd extends StatefulWidget { 24 | const DeviceAdd({ 25 | Key? key, 26 | required this.buttonKey, 27 | }) : super(key: key); 28 | 29 | final GlobalKey buttonKey; 30 | 31 | @override 32 | _DeviceAddState createState() => _DeviceAddState(); 33 | } 34 | 35 | class _DeviceAddState extends State { 36 | Offset getButtonPosition() { 37 | final context = widget.buttonKey.currentContext; 38 | final renderbox = context?.findRenderObject() as RenderBox?; 39 | 40 | if (renderbox == null) return Offset.zero; 41 | return renderbox.localToGlobal(Offset.zero); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return Stack( 47 | children: [ 48 | // background 49 | SizedBox.expand( 50 | child: GestureDetector( 51 | onTap: () => Navigator.of(context).pop(), 52 | child: const DecoratedBox( 53 | decoration: BoxDecoration(color: Color(0x15000000)), 54 | ), 55 | ), 56 | ), 57 | Align( 58 | alignment: Alignment.bottomRight, 59 | child: ConstrainedBox( 60 | constraints: const BoxConstraints( 61 | maxWidth: 600, 62 | maxHeight: 400, 63 | ), 64 | child: Material( 65 | color: Colors.transparent, 66 | elevation: 2, 67 | child: CustomPaint( 68 | painter: BackgroundShape( 69 | backgroundColor: context.theme.backgroundColor), 70 | child: const Center( 71 | child: Text('aaaaaah'), 72 | ), 73 | ), 74 | ), 75 | ), 76 | ), 77 | ], 78 | ); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 11 | 18 | 22 | 26 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /lib/features/device_list/widgets/device_list_switch.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:wled/core/core.dart'; 3 | 4 | class DeviceListSwitch extends StatefulWidget { 5 | const DeviceListSwitch({ 6 | Key? key, 7 | required this.value, 8 | this.onChanged, 9 | this.height = 30, 10 | this.width = 50, 11 | }) : super(key: key); 12 | 13 | final bool value; 14 | final ValueChanged? onChanged; 15 | final double height; 16 | final double width; 17 | 18 | @override 19 | _DeviceListSwitchState createState() => _DeviceListSwitchState(); 20 | } 21 | 22 | class _DeviceListSwitchState extends State { 23 | 24 | late bool _value; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | _value = widget.value; 30 | } 31 | 32 | @override 33 | void didUpdateWidget(covariant DeviceListSwitch oldWidget) { 34 | super.didUpdateWidget(oldWidget); 35 | if (widget.value != _value) setState(() => _value = widget.value); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return GestureDetector( 41 | onTap: () { 42 | widget.onChanged?.call(!_value); 43 | setState(() => _value = !_value); 44 | }, 45 | child: Stack( 46 | clipBehavior: Clip.none, 47 | children: [ 48 | SizedBox( 49 | height: widget.height, 50 | width: widget.width, 51 | child: DecoratedBox( 52 | decoration: BoxDecoration( 53 | color: const Color.fromRGBO(0, 0, 0, 0.1544), 54 | borderRadius: BorderRadius.all(Radius.circular(widget.height)), 55 | ), 56 | ), 57 | ), 58 | AnimatedPositioned( 59 | key: const Key('test_switch_position'), 60 | duration: Kduration.short, 61 | left: _value ? widget.width - widget.height : 0, 62 | child: DecoratedBox( 63 | decoration: BoxDecoration( 64 | borderRadius: BorderRadius.all(Radius.circular(widget.height)), 65 | color: const Color.fromRGBO(255, 255, 255, 1), 66 | boxShadow: const [ 67 | BoxShadow( 68 | blurRadius: 3, 69 | offset: Offset(0, 1), 70 | color: Color.fromRGBO(0, 0, 0, 0.3848), 71 | ), 72 | ], 73 | ), 74 | child: SizedBox.square( 75 | dimension: widget.height, 76 | ), 77 | ), 78 | ), 79 | ], 80 | ), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/features/device_list/views/device_list_view.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:wled/core/core.dart'; 4 | 5 | import '../bloc/device_list_bloc.dart'; 6 | import '../widgets/device_list_grid_delegate.dart'; 7 | import '../widgets/device_list_item.dart'; 8 | 9 | /// DeviceListView entry point 10 | class DeviceListView extends StatelessWidget { 11 | const DeviceListView({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) => BlocProvider( 15 | create: (_) => getIt()..add(const Initial()), 16 | child: const DeviceListBuilder(), 17 | ); 18 | } 19 | 20 | /// main DeviceListBuilderView state builder 21 | class DeviceListBuilder extends StatelessWidget { 22 | const DeviceListBuilder({Key? key}) : super(key: key); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | final bloc = context.read(); 27 | 28 | return Scaffold( 29 | body: Center( 30 | child: BlocBuilder( 31 | builder: (context, state) { 32 | if (state is Empty) return Text(context.locale.deviceListEmpty); 33 | if (state is Found) { 34 | return Padding( 35 | padding: const EdgeInsets.symmetric( 36 | horizontal: Kpadding.medium, 37 | ), 38 | child: GridView.builder( 39 | gridDelegate: const DeviceListSliverGridDelegate( 40 | mainAxisSpacing: Kpadding.medium, 41 | crossAxisSpacing: Kpadding.medium, 42 | maxCrossAxisExtent: 600, 43 | height: 108, 44 | ), 45 | itemCount: state.devices.length, 46 | itemBuilder: (BuildContext context, int index) { 47 | final device = state.devices[index]; 48 | 49 | return DeviceListItem( 50 | key: ValueKey(device), 51 | device: device, 52 | onPower: (v) => bloc.add(DevicePower(device)), 53 | onPressed: () => bloc.add(DevicePressed(device)), 54 | onSlide: (v) => bloc.add(DeviceSlider(device, v)), 55 | ); 56 | }, 57 | ), 58 | ); 59 | } 60 | 61 | return const LoadingWidget(); 62 | }, 63 | ), 64 | ), 65 | floatingActionButton: FloatingActionButton( 66 | key: bloc.addButtonKey, 67 | onPressed: () => bloc.add(DeviceAdd(context)), 68 | child: const Icon(Icons.add), 69 | ), 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .classpath 21 | .project 22 | .settings/ 23 | .vscode/ 24 | 25 | # Flutter repo-specific 26 | /bin/cache/ 27 | /bin/internal/bootstrap.bat 28 | /bin/internal/bootstrap.sh 29 | /bin/mingit/ 30 | /dev/benchmarks/mega_gallery/ 31 | /dev/bots/.recipe_deps 32 | /dev/bots/android_tools/ 33 | /dev/devicelab/ABresults*.json 34 | /dev/docs/doc/ 35 | /dev/docs/flutter.docs.zip 36 | /dev/docs/lib/ 37 | /dev/docs/pubspec.yaml 38 | /dev/integration_tests/**/xcuserdata 39 | /dev/integration_tests/**/Pods 40 | /packages/flutter/coverage/ 41 | version 42 | analysis_benchmark.json 43 | 44 | # packages file containing multi-root paths 45 | .packages.generated 46 | 47 | # Flutter/Dart/Pub related 48 | **/doc/api/ 49 | .dart_tool/ 50 | .flutter-plugins 51 | .flutter-plugins-dependencies 52 | **/generated_plugin_registrant.dart 53 | .packages 54 | .pub-cache/ 55 | .pub/ 56 | build/ 57 | flutter_*.png 58 | linked_*.ds 59 | unlinked.ds 60 | unlinked_spec.ds 61 | 62 | # Android related 63 | **/android/**/gradle-wrapper.jar 64 | **/android/.gradle 65 | **/android/captures/ 66 | **/android/gradlew 67 | **/android/gradlew.bat 68 | **/android/local.properties 69 | **/android/**/GeneratedPluginRegistrant.java 70 | **/android/key.properties 71 | *.jks 72 | 73 | # iOS/XCode related 74 | **/ios/**/*.mode1v3 75 | **/ios/**/*.mode2v3 76 | **/ios/**/*.moved-aside 77 | **/ios/**/*.pbxuser 78 | **/ios/**/*.perspectivev3 79 | **/ios/**/*sync/ 80 | **/ios/**/.sconsign.dblite 81 | **/ios/**/.tags* 82 | **/ios/**/.vagrant/ 83 | **/ios/**/DerivedData/ 84 | **/ios/**/Icon? 85 | **/ios/**/Pods/ 86 | **/ios/**/.symlinks/ 87 | **/ios/**/profile 88 | **/ios/**/xcuserdata 89 | **/ios/.generated/ 90 | **/ios/Flutter/.last_build_id 91 | **/ios/Flutter/App.framework 92 | **/ios/Flutter/Flutter.framework 93 | **/ios/Flutter/Flutter.podspec 94 | **/ios/Flutter/Generated.xcconfig 95 | **/ios/Flutter/ephemeral 96 | **/ios/Flutter/app.flx 97 | **/ios/Flutter/app.zip 98 | **/ios/Flutter/flutter_assets/ 99 | **/ios/Flutter/flutter_export_environment.sh 100 | **/ios/ServiceDefinitions.json 101 | **/ios/Runner/GeneratedPluginRegistrant.* 102 | 103 | # macOS 104 | **/macos/Flutter/GeneratedPluginRegistrant.swift 105 | 106 | # Coverage 107 | coverage/ 108 | 109 | # Symbols 110 | app.*.symbols 111 | 112 | # Exceptions to above rules. 113 | !**/ios/**/default.mode1v3 114 | !**/ios/**/default.mode2v3 115 | !**/ios/**/default.pbxuser 116 | !**/ios/**/default.perspectivev3 117 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 118 | !/dev/ci/**/Gemfile.lock -------------------------------------------------------------------------------- /lib/core/app/app_router.gr.dart: -------------------------------------------------------------------------------- 1 | // ************************************************************************** 2 | // AutoRouteGenerator 3 | // ************************************************************************** 4 | 5 | // GENERATED CODE - DO NOT MODIFY BY HAND 6 | 7 | // ************************************************************************** 8 | // AutoRouteGenerator 9 | // ************************************************************************** 10 | // 11 | // ignore_for_file: type=lint 12 | 13 | import 'package:auto_route/auto_route.dart' as _i2; 14 | import 'package:flutter/material.dart' as _i3; 15 | 16 | import '../../features/features.dart' as _i1; 17 | 18 | class AppRouter extends _i2.RootStackRouter { 19 | AppRouter([_i3.GlobalKey<_i3.NavigatorState>? navigatorKey]) 20 | : super(navigatorKey); 21 | 22 | @override 23 | final Map pagesMap = { 24 | DeviceListRoute.name: (routeData) { 25 | return _i2.MaterialPageX( 26 | routeData: routeData, child: const _i1.DeviceListView()); 27 | }, 28 | DeviceControlRoute.name: (routeData) { 29 | final args = routeData.argsAs(); 30 | return _i2.MaterialPageX( 31 | routeData: routeData, 32 | child: _i1.DeviceControlView(args.deviceAddress, args.deviceName, 33 | key: args.key)); 34 | } 35 | }; 36 | 37 | @override 38 | List<_i2.RouteConfig> get routes => [ 39 | _i2.RouteConfig('/#redirect', 40 | path: '/', redirectTo: 'devices', fullMatch: true), 41 | _i2.RouteConfig(DeviceListRoute.name, path: 'devices'), 42 | _i2.RouteConfig(DeviceControlRoute.name, path: 'controls') 43 | ]; 44 | } 45 | 46 | /// generated route for 47 | /// [_i1.DeviceListView] 48 | class DeviceListRoute extends _i2.PageRouteInfo { 49 | const DeviceListRoute() : super(DeviceListRoute.name, path: 'devices'); 50 | 51 | static const String name = 'DeviceListRoute'; 52 | } 53 | 54 | /// generated route for 55 | /// [_i1.DeviceControlView] 56 | class DeviceControlRoute extends _i2.PageRouteInfo { 57 | DeviceControlRoute( 58 | {required String deviceAddress, required String deviceName, _i3.Key? key}) 59 | : super(DeviceControlRoute.name, 60 | path: 'controls', 61 | args: DeviceControlRouteArgs( 62 | deviceAddress: deviceAddress, 63 | deviceName: deviceName, 64 | key: key)); 65 | 66 | static const String name = 'DeviceControlRoute'; 67 | } 68 | 69 | class DeviceControlRouteArgs { 70 | const DeviceControlRouteArgs( 71 | {required this.deviceAddress, required this.deviceName, this.key}); 72 | 73 | final String deviceAddress; 74 | 75 | final String deviceName; 76 | 77 | final _i3.Key? key; 78 | 79 | @override 80 | String toString() { 81 | return 'DeviceControlRouteArgs{deviceAddress: $deviceAddress, deviceName: $deviceName, key: $key}'; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/core/utils/context_extensions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:wled/core/core.dart'; 3 | 4 | /// usefull extension on [BuildContext], like sizing and orientation 5 | /// mostly taken from https://github.com/jonataslaw/getx/blob/master/lib/get_utils/src/extensions/context_extensions.dart 6 | extension ContextExtension on BuildContext { 7 | /// get the currently active locale 8 | Localization get locale => Localization.of(this); 9 | 10 | /// same as Theme.of(this) 11 | ThemeData get theme => Theme.of(this); 12 | 13 | /// The same of [MediaQuery.of(context).size] 14 | Size get mediaQuerySize => MediaQuery.of(this).size; 15 | 16 | /// The same of [MediaQuery.of(context).size.height] 17 | /// Note: updates when you rezise your screen (like on a browser or 18 | /// desktop window) 19 | double get height => mediaQuerySize.height; 20 | 21 | /// The same of [MediaQuery.of(context).size.width] 22 | /// Note: updates when you rezise your screen (like on a browser or 23 | /// desktop window) 24 | double get width => mediaQuerySize.width; 25 | 26 | /// similar to [MediaQuery.of(context).padding] 27 | EdgeInsets get mediaQueryPadding => MediaQuery.of(this).padding; 28 | 29 | /// similar to [MediaQuery.of(context).padding] 30 | MediaQueryData get mediaQuery => MediaQuery.of(this); 31 | 32 | /// similar to [MediaQuery.of(context).viewPadding] 33 | EdgeInsets get mediaQueryViewPadding => MediaQuery.of(this).viewPadding; 34 | 35 | /// similar to [MediaQuery.of(context).viewInsets] 36 | EdgeInsets get mediaQueryViewInsets => MediaQuery.of(this).viewInsets; 37 | 38 | /// similar to [MediaQuery.of(context).orientation] 39 | Orientation get orientation => MediaQuery.of(this).orientation; 40 | 41 | /// check if device is on landscape mode 42 | bool get isLandscape => orientation == Orientation.landscape; 43 | 44 | /// check if device is on portrait mode 45 | bool get isPortrait => orientation == Orientation.portrait; 46 | 47 | /// similar to [MediaQuery.of(this).devicePixelRatio] 48 | double get devicePixelRatio => MediaQuery.of(this).devicePixelRatio; 49 | 50 | /// similar to [MediaQuery.of(this).textScaleFactor] 51 | double get textScaleFactor => MediaQuery.of(this).textScaleFactor; 52 | 53 | /// get the shortestSide from screen 54 | double get mediaQueryShortestSide => mediaQuerySize.shortestSide; 55 | 56 | /// True if width be larger than 800 57 | bool get showNavbar => width > 800; 58 | 59 | /// True if the shortestSide is smaller than 600p 60 | bool get isPhone => mediaQueryShortestSide < 600; 61 | 62 | // true if screen is both smaller than 600p and a vertical orientation 63 | bool get isPortraitPhone => isPhone && isPortrait; 64 | 65 | /// True if the shortestSide is largest than 600p 66 | bool get isSmallTablet => mediaQueryShortestSide >= 600; 67 | 68 | /// True if the shortestSide is largest than 720p 69 | bool get isLargeTablet => mediaQueryShortestSide >= 720; 70 | 71 | /// True if the current device is Tablet 72 | bool get isTablet => isSmallTablet || isLargeTablet; 73 | } 74 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | 28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 29 | 30 | # Published to parent scope for install step. 31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 35 | 36 | list(APPEND FLUTTER_LIBRARY_HEADERS 37 | "fl_basic_message_channel.h" 38 | "fl_binary_codec.h" 39 | "fl_binary_messenger.h" 40 | "fl_dart_project.h" 41 | "fl_engine.h" 42 | "fl_json_message_codec.h" 43 | "fl_json_method_codec.h" 44 | "fl_message_codec.h" 45 | "fl_method_call.h" 46 | "fl_method_channel.h" 47 | "fl_method_codec.h" 48 | "fl_method_response.h" 49 | "fl_plugin_registrar.h" 50 | "fl_plugin_registry.h" 51 | "fl_standard_message_codec.h" 52 | "fl_standard_method_codec.h" 53 | "fl_string_codec.h" 54 | "fl_value.h" 55 | "fl_view.h" 56 | "flutter_linux.h" 57 | ) 58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 59 | add_library(flutter INTERFACE) 60 | target_include_directories(flutter INTERFACE 61 | "${EPHEMERAL_DIR}" 62 | ) 63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 64 | target_link_libraries(flutter INTERFACE 65 | PkgConfig::GTK 66 | PkgConfig::GLIB 67 | PkgConfig::GIO 68 | ) 69 | add_dependencies(flutter flutter_assemble) 70 | 71 | # === Flutter tool backend === 72 | # _phony_ is a non-existent file to force this command to run every time, 73 | # since currently there's no way to get a full input/output list from the 74 | # flutter tool. 75 | add_custom_command( 76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 78 | COMMAND ${CMAKE_COMMAND} -E env 79 | ${FLUTTER_TOOL_ENVIRONMENT} 80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 82 | VERBATIM 83 | ) 84 | add_custom_target(flutter_assemble DEPENDS 85 | "${FLUTTER_LIBRARY}" 86 | ${FLUTTER_LIBRARY_HEADERS} 87 | ) 88 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/core/l10n/generated/l10n.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | import 'package:flutter/material.dart'; 3 | import 'package:intl/intl.dart'; 4 | import 'intl/messages_all.dart'; 5 | 6 | // ************************************************************************** 7 | // Generator: Flutter Intl IDE plugin 8 | // Made by Localizely 9 | // ************************************************************************** 10 | 11 | // ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars 12 | // ignore_for_file: join_return_with_assignment, prefer_final_in_for_each 13 | // ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes 14 | 15 | class Localization { 16 | Localization(); 17 | 18 | static Localization? _current; 19 | 20 | static Localization get current { 21 | assert(_current != null, 22 | 'No instance of Localization was loaded. Try to initialize the Localization delegate before accessing Localization.current.'); 23 | return _current!; 24 | } 25 | 26 | static const AppLocalizationDelegate delegate = AppLocalizationDelegate(); 27 | 28 | static Future load(Locale locale) { 29 | final name = (locale.countryCode?.isEmpty ?? false) 30 | ? locale.languageCode 31 | : locale.toString(); 32 | final localeName = Intl.canonicalizedLocale(name); 33 | return initializeMessages(localeName).then((_) { 34 | Intl.defaultLocale = localeName; 35 | final instance = Localization(); 36 | Localization._current = instance; 37 | 38 | return instance; 39 | }); 40 | } 41 | 42 | static Localization of(BuildContext context) { 43 | final instance = Localization.maybeOf(context); 44 | assert(instance != null, 45 | 'No instance of Localization present in the widget tree. Did you add Localization.delegate in localizationsDelegates?'); 46 | return instance!; 47 | } 48 | 49 | static Localization? maybeOf(BuildContext context) { 50 | return Localizations.of(context, Localization); 51 | } 52 | 53 | /// `off` 54 | String get deviceListPowerOff { 55 | return Intl.message( 56 | 'off', 57 | name: 'deviceListPowerOff', 58 | desc: '', 59 | args: [], 60 | ); 61 | } 62 | 63 | /// `No devices found, Lets add some` 64 | String get deviceListEmpty { 65 | return Intl.message( 66 | 'No devices found, Lets add some', 67 | name: 'deviceListEmpty', 68 | desc: '', 69 | args: [], 70 | ); 71 | } 72 | } 73 | 74 | class AppLocalizationDelegate extends LocalizationsDelegate { 75 | const AppLocalizationDelegate(); 76 | 77 | List get supportedLocales { 78 | return const [ 79 | Locale.fromSubtags(languageCode: 'en'), 80 | Locale.fromSubtags(languageCode: 'nl'), 81 | ]; 82 | } 83 | 84 | @override 85 | bool isSupported(Locale locale) => _isSupported(locale); 86 | @override 87 | Future load(Locale locale) => Localization.load(locale); 88 | @override 89 | bool shouldReload(AppLocalizationDelegate old) => false; 90 | 91 | bool _isSupported(Locale locale) { 92 | for (var supportedLocale in supportedLocales) { 93 | if (supportedLocale.languageCode == locale.languageCode) { 94 | return true; 95 | } 96 | } 97 | return false; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /lib/features/device_list/widgets/device_list_grid_delegate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/rendering.dart'; 4 | 5 | class DeviceListSliverGridDelegate extends SliverGridDelegate { 6 | /// Creates a delegate that makes grid layouts with tiles that have a maximum 7 | /// cross-axis extent. 8 | /// 9 | /// All of the arguments except [mainAxisExtent] must not be null. 10 | /// The [maxCrossAxisExtent], [mainAxisExtent], [mainAxisSpacing], 11 | /// and [crossAxisSpacing] arguments must not be negative. 12 | /// The [height] argument must be greater than zero. 13 | const DeviceListSliverGridDelegate({ 14 | required this.maxCrossAxisExtent, 15 | required this.height, 16 | this.mainAxisSpacing = 0.0, 17 | this.crossAxisSpacing = 0.0, 18 | this.mainAxisExtent, 19 | }); 20 | 21 | /// The maximum extent of tiles in the cross axis. 22 | /// 23 | /// This delegate will select a cross-axis extent for the tiles that is as 24 | /// large as possible subject to the following conditions: 25 | /// 26 | /// - The extent evenly divides the cross-axis extent of the grid. 27 | /// - The extent is at most [maxCrossAxisExtent]. 28 | /// 29 | /// For example, if the grid is vertical, the grid is 500.0 pixels wide, and 30 | /// [maxCrossAxisExtent] is 150.0, this delegate will create a grid with 4 31 | /// columns that are 125.0 pixels wide. 32 | final double maxCrossAxisExtent; 33 | 34 | /// The number of logical pixels between each child along the main axis. 35 | final double mainAxisSpacing; 36 | 37 | /// The number of logical pixels between each child along the cross axis. 38 | final double crossAxisSpacing; 39 | 40 | /// the child widgets fixed height 41 | final double height; 42 | 43 | /// The extent of each tile in the main axis. If provided it would define the 44 | /// logical pixels taken by each tile in the main-axis. 45 | /// 46 | /// If null, [height] is used instead. 47 | final double? mainAxisExtent; 48 | 49 | bool _debugAssertIsValid(double crossAxisExtent) { 50 | assert(crossAxisExtent > 0.0); 51 | assert(maxCrossAxisExtent > 0.0); 52 | assert(mainAxisSpacing >= 0.0); 53 | assert(crossAxisSpacing >= 0.0); 54 | assert(height > 0.0); 55 | return true; 56 | } 57 | 58 | @override 59 | SliverGridLayout getLayout(SliverConstraints constraints) { 60 | assert(_debugAssertIsValid(constraints.crossAxisExtent)); 61 | 62 | final crossAxisCount = 63 | (constraints.crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing)) 64 | .ceil(); 65 | 66 | final usableCrossAxisExtent = max( 67 | 0, 68 | constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1), 69 | ); 70 | 71 | final childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount; 72 | final childMainAxisExtent = mainAxisExtent ?? height; 73 | 74 | return SliverGridRegularTileLayout( 75 | crossAxisCount: crossAxisCount, 76 | mainAxisStride: childMainAxisExtent + mainAxisSpacing, 77 | crossAxisStride: childCrossAxisExtent + crossAxisSpacing, 78 | childMainAxisExtent: childMainAxisExtent, 79 | childCrossAxisExtent: childCrossAxisExtent, 80 | reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection), 81 | ); 82 | } 83 | 84 | @override 85 | bool shouldRelayout(DeviceListSliverGridDelegate oldDelegate) { 86 | return oldDelegate.maxCrossAxisExtent != maxCrossAxisExtent || 87 | oldDelegate.mainAxisSpacing != mainAxisSpacing || 88 | oldDelegate.crossAxisSpacing != crossAxisSpacing || 89 | oldDelegate.height != height || 90 | oldDelegate.mainAxisExtent != mainAxisExtent; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(wled_app LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "wled_app") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources//app_icon.ico" 56 | IDI_APP_ICON_32 ICON "resources//app_icon_32.ico" 57 | IDI_APP_ICON_64 ICON "resources//app_icon_64.ico" 58 | IDI_APP_ICON_128 ICON "resources//app_icon_128.ico" 59 | IDI_APP_ICON_256 ICON "resources//app_icon_256.ico" 60 | IDI_APP_ICON_512 ICON "resources//app_icon_512.ico" 61 | IDI_APP_ICON_1024 ICON "resources//app_icon_1024.ico" 62 | 63 | 64 | ///////////////////////////////////////////////////////////////////////////// 65 | // 66 | // Version 67 | // 68 | 69 | #ifdef FLUTTER_BUILD_NUMBER 70 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 71 | #else 72 | #define VERSION_AS_NUMBER 1,0,0 73 | #endif 74 | 75 | #ifdef FLUTTER_BUILD_NAME 76 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 77 | #else 78 | #define VERSION_AS_STRING "1.0.0" 79 | #endif 80 | 81 | VS_VERSION_INFO VERSIONINFO 82 | FILEVERSION VERSION_AS_NUMBER 83 | PRODUCTVERSION VERSION_AS_NUMBER 84 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 85 | #ifdef _DEBUG 86 | FILEFLAGS VS_FF_DEBUG 87 | #else 88 | FILEFLAGS 0x0L 89 | #endif 90 | FILEOS VOS__WINDOWS32 91 | FILETYPE VFT_APP 92 | FILESUBTYPE 0x0L 93 | BEGIN 94 | BLOCK "StringFileInfo" 95 | BEGIN 96 | BLOCK "040904e4" 97 | BEGIN 98 | VALUE "CompanyName", "com.casvanluijtelaar" "\0" 99 | VALUE "FileDescription", "A new Flutter project." "\0" 100 | VALUE "FileVersion", VERSION_AS_STRING "\0" 101 | VALUE "InternalName", "wled_app" "\0" 102 | VALUE "LegalCopyright", "Copyright (C) 2022 com.casvanluijtelaar. All rights reserved." "\0" 103 | VALUE "OriginalFilename", "wled_app.exe" "\0" 104 | VALUE "ProductName", "wled_app" "\0" 105 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 106 | END 107 | END 108 | BLOCK "VarFileInfo" 109 | BEGIN 110 | VALUE "Translation", 0x409, 1252 111 | END 112 | END 113 | 114 | #endif // English (United States) resources 115 | ///////////////////////////////////////////////////////////////////////////// 116 | 117 | 118 | 119 | #ifndef APSTUDIO_INVOKED 120 | ///////////////////////////////////////////////////////////////////////////// 121 | // 122 | // Generated from the TEXTINCLUDE 3 resource. 123 | // 124 | 125 | 126 | ///////////////////////////////////////////////////////////////////////////// 127 | #endif // not APSTUDIO_INVOKED 128 | -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | wled_app 30 | 31 | 32 | 33 | 36 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /lib/features/wled_device/models/info.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'info.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_DeviceInfo _$$_DeviceInfoFromJson(Map json) => 10 | _$_DeviceInfo( 11 | version: json['ver'] as String, 12 | buildId: json['vid'] as int, 13 | leds: LedsDeviceInfo.fromJson(json['leds'] as Map), 14 | sendReceiveToggle: json['str'] as bool, 15 | name: json['name'] as String, 16 | udpPort: json['udpport'] as int, 17 | live: json['live'] as bool, 18 | lm: json['lm'] as String, 19 | lip: json['lip'] as String, 20 | websockets: json['ws'] as int, 21 | effectsCount: json['fxcount'] as int, 22 | paletteCount: json['palcount'] as int, 23 | wifi: Wifi.fromJson(json['wifi'] as Map), 24 | fs: FileSystem.fromJson(json['fs'] as Map), 25 | numberOfDevicesOnNetwork: json['ndc'] as int, 26 | platformname: json['arch'] as String, 27 | sdkVersion: json['core'] as String, 28 | lwIpVersion: json['lwip'] as int, 29 | availableHeapMemory: json['freeheap'] as int, 30 | uptime: json['uptime'] as int, 31 | vendorName: json['brand'] as String, 32 | productName: json['product'] as String, 33 | macAddress: json['mac'] as String, 34 | ipAddress: json['ip'] as String, 35 | ); 36 | 37 | Map _$$_DeviceInfoToJson(_$_DeviceInfo instance) => 38 | { 39 | 'ver': instance.version, 40 | 'vid': instance.buildId, 41 | 'leds': instance.leds, 42 | 'str': instance.sendReceiveToggle, 43 | 'name': instance.name, 44 | 'udpport': instance.udpPort, 45 | 'live': instance.live, 46 | 'lm': instance.lm, 47 | 'lip': instance.lip, 48 | 'ws': instance.websockets, 49 | 'fxcount': instance.effectsCount, 50 | 'palcount': instance.paletteCount, 51 | 'wifi': instance.wifi, 52 | 'fs': instance.fs, 53 | 'ndc': instance.numberOfDevicesOnNetwork, 54 | 'arch': instance.platformname, 55 | 'core': instance.sdkVersion, 56 | 'lwip': instance.lwIpVersion, 57 | 'freeheap': instance.availableHeapMemory, 58 | 'uptime': instance.uptime, 59 | 'brand': instance.vendorName, 60 | 'product': instance.productName, 61 | 'mac': instance.macAddress, 62 | 'ip': instance.ipAddress, 63 | }; 64 | 65 | _$_LedsDeviceInfo _$$_LedsDeviceInfoFromJson(Map json) => 66 | _$_LedsDeviceInfo( 67 | count: json['count'] as int, 68 | fps: json['fps'] as int, 69 | isRgbw: json['rgbw'] as bool, 70 | showWhiteChannelSlider: json['wv'] as bool, 71 | power: json['pwr'] as int, 72 | maxPower: json['maxpwr'] as int, 73 | maxSegments: json['maxseg'] as int, 74 | ); 75 | 76 | Map _$$_LedsDeviceInfoToJson(_$_LedsDeviceInfo instance) => 77 | { 78 | 'count': instance.count, 79 | 'fps': instance.fps, 80 | 'rgbw': instance.isRgbw, 81 | 'wv': instance.showWhiteChannelSlider, 82 | 'pwr': instance.power, 83 | 'maxpwr': instance.maxPower, 84 | 'maxseg': instance.maxSegments, 85 | }; 86 | 87 | _$_Wifi _$$_WifiFromJson(Map json) => _$_Wifi( 88 | bssid: json['bssid'] as String, 89 | signalStrength: json['signal'] as int, 90 | channel: json['channel'] as int, 91 | ); 92 | 93 | Map _$$_WifiToJson(_$_Wifi instance) => { 94 | 'bssid': instance.bssid, 95 | 'signal': instance.signalStrength, 96 | 'channel': instance.channel, 97 | }; 98 | 99 | _$_FileSystem _$$_FileSystemFromJson(Map json) => 100 | _$_FileSystem( 101 | spaceUsed: json['u'] as int, 102 | spaceTotal: json['t'] as int, 103 | presetModificationTime: json['pmt'] as int, 104 | ); 105 | 106 | Map _$$_FileSystemToJson(_$_FileSystem instance) => 107 | { 108 | 'u': instance.spaceUsed, 109 | 't': instance.spaceTotal, 110 | 'pmt': instance.presetModificationTime, 111 | }; 112 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | #include 10 | 11 | struct _MyApplication { 12 | GtkApplication parent_instance; 13 | char** dart_entrypoint_arguments; 14 | }; 15 | 16 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 17 | 18 | // Implements GApplication::activate. 19 | static void my_application_activate(GApplication* application) { 20 | MyApplication* self = MY_APPLICATION(application); 21 | GtkWindow* window = 22 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 23 | 24 | // Use a header bar when running in GNOME as this is the common style used 25 | // by applications and is the setup most users will be using (e.g. Ubuntu 26 | // desktop). 27 | // If running on X and not using GNOME then just use a traditional title bar 28 | // in case the window manager does more exotic layout, e.g. tiling. 29 | // If running on Wayland assume the header bar will work (may need changing 30 | // if future cases occur). 31 | gboolean use_header_bar = TRUE; 32 | #ifdef GDK_WINDOWING_X11 33 | GdkScreen* screen = gtk_window_get_screen(window); 34 | if (GDK_IS_X11_SCREEN(screen)) { 35 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 36 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 37 | use_header_bar = FALSE; 38 | } 39 | } 40 | #endif 41 | if (use_header_bar) { 42 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 43 | gtk_widget_show(GTK_WIDGET(header_bar)); 44 | gtk_header_bar_set_title(header_bar, "wled"); 45 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 46 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 47 | } else { 48 | gtk_window_set_title(window, "wled"); 49 | } 50 | 51 | auto bdw = bitsdojo_window_from(window); // <--- add this line 52 | bdw->setCustomFrame(true); // <-- add this line 53 | //gtk_window_set_default_size(window, 1280, 720); // <-- comment this line 54 | gtk_widget_show(GTK_WIDGET(window)); 55 | 56 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 57 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 58 | 59 | FlView* view = fl_view_new(project); 60 | gtk_widget_show(GTK_WIDGET(view)); 61 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 62 | 63 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 64 | 65 | gtk_widget_grab_focus(GTK_WIDGET(view)); 66 | } 67 | 68 | // Implements GApplication::local_command_line. 69 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 70 | MyApplication* self = MY_APPLICATION(application); 71 | // Strip out the first argument as it is the binary name. 72 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 73 | 74 | g_autoptr(GError) error = nullptr; 75 | if (!g_application_register(application, nullptr, &error)) { 76 | g_warning("Failed to register: %s", error->message); 77 | *exit_status = 1; 78 | return TRUE; 79 | } 80 | 81 | g_application_activate(application); 82 | *exit_status = 0; 83 | 84 | return TRUE; 85 | } 86 | 87 | // Implements GObject::dispose. 88 | static void my_application_dispose(GObject* object) { 89 | MyApplication* self = MY_APPLICATION(object); 90 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 91 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 92 | } 93 | 94 | static void my_application_class_init(MyApplicationClass* klass) { 95 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 96 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 97 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 98 | } 99 | 100 | static void my_application_init(MyApplication* self) {} 101 | 102 | MyApplication* my_application_new() { 103 | return MY_APPLICATION(g_object_new(my_application_get_type(), 104 | "application-id", APPLICATION_ID, 105 | "flags", G_APPLICATION_NON_UNIQUE, 106 | nullptr)); 107 | } 108 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "wled") 5 | set(APPLICATION_ID "com.casvanluijtelaar.wled") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /lib/features/wled_device/models/state.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'state.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | _$_DeviceState _$$_DeviceStateFromJson(Map json) => 10 | _$_DeviceState( 11 | isEnabled: json['on'] as bool? ?? false, 12 | brightness: json['bri'] as int? ?? 0, 13 | transition: json['transition'] as int? ?? 0, 14 | tt: json['tt'] as int? ?? 0, 15 | presetId: json['ps'] as int? ?? -1, 16 | presetSaveSlot: json['psave'] as int? ?? 0, 17 | playlist: json['pl'] as int? ?? 0, 18 | nightlight: json['nl'] == null 19 | ? const Nightlight() 20 | : Nightlight.fromJson(json['nl'] as Map), 21 | udpn: json['udpn'] == null 22 | ? const Udpn() 23 | : Udpn.fromJson(json['udpn'] as Map), 24 | verbose: json['v'] as bool? ?? false, 25 | reboot: json['rb'] as bool? ?? false, 26 | liveoverride: json['lor'] as int? ?? 0, 27 | time: json['time'] as int? ?? 0, 28 | mainSegment: json['mainseg'] as int? ?? 0, 29 | segments: (json['seg'] as List?) 30 | ?.map((e) => Segment.fromJson(e as Map)) 31 | .toList() ?? 32 | const [], 33 | ); 34 | 35 | Map _$$_DeviceStateToJson(_$_DeviceState instance) => 36 | { 37 | 'on': instance.isEnabled, 38 | 'bri': instance.brightness, 39 | 'transition': instance.transition, 40 | 'tt': instance.tt, 41 | 'ps': instance.presetId, 42 | 'psave': instance.presetSaveSlot, 43 | 'pl': instance.playlist, 44 | 'nl': instance.nightlight, 45 | 'udpn': instance.udpn, 46 | 'v': instance.verbose, 47 | 'rb': instance.reboot, 48 | 'lor': instance.liveoverride, 49 | 'time': instance.time, 50 | 'mainseg': instance.mainSegment, 51 | 'seg': instance.segments, 52 | }; 53 | 54 | _$_Nightlight _$$_NightlightFromJson(Map json) => 55 | _$_Nightlight( 56 | isEnabled: json['on'] as bool? ?? false, 57 | duration: json['dur'] as int? ?? 1, 58 | mode: json['mode'] as int? ?? 0, 59 | targetBrightness: json['tbri'] as int? ?? 0, 60 | timeRemaining: json['rem'] as int? ?? -1, 61 | ); 62 | 63 | Map _$$_NightlightToJson(_$_Nightlight instance) => 64 | { 65 | 'on': instance.isEnabled, 66 | 'dur': instance.duration, 67 | 'mode': instance.mode, 68 | 'tbri': instance.targetBrightness, 69 | 'rem': instance.timeRemaining, 70 | }; 71 | 72 | _$_Udpn _$$_UdpnFromJson(Map json) => _$_Udpn( 73 | send: json['send'] as bool? ?? false, 74 | receive: json['recv'] as bool? ?? false, 75 | nn: json['nn'] as bool? ?? false, 76 | ); 77 | 78 | Map _$$_UdpnToJson(_$_Udpn instance) => { 79 | 'send': instance.send, 80 | 'recv': instance.receive, 81 | 'nn': instance.nn, 82 | }; 83 | 84 | _$_Segment _$$_SegmentFromJson(Map json) => _$_Segment( 85 | id: json['id'] as int? ?? 0, 86 | start: json['start'] as int? ?? 0, 87 | stop: json['stop'] as int? ?? 0, 88 | length: json['len'] as int? ?? 0, 89 | grouping: json['grp'] as int? ?? 0, 90 | spacing: json['spc'] as int? ?? 0, 91 | offset: json['of'] as int? ?? 0, 92 | colors: json['col'] == null 93 | ? const [] 94 | : const ColorConverter().fromJson(json['col'] as List>), 95 | effect: json['fx'] as int? ?? 0, 96 | effectSpeed: json['sx'] as int? ?? 100, 97 | effectIntensity: json['ix'] as int? ?? 100, 98 | paletteId: json['pal'] as int? ?? 0, 99 | selected: json['sel'] as bool? ?? false, 100 | reverse: json['rev'] as bool? ?? false, 101 | enabled: json['on'] as bool? ?? false, 102 | brightness: json['bri'] as int? ?? 0, 103 | mirror: json['mi'] as bool? ?? false, 104 | ); 105 | 106 | Map _$$_SegmentToJson(_$_Segment instance) => 107 | { 108 | 'id': instance.id, 109 | 'start': instance.start, 110 | 'stop': instance.stop, 111 | 'len': instance.length, 112 | 'grp': instance.grouping, 113 | 'spc': instance.spacing, 114 | 'of': instance.offset, 115 | 'col': const ColorConverter().toJson(instance.colors), 116 | 'fx': instance.effect, 117 | 'sx': instance.effectSpeed, 118 | 'ix': instance.effectIntensity, 119 | 'pal': instance.paletteId, 120 | 'sel': instance.selected, 121 | 'rev': instance.reverse, 122 | 'on': instance.enabled, 123 | 'bri': instance.brightness, 124 | 'mi': instance.mirror, 125 | }; 126 | -------------------------------------------------------------------------------- /lib/features/device_list/bloc/device_list_bloc.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:bloc/bloc.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:freezed_annotation/freezed_annotation.dart'; 7 | import 'package:injectable/injectable.dart'; 8 | import 'package:url_launcher/url_launcher.dart'; 9 | import 'package:wled/core/core.dart'; 10 | import 'package:wled/features/features.dart'; 11 | 12 | part 'device_list_bloc.freezed.dart'; 13 | part 'device_list_event.dart'; 14 | part 'device_list_state.dart'; 15 | 16 | @injectable 17 | class DeviceListBloc extends Bloc { 18 | DeviceListBloc( 19 | this._updateRepository, 20 | this._router, 21 | ) : super(const Loading()) { 22 | on(_onInit); 23 | on(_onAdd); 24 | on(_onDevicePower, transformer: BlocTransformers.debounce); 25 | on(_onDeviceSlider, transformer: BlocTransformers.debounce); 26 | on(_onDevicePressed); 27 | on(_onPeriodic); 28 | 29 | /// periodically callback for updating devices on a fixed time interval 30 | _periodicStream = Stream.periodic(Kduration.xLarge) 31 | .listen((_) => add(const ListPeriodic())); 32 | } 33 | 34 | final DeviceUpdateRepository _updateRepository; 35 | final AppRouter _router; 36 | 37 | late final StreamSubscription _periodicStream; 38 | final GlobalKey addButtonKey = GlobalKey(); 39 | 40 | Future _onInit(Initial _, Emitter emit) async { 41 | // emit Loading 42 | emit(const Loading()); 43 | // look for saved devices 44 | final savedDevices = []; 45 | // if no found return Empty 46 | if (savedDevices.isEmpty) return emit(const Empty()); 47 | // if some found return Found 48 | emit(Found(savedDevices)); 49 | } 50 | 51 | /// periodically updates all wled devices in the current state with new data 52 | /// when there are wled devices in the current state. 53 | Future _onPeriodic(ListPeriodic _, Emitter emit) async { 54 | if (state is! Found) return; 55 | final devices = List.from((state as Found).devices); 56 | 57 | final futures = devices.map(_updateRepository.updateFromDevice); 58 | final results = await Future.wait(futures); 59 | 60 | emit(Found(results)); 61 | } 62 | 63 | /// navigate to the the device add screen and add any new devices 64 | /// to the state 65 | Future _onAdd(DeviceAdd event, Emitter emit) async { 66 | final devices = await Overlays.showDialog>( 67 | event.context, 68 | DeviceAddView(buttonKey: addButtonKey), 69 | ); 70 | 71 | if (devices == null || devices.isEmpty) return; 72 | emit(Found(_addOrReplaceDeviceInState(devices))); 73 | } 74 | 75 | /// when a device is pressed, and the device is functional we want to open 76 | /// the controls. currently the in-app-webview-controls are only available 77 | /// on ios and android, so navigate to the device control route on those 78 | /// platforms. on other platforms open a browser window. update the device 79 | /// after the route is returned 80 | Future _onDevicePressed(DevicePressed event, Emitter emit) async { 81 | if (event.device.status != DeviceStatus.functional) return; 82 | 83 | final address = 'http://${event.device.info.ipAddress}/'; 84 | final name = event.device.info.name; 85 | 86 | if (!(Platform.isAndroid || Platform.isIOS)) return launch(address); 87 | _router.push(DeviceControlRoute(deviceName: name, deviceAddress: address)); 88 | } 89 | 90 | /// sends a simple power toggle command to the wled device in this event. 91 | /// then adds the updated device to the state 92 | Future _onDevicePower(DevicePower event, Emitter emit) async { 93 | final isEnabled = event.device.state.isEnabled; 94 | final state = event.device.state.copyWith(isEnabled: !isEnabled); 95 | 96 | final device = await _updateRepository.updateFromDevice( 97 | event.device.copyWith(state: state), 98 | ); 99 | 100 | emit(_addOrReplaceDeviceInState([device])); 101 | } 102 | 103 | /// sends a specific brightness value to the wled device in this event. 104 | /// then updates this wled device in the state with new data 105 | Future _onDeviceSlider(DeviceSlider event, Emitter emit) async { 106 | final state = event.device.state.copyWith(brightness: event.value); 107 | final device = await _updateRepository.updateFromDevice( 108 | event.device.copyWith(state: state), 109 | ); 110 | 111 | emit(_addOrReplaceDeviceInState([device])); 112 | } 113 | 114 | /// takes a [WledDevice] and compares it to the current state. if the state 115 | /// does not contain this device it adds it to the correct position. if it 116 | /// does contain the device it replaces the old device with the new one 117 | List _addOrReplaceDeviceInState(List newDevices) { 118 | if (state is! Found) return newDevices; 119 | 120 | final devices = (state as Found).devices; 121 | newDevices.forEach(devices.addOrReplace); 122 | return devices; 123 | } 124 | 125 | @override 126 | Future close() { 127 | _periodicStream.cancel(); 128 | return super.close(); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /lib/features/device_list/widgets/device_list_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_feather_icons/flutter_feather_icons.dart'; 3 | import 'package:wled/core/core.dart'; 4 | import 'package:wled/features/wled_device/wled_device.dart'; 5 | 6 | import './device_list_slider.dart'; 7 | import './device_list_switch.dart'; 8 | 9 | /// card that displays the Wled device in the main device list and has a couple 10 | /// controls like brightness and power 11 | class DeviceListItem extends StatelessWidget { 12 | const DeviceListItem({ 13 | Key? key, 14 | required this.device, 15 | this.onSave, 16 | this.onDelete, 17 | this.onEdit, 18 | this.onPower, 19 | this.onPressed, 20 | this.onSlide, 21 | }) : super(key: key); 22 | 23 | final WledDevice device; 24 | final VoidCallback? onSave; 25 | final VoidCallback? onDelete; 26 | final VoidCallback? onEdit; 27 | final VoidCallback? onPressed; 28 | final ValueChanged? onPower; 29 | final ValueChanged? onSlide; 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | final theme = context.theme; 34 | 35 | /// the device also needs to be disabled when the device cant be reached 36 | /// ( because then we couldn't turn it on anyways ) 37 | final isEnabled = 38 | device.state.isEnabled && device.status == DeviceStatus.functional; 39 | 40 | /// background gradients for pure black or pure white don't work very well 41 | /// so clamp the background color within a nice color range 42 | final color = device.state.segments[device.state.mainSegment].colors.first 43 | .clamp(0.15, 0.85); 44 | 45 | /// for functional, enabled devices show a nice gradient based on the 46 | /// active color, otherwise show the default background card color 47 | final colors = isEnabled 48 | ? [color.lighten(), color, color.darken()] 49 | : [theme.cardColor, theme.cardColor]; 50 | 51 | /// to make sure the text is always readable we want to switch between 52 | /// white and black textcolor depending on the background color luminance 53 | final textColor = color.computeLuminance() < 0.6 || !isEnabled 54 | ? const Color(0xFFFFFFFF) 55 | : const Color(0xFF555555); 56 | 57 | /// main item interactions, 58 | return InkWell( 59 | customBorder: RoundedRectangleBorder( 60 | borderRadius: BorderRadius.circular(12), 61 | ), 62 | onTap: onPressed, 63 | child: DecoratedBox( 64 | decoration: BoxDecoration( 65 | borderRadius: BorderRadius.circular(12), 66 | gradient: LinearGradient(colors: colors), 67 | boxShadow: const [ 68 | BoxShadow( 69 | blurRadius: 10, 70 | offset: Offset(0, 3), 71 | color: Color.fromRGBO(0, 0, 0, 0.2935), 72 | ) 73 | ], 74 | ), 75 | child: Column( 76 | mainAxisSize: MainAxisSize.min, 77 | children: [ 78 | Padding( 79 | padding: const EdgeInsets.all(20), 80 | child: Row( 81 | children: [ 82 | Icon(Icons.lightbulb, color: textColor), 83 | Padding( 84 | padding: const EdgeInsets.only(left: 20), 85 | child: Column( 86 | mainAxisSize: MainAxisSize.min, 87 | crossAxisAlignment: CrossAxisAlignment.start, 88 | children: [ 89 | Text( 90 | device.info.name, 91 | style: theme.textTheme.headline4! 92 | .copyWith(color: textColor), 93 | ), 94 | Text( 95 | device.info.ipAddress, 96 | style: theme.textTheme.subtitle1! 97 | .copyWith(color: textColor), 98 | ) 99 | ], 100 | ), 101 | ), 102 | 103 | const Spacer(), 104 | 105 | /// only show the enable switch when the device 106 | /// can actually be used 107 | if (device.status == DeviceStatus.functional) 108 | DeviceListSwitch( 109 | value: isEnabled, 110 | onChanged: onPower, 111 | ), 112 | 113 | /// menu button that opens the options menu in a bottom sheet 114 | const SizedBox(width: Kpadding.medium), 115 | RoundIconButton( 116 | icon: FeatherIcons.delete, 117 | onPressed: () {}, 118 | ), 119 | ], 120 | ), 121 | ), 122 | 123 | /// It's possible that a saved device is displayed but actually 124 | /// isn't usuable (e.g. when you're on a different network ) 125 | /// so dont shot the slider if the device isnt reachable 126 | if (device.status == DeviceStatus.functional) 127 | DeviceListSlider( 128 | color: color, 129 | value: device.state.brightness.toDouble().clamp(0, 255), 130 | enabled: isEnabled, 131 | onChanged: onSlide, 132 | ) 133 | else 134 | const Center(child: Text('device not reachable')), 135 | ], 136 | ), 137 | ), 138 | ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/features/wled_device/models/info.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'info.freezed.dart'; 4 | part 'info.g.dart'; 5 | 6 | @freezed 7 | class DeviceInfo with _$DeviceInfo { 8 | const factory DeviceInfo({ 9 | // Version name. 10 | @JsonKey(name: 'ver') required String version, 11 | // Build ID (YYMMDDB, B = daily build index). 12 | @JsonKey(name: 'vid') required int buildId, 13 | // Contains info about the LED setup. 14 | @JsonKey(name: 'leds') required LedsDeviceInfo leds, 15 | // If true, an UI with only a single button for toggling sync should toggle 16 | // receive+send, otherwise send only 17 | @JsonKey(name: 'str') required bool sendReceiveToggle, 18 | // Friendly name of the light. Intended for display in lists and titles. 19 | @JsonKey(name: 'name') required String name, 20 | // The UDP port for realtime packets and WLED broadcast. 21 | @JsonKey(name: 'udpport') required int udpPort, 22 | // If true, the software is currently receiving realtime data via UDP 23 | // or E1.31. 24 | @JsonKey(name: 'live') required bool live, 25 | // DeviceInfo about the realtime data source 26 | @JsonKey(name: 'lm') required String lm, 27 | // Realtime data source IP address 28 | @JsonKey(name: 'lip') required String lip, 29 | // Number of currently connected WebSockets clients. -1 indicates that 30 | // WS is unsupported in this build. 31 | @JsonKey(name: 'ws') required int websockets, 32 | // Number of effects included. 33 | @JsonKey(name: 'fxcount') required int effectsCount, 34 | // Number of palettes configured. 35 | @JsonKey(name: 'palcount') required int paletteCount, 36 | // DeviceInfo about current signal strength 37 | @JsonKey(name: 'wifi') required Wifi wifi, 38 | // DeviceInfo about the embedded LittleFS filesystem (since 0.11.0) 39 | @JsonKey(name: 'fs') required FileSystem fs, 40 | // Number of other WLED devices discovered on the network. -1 if Node 41 | // discovery disabled. (since 0.12.0) 42 | @JsonKey(name: 'ndc') required int numberOfDevicesOnNetwork, 43 | // Name of the platform. 44 | @JsonKey(name: 'arch') required String platformname, 45 | // Version of the underlying (Arduino core) SDK. 46 | @JsonKey(name: 'core') required String sdkVersion, 47 | // Version of LwIP. 1 or 2 on ESP8266, 0 (does not apply) on ESP32. 48 | // Deprecated, removal in 0.14.0 49 | @JsonKey(name: 'lwip') required int lwIpVersion, 50 | // Bytes of heap memory (RAM) currently available. Problematic if <10k. 51 | @JsonKey(name: 'freeheap') required int availableHeapMemory, 52 | // Time since the last boot/reset in seconds. 53 | @JsonKey(name: 'uptime') required int uptime, 54 | // The producer/vendor of the light. Always WLED for standard installations. 55 | @JsonKey(name: 'brand') required String vendorName, 56 | // The product name. Always FOSS for standard installations. 57 | @JsonKey(name: 'product') required String productName, 58 | // The hexadecimal hardware MAC address of the light, lowercase and without 59 | // colons. 60 | @JsonKey(name: 'mac') required String macAddress, 61 | // The IP address of this instance. Empty string if not connected. 62 | // (since 0.13.0) 63 | @JsonKey(name: 'ip') required String ipAddress, 64 | }) = _DeviceInfo; 65 | 66 | factory DeviceInfo.fromJson(Map json) => 67 | _$DeviceInfoFromJson(json); 68 | } 69 | 70 | @freezed 71 | class LedsDeviceInfo with _$LedsDeviceInfo { 72 | const factory LedsDeviceInfo({ 73 | // Total LED count. 74 | @JsonKey(name: 'count') required int count, 75 | // Current frames per second. (available since 0.12.0) 76 | @JsonKey(name: 'fps') required int fps, 77 | // true if LEDs are 4-channel (RGBW). 78 | @JsonKey(name: 'rgbw') required bool isRgbw, 79 | // true if a white channel slider should be displayed. 80 | // (available since 0.10.0) 81 | @JsonKey(name: 'wv') required bool showWhiteChannelSlider, 82 | // Current LED power usage in milliamps as determined by the ABL. 0 if ABL 83 | // is disabled. 84 | @JsonKey(name: 'pwr') required int power, 85 | // Maximum power budget in milliamps for the ABL. 0 if ABL is disabled. 86 | @JsonKey(name: 'maxpwr') required int maxPower, 87 | // Maximum number of segments supported by this version. 88 | @JsonKey(name: 'maxseg') required int maxSegments, 89 | }) = _LedsDeviceInfo; 90 | 91 | factory LedsDeviceInfo.fromJson(Map json) => 92 | _$LedsDeviceInfoFromJson(json); 93 | } 94 | 95 | @freezed 96 | class Wifi with _$Wifi { 97 | const factory Wifi({ 98 | // The BSSID of the currently connected network. 99 | @JsonKey(name: 'bssid') required String bssid, 100 | // Relative signal quality of the current connection. 101 | @JsonKey(name: 'signal') required int signalStrength, 102 | // The current WiFi channel. 103 | @JsonKey(name: 'channel') required int channel, 104 | }) = _Wifi; 105 | 106 | factory Wifi.fromJson(Map json) => _$WifiFromJson(json); 107 | } 108 | 109 | @freezed 110 | class FileSystem with _$FileSystem { 111 | const factory FileSystem({ 112 | // Estimate of used filesystem space in kilobytes 113 | @JsonKey(name: 'u') required int spaceUsed, 114 | // Total filesystem size in kilobytes 115 | @JsonKey(name: 't') required int spaceTotal, 116 | // Unix timestamp for the last modification to the presets.json file. 117 | // Not accurate after boot or after using /edit 118 | @JsonKey(name: 'pmt') required int presetModificationTime, 119 | }) = _FileSystem; 120 | 121 | factory FileSystem.fromJson(Map json) => 122 | _$FileSystemFromJson(json); 123 | } 124 | --------------------------------------------------------------------------------