aboutsummaryrefslogtreecommitdiffstats
path: root/lib/vehicle-signals
diff options
context:
space:
mode:
authorScott Murray <scott.murray@konsulko.com>2022-12-28 15:05:26 -0500
committerScott Murray <scott.murray@konsulko.com>2022-12-29 06:35:38 +0000
commit4fbd3fdb9e01c197d972b78961f0d033534a5cc7 (patch)
treedef7bfc0d0f11746006439b33019b61dfc16e1b8 /lib/vehicle-signals
parente21709c9601209e26d09dea0a45e37f0636bb605 (diff)
Add volume control to bottom panel
Changes: - Import a reworked version of the KUKSA.val client code from the Flutter dashboard app, with the aggregated signal Riverpod provider replaced with per-signal providers for the signal the homescreen needs and a couple of temperature ones it might use. Using separate providers is more in-line with recommended Riverpod best practices. - Various tweaks to enable using Riverpod. - Split the bottom panel out into its own widget, and add a stack in it to layer the default logo panel with the volume control slider, which has been added as a new widget definition to provide the hook to drive timer based lowering behavior like the Qt homescreen does. - The KUKSA.val connection widget has been added to the bottom panel rather than overriding the top-level widget as in the dashboard and HVAC apps. This seems preferable with respect to still providing some functionality in the event KUKSA.val is unavailable. - Remove the old demo dashboard and HVAC pages that are now unused, along with the image assets they needed, to allow cleaning up pubspec.yaml and ease maintenance. Bug-AGL: SPEC-4659 Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: I5d9180a3461948a58321564e71134c4961ce0ef7
Diffstat (limited to 'lib/vehicle-signals')
-rw-r--r--lib/vehicle-signals/viss_config.dart29
-rw-r--r--lib/vehicle-signals/viss_connected_widget.dart65
-rw-r--r--lib/vehicle-signals/viss_connection_widget.dart41
-rw-r--r--lib/vehicle-signals/viss_methods.dart116
-rw-r--r--lib/vehicle-signals/vss_path.dart44
-rw-r--r--lib/vehicle-signals/vss_providers.dart130
6 files changed, 425 insertions, 0 deletions
diff --git a/lib/vehicle-signals/viss_config.dart b/lib/vehicle-signals/viss_config.dart
new file mode 100644
index 0000000..c2be5ee
--- /dev/null
+++ b/lib/vehicle-signals/viss_config.dart
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: Apache-2.0
+//import 'dart:convert';
+import 'dart:io';
+
+import 'package:flutter_homescreen/config.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+//import 'package:http/http.dart' as http;
+
+final sockConnectprovider = FutureProvider.family<WebSocket, HttpClient>(
+ (ref, client) => connect(client, ref));
+
+Future<HttpClient> initializeClient() async {
+ SecurityContext ctx = SecurityContext.defaultContext;
+
+ HttpClient client = HttpClient(context: ctx)
+ ..findProxy = null
+ ..badCertificateCallback = (cert, host, port) {
+ return true;
+ };
+ return client;
+}
+
+Future<WebSocket> connect(HttpClient client, ref) async {
+ final config = ref.read(ConfigStateprovider);
+ WebSocket socket = await WebSocket.connect(
+ "wss://${config.hostname}:${config.port}",
+ customClient: client);
+ return socket;
+}
diff --git a/lib/vehicle-signals/viss_connected_widget.dart b/lib/vehicle-signals/viss_connected_widget.dart
new file mode 100644
index 0000000..dd3e4aa
--- /dev/null
+++ b/lib/vehicle-signals/viss_connected_widget.dart
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: Apache-2.0
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter_homescreen/vehicle-signals/viss_config.dart';
+import 'package:flutter_homescreen/vehicle-signals/viss_methods.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_homescreen/vehicle-signals/vss_providers.dart';
+
+class VISServerConnectedWidget extends ConsumerStatefulWidget {
+ const VISServerConnectedWidget(
+ {Key? key, required this.client, required this.socket})
+ : super(key: key);
+ final WebSocket socket;
+ final HttpClient client;
+
+ @override
+ ConsumerState<VISServerConnectedWidget> createState() =>
+ _VISServerConnectedWidgetState();
+}
+
+class _VISServerConnectedWidgetState
+ extends ConsumerState<VISServerConnectedWidget> {
+ late Timer _timer;
+
+ void _updateSocket() {
+ ref.read(VISServerSocketProvider.notifier).update(widget.socket);
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ VISS.init(widget.socket, ref);
+ Future.delayed(Duration.zero, () => _updateSocket());
+ _timer = Timer.periodic(const Duration(seconds: 2), (timer) {
+ if (widget.socket.readyState == 3) {
+ ref.refresh(sockConnectprovider(widget.client));
+ }
+ });
+ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
+ widget.socket.listen(
+ (data) {
+ VISS.parseData(ref, data);
+ },
+ onError: (e, stk) {
+ print(e.toString());
+ ref.refresh(sockConnectprovider(widget.client));
+ },
+ );
+ });
+ }
+
+ @override
+ void dispose() {
+ super.dispose();
+ _timer.cancel();
+ widget.socket.close(786887, "Connection lost with server!");
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Container();
+ }
+}
diff --git a/lib/vehicle-signals/viss_connection_widget.dart b/lib/vehicle-signals/viss_connection_widget.dart
new file mode 100644
index 0000000..a2abee7
--- /dev/null
+++ b/lib/vehicle-signals/viss_connection_widget.dart
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: Apache-2.0
+import 'dart:io';
+import 'package:flutter_homescreen/vehicle-signals/viss_config.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import 'viss_connected_widget.dart';
+
+class VISServerConnectionWidget extends ConsumerWidget {
+ VISServerConnectionWidget({Key? key, required this.client}) : super(key: key);
+ final HttpClient client;
+ late WebSocket socket;
+
+ @override
+ Widget build(BuildContext context, ref) {
+ final sockConnect = ref.watch(sockConnectprovider(client));
+
+ return sockConnect.when(
+ data: (socket) {
+ this.socket = socket;
+ this.socket.pingInterval = const Duration(seconds: 2);
+ return VISServerConnectedWidget(client: client, socket: this.socket);
+ },
+ error: (e, stk) {
+ print(e);
+ ref.refresh(sockConnectprovider(client));
+ return Container(
+ child: const Text('Connection Error',
+ style: TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ color: Colors.white)));
+ },
+ loading: () => Container(
+ child: Text('Connected',
+ style: TextStyle(
+ fontSize: 20,
+ fontWeight: FontWeight.bold,
+ color: Colors.white))));
+ }
+}
diff --git a/lib/vehicle-signals/viss_methods.dart b/lib/vehicle-signals/viss_methods.dart
new file mode 100644
index 0000000..8adcc80
--- /dev/null
+++ b/lib/vehicle-signals/viss_methods.dart
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: Apache-2.0
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:flutter_homescreen/vehicle-signals/vss_providers.dart';
+import 'package:flutter_homescreen/vehicle-signals/vss_path.dart';
+import 'package:flutter_homescreen/config.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+class VISS {
+ static const requestId = "test-id";
+
+ static void init(WebSocket socket, WidgetRef ref) {
+ authorize(socket, ref);
+ subscribe(socket, ref, VSSPath.vehicleMediaVolume);
+ }
+
+ static void update(WebSocket socket, WidgetRef ref) {
+ get(socket, ref, VSSPath.vehicleMediaVolume);
+ }
+
+ static void authorize(WebSocket socket, WidgetRef ref) {
+ final config = ref.read(ConfigStateprovider);
+
+ Map<String, dynamic> map = {
+ "action": "authorize",
+ "tokens": config.kuksaAuthToken,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void get(WebSocket socket, WidgetRef ref, String path) {
+ final config = ref.read(ConfigStateprovider);
+
+ Map<String, dynamic> map = {
+ "action": "get",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void set(WebSocket socket, WidgetRef ref, String path, String value) {
+ final config = ref.read(ConfigStateprovider);
+ Map<String, dynamic> map = {
+ "action": "set",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId,
+ "value": value
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void subscribe(WebSocket socket, WidgetRef ref, String path) {
+ final config = ref.read(ConfigStateprovider);
+
+ Map<String, dynamic> map = {
+ "action": "subscribe",
+ "tokens": config.kuksaAuthToken,
+ "path": path,
+ "requestId": requestId
+ };
+ socket.add(jsonEncode(map));
+ }
+
+ static void parseData(WidgetRef ref, String data) {
+ Map<String, dynamic> dataMap = jsonDecode(data);
+ if (dataMap["action"] == "subscription" || dataMap["action"] == "get") {
+ if (dataMap.containsKey("data")) {
+ if ((dataMap["data"] as Map<String, dynamic>).containsKey("dp") &&
+ (dataMap["data"] as Map<String, dynamic>).containsKey("path")) {
+ String path = dataMap["data"]["path"];
+ Map<String, dynamic> dp = dataMap["data"]["dp"];
+ if (dp.containsKey("value")) {
+ if (dp["value"] != "---") {
+ switch (path) {
+ case VSSPath.vehicleMediaVolume:
+ ref
+ .read(vehicleSignalMediaVolumeProvider.notifier)
+ .update(volume: dp["value"]);
+ break;
+ case VSSPath.vehicleInsideTemperature:
+ ref
+ .read(vehicleSignalInsideTempProvider.notifier)
+ .update(temp: dp["value"]);
+ break;
+ case VSSPath.vehicleOutsideTemperature:
+ ref
+ .read(vehicleSignalOutsideTempProvider.notifier)
+ .update(temp: dp["value"]);
+ break;
+ default:
+ break;
+ }
+ } else {
+ print("ERROR: Invalid VIS response, data not available");
+ }
+ } else {
+ print("ERROR: Invalid VIS response, no 'value' key");
+ }
+ } else if ((!dataMap["data"] as Map<String, dynamic>)
+ .containsKey("path")) {
+ print("ERROR: Invalid VIS response, no 'path' key");
+ } else if ((dataMap["data"] as Map<String, dynamic>)
+ .containsKey("dp")) {
+ print("ERROR: Invalid VIS response, no 'dp' key");
+ }
+ } else {
+ print("ERROR: Invalid VIS response, no 'data' key");
+ }
+ }
+ }
+}
diff --git a/lib/vehicle-signals/vss_path.dart b/lib/vehicle-signals/vss_path.dart
new file mode 100644
index 0000000..3bfc9c1
--- /dev/null
+++ b/lib/vehicle-signals/vss_path.dart
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+class VSSPath {
+ static const String vehicleSpeed = "Vehicle.Speed";
+
+ static const String vehicleEngineRPM =
+ "Vehicle.Powertrain.CombustionEngine.Speed";
+
+ static const String vehicleFuelLevel = "Vehicle.Powertrain.FuelSystem.Level";
+
+ static const String vehicleInsideTemperature =
+ "Vehicle.Cabin.HVAC.AmbientAirTemperature";
+
+ static const String vehicleOutsideTemperature =
+ "Vehicle.Exterior.AirTemperature";
+
+ static const String vehicleFrontLeftTire =
+ "Vehicle.Chassis.Axle.Row1.Wheel.Left.Tire.Pressure";
+
+ static const String vehicleFrontRightTire =
+ "Vehicle.Chassis.Axle.Row1.Wheel.Right.Tire.Pressure";
+
+ static const String vehicleRearLeftTire =
+ "Vehicle.Chassis.Axle.Row2.Wheel.Left.Tire.Pressure";
+
+ static const String vehicleRearRightTire =
+ "Vehicle.Chassis.Axle.Row2.Wheel.Right.Tire.Pressure";
+
+ static const String vehicleIsChildLockActiveLeft =
+ "Vehicle.Cabin.Door.Row2.Left.IsChildLockActive";
+
+ static const String vehicleIsChildLockActiveRight =
+ "Vehicle.Cabin.Door.Row2.Right.IsChildLockActive";
+
+ static const String vehicleCurrentLongitude =
+ "Vehicle.CurrentLocation.Longitude";
+
+ static const String vehicleCurrentLatitude =
+ "Vehicle.CurrentLocation.Latitude";
+
+ static const String vehicleFuelRate = "Vehicle.OBD.FuelRate";
+
+ static const String vehicleMediaVolume =
+ "Vehicle.Cabin.Infotainment.Media.Volume";
+}
diff --git a/lib/vehicle-signals/vss_providers.dart b/lib/vehicle-signals/vss_providers.dart
new file mode 100644
index 0000000..630a273
--- /dev/null
+++ b/lib/vehicle-signals/vss_providers.dart
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: Apache-2.0
+//import 'dart:ffi';
+import 'dart:io';
+import 'package:meta/meta.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+@immutable
+class VISServerSocket {
+ const VISServerSocket({required this.socket});
+
+ final WebSocket? socket;
+
+ VISServerSocket copyWith({WebSocket? socket}) {
+ return VISServerSocket(socket: socket ?? this.socket);
+ }
+}
+
+class VISServerSocketNotifier extends StateNotifier<VISServerSocket> {
+ VISServerSocketNotifier() : super(_initialValue);
+
+ static final VISServerSocket _initialValue = VISServerSocket(socket: null);
+
+ void update(WebSocket socket) {
+ state = state.copyWith(socket: socket);
+ }
+}
+
+final VISServerSocketProvider =
+ StateNotifierProvider<VISServerSocketNotifier, VISServerSocket>((ref) {
+ return VISServerSocketNotifier();
+});
+
+// Media Volume
+
+@immutable
+class VehicleSignalMediaVolume {
+ const VehicleSignalMediaVolume({required this.volume});
+
+ final int volume;
+
+ VehicleSignalMediaVolume copyWith({int? volume}) {
+ return VehicleSignalMediaVolume(volume: volume ?? this.volume);
+ }
+}
+
+class VehicleSignalMediaVolumeNotifier
+ extends StateNotifier<VehicleSignalMediaVolume> {
+ VehicleSignalMediaVolumeNotifier() : super(_initialValue);
+
+ static final VehicleSignalMediaVolume _initialValue =
+ VehicleSignalMediaVolume(volume: 50);
+
+ void update({int? volume}) {
+ int? n = volume;
+ if (n != null) {
+ n = n.toUnsigned(8);
+ if (n > 100) n = 100;
+ }
+ state = state.copyWith(volume: n);
+ }
+}
+
+//final vehicleSignalMediaVolumeProvider = StateNotifierProvider<
+// VehicleSignalMediaVolumeNotifier, VehicleSignalMediaVolume>((ref) {
+// return VehicleSignalMediaVolumeNotifier();
+//});
+
+final vehicleSignalMediaVolumeProvider = StateNotifierProvider<
+ VehicleSignalMediaVolumeNotifier,
+ VehicleSignalMediaVolume>((ref) => VehicleSignalMediaVolumeNotifier());
+
+// Inside Temperature
+
+@immutable
+class VehicleSignalInsideTemp {
+ const VehicleSignalInsideTemp({required this.temp});
+
+ final double temp;
+
+ VehicleSignalInsideTemp copyWith({double? temp}) {
+ return VehicleSignalInsideTemp(temp: temp ?? this.temp);
+ }
+}
+
+class VehicleSignalInsideTempNotifier
+ extends StateNotifier<VehicleSignalInsideTemp> {
+ VehicleSignalInsideTempNotifier() : super(_initialValue);
+
+ static final VehicleSignalInsideTemp _initialValue =
+ VehicleSignalInsideTemp(temp: 25);
+
+ void update({double? temp}) {
+ state = state.copyWith(temp: temp);
+ }
+}
+
+final vehicleSignalInsideTempProvider = StateNotifierProvider<
+ VehicleSignalInsideTempNotifier, VehicleSignalInsideTemp>(
+ (ref) => VehicleSignalInsideTempNotifier(),
+);
+
+// Outside Temperature
+
+@immutable
+class VehicleSignalOutsideTemp {
+ const VehicleSignalOutsideTemp({required this.temp});
+
+ final double temp;
+
+ VehicleSignalOutsideTemp copyWith({double? temp}) {
+ return VehicleSignalOutsideTemp(temp: temp ?? this.temp);
+ }
+}
+
+class VehicleSignalOutsideTempNotifier
+ extends StateNotifier<VehicleSignalOutsideTemp> {
+ VehicleSignalOutsideTempNotifier() : super(_initialValue);
+
+ static final VehicleSignalOutsideTemp _initialValue =
+ VehicleSignalOutsideTemp(temp: 32);
+
+ void update({double? temp}) {
+ state = state.copyWith(temp: temp);
+ }
+}
+
+final vehicleSignalOutsideTempProvider = StateNotifierProvider<
+ VehicleSignalOutsideTempNotifier, VehicleSignalOutsideTemp>(
+ (ref) => VehicleSignalOutsideTempNotifier(),
+);