Compare commits

..

1 Commits

26 changed files with 396 additions and 458 deletions

View File

@ -17,8 +17,8 @@ class DeviceLocationDetailsServiceDecorator implements DeviceLocationService {
'reverse', 'reverse',
queryParameters: { queryParameters: {
'format': 'json', 'format': 'json',
'lat': 25.1880567, 'lat': param.latitude,
'lon': 55.266608, 'lon': param.longitude,
}, },
); );

View File

@ -57,9 +57,6 @@ class Status {
}; };
} }
factory Status.fromJson(String source) => Status.fromMap(json.decode(source));
String toJson() => json.encode(toMap());
Status copyWith({ Status copyWith({
String? code, String? code,
dynamic value, dynamic value,
@ -69,4 +66,8 @@ class Status {
value: value ?? this.value, value: value ?? this.value,
); );
} }
factory Status.fromJson(String source) => Status.fromMap(json.decode(source));
String toJson() => json.encode(toMap());
} }

View File

@ -68,6 +68,7 @@ class DeviceManagementBody extends StatelessWidget with HelperResponsiveLayout {
children: [ children: [
Expanded(child: SpaceTreeView( Expanded(child: SpaceTreeView(
onSelect: () { onSelect: () {
context.read<DeviceManagementBloc>().add(ResetFilters());
context.read<DeviceManagementBloc>().add(FetchDevices(context)); context.read<DeviceManagementBloc>().add(FetchDevices(context));
}, },
)), )),

View File

@ -62,10 +62,9 @@ class CurtainModuleItems extends StatelessWidget with HelperResponsiveLayout {
BlocProvider.of<CurtainModuleBloc>(context), BlocProvider.of<CurtainModuleBloc>(context),
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'timer', category: 'CUR_2',
code: 'control', code: 'control',
countdownCode: 'timer',
deviceType: 'CUR_2',
), ),
)); ));
}, },

View File

@ -40,7 +40,7 @@ class OneGangGlassSwitchBloc
emit(OneGangGlassSwitchLoading()); emit(OneGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId, emit);
deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status); deviceStatus = OneGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(OneGangGlassSwitchStatusLoaded(deviceStatus)); emit(OneGangGlassSwitchStatusLoaded(deviceStatus));
} catch (e) { } catch (e) {
@ -48,28 +48,42 @@ class OneGangGlassSwitchBloc
} }
} }
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription; void _listenToChanges(
String deviceId,
void _listenToChanges(String deviceId) { Emitter<OneGangGlassSwitchState> emit,
) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async { final stream = ref.onValue;
if (event.snapshot.value == null) return;
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>; stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
final statusList = <Status>[]; final statusList = <Status>[];
if (data['status'] != null) {
usersMap['status'].forEach((element) { for (var element in data['status']) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList.add(
}); Status(
code: element['code'].toString(),
deviceStatus = value: element['value'].toString(),
OneGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList); ),
);
}
}
if (statusList.isNotEmpty) {
final newStatus = OneGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
}
}
}
}); });
} catch (_) {} } catch (e) {
emit(OneGangGlassSwitchError('Failed to listen to changes: $e'));
}
} }
void _onStatusUpdated( void _onStatusUpdated(
@ -160,10 +174,4 @@ class OneGangGlassSwitchBloc
deviceStatus = deviceStatus.copyWith(switch1: value); deviceStatus = deviceStatus.copyWith(switch1: value);
} }
} }
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
} }

View File

@ -90,8 +90,6 @@ class OneGangGlassSwitchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_1', category: 'switch_1',
deviceUuid: deviceId, deviceUuid: deviceId,
countdownCode: 'countdown_1',
deviceType: '1GT',
), ),
)); ));
}, },

View File

@ -80,8 +80,6 @@ class WallLightDeviceControl extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_1', category: 'switch_1',
deviceUuid: deviceId, deviceUuid: deviceId,
countdownCode: 'countdown_1',
deviceType: '1G',
), ),
)); ));
}, },

View File

