From bf6559c990575f8ac7aa1910c2a28adf2fe80e42 Mon Sep 17 00:00:00 2001 From: estevez Date: Sun, 16 Sep 2018 18:02:12 +0300 Subject: [PATCH] [#19] Implement Home Assistant state events listener --- lib/data_model.dart | 50 +++++++++++++++++++++++++++++++++++---------- lib/main.dart | 9 ++++++++ pubspec.lock | 7 +++++++ pubspec.yaml | 1 + 4 files changed, 56 insertions(+), 11 deletions(-) diff --git a/lib/data_model.dart b/lib/data_model.dart index fcfdca2..36e6e7e 100644 --- a/lib/data_model.dart +++ b/lib/data_model.dart @@ -1,5 +1,11 @@ part of 'main.dart'; +class StateChangedEvent { + String entityId; + + StateChangedEvent(this.entityId); +} + class HassioDataModel { String _hassioAPIEndpoint; String _hassioPassword; @@ -28,7 +34,8 @@ class HassioDataModel { if ((_fetchCompleter != null) && (!_fetchCompleter.isCompleted)) { debugPrint("Previous fetch is not complited"); } else { - _fetchingTimer = new Timer(new Duration(seconds: 10), () { + //Fetch timeout timer + _fetchingTimer = Timer(Duration(seconds: 10), () { _fetchCompleter.completeError({"message": "Data fetching timeout."}); _hassioChannel.sink.close(); _hassioChannel = null; @@ -76,15 +83,15 @@ class HassioDataModel { } _handleMessage(Completer connectionCompleter, String message) { - debugPrint("<[Receive]Message from Home Assistant:"); var data = json.decode(message); - debugPrint(" type: ${data['type']}"); + debugPrint("[Received]Message type: ${data['type']}"); if (data["type"] == "auth_required") { debugPrint(" sending auth!"); _sendMessageRaw('{"type": "auth","api_password": "$_hassioPassword"}'); } else if (data["type"] == "auth_ok") { debugPrint(" auth done"); - debugPrint("Connection done"); + debugPrint("Connection done."); + sendSubscribe(); connectionCompleter.complete(); } else if (data["type"] == "auth_invalid") { connectionCompleter.completeError({message: "Auth error: ${data["message"]}"}); @@ -99,18 +106,35 @@ class HassioDataModel { } else if (data["id"] == _currentMssageId) { debugPrint("Request id:$_currentMssageId was successful"); } else { - _handleErrorMessage({"message" : "Wrong message ID"}); + debugPrint("Skipped message due to messageId:"); + debugPrint(message); } } else { _handleErrorMessage(data["error"]); } + } else if (data["type"] == "event") { + if ((data["event"] != null) && (data["event"]["event_type"] == "state_changed")) { + _handleEntityStateChange(data["event"]["data"]); + } else if (data["event"] != null) { + debugPrint("Unhandled event type: ${data["event"]["event_type"]}"); + } else { + debugPrint("Event is null"); + } + } else { + debugPrint("Unknown message type"); + debugPrint(message); } } _handleErrorMessage(Object error) { debugPrint("Error: ${error.toString()}"); - if (!_statesCompleter.isCompleted) _statesCompleter.completeError(error); - if (!_servicesCompleter.isCompleted) _servicesCompleter.completeError(error); + if ((_statesCompleter != null) && (!_statesCompleter.isCompleted)) _statesCompleter.completeError(error); + if ((_servicesCompleter != null) && (!_servicesCompleter.isCompleted)) _servicesCompleter.completeError(error); + } + + void sendSubscribe() { + _incrementMessageId(); + _sendMessageRaw('{"id": $_currentMssageId, "type": "subscribe_events"}'); } Future _getStates() { @@ -137,14 +161,19 @@ class HassioDataModel { _sendMessageRaw(message) { _reConnectSocket().then((r) { - debugPrint(">[Send]Sending to Home Assistant:"); - debugPrint(" $message"); + debugPrint("[Sent]$message"); _hassioChannel.sink.add(message); }).catchError((e){ debugPrint("Unable to connect for sending: $e"); }); } + void _handleEntityStateChange(Map eventData) { + String entityId = eventData["entity_id"]; + _entitiesData[entityId].addAll(eventData["new_state"]); + eventBus.fire(new StateChangedEvent(eventData["entity_id"])); + } + void _parseServices(Map data) { Map result = {}; debugPrint("Parsing ${data.length} Home Assistant service domains"); @@ -202,11 +231,10 @@ class HassioDataModel { }); //Gethering information for UI - + debugPrint("Gethering views"); uiGroups.forEach((viewId) { var viewGroup = _entitiesData[viewId]; Map viewGroupStructure = {}; - debugPrint("Gethering views"); if (viewGroup != null) { viewGroupStructure["standalone"] = []; viewGroupStructure["groups"] = []; diff --git a/lib/main.dart b/lib/main.dart index 607b0f6..49ba0f3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,10 +7,13 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:web_socket_channel/io.dart'; import 'package:web_socket_channel/status.dart' as socketStatus; import 'package:progress_indicators/progress_indicators.dart'; +import 'package:event_bus/event_bus.dart'; part 'settings.dart'; part 'data_model.dart'; +EventBus eventBus = new EventBus(); + void main() => runApp(new HassClientApp()); class HassClientApp extends StatelessWidget { @@ -71,6 +74,12 @@ class _MainPageState extends State { String _hassioPassword = prefs.getString('hassio-password'); _dataModel = HassioDataModel(_hassioAPIEndpoint, _hassioPassword); await _refreshData(); + eventBus.on().listen((event) { + debugPrint("State change event for ${event.entityId}"); + setState(() { + _entitiesData = _dataModel.entities; + }); + }); } _refreshData() async { diff --git a/pubspec.lock b/pubspec.lock index 2681079..d1c4b37 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -71,6 +71,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" + event_bus: + dependency: "direct main" + description: + name: event_bus + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.1" flutter: dependency: "direct main" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 023e20d..c62f0b7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: sdk: flutter shared_preferences: any progress_indicators: ^0.1.2 + event_bus: ^1.0.1 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.