3 Commits

Author SHA1 Message Date
iGoX 400dee57d4 Home Screen: Inform on connection lose 2026-03-24 21:58:42 +01:00
iGoX b4ff5884b9 [Android] Fix network permission (#2)
Reviewed-on: #2
2026-03-21 04:50:59 +01:00
iGoX beccfa05b3 README.md update (#1)
Reviewed-on: #1
2026-03-21 02:19:02 +01:00
7 changed files with 122 additions and 23 deletions
+51
View File
@@ -0,0 +1,51 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
.flutter-plugins-dependencies
android/key.properties
windows/installer/*
bugreport*
!downloads/*
+30
View File
@@ -0,0 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
- platform: windows
create_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
base_revision: 2c9eb20739dfec95e2c74bd3dfa4601b0a8a36aa
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
+4 -4
View File
@@ -1,6 +1,6 @@
# BusyLight Buddy # BusyLight Buddy
Multiplatform Flutter app to control your DIY [BusyLight](https://github.com/igox/busylight) (ESP32 + MicroPython + Microdot). Multiplatform Flutter app to control your DIY [BusyLight](https://code.igox.org/iGoX/busylight) (ESP32 + MicroPython + Microdot).
Supports **iOS**, **ipadOS**, **Android**, **macOS**, and **Windows**. Supports **iOS**, **ipadOS**, **Android**, **macOS**, and **Windows**.
@@ -8,10 +8,10 @@ Supports **iOS**, **ipadOS**, **Android**, **macOS**, and **Windows**.
## Downloads ## Downloads
[![Windows](https://img.shields.io/badge/Windows-Installer-0078D4?style=for-the-badge&logo=windows&logoColor=white)](https://github.com/igox/busylight-buddy/releases/download/v0.0.1/BusyLight-Buddy-Installer.exe) [![Windows](https://img.shields.io/badge/Windows-Installer-0078D4?style=for-the-badge&logo=windows&logoColor=white)](https://code.igox.org/iGoX/busylight-buddy/releases/download/v0.0.1/BusyLight-Buddy-Installer.exe)
[![Android](https://img.shields.io/badge/Android-APK-3DDC84?style=for-the-badge&logo=android&logoColor=white)](https://github.com/igox/busylight-buddy/releases/download/v0.0.1/org.igox.apps.android.busylight-buddy-release.apk) [![Android](https://img.shields.io/badge/Android-APK-3DDC84?style=for-the-badge&logo=android&logoColor=white)](https://code.igox.org/iGoX/busylight-buddy/releases/download/v0.0.1/org.igox.apps.android.busylight-buddy-release.apk)
Or browse all releases on the [Releases page](https://github.com/igox/busylight-buddy/releases). Or browse all releases on the [Releases page](https://code.igox.org/iGoX/busylight-buddy/releases).
--- ---
+3 -1
View File
@@ -1,8 +1,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application <application
android:label="BusyLight Buddy" android:label="BusyLight Buddy"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
+9 -1
View File
@@ -149,6 +149,11 @@ final colorProvider = StateNotifierProvider<ColorNotifier, BusylightColor>(
}, },
); );
// ── Connection lost indicator ─────────────────────────────────────────────────
// Set to true by the polling notifier when a poll fails, false on success.
final connectionLostProvider = StateProvider<bool>((_) => false);
// ── Background polling ──────────────────────────────────────────────────────── // ── Background polling ────────────────────────────────────────────────────────
// Periodically pulls status + color from the device and silently updates state. // Periodically pulls status + color from the device and silently updates state.
@@ -181,8 +186,11 @@ class PollingNotifier extends StateNotifier<void> {
_ref.read(busylightStatusProvider.notifier).setLocalStatus(status); _ref.read(busylightStatusProvider.notifier).setLocalStatus(status);
_ref.read(colorProvider.notifier).silentSet(color); _ref.read(colorProvider.notifier).silentSet(color);
_ref.read(brightnessProvider.notifier).silentSet(color.brightness); _ref.read(brightnessProvider.notifier).silentSet(color.brightness);
// Connection restored — clear the lost flag
_ref.read(connectionLostProvider.notifier).state = false;
} catch (_) { } catch (_) {
// Silently ignore poll errors — connection issues are shown on manual refresh // Signal connectivity issue to the UI
_ref.read(connectionLostProvider.notifier).state = true;
} }
} }
+25 -13
View File
@@ -118,10 +118,11 @@ class _BodyState extends ConsumerState<_Body> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final statusAsync = ref.watch(busylightStatusProvider); final statusAsync = ref.watch(busylightStatusProvider);
final brightness = ref.watch(brightnessProvider); final brightness = ref.watch(brightnessProvider);
final color = ref.watch(colorProvider); final color = ref.watch(colorProvider);
final presets = ref.watch(presetsProvider); final presets = ref.watch(presetsProvider);
final connectionLost = ref.watch(connectionLostProvider);
final status = statusAsync.valueOrNull ?? BusylightStatus.off; final status = statusAsync.valueOrNull ?? BusylightStatus.off;
final displayColor = _statusColor(status, color); final displayColor = _statusColor(status, color);
@@ -129,7 +130,7 @@ class _BodyState extends ConsumerState<_Body> {
return ListView( return ListView(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
children: [ children: [
// Live color preview dot // Live color preview dot — shows wifi-off icon when connection lost
Center( Center(
child: AnimatedContainer( child: AnimatedContainer(
duration: const Duration(milliseconds: 400), duration: const Duration(milliseconds: 400),
@@ -137,10 +138,12 @@ class _BodyState extends ConsumerState<_Body> {
height: 100, height: 100,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: status == BusylightStatus.off color: connectionLost
? Colors.grey.shade900 ? Colors.grey.shade900
: displayColor.withOpacity(brightness), : status == BusylightStatus.off
boxShadow: status != BusylightStatus.off ? Colors.grey.shade900
: displayColor.withOpacity(brightness),
boxShadow: (!connectionLost && status != BusylightStatus.off)
? [BoxShadow( ? [BoxShadow(
color: displayColor.withOpacity(0.5 * brightness), color: displayColor.withOpacity(0.5 * brightness),
blurRadius: 40, blurRadius: 40,
@@ -148,13 +151,20 @@ class _BodyState extends ConsumerState<_Body> {
)] )]
: null, : null,
), ),
child: connectionLost
? const Icon(Icons.wifi_off, color: Colors.grey, size: 36)
: null,
), ),
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
Center( Center(
child: Text( child: Text(
status.label.toUpperCase(), connectionLost ? 'NO CONNECTION' : status.label.toUpperCase(),
style: const TextStyle(color: Colors.grey, letterSpacing: 2, fontSize: 13), style: TextStyle(
color: connectionLost ? Colors.red.shade700 : Colors.grey,
letterSpacing: 2,
fontSize: 13,
),
), ),
), ),
const SizedBox(height: 36), const SizedBox(height: 36),
@@ -177,9 +187,11 @@ class _BodyState extends ConsumerState<_Body> {
BusylightStatus.off, BusylightStatus.off,
].map((s) => StatusButton( ].map((s) => StatusButton(
status: s, status: s,
isActive: status == s, isActive: !connectionLost && status == s,
isPending: _pendingStatus == s, isPending: _pendingStatus == s,
onTap: _pendingStatus == null ? () => _setStatus(s) : () {}, onTap: (!connectionLost && _pendingStatus == null)
? () => _setStatus(s)
: () {},
)).toList(), )).toList(),
), ),
const SizedBox(height: 32), const SizedBox(height: 32),
@@ -188,7 +200,7 @@ class _BodyState extends ConsumerState<_Body> {
_PresetsScroller( _PresetsScroller(
presets: presets, presets: presets,
pendingPresetId: _pendingPresetId, pendingPresetId: _pendingPresetId,
onPresetTap: (_pendingStatus == null && _pendingPresetId == null) onPresetTap: (!connectionLost && _pendingStatus == null && _pendingPresetId == null)
? _applyPreset ? _applyPreset
: (_) {}, : (_) {},
onPresetDelete: (preset) => ref.read(presetsProvider.notifier).remove(preset.id), onPresetDelete: (preset) => ref.read(presetsProvider.notifier).remove(preset.id),
-4
View File
@@ -330,14 +330,10 @@
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
inputPaths = (
);
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
); );
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";