@ -47,7 +47,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
final success = await RemoteControlDeviceService().controlDevice( final success = await RemoteControlDeviceService().controlDevice(
deviceUuid: deviceId, deviceUuid: deviceId,
status: Status( status: Status(
code: event.countdownCode, code: 'countdown_1',
value: 0, value: 0,
), ),
); );
@ -80,18 +80,15 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
) { ) {
if (state is ScheduleLoaded) { if (state is ScheduleLoaded) {
final currentState = state as ScheduleLoaded; final currentState = state as ScheduleLoaded;
emit(currentState.copyWith( emit(currentState.copyWith(
countdownSeconds: currentState.countdownSeconds,
selectedTime: currentState.selectedTime,
deviceId: deviceId,
scheduleMode: event.scheduleMode, scheduleMode: event.scheduleMode,
countdownHours: currentState.countdownHours, countdownRemaining: Duration.zero,
countdownMinutes: currentState.countdownMinutes, countdownHours: 0,
inchingHours: currentState.inchingHours, countdownMinutes: 0,
inchingMinutes: currentState.inchingMinutes, inchingHours: 0,
inchingMinutes: 0,
isCountdownActive: false,
isInchingActive: false, isInchingActive: false,
isCountdownActive: currentState.countdownRemaining > Duration.zero,
)); ));
} }
} }
@ -224,6 +221,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
deviceId, deviceId,
event.category, event.category,
); );
if (state is ScheduleLoaded) { if (state is ScheduleLoaded) {
final currentState = state as ScheduleLoaded; final currentState = state as ScheduleLoaded;
emit(currentState.copyWith( emit(currentState.copyWith(
@ -287,8 +285,9 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
) async { ) async {
try { try {
if (state is ScheduleLoaded) { if (state is ScheduleLoaded) {
final dateTime = DateTime.parse(event.time);
Status status = Status(code: '', value: ''); Status status = Status(code: '', value: '');
if (event.deviceType == 'CUR_2') { if (event.category == 'CUR_2') {
status = status.copyWith( status = status.copyWith(
code: 'control', code: 'control',
value: event.functionOn == true ? 'open' : 'close'); value: event.functionOn == true ? 'open' : 'close');
@ -296,8 +295,6 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
status = status =
status.copyWith(code: event.category, value: event.functionOn); status.copyWith(code: event.category, value: event.functionOn);
} }
final dateTime = DateTime.parse(event.time);
final updatedSchedule = ScheduleEntry( final updatedSchedule = ScheduleEntry(
scheduleId: event.scheduleId, scheduleId: event.scheduleId,
category: event.category, category: event.category,
@ -408,7 +405,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
final totalSeconds = final totalSeconds =
Duration(hours: event.hours, minutes: event.minutes).inSeconds; Duration(hours: event.hours, minutes: event.minutes).inSeconds;
final code = event.mode == ScheduleModes.countdown final code = event.mode == ScheduleModes.countdown
? event.countDownCode ? 'countdown_1'
: 'switch_inching'; : 'switch_inching';
final currentState = state as ScheduleLoaded; final currentState = state as ScheduleLoaded;
final duration = Duration(seconds: totalSeconds); final duration = Duration(seconds: totalSeconds);
@ -435,7 +432,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
); );
if (success) { if (success) {
if (code == event.countDownCode) { if (code == 'countdown_1') {
final countdownDuration = Duration(seconds: totalSeconds); final countdownDuration = Duration(seconds: totalSeconds);
emit( emit(
@ -449,7 +446,7 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
); );
if (countdownDuration.inSeconds > 0) { if (countdownDuration.inSeconds > 0) {
_startCountdownTimer(emit, countdownDuration, event.countDownCode); _startCountdownTimer(emit, countdownDuration);
} else { } else {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
emit( emit(
@ -479,7 +476,9 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
} }
void _startCountdownTimer( void _startCountdownTimer(
Emitter<ScheduleState> emit, Duration duration, String countdownCode) { Emitter<ScheduleState> emit,
Duration duration,
) {
_countdownTimer?.cancel(); _countdownTimer?.cancel();
_countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _countdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_currentCountdown != null && _currentCountdown! > Duration.zero) { if (_currentCountdown != null && _currentCountdown! > Duration.zero) {
@ -489,7 +488,6 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
} else { } else {
timer.cancel(); timer.cancel();
add(StopScheduleEvent( add(StopScheduleEvent(
countdownCode: countdownCode,
mode: _currentCountdown == null mode: _currentCountdown == null
? ScheduleModes.countdown ? ScheduleModes.countdown
: ScheduleModes.inching, : ScheduleModes.inching,
@ -526,75 +524,70 @@ class ScheduleBloc extends Bloc<ScheduleEvent, ScheduleState> {
try { try {
final status = final status =
await DevicesManagementApi().getDeviceStatus(event.deviceId); await DevicesManagementApi().getDeviceStatus(event.deviceId);
int totalSeconds = 0; print(status.status);
final countdownItem = status.status.firstWhere(
(item) => item.code == event.countdownCode,
orElse: () => Status(code: '', value: 0),
);
totalSeconds = (countdownItem.value as int?) ?? 0;
final countdownHours = totalSeconds ~/ 3600;
final countdownMinutes = (totalSeconds % 3600) ~/ 60;
final countdownSeconds = totalSeconds % 60;
final deviceStatus = final deviceStatus =
WaterHeaterStatusModel.fromJson(event.deviceId, status.status); WaterHeaterStatusModel.fromJson(event.deviceId, status.status);
final isCountdownActive = totalSeconds > 0;
final isInchingActive = !isCountdownActive &&
(deviceStatus.inchingHours > 0 || deviceStatus.inchingMinutes > 0);
final newState = state is ScheduleLoaded final scheduleMode =
? (state as ScheduleLoaded).copyWith( deviceStatus.countdownHours > 0 || deviceStatus.countdownMinutes > 0
scheduleMode: ScheduleModes.schedule, ? ScheduleModes.countdown
countdownHours: countdownHours, : deviceStatus.inchingHours > 0 || deviceStatus.inchingMinutes > 0
countdownMinutes: countdownMinutes, ? ScheduleModes.inching
countdownSeconds: countdownSeconds, : ScheduleModes.schedule;
final isCountdown = scheduleMode == ScheduleModes.countdown;
final isInching = scheduleMode == ScheduleModes.inching;
Duration? countdownRemaining;
var isCountdownActive = false;
var isInchingActive = false;
if (isCountdown) {
countdownRemaining = Duration(
hours: deviceStatus.countdownHours,
minutes: deviceStatus.countdownMinutes,
);
isCountdownActive = countdownRemaining > Duration.zero;
} else if (isInching) {
isInchingActive = Duration(
hours: deviceStatus.inchingHours,
minutes: deviceStatus.inchingMinutes,
) >
Duration.zero;
}
if (state is ScheduleLoaded) {
final currentState = state as ScheduleLoaded;
emit(currentState.copyWith(
scheduleMode: scheduleMode,
countdownHours: deviceStatus.countdownHours,
countdownMinutes: deviceStatus.countdownMinutes,
inchingHours: deviceStatus.inchingHours, inchingHours: deviceStatus.inchingHours,
inchingMinutes: deviceStatus.inchingMinutes, inchingMinutes: deviceStatus.inchingMinutes,
isCountdownActive: isCountdownActive, isCountdownActive: isCountdownActive,
isInchingActive: isInchingActive, isInchingActive: isInchingActive,
countdownRemaining: isCountdownActive countdownRemaining: countdownRemaining ?? Duration.zero,
? Duration(seconds: totalSeconds) ));
: Duration.zero, } else {
) emit(ScheduleLoaded(
: ScheduleLoaded(
scheduleMode: ScheduleModes.schedule,
schedules: const [], schedules: const [],
selectedTime: null, selectedTime: null,
selectedDays: List.filled(7, false), selectedDays: List.filled(7, false),
functionOn: false, functionOn: false,
isEditing: false, isEditing: false,
deviceId: event.deviceId, deviceId: deviceId,
countdownHours: countdownHours, scheduleMode: scheduleMode,
countdownMinutes: countdownMinutes, countdownHours: deviceStatus.countdownHours,
countdownSeconds: countdownSeconds, countdownMinutes: deviceStatus.countdownMinutes,
inchingHours: deviceStatus.inchingHours, inchingHours: deviceStatus.inchingHours,
inchingMinutes: deviceStatus.inchingMinutes, inchingMinutes: deviceStatus.inchingMinutes,
isCountdownActive: isCountdownActive, isCountdownActive: isCountdownActive,
isInchingActive: isInchingActive, isInchingActive: isInchingActive,
countdownRemaining: isCountdownActive countdownRemaining: countdownRemaining ?? Duration.zero,
? Duration(seconds: totalSeconds)
: Duration.zero,
);
emit(newState);
if (isCountdownActive) {
_countdownTimer?.cancel();
_currentCountdown = Duration(seconds: totalSeconds);
countdownRemaining = _currentCountdown!;
if (totalSeconds > 0) {
_startCountdownTimer(
emit, Duration(seconds: totalSeconds), event.countdownCode);
} else {
add(StopScheduleEvent(
countdownCode: event.countdownCode,
mode: ScheduleModes.countdown,
deviceId: event.deviceId,
)); ));
} }
} else {
_countdownTimer?.cancel(); // if (isCountdownActive && countdownRemaining != null) {
} // _startCountdownTimer(emit, countdownRemaining);
// }
} catch (e) { } catch (e) {
emit(ScheduleError('Failed to fetch device status: $e')); emit(ScheduleError('Failed to fetch device status: $e'));
} }

View File

@ -91,7 +91,6 @@ class ScheduleEditEvent extends ScheduleEvent {
final String time; final String time;
final List<String> selectedDays; final List<String> selectedDays;
final bool functionOn; final bool functionOn;
final String deviceType;
const ScheduleEditEvent({ const ScheduleEditEvent({
required this.scheduleId, required this.scheduleId,
@ -99,7 +98,6 @@ class ScheduleEditEvent extends ScheduleEvent {
required this.time, required this.time,
required this.selectedDays, required this.selectedDays,
required this.functionOn, required this.functionOn,
required this.deviceType,
}); });
@override @override
@ -109,7 +107,6 @@ class ScheduleEditEvent extends ScheduleEvent {
time, time,
selectedDays, selectedDays,
functionOn, functionOn,
deviceType,
]; ];
} }
@ -141,13 +138,11 @@ class ScheduleUpdateEntryEvent extends ScheduleEvent {
class UpdateScheduleModeEvent extends ScheduleEvent { class UpdateScheduleModeEvent extends ScheduleEvent {
final ScheduleModes scheduleMode; final ScheduleModes scheduleMode;
final String countdownCode;
const UpdateScheduleModeEvent( const UpdateScheduleModeEvent({required this.scheduleMode});
{required this.scheduleMode, required this.countdownCode});
@override @override
List<Object> get props => [scheduleMode, countdownCode!]; List<Object> get props => [scheduleMode];
} }
class UpdateCountdownTimeEvent extends ScheduleEvent { class UpdateCountdownTimeEvent extends ScheduleEvent {
@ -182,32 +177,28 @@ class StartScheduleEvent extends ScheduleEvent {
final ScheduleModes mode; final ScheduleModes mode;
final int hours; final int hours;
final int minutes; final int minutes;
final String countDownCode;
const StartScheduleEvent({ const StartScheduleEvent({
required this.mode, required this.mode,
required this.hours, required this.hours,
required this.minutes, required this.minutes,
required this.countDownCode,
}); });
@override @override
List<Object?> get props => [mode, hours, minutes, countDownCode]; List<Object?> get props => [mode, hours, minutes];
} }
class StopScheduleEvent extends ScheduleEvent { class StopScheduleEvent extends ScheduleEvent {
final ScheduleModes mode; final ScheduleModes mode;
final String deviceId; final String deviceId;
final String countdownCode;
const StopScheduleEvent({ const StopScheduleEvent({
required this.mode, required this.mode,
required this.deviceId, required this.deviceId,
required this.countdownCode,
}); });
@override @override
List<Object?> get props => [mode, deviceId, countdownCode]; List<Object?> get props => [mode, deviceId];
} }
class ScheduleDecrementCountdownEvent extends ScheduleEvent { class ScheduleDecrementCountdownEvent extends ScheduleEvent {
@ -219,13 +210,11 @@ class ScheduleDecrementCountdownEvent extends ScheduleEvent {
class ScheduleFetchStatusEvent extends ScheduleEvent { class ScheduleFetchStatusEvent extends ScheduleEvent {
final String deviceId; final String deviceId;
final String countdownCode;
const ScheduleFetchStatusEvent( const ScheduleFetchStatusEvent(this.deviceId);
{required this.deviceId, required this.countdownCode});
@override @override
List<Object> get props => [deviceId, countdownCode]; List<Object> get props => [deviceId];
} }
class DeleteScheduleEvent extends ScheduleEvent { class DeleteScheduleEvent extends ScheduleEvent {

View File

@ -29,7 +29,7 @@ class ScheduleLoaded extends ScheduleState {
final int inchingSeconds; final int inchingSeconds;
final bool isInchingActive; final bool isInchingActive;
final ScheduleModes scheduleMode; final ScheduleModes scheduleMode;
final Duration countdownRemaining; final Duration? countdownRemaining;
final int? countdownSeconds; final int? countdownSeconds;
const ScheduleLoaded({ const ScheduleLoaded({
@ -48,7 +48,7 @@ class ScheduleLoaded extends ScheduleState {
this.inchingMinutes = 0, this.inchingMinutes = 0,
this.isInchingActive = false, this.isInchingActive = false,
this.scheduleMode = ScheduleModes.countdown, this.scheduleMode = ScheduleModes.countdown,
this.countdownRemaining = Duration.zero, this.countdownRemaining,
}); });
ScheduleLoaded copyWith({ ScheduleLoaded copyWith({

View File

@ -11,7 +11,6 @@ class CountdownModeButtons extends StatelessWidget {
final String deviceId; final String deviceId;
final int hours; final int hours;
final int minutes; final int minutes;
final String countDownCode;
const CountdownModeButtons({ const CountdownModeButtons({
super.key, super.key,
@ -19,7 +18,6 @@ class CountdownModeButtons extends StatelessWidget {
required this.deviceId, required this.deviceId,
required this.hours, required this.hours,
required this.minutes, required this.minutes,
required this.countDownCode,
}); });
@override @override
@ -45,7 +43,6 @@ class CountdownModeButtons extends StatelessWidget {
StopScheduleEvent( StopScheduleEvent(
mode: ScheduleModes.countdown, mode: ScheduleModes.countdown,
deviceId: deviceId, deviceId: deviceId,
countdownCode: countDownCode,
), ),
); );
}, },
@ -60,7 +57,7 @@ class CountdownModeButtons extends StatelessWidget {
mode: ScheduleModes.countdown, mode: ScheduleModes.countdown,
hours: hours, hours: hours,
minutes: minutes, minutes: minutes,
countDownCode: countDownCode), ),
); );
}, },
backgroundColor: ColorsManager.primaryColor, backgroundColor: ColorsManager.primaryColor,

View File

@ -75,33 +75,23 @@ class _CountdownInchingViewState extends State<CountdownInchingView> {
final isCountDown = state.scheduleMode == ScheduleModes.countdown; final isCountDown = state.scheduleMode == ScheduleModes.countdown;
final isActive = final isActive =
isCountDown ? state.isCountdownActive : state.isInchingActive; isCountDown ? state.isCountdownActive : state.isInchingActive;
final displayHours = isActive && state.countdownRemaining != null
final displayHours = ? state.countdownRemaining!.inHours
isActive && state.countdownRemaining != Duration.zero
? state.countdownRemaining.inHours
: (isCountDown ? state.countdownHours : state.inchingHours); : (isCountDown ? state.countdownHours : state.inchingHours);
final displayMinutes = isActive && state.countdownRemaining != null
final displayMinutes = ? state.countdownRemaining!.inMinutes.remainder(60)
isActive && state.countdownRemaining != Duration.zero
? state.countdownRemaining.inMinutes.remainder(60)
: (isCountDown ? state.countdownMinutes : state.inchingMinutes); : (isCountDown ? state.countdownMinutes : state.inchingMinutes);
final displaySeconds = isActive && state.countdownRemaining != null
? state.countdownRemaining!.inSeconds.remainder(60)
: (isCountDown ? state.countdownSeconds : state.inchingSeconds);
final displaySeconds = _updateControllers(displayHours, displayMinutes, displaySeconds!);
isActive && state.countdownRemaining != Duration.zero
? state.countdownRemaining.inSeconds.remainder(60)
: (isCountDown ? (state.countdownSeconds ?? 0) : 0);
_updateControllers(displayHours, displayMinutes, displaySeconds); if (displayHours == 0 && displayMinutes == 0 && displaySeconds == 0) {
if (isActive &&
displayHours == 0 &&
displayMinutes == 0 &&
displaySeconds == 0) {
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
StopScheduleEvent( StopScheduleEvent(
mode: ScheduleModes.countdown, mode: ScheduleModes.countdown,
deviceId: widget.deviceId, deviceId: widget.deviceId,
countdownCode: '',
), ),
); );
} }

View File

@ -43,9 +43,7 @@ class InchingModeButtons extends StatelessWidget {
onPressed: () { onPressed: () {
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
StopScheduleEvent( StopScheduleEvent(
deviceId: deviceId, deviceId: deviceId, mode: ScheduleModes.inching),
mode: ScheduleModes.inching,
countdownCode: ''),
); );
}, },
backgroundColor: Colors.red, backgroundColor: Colors.red,

View File

@ -18,15 +18,11 @@ class BuildScheduleView extends StatelessWidget {
super.key, super.key,
required this.deviceUuid, required this.deviceUuid,
required this.category, required this.category,
required this.countdownCode,
this.code, this.code,
required this.deviceType,
}); });
final String deviceUuid; final String deviceUuid;
final String category; final String category;
final String? code; final String? code;
final String? countdownCode;
final String deviceType;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -35,8 +31,7 @@ class BuildScheduleView extends StatelessWidget {
deviceId: deviceUuid, deviceId: deviceUuid,
) )
..add(ScheduleGetEvent(category: category)) ..add(ScheduleGetEvent(category: category))
..add(ScheduleFetchStatusEvent( ..add(ScheduleFetchStatusEvent(deviceUuid)),
deviceId: deviceUuid, countdownCode: countdownCode ?? '')),
child: Dialog( child: Dialog(
backgroundColor: Colors.white, backgroundColor: Colors.white,
insetPadding: const EdgeInsets.all(20), insetPadding: const EdgeInsets.all(20),
@ -57,22 +52,21 @@ class BuildScheduleView extends StatelessWidget {
children: [ children: [
const ScheduleHeader(), const ScheduleHeader(),
const SizedBox(height: 20), const SizedBox(height: 20),
if (deviceType == 'CUR_2') if (category == 'CUR_2')
const SizedBox() const SizedBox()
else else
ScheduleModeSelector( ScheduleModeSelector(
countdownCode: countdownCode ?? '',
currentMode: state.scheduleMode, currentMode: state.scheduleMode,
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.schedule) if (state.scheduleMode == ScheduleModes.schedule)
ScheduleManagementUI( ScheduleManagementUI(
deviceType: deviceType,
category: category, category: category,
deviceUuid: deviceUuid, deviceUuid: deviceUuid,
onAddSchedule: () async { onAddSchedule: () async {
final entry = await ScheduleDialogHelper final entry = await ScheduleDialogHelper
.showAddScheduleDialog(context, .showAddScheduleDialog(
context,
schedule: ScheduleEntry( schedule: ScheduleEntry(
category: category, category: category,
time: '', time: '',
@ -82,7 +76,7 @@ class BuildScheduleView extends StatelessWidget {
), ),
isEdit: false, isEdit: false,
code: code, code: code,
deviceType: deviceType); );
if (entry != null) { if (entry != null) {
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
ScheduleAddEvent( ScheduleAddEvent(
@ -96,7 +90,6 @@ class BuildScheduleView extends StatelessWidget {
} }
}, },
), ),
if (deviceType != 'CUR_2')
if (state.scheduleMode == ScheduleModes.countdown || if (state.scheduleMode == ScheduleModes.countdown ||
state.scheduleMode == ScheduleModes.inching) state.scheduleMode == ScheduleModes.inching)
CountdownInchingView( CountdownInchingView(
@ -105,7 +98,6 @@ class BuildScheduleView extends StatelessWidget {
const SizedBox(height: 20), const SizedBox(height: 20),
if (state.scheduleMode == ScheduleModes.countdown) if (state.scheduleMode == ScheduleModes.countdown)
CountdownModeButtons( CountdownModeButtons(
countDownCode: countdownCode ?? '',
isActive: state.isCountdownActive, isActive: state.isCountdownActive,
deviceId: deviceUuid, deviceId: deviceUuid,
hours: state.countdownHours, hours: state.countdownHours,

View File

@ -8,13 +8,11 @@ class ScheduleManagementUI extends StatelessWidget {
final String deviceUuid; final String deviceUuid;
final VoidCallback onAddSchedule; final VoidCallback onAddSchedule;
final String category; final String category;
final String deviceType;
const ScheduleManagementUI({ const ScheduleManagementUI({
super.key, super.key,
required this.deviceUuid, required this.deviceUuid,
required this.onAddSchedule, required this.onAddSchedule,
required this.deviceType,
this.category = 'switch_1', this.category = 'switch_1',
}); });
@ -46,11 +44,7 @@ class ScheduleManagementUI extends StatelessWidget {
), ),
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
ScheduleTableWidget( ScheduleTableWidget(deviceUuid: deviceUuid, category: category),
deviceUuid: deviceUuid,
category: category,
deviceType: deviceType,
),
], ],
); );
} }

View File

@ -7,12 +7,10 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
class ScheduleModeSelector extends StatelessWidget { class ScheduleModeSelector extends StatelessWidget {
final ScheduleModes currentMode; final ScheduleModes currentMode;
final String countdownCode;
const ScheduleModeSelector({ const ScheduleModeSelector({
super.key, super.key,
required this.currentMode, required this.currentMode,
required this.countdownCode,
}); });
@override @override
@ -73,8 +71,7 @@ class ScheduleModeSelector extends StatelessWidget {
onChanged: (ScheduleModes? value) { onChanged: (ScheduleModes? value) {
if (value != null) { if (value != null) {
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
UpdateScheduleModeEvent( UpdateScheduleModeEvent(scheduleMode: value),
scheduleMode: value, countdownCode: countdownCode),
); );
if (value == ScheduleModes.schedule) { if (value == ScheduleModes.schedule) {
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(

View File

@ -12,13 +12,11 @@ import 'package:syncrow_web/utils/format_date_time.dart';
class ScheduleTableWidget extends StatelessWidget { class ScheduleTableWidget extends StatelessWidget {
final String deviceUuid; final String deviceUuid;
final String category; final String category;
final String deviceType;
const ScheduleTableWidget({ const ScheduleTableWidget({
super.key, super.key,
required this.deviceUuid, required this.deviceUuid,
this.category = 'switch_1', this.category = 'switch_1',
required this.deviceType,
}); });
@override @override
@ -27,14 +25,13 @@ class ScheduleTableWidget extends StatelessWidget {
create: (_) => ScheduleBloc( create: (_) => ScheduleBloc(
deviceId: deviceUuid, deviceId: deviceUuid,
)..add(ScheduleGetEvent(category: category)), )..add(ScheduleGetEvent(category: category)),
child: _ScheduleTableView(deviceType), child: _ScheduleTableView(),
); );
} }
} }
class _ScheduleTableView extends StatelessWidget { class _ScheduleTableView extends StatelessWidget {
final String deviceType; const _ScheduleTableView();
const _ScheduleTableView(this.deviceType);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -84,7 +81,7 @@ class _ScheduleTableView extends StatelessWidget {
bottomLeft: Radius.circular(20), bottomLeft: Radius.circular(20),
bottomRight: Radius.circular(20)), bottomRight: Radius.circular(20)),
), ),
child: _buildTableBody(state.schedules, context, deviceType)); child: _buildTableBody(state.schedules, context));
} }
if (state is ScheduleError) { if (state is ScheduleError) {
return Center(child: Text(state.error)); return Center(child: Text(state.error));
@ -126,8 +123,7 @@ class _ScheduleTableView extends StatelessWidget {
); );
} }
Widget _buildTableBody( Widget _buildTableBody(List<ScheduleModel> schedules, BuildContext context) {
List<ScheduleModel> schedules, BuildContext context, String deviceType) {
return SizedBox( return SizedBox(
height: 200, height: 200,
child: SingleChildScrollView( child: SingleChildScrollView(
@ -136,8 +132,7 @@ class _ScheduleTableView extends StatelessWidget {
defaultVerticalAlignment: TableCellVerticalAlignment.middle, defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: [ children: [
for (int i = 0; i < schedules.length; i++) for (int i = 0; i < schedules.length; i++)
_buildScheduleRow(schedules[i], i, context, _buildScheduleRow(schedules[i], i, context),
deviceType: deviceType),
], ],
), ),
), ),
@ -160,19 +155,25 @@ class _ScheduleTableView extends StatelessWidget {
} }
TableRow _buildScheduleRow( TableRow _buildScheduleRow(
ScheduleModel schedule, int index, BuildContext context, ScheduleModel schedule, int index, BuildContext context) {
{required String deviceType}) {
return TableRow( return TableRow(
children: [ children: [
Center( Center(
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
onTap: () { onTap: () {
bool temp;
if (schedule.category == 'CUR_2') {
temp = schedule.function.value == 'open' ? true : false;
} else {
temp = schedule.function.value as bool;
}
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
ScheduleUpdateEntryEvent( ScheduleUpdateEntryEvent(
category: schedule.category, category: schedule.category,
scheduleId: schedule.scheduleId, scheduleId: schedule.scheduleId,
functionOn: schedule.function.value, functionOn: temp,
// schedule.function.value,
enable: !schedule.enable, enable: !schedule.enable,
), ),
); );
@ -194,9 +195,8 @@ class _ScheduleTableView extends StatelessWidget {
child: Text(_getSelectedDays( child: Text(_getSelectedDays(
ScheduleModel.parseSelectedDays(schedule.days)))), ScheduleModel.parseSelectedDays(schedule.days)))),
Center(child: Text(formatIsoStringToTime(schedule.time, context))), Center(child: Text(formatIsoStringToTime(schedule.time, context))),
if (deviceType == 'CUR_2') if (schedule.category == 'CUR_2')
Center( Center(child: Text(schedule.function.value))
child: Text(schedule.function.value == true ? 'open' : 'close'))
else else
Center(child: Text(schedule.function.value ? 'On' : 'Off')), Center(child: Text(schedule.function.value ? 'On' : 'Off')),
Center( Center(
@ -206,14 +206,14 @@ class _ScheduleTableView extends StatelessWidget {
TextButton( TextButton(
style: TextButton.styleFrom(padding: EdgeInsets.zero), style: TextButton.styleFrom(padding: EdgeInsets.zero),
onPressed: () { onPressed: () {
ScheduleDialogHelper.showAddScheduleDialog(context, ScheduleDialogHelper.showAddScheduleDialog(
context,
schedule: ScheduleEntry.fromScheduleModel(schedule), schedule: ScheduleEntry.fromScheduleModel(schedule),
isEdit: true, isEdit: true,
deviceType: deviceType) ).then((updatedSchedule) {
.then((updatedSchedule) {
if (updatedSchedule != null) { if (updatedSchedule != null) {
bool temp; bool temp;
if (deviceType == 'CUR_2') { if (schedule.category == 'CUR_2') {
updatedSchedule.function.value == 'open' updatedSchedule.function.value == 'open'
? temp = true ? temp = true
: temp = false; : temp = false;
@ -222,7 +222,6 @@ class _ScheduleTableView extends StatelessWidget {
} }
context.read<ScheduleBloc>().add( context.read<ScheduleBloc>().add(
ScheduleEditEvent( ScheduleEditEvent(
deviceType: deviceType,
scheduleId: schedule.scheduleId, scheduleId: schedule.scheduleId,
category: schedule.category, category: schedule.category,
time: updatedSchedule.time, time: updatedSchedule.time,

View File

@ -41,7 +41,7 @@ class ThreeGangGlassSwitchBloc
emit(ThreeGangGlassSwitchLoading()); emit(ThreeGangGlassSwitchLoading());
try { try {
final status = await DevicesManagementApi().getDeviceStatus(event.deviceId); final status = await DevicesManagementApi().getDeviceStatus(event.deviceId);
_listenToChanges(event.deviceId); _listenToChanges(event.deviceId, emit);
deviceStatus = deviceStatus =
ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status); ThreeGangGlassStatusModel.fromJson(event.deviceId, status.status);
emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus)); emit(ThreeGangGlassSwitchStatusLoaded(deviceStatus));
@ -50,28 +50,42 @@ class ThreeGangGlassSwitchBloc
} }
} }
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription; void _listenToChanges(
String deviceId,
void _listenToChanges(String deviceId) { Emitter<ThreeGangGlassSwitchState> emit,
) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final ref = FirebaseDatabase.instance.ref('device-status/$deviceId');
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async { final stream = ref.onValue;
if (event.snapshot.value == null) return;
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>; stream.listen((DatabaseEvent event) {
final data = event.snapshot.value as Map<dynamic, dynamic>?;
if (data == null) return;
final statusList = <Status>[]; final statusList = <Status>[];
if (data['status'] != null) {
usersMap['status'].forEach((element) { for (var element in data['status']) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList.add(
}); Status(
code: element['code'].toString(),
deviceStatus = value: element['value'].toString(),
ThreeGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList); ),
);
}
}
if (statusList.isNotEmpty) {
final newStatus = ThreeGangGlassStatusModel.fromJson(deviceId, statusList);
if (newStatus != deviceStatus) {
deviceStatus = newStatus;
if (!isClosed) {
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
}
}
}
}); });
} catch (_) {} } catch (e) {
emit(ThreeGangGlassSwitchError('Failed to listen to changes: $e'));
}
} }
void _onStatusUpdated( void _onStatusUpdated(
@ -170,10 +184,4 @@ class ThreeGangGlassSwitchBloc
break; break;
} }
} }
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
} }

View File

@ -111,8 +111,6 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_1', category: 'switch_1',
deviceUuid: deviceId, deviceUuid: deviceId,
countdownCode: 'countdown_1',
deviceType: '3GT',
), ),
)); ));
}, },
@ -129,8 +127,6 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_2', category: 'switch_2',
deviceUuid: deviceId, deviceUuid: deviceId,
countdownCode: 'countdown_2',
deviceType: '3GT',
), ),
)); ));
}, },
@ -147,8 +143,6 @@ class ThreeGangGlassSwitchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_3', category: 'switch_3',
deviceUuid: deviceId, deviceUuid: deviceId,
countdownCode: 'countdown_3',
deviceType: '3GT',
), ),
)); ));
}, },

View File

@ -102,8 +102,6 @@ class LivingRoomDeviceControlsView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_1', category: 'switch_1',
countdownCode: 'countdown_1',
deviceType: '3G',
), ),
)); ));
}, },
@ -120,8 +118,6 @@ class LivingRoomDeviceControlsView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_2', category: 'switch_2',
countdownCode: 'countdown_2',
deviceType: '3G',
), ),
)); ));
}, },
@ -138,8 +134,6 @@ class LivingRoomDeviceControlsView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_3', category: 'switch_3',
countdownCode: 'countdown_3',
deviceType: '3G',
), ),
)); ));
}, },

View File

@ -1,20 +1,21 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/device_status.dart';
import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/factory_reset_model.dart';
import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart'; import 'package:syncrow_web/pages/device_managment/two_g_glass_switch/models/two_gang_glass_status_model.dart';
import 'package:syncrow_web/services/batch_control_devices_service.dart'; import 'package:syncrow_web/services/batch_control_devices_service.dart';
import 'package:syncrow_web/services/control_device_service.dart'; import 'package:syncrow_web/services/control_device_service.dart';
import 'package:syncrow_web/services/devices_mang_api.dart'; import 'package:syncrow_web/services/devices_mang_api.dart';
part 'two_gang_glass_switch_event.dart'; part 'two_gang_glass_switch_event.dart';
part 'two_gang_glass_switch_state.dart'; part 'two_gang_glass_switch_state.dart';
class TwoGangGlassSwitchBloc class TwoGangGlassSwitchBloc
extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> { extends Bloc<TwoGangGlassSwitchEvent, TwoGangGlassSwitchState> {
final String deviceId; final String deviceId;
final ControlDeviceService controlDeviceService; final ControlDeviceService controlDeviceService;
@ -50,28 +51,29 @@
} }
} }
StreamSubscription<DatabaseEvent>? _deviceStatusSubscription;
void _listenToChanges(String deviceId) { void _listenToChanges(String deviceId) {
try { try {
final ref = FirebaseDatabase.instance.ref('device-status/$deviceId'); final ref = FirebaseDatabase.instance.ref(
_deviceStatusSubscription = ref.onValue.listen((DatabaseEvent event) async { 'device-status/$deviceId',
if (event.snapshot.value == null) return; );
final usersMap = event.snapshot.value! as Map<dynamic, dynamic>; ref.onValue.listen((event) {
final eventsMap = event.snapshot.value as Map<dynamic, dynamic>;
final statusList = <Status>[]; List<Status> statusList = [];
eventsMap['status'].forEach((element) {
usersMap['status'].forEach((element) {
statusList.add(Status(code: element['code'], value: element['value'])); statusList.add(Status(code: element['code'], value: element['value']));
}); });
deviceStatus = deviceStatus = TwoGangGlassStatusModel.fromJson(deviceId, statusList);
TwoGangGlassStatusModel.fromJson(usersMap['productUuid'], statusList);
add(StatusUpdated(deviceStatus)); add(StatusUpdated(deviceStatus));
}); });
} catch (_) {} } catch (_) {
log(
'Error listening to changes',
name: 'TwoGangGlassSwitchBloc._listenToChanges',
);
}
} }
Future<void> _onControl( Future<void> _onControl(
@ -168,10 +170,4 @@
break; break;
} }
} }
@override
Future<void> close() {
_deviceStatusSubscription?.cancel();
return super.close();
}
} }

