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 { final String device_id; String? device_descriptor; bool led_state; bool power_state; double power_draw; bool power_control; static Future create(Map device_info) async { final String device_id = device_info['id']; final String? device_descriptor = device_info['desc']; final bool power_control = device_info['toggle']; final response = await http .get(Uri.parse("${Constants.BASE_URL}/plug_state/$device_id")); if (response.statusCode != 200) { throw Exception("Failed to fetch plug_state for $device_id"); } final Map device_state = Map.castFrom(jsonDecode(jsonDecode(response.body))); return Plug( device_id, device_descriptor, device_state["led"], device_state["power"], device_state["power_draw"], power_control && device_state["power_draw"] < 15, ); } Plug(this.device_id, this.device_descriptor, this.led_state, this.power_state, this.power_draw, this.power_control); @override Widget create_widget(BuildContext context) { const double header_height = 40; const double info_height = 30; const double info_width = 60; const double x_offset = 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: header_height, 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: info_height, 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(x_offset, 0.0), child: SizedBox( width: info_width, child: PlugLed( device_id: device_id, led_state: led_state))), ])) ]), TableRow(children: [ SizedBox( height: info_height, 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(x_offset, 0.0), child: SizedBox( width: info_width, child: PlugPower( device_id: device_id, power_state: power_state, power_control: power_control))), ])) ]), TableRow(children: [ SizedBox( height: info_height, 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(x_offset, 0.0), child: SizedBox( width: info_width, child: Text( textAlign: TextAlign.center, "$power_draw W"))) ])) ]) ]); } } class PlugLed extends StatefulWidget { final String device_id; bool led_state; PlugLed({super.key, required this.device_id, required this.led_state}); @override State createState() => PlugLedState(); } class PlugLedState extends State { String _led_state_info = ""; void _toggle_led_state() { String target_state; if (widget.led_state) { target_state = "off"; } else { target_state = "on"; } change_plug_state(widget.device_id, "led", target_state) .then((device_state) { widget.led_state = device_state["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 { final String device_id; bool power_state; bool power_control; PlugPower( {super.key, required this.device_id, required this.power_state, required this.power_control}); @override State createState() => PlugPowerState(); } class PlugPowerState extends State { String _power_state_info = ""; void _toggle_power_state() { String target_state; if (widget.power_state) { target_state = "off"; } else { target_state = "on"; } change_plug_state(widget.device_id, "power", target_state) .then((device_state) { widget.power_state = device_state["power"]; widget.power_control = device_state["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 device_id, String module, String state) async { var response = await http.post( Uri.parse("${Constants.BASE_URL}/plug/$device_id/${module}_$state")); if (response.statusCode != 200) { throw Exception("Failed to post new state"); } response = await http.get(Uri.parse("${Constants.BASE_URL}/plug_state/$device_id")); if (response.statusCode != 200) { throw Exception("Failed to fetch plug_state for $device_id"); } return Map.castFrom(jsonDecode(jsonDecode(response.body))); }