Split separate devices

This commit is contained in:
hodasemi 2023-10-13 23:33:13 +02:00
parent 65b9990394
commit ada210206f
8 changed files with 459 additions and 308 deletions

View file

@ -1,307 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'constants.dart';
class Category {
Category(this.name) : devices = [];
final String name;
List<Device> devices;
static Future<List<Category>> fetch() async {
final response = await http.get(Uri.parse("${Constants.BASE_URL}/devices"));
if (response.statusCode != 200) {
throw Exception("Failed to fetch devices");
}
final List<Category> categories = [];
final Map<String, List<dynamic>> json =
Map.castFrom(jsonDecode(jsonDecode(response.body)));
for (MapEntry<String, List<dynamic>> category_entry in json.entries) {
final Category category = Category(category_entry.key);
for (dynamic device_info_dyn in category_entry.value) {
final Map<String, dynamic> device_info = device_info_dyn;
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<String, dynamic> device_state =
Map.castFrom(jsonDecode(jsonDecode(response.body)));
final Device device = Device(
device_id,
device_descriptor,
device_state["led"],
device_state["power"],
device_state["power_draw"],
power_control && device_state["power_draw"] < 15,
);
category.devices.add(device);
}
categories.add(category);
}
return categories;
}
}
class CategoryWidget extends StatelessWidget {
final Category category;
CategoryWidget({super.key, required this.category});
String Name() {
return category.name
.split('_')
.map((s) => "${s[0].toUpperCase()}${s.substring(1)}")
.join(' ');
}
@override
Widget build(BuildContext context) {
const double header_height = 40;
const double info_height = 30;
const double x_offset = 0.6;
return Wrap(
spacing: 25,
runSpacing: 25,
children: category.devices.map((device) {
return Table(
border: TableBorder.all(),
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
columnWidths: const {
0: FixedColumnWidth(200.0),
1: FixedColumnWidth(200.0),
},
children: [
TableRow(
decoration: BoxDecoration(
color: Colors.deepPurple[200],
),
children: [
SizedBox(
height: header_height,
child: Stack(
children: [
Align(
child: Text(
textAlign: TextAlign.center,
device.device_descriptor ??
device.device_id)),
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () {},
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.025, 0.0),
child: DeviceLed(
device_id: device.device_id,
led_state: device.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.1, 0.0),
child: DevicePower(
device_id: device.device_id,
power_state: device.power_state,
power_control: device.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: Text(
textAlign: TextAlign.center,
"${device.power_draw} W"))
]))
])
]);
}).toList(),
);
}
}
class Device {
final String device_id;
String? device_descriptor;
bool led_state;
bool power_state;
double power_draw;
bool power_control;
Device(this.device_id, this.device_descriptor, this.led_state,
this.power_state, this.power_draw, this.power_control);
}
class DeviceLed extends StatefulWidget {
final String device_id;
final bool led_state;
DeviceLed({super.key, required this.device_id, required this.led_state});
@override
State<DeviceLed> createState() => DeviceLedState(device_id, led_state);
}
class DeviceLedState extends State<DeviceLed> {
final String device_id;
bool led_state;
String _led_state_info;
DeviceLedState(this.device_id, this.led_state)
: this._led_state_info = led_state ? "On" : "Off";
void _toggle_led_state() {
String target_state;
if (led_state) {
target_state = "off";
} else {
target_state = "on";
}
change_plug_state(device_id, "led", target_state).then((device_state) {
led_state = device_state["led"];
setState(() {
_led_state_info = led_state ? "On" : "Off";
});
});
}
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: _toggle_led_state,
child: Text(textAlign: TextAlign.center, _led_state_info));
}
}
class DevicePower extends StatefulWidget {
final String device_id;
final bool power_state;
final bool power_control;
DevicePower(
{super.key,
required this.device_id,
required this.power_state,
required this.power_control});
@override
State<DevicePower> createState() =>
DevicePowerState(device_id, power_state, power_control);
}
class DevicePowerState extends State<DevicePower> {
final String device_id;
bool power_state;
String _power_state_info;
bool power_control;
DevicePowerState(this.device_id, this.power_state, this.power_control)
: this._power_state_info = power_state ? "On" : "Off";
void _toggle_power_state() {
String target_state;
if (power_state) {
target_state = "off";
} else {
target_state = "on";
}
change_plug_state(device_id, "power", target_state).then((device_state) {
power_state = device_state["power"];
power_control = device_state["power_draw"] < 15;
setState(() {
_power_state_info = power_state ? "On" : "Off";
});
});
}
@override
Widget build(BuildContext context) {
if (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<Map<String, dynamic>> 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)));
}

View file