View File

@ -102,8 +102,6 @@ class TwoGangGlassSwitchControlView extends StatelessWidget
builder: (ctx) => BlocProvider.value( builder: (ctx) => BlocProvider.value(
value: BlocProvider.of<TwoGangGlassSwitchBloc>(context), value: BlocProvider.of<TwoGangGlassSwitchBloc>(context),
child: BuildScheduleView( child: BuildScheduleView(
deviceType: '2GT',
countdownCode: 'countdown_1',
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_1', category: 'switch_1',
), ),
@ -120,8 +118,6 @@ class TwoGangGlassSwitchControlView extends StatelessWidget
builder: (ctx) => BlocProvider.value( builder: (ctx) => BlocProvider.value(
value: BlocProvider.of<TwoGangGlassSwitchBloc>(context), value: BlocProvider.of<TwoGangGlassSwitchBloc>(context),
child: BuildScheduleView( child: BuildScheduleView(
deviceType: '2GT',
countdownCode: 'countdown_2',
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_2', category: 'switch_2',
), ),

View File

@ -97,8 +97,6 @@ class TwoGangBatchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_1', category: 'switch_1',
deviceUuid: deviceIds.first, deviceUuid: deviceIds.first,
countdownCode: 'countdown_1',
deviceType: '2G',
), ),
)); ));
}, },
@ -116,8 +114,6 @@ class TwoGangBatchControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
category: 'switch_2', category: 'switch_2',
deviceUuid: deviceIds.first, deviceUuid: deviceIds.first,
countdownCode: 'countdown_2',
deviceType: '2G',
), ),
)); ));
}, },
@ -125,7 +121,10 @@ class TwoGangBatchControlView extends StatelessWidget
subtitle: 'Scheduling', subtitle: 'Scheduling',
iconPath: Assets.scheduling, iconPath: Assets.scheduling,
), ),
// FirmwareUpdateWidget(
// deviceId: deviceIds.first,
// version: 12,
// ),
FactoryResetWidget(callFactoryReset: () { FactoryResetWidget(callFactoryReset: () {
context.read<TwoGangSwitchBloc>().add( context.read<TwoGangSwitchBloc>().add(
TwoGangFactoryReset( TwoGangFactoryReset(

View File

@ -103,8 +103,6 @@ class TwoGangDeviceControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_1', category: 'switch_1',
countdownCode: 'countdown_1',
deviceType: '2G',
), ),
)); ));
}, },
@ -127,8 +125,6 @@ class TwoGangDeviceControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: deviceId, deviceUuid: deviceId,
category: 'switch_2', category: 'switch_2',
countdownCode: 'countdown_2',
deviceType: '2G',
), ),
)); ));
}, },

