import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import '../constants.dart'; import '../states/plug_settings.dart'; import 'devices.dart'; class Plug extends Device { Plug(this.device_id, this.device_descriptor, this.led_state, this.power_state, this.power_draw, this.power_control); final String device_id; String? device_descriptor; bool led_state; bool power_state; double power_draw; bool power_control; static Future create(Map deviceInfo) async { final String deviceId = deviceInfo['id']; final String? deviceDescriptor = deviceInfo['desc']; final bool powerControl = deviceInfo['toggle']; final response = await http .get(Uri.parse("${Constants.BASE_URL}/plug_state/$deviceId")); if (response.statusCode != 200) { throw Exception("Failed to fetch plug_state for $deviceId"); } final Map deviceState = Map.castFrom(jsonDecode(jsonDecode(response.body))); return Plug( deviceId, deviceDescriptor, deviceState["led"], deviceState["power"], deviceState["power_draw"], powerControl && deviceState["power_draw"] < 15, ); } @override Widget create_widget(BuildContext context) { const double headerHeight = 40; const double infoHeight = 30; const double infoWidth = 60; const double xOffset = 0.9; return Table( border: TableBorder( borderRadius: BorderRadius.circular(Constants.TABLE_RADIUS)), defaultVerticalAlignment: TableCellVerticalAlignment.middle, columnWidths: const { 0: FixedColumnWidth(200.0), 1: FixedColumnWidth(200.0), }, children: [ TableRow( decoration: BoxDecoration( color: Colors.deepPurple[200], borderRadius: BorderRadius.circular(Constants.TABLE_RADIUS), ), children: [ SizedBox( height: headerHeight, child: Stack( children: [ Align( child: Text( style: const TextStyle( fontWeight: FontWeight.bold), textAlign: TextAlign.center, device_descriptor ?? device_id)), Align( alignment: Alignment.centerRight, child: IconButton( onPressed: () { Navigator.of(context).pushReplacementNamed( '/plug_settings', arguments: PlugSettingsArguments( device_id, device_descriptor)); }, icon: const Icon(Icons.settings)), ), ], )) ]), TableRow(children: [ SizedBox( height: infoHeight, child: Stack(children: [ const Align( alignment: Alignment(-0.9, 0.0), child: Text( style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, "LED")), Align( alignment: const Alignment(xOffset, 0.0), child: SizedBox( width: infoWidth, child: PlugLed( device_id: device_id, led_state: led_state))), ])) ]), TableRow(children: [ SizedBox( height: infoHeight, child: Stack(children: [ const Align( alignment: Alignment(-0.9, 0.0), child: Text( style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, "Power")), Align( alignment: const Alignment(xOffset, 0.0), child: SizedBox( width: infoWidth, child: PlugPower( device_id: device_id, power_state: power_state, power_control: power_control))), ])) ]), TableRow(children: [ SizedBox( height: infoHeight, child: Stack(children: [ const Align( alignment: Alignment(-0.9, 0.0), child: Text( style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, "Power Draw")), Align( alignment: const Alignment(xOffset, 0.0), child: SizedBox( width: infoWidth, child: Text( textAlign: TextAlign.center, "$power_draw W"))) ])) ]) ]); } } class PlugLed extends StatefulWidget { PlugLed({super.key, required this.device_id, required this.led_state}); final String device_id; bool led_state; @override State createState() => PlugLedState(); } class PlugLedState extends State { String _led_state_info = ""; void _toggle_led_state() { String targetState; if (widget.led_state) { targetState = "off"; } else { targetState = "on"; } change_plug_state(widget.device_id, "led", targetState) .then((deviceState) { widget.led_state = deviceState["led"]; setState(() { _led_state_info = widget.led_state ? "On" : "Off"; }); }); } @override Widget build(BuildContext context) { _led_state_info = widget.led_state ? "On" : "Off"; return TextButton( onPressed: _toggle_led_state, child: Text(textAlign: TextAlign.center, _led_state_info)); } } class PlugPower extends StatefulWidget { PlugPower( {super.key, required this.device_id, required this.power_state, required this.power_control}); final String device_id; bool power_state; bool power_control; @override State createState() => PlugPowerState(); } class PlugPowerState extends State { String _power_state_info = ""; void _toggle_power_state() { String targetState; if (widget.power_state) { targetState = "off"; } else { targetState = "on"; } change_plug_state(widget.device_id, "power", targetState) .then((deviceState) { widget.power_state = deviceState["power"]; widget.power_control = deviceState["power_draw"] < 15; setState(() { _power_state_info = widget.power_state ? "On" : "Off"; }); }); } @override Widget build(BuildContext context) { _power_state_info = widget.power_state ? "On" : "Off"; if (widget.power_control) { return TextButton( onPressed: _toggle_power_state, child: Text(textAlign: TextAlign.center, _power_state_info)); } else { return Text(_power_state_info, textAlign: TextAlign.center); } } } Future> change_plug_state( String deviceId, String module, String state) async { var response = await http.post( Uri.parse("${Constants.BASE_URL}/plug/$deviceId/${module}_$state")); if (response.statusCode != 200) { throw Exception("Failed to post new state"); } response = await http.get(Uri.parse("${Constants.BASE_URL}/plug_state/$deviceId")); if (response.statusCode != 200) { throw Exception("Failed to fetch plug_state for $deviceId"); } return Map.castFrom(jsonDecode(jsonDecode(response.body))); }