@ -0,0 +1,133 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../constants.dart';
import 'dish_washer.dart';
import 'plug.dart';
import 'temp_humid.dart';
import 'thermostat.dart';
import 'washing_machine.dart';
class Category {
Category(this.name) : devices = [];
final String name;
List<Device> devices;
static Future<List<Category>> fetch() async {
final response = await http.get(Uri.parse("${Constants.BASE_URL}/devices"));
if (response.statusCode != 200) {
throw Exception("Failed to fetch devices");
}
final List<Category> categories = [];
final Map<String, List<dynamic>> json =
Map.castFrom(jsonDecode(jsonDecode(response.body)));
// create plugs
{
final Category category = Category('plugs');
final List<dynamic> plugs = json['plugs']!;
for (dynamic device_info_dyn in plugs) {
final Map<String, dynamic> device_info = device_info_dyn;
category.devices.add(await Plug.create(device_info));
}
categories.add(category);
}
// create thermostat
{
final Category category = Category('thermostat');
final List<dynamic> thermostats = json['thermostat']!;
for (dynamic device_info_dyn in thermostats) {
final Map<String, dynamic> device_info = device_info_dyn;
category.devices.add(await Thermostat.create(device_info));
}
categories.add(category);
}
// create temperature_and_humidity
{
final Category category = Category('temperature_and_humidity');
final List<dynamic> temperature_and_humidities =
json['temperature_and_humidity']!;
for (dynamic device_info_dyn in temperature_and_humidities) {
final Map<String, dynamic> device_info = device_info_dyn;
category.devices.add(await TemperatureHumidity.create(device_info));
}
categories.add(category);
}
// create dish_washer
{
final Category category = Category('dish_washer');
final List<dynamic> dish_washer = json['dish_washer']!;
for (dynamic device_info_dyn in dish_washer) {
final Map<String, dynamic> device_info = device_info_dyn;
category.devices.add(await DishWasher.create(device_info));
}
categories.add(category);
}
// create washing_machines
{
final Category category = Category('washing_machines');
final List<dynamic> washing_machines = json['washing_machines']!;
for (dynamic device_info_dyn in washing_machines) {
final Map<String, dynamic> device_info = device_info_dyn;
category.devices.add(await WashingMachine.create(device_info));
}
categories.add(category);
}
return categories;
}
}
class CategoryWidget extends StatelessWidget {
final Category category;
CategoryWidget({super.key, required this.category});
String Name() {
return category.name
.split('_')
.map((s) => "${s[0].toUpperCase()}${s.substring(1)}")
.join(' ');
}
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 25,
runSpacing: 25,
children:
category.devices.map((device) => device.create_widget()).toList(),
);
}
}
abstract class Device {
Widget create_widget();
}

View file

@ -0,0 +1,14 @@
import 'package:flutter/src/widgets/framework.dart';
import 'devices.dart';
class DishWasher extends Device {
static Future<DishWasher> create(Map<String, dynamic> device_info) async {
throw UnimplementedError();
}
@override
Widget create_widget() {
throw UnimplementedError();
}
}

View file

@ -0,0 +1,265 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../constants.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<Plug> create(Map<String, dynamic> 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<String, dynamic> 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() {
const double header_height = 40;
const double info_height = 30;
const double info_width = 60;
const double x_offset = 0.9;
const double radius = 15;
return Table(
border: TableBorder(borderRadius: BorderRadius.circular(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(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: () {},
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;
final bool led_state;
PlugLed({super.key, required this.device_id, required this.led_state});
@override
State<PlugLed> createState() => PlugLedState(device_id, led_state);
}
class PlugLedState extends State<PlugLed> {
final String device_id;
bool led_state;
String _led_state_info;
PlugLedState(this.device_id, this.led_state)
: this._led_state_info = led_state ? "On" : "Off";
void _toggle_led_state() {
String target_state;
if (led_state) {
target_state = "off";
} else {
target_state = "on";
}
change_plug_state(device_id, "led", target_state).then((device_state) {
led_state = device_state["led"];
setState(() {
_led_state_info = led_state ? "On" : "Off";
});
});
}
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: _toggle_led_state,
child: Text(textAlign: TextAlign.center, _led_state_info));
}
}
class PlugPower extends StatefulWidget {
final String device_id;
final bool power_state;
final bool power_control;
PlugPower(
{super.key,
required this.device_id,
required this.power_state,
required this.power_control});
@override
State<PlugPower> createState() =>
PlugPowerState(device_id, power_state, power_control);
}
class PlugPowerState extends State<PlugPower> {
final String device_id;
bool power_state;
String _power_state_info;
bool power_control;
PlugPowerState(this.device_id, this.power_state, this.power_control)
: this._power_state_info = power_state ? "On" : "Off";
void _toggle_power_state() {
String target_state;
if (power_state) {
target_state = "off";
} else {
target_state = "on";
}
change_plug_state(device_id, "power", target_state).then((device_state) {
power_state = device_state["power"];
power_control = device_state["power_draw"] < 15;
setState(() {
_power_state_info = power_state ? "On" : "Off";
});
});
}
@override
Widget build(BuildContext context) {
if (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<Map<String, dynamic>> 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)));
}

View file

@ -0,0 +1,16 @@
import 'package:flutter/src/widgets/framework.dart';
import 'devices.dart';
class TemperatureHumidity extends Device {
static Future<TemperatureHumidity> create(
Map<String, dynamic> device_info) async {
throw UnimplementedError();
}
@override
Widget create_widget() {
// TODO: implement create_widget
throw UnimplementedError();
}
}

View file

@ -0,0 +1,15 @@
import 'package:flutter/src/widgets/framework.dart';
import 'devices.dart';
class Thermostat extends Device {
static Future<Thermostat> create(Map<String, dynamic> device_info) async {
throw UnimplementedError();
}
@override
Widget create_widget() {
// TODO: implement create_widget
throw UnimplementedError();
}
}

View file

@ -0,0 +1,15 @@
import 'package:flutter/src/widgets/framework.dart';
import 'devices.dart';
class WashingMachine extends Device {
static Future<WashingMachine> create(Map<String, dynamic> device_info) async {
throw UnimplementedError();
}
@override
Widget create_widget() {
// TODO: implement create_widget
throw UnimplementedError();
}
}

View file

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'constants.dart';
import 'devices.dart';
import 'devices/devices.dart';
void main() {
runApp(const MyApp());