View File

@ -18,10 +18,9 @@ class ScheduleDialogHelper {
ScheduleEntry? schedule, ScheduleEntry? schedule,
bool isEdit = false, bool isEdit = false,
String? code, String? code,
required String deviceType,
}) { }) {
bool temp; bool temp;
if (deviceType == 'CUR_2') { if (schedule?.category == 'CUR_2') {
temp = schedule!.function.value == 'open' ? true : false; temp = schedule!.function.value == 'open' ? true : false;
} else { } else {
temp = schedule!.function.value; temp = schedule!.function.value;
@ -104,7 +103,8 @@ class ScheduleDialogHelper {
setState(() => selectedDays[i] = v); setState(() => selectedDays[i] = v);
}), }),
const SizedBox(height: 16), const SizedBox(height: 16),
_buildFunctionSwitch(deviceType, ctx, functionOn!, (v) { _buildFunctionSwitch(schedule!.category, ctx, functionOn!,
(v) {
setState(() => functionOn = v); setState(() => functionOn = v);
}), }),
], ],
@ -124,25 +124,28 @@ class ScheduleDialogHelper {
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
dynamic temp; dynamic temp;
if (deviceType == 'CUR_2') { if (schedule?.category == 'CUR_2') {
temp = functionOn! ? 'open' : 'close'; temp = functionOn! ? 'open' : 'close';
} else { } else {
temp = functionOn; temp = functionOn;
} }
print(temp);
final entry = ScheduleEntry( final entry = ScheduleEntry(
category: schedule?.category ?? 'switch_1', category: schedule?.category ?? 'switch_1',
time: _formatTimeOfDayToISO(selectedTime), time: _formatTimeOfDayToISO(selectedTime),
function: Status( function: Status(
code: code ?? 'switch_1', code: code ?? 'switch_1',
value: temp, value: temp,
// functionOn,
), ),
days: _convertSelectedDaysToStrings(selectedDays), days: _convertSelectedDaysToStrings(selectedDays),
scheduleId: schedule.scheduleId, scheduleId: schedule?.scheduleId,
); );
Navigator.pop(ctx, entry); Navigator.pop(ctx, entry);
}, },
child: const Text('Save'), child: const Text('Save'),
)), ),
),
], ],
); );
}, },

View File

@ -84,8 +84,6 @@ class WaterHeaterDeviceControlView extends StatelessWidget
child: BuildScheduleView( child: BuildScheduleView(
deviceUuid: device.uuid ?? '', deviceUuid: device.uuid ?? '',
category: 'switch_1', category: 'switch_1',
countdownCode: 'countdown_1',
deviceType: 'WH',
), ),
)); ));
}, },