diff --git a/lib/home_assistant.class.dart b/lib/home_assistant.class.dart index ec8fa46..2561f22 100644 --- a/lib/home_assistant.class.dart +++ b/lib/home_assistant.class.dart @@ -56,27 +56,29 @@ class HomeAssistant { Completer _fetchCompleter; - Future fetchData() { + Future fetchData(bool uiOnly) { if (_fetchCompleter != null && !_fetchCompleter.isCompleted) { Logger.w("Previous data fetch is not completed yet"); return _fetchCompleter.future; } - if (entities == null) entities = EntityCollection(ConnectionManager().httpWebHost); _fetchCompleter = Completer(); List futures = []; - futures.add(_getStates(null)); + if (!uiOnly) { + if (entities == null) entities = EntityCollection(ConnectionManager().httpWebHost); + futures.add(_getStates(null)); + futures.add(_getConfig(null)); + futures.add(_getUserInfo(null)); + futures.add(_getPanels(null)); + futures.add(_getServices(null)); + } if (!autoUi) { futures.add(_getLovelace(null)); } - futures.add(_getConfig(null)); - futures.add(_getUserInfo(null)); - futures.add(_getPanels(null)); - futures.add(_getServices(null)); Future.wait(futures).then((_) { if (isMobileAppEnabled) { _createUI(); _fetchCompleter.complete(); - MobileAppIntegrationManager.checkAppRegistration(); + if (!uiOnly) MobileAppIntegrationManager.checkAppRegistration(); } else { _fetchCompleter.completeError(HAError("Mobile app component not found", actions: [HAErrorAction.tryAgain(), HAErrorAction(type: HAErrorActionType.URL ,title: "Help",url: "http://ha-client.app/docs#mobile-app-integration")])); } @@ -300,9 +302,7 @@ class HomeAssistant { void _handleLovelaceUpdate() { if (_fetchCompleter != null && _fetchCompleter.isCompleted) { - eventBus.fire(new StateChangedEvent( - needToRebuildUI: true - )); + eventBus.fire(new LovelaceChangedEvent()); } } @@ -317,215 +317,14 @@ class HomeAssistant { } } - void _parseLovelace() { - Logger.d("--Title: ${_rawLovelaceData["title"]}"); - ui.title = _rawLovelaceData["title"]; - int viewCounter = 0; - Logger.d("--Views count: ${_rawLovelaceData['views'].length}"); - _rawLovelaceData["views"].forEach((rawView){ - Logger.d("----view id: ${rawView['id']}"); - HAView view = HAView( - count: viewCounter, - id: "${rawView['id']}", - name: rawView['title'], - iconName: rawView['icon'], - panel: rawView['panel'] ?? false, - ); - - if (rawView['badges'] != null && rawView['badges'] is List) { - rawView['badges'].forEach((entity) { - if (entity is String) { - if (entities.isExist(entity)) { - Entity e = entities.get(entity); - view.badges.add(e); - } - } else { - String eId = '${entity['entity']}'; - if (entities.isExist(eId)) { - Entity e = entities.get(eId); - view.badges.add(e); - } - } - }); - } - - view.cards.addAll(_createLovelaceCards(rawView["cards"] ?? [])); - ui.views.add( - view - ); - viewCounter += 1; - }); - } - - List _createLovelaceCards(List rawCards) { - List result = []; - rawCards.forEach((rawCard){ - try { - //bool isThereCardOptionsInside = rawCard["card"] != null; - var rawCardInfo = rawCard["card"] ?? rawCard; - HACard card = HACard( - id: "card", - name: rawCardInfo["title"] ?? rawCardInfo["name"], - type: rawCardInfo['type'] ?? CardType.ENTITIES, - columnsCount: rawCardInfo['columns'] ?? 4, - showName: (rawCardInfo['show_name'] ?? rawCard['show_name']) ?? true, - showHeaderToggle: (rawCardInfo['show_header_toggle'] ?? rawCard['show_header_toggle']) ?? true, - showState: (rawCardInfo['show_state'] ?? rawCard['show_state']) ?? true, - showEmpty: (rawCardInfo['show_empty'] ?? rawCard['show_empty']) ?? true, - stateFilter: (rawCard['state_filter'] ?? rawCardInfo['state_filter']) ?? [], - states: rawCardInfo['states'], - conditions: rawCard['conditions'] ?? [], - content: rawCardInfo['content'], - min: rawCardInfo['min'] ?? 0, - max: rawCardInfo['max'] ?? 100, - unit: rawCardInfo['unit'], - severity: rawCardInfo['severity'] - ); - if (rawCardInfo["cards"] != null) { - card.childCards = _createLovelaceCards(rawCardInfo["cards"]); - } - var rawEntities = rawCard["entities"] ?? rawCardInfo["entities"]; - rawEntities?.forEach((rawEntity) { - if (rawEntity is String) { - if (entities.isExist(rawEntity)) { - card.entities.add(EntityWrapper(entity: entities.get(rawEntity))); - } else { - card.entities.add(EntityWrapper(entity: Entity.missed(rawEntity))); - } - } else { - if (rawEntity["type"] == "divider") { - card.entities.add(EntityWrapper(entity: Entity.divider())); - } else if (rawEntity["type"] == "section") { - card.entities.add(EntityWrapper(entity: Entity.section(rawEntity["label"] ?? ""))); - } else if (rawEntity["type"] == "call-service") { - Map uiActionData = { - "tap_action": { - "action": EntityUIAction.callService, - "service": rawEntity["service"], - "service_data": rawEntity["service_data"] - }, - "hold_action": EntityUIAction.none - }; - card.entities.add(EntityWrapper( - entity: Entity.callService( - icon: rawEntity["icon"], - name: rawEntity["name"], - service: rawEntity["service"], - actionName: rawEntity["action_name"] - ), - uiAction: EntityUIAction(rawEntityData: uiActionData) - ) - ); - } else if (rawEntity["type"] == "weblink") { - Map uiActionData = { - "tap_action": { - "action": EntityUIAction.navigate, - "service": rawEntity["url"] - }, - "hold_action": EntityUIAction.none - }; - card.entities.add(EntityWrapper( - entity: Entity.weblink( - icon: rawEntity["icon"], - name: rawEntity["name"], - url: rawEntity["url"] - ), - uiAction: EntityUIAction(rawEntityData: uiActionData) - ) - ); - } else if (entities.isExist(rawEntity["entity"])) { - Entity e = entities.get(rawEntity["entity"]); - card.entities.add( - EntityWrapper( - entity: e, - overrideName: rawEntity["name"], - overrideIcon: rawEntity["icon"], - stateFilter: rawEntity['state_filter'] ?? [], - uiAction: EntityUIAction(rawEntityData: rawEntity) - ) - ); - } else { - card.entities.add(EntityWrapper(entity: Entity.missed(rawEntity["entity"]))); - } - } - }); - var rawSingleEntity = rawCard["entity"] ?? rawCardInfo["entity"]; - if (rawSingleEntity != null) { - var en = rawSingleEntity; - if (en is String) { - if (entities.isExist(en)) { - Entity e = entities.get(en); - card.linkedEntityWrapper = EntityWrapper( - entity: e, - overrideIcon: rawCardInfo["icon"], - overrideName: rawCardInfo["name"], - uiAction: EntityUIAction(rawEntityData: rawCard) - ); - } else { - card.linkedEntityWrapper = EntityWrapper(entity: Entity.missed(en)); - } - } else { - if (entities.isExist(en["entity"])) { - Entity e = entities.get(en["entity"]); - card.linkedEntityWrapper = EntityWrapper( - entity: e, - overrideIcon: en["icon"], - overrideName: en["name"], - stateFilter: en['state_filter'] ?? [], - uiAction: EntityUIAction(rawEntityData: rawCard) - ); - } else { - card.linkedEntityWrapper = EntityWrapper(entity: Entity.missed(en["entity"])); - } - } - } - result.add(card); - } catch (e) { - Logger.e("There was an error parsing card: ${e.toString()}"); - } - }); - return result; - } - void _createUI() { - ui = HomeAssistantUI(); if (!autoUi && (_rawLovelaceData != null)) { Logger.d("Creating Lovelace UI"); - _parseLovelace(); + ui = HomeAssistantUI(_rawLovelaceData); } else { - Logger.d("Creating group-based UI"); - int viewCounter = 0; - if (!entities.hasDefaultView) { - HAView view = HAView( - count: viewCounter, - id: "group.default_view", - name: "Home", - childEntities: entities.filterEntitiesForDefaultView() - ); - ui.views.add( - view - ); - viewCounter += 1; - } - entities.viewEntities.forEach((viewEntity) { - HAView view = HAView( - count: viewCounter, - id: viewEntity.entityId, - name: viewEntity.displayName, - childEntities: viewEntity.childEntities - ); - view.linkedEntity = viewEntity; - ui.views.add( - view - ); - viewCounter += 1; - }); + Logger.e("No lovelace config!!!!"); } } - - Widget buildViews(BuildContext context, TabController tabController) { - return ui.build(context, tabController); - } } /* diff --git a/lib/pages/main/main.page.dart b/lib/pages/main/main.page.dart index b2cbf89..ef34eb0 100644 --- a/lib/pages/main/main.page.dart +++ b/lib/pages/main/main.page.dart @@ -12,6 +12,7 @@ class MainPage extends StatefulWidget { class _MainPageState extends State with WidgetsBindingObserver, TickerProviderStateMixin { StreamSubscription _stateSubscription; + StreamSubscription _lovelaceSubscription; StreamSubscription _settingsSubscription; StreamSubscription _serviceCallSubscription; StreamSubscription _showEntityPageSubscription; @@ -106,23 +107,23 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker }); } - void _quickLoad() { + void _quickLoad({bool uiOnly: false}) { _hideBottomBar(); _showInfoBottomBar(progress: true,); ConnectionManager().init(loadSettings: false, forceReconnect: false).then((_){ - _fetchData(useCache: false); + _fetchData(useCache: false, uiOnly: uiOnly); }, onError: (e) { _setErrorState(e); }); } - _fetchData({useCache: false}) async { - if (useCache) { + _fetchData({useCache: false, uiOnly: false}) async { + if (useCache && !uiOnly) { HomeAssistant().fetchDataFromCache().then((_) { setState((){}); }); } - await HomeAssistant().fetchData().then((_) { + await HomeAssistant().fetchData(uiOnly).then((_) { _hideBottomBar(); if (_entityToShow != null) { _entityToShow = HomeAssistant().entities.get(_entityToShow.entityId); @@ -160,9 +161,14 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker } }); } + if (_lovelaceSubscription == null) { + _lovelaceSubscription = eventBus.on().listen((event) { + _quickLoad(); + }); + } if (_reloadUISubscription == null) { _reloadUISubscription = eventBus.on().listen((event){ - _quickLoad(); + _quickLoad(uiOnly: true); }); } if (_showPopupDialogSubscription == null) { @@ -708,7 +714,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker direction: Axis.horizontal, children: [ Expanded( - child: HomeAssistant().buildViews(context, _viewsTabController), + child: HomeAssistant().ui.build(context, _viewsTabController), ), Container( width: Sizes.mainPageScreenSeparatorWidth, @@ -723,7 +729,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker } else if (_entityToShow != null) { mainScrollBody = EntityPageLayout(entity: _entityToShow, showClose: true,); } else { - mainScrollBody = HomeAssistant().buildViews(context, _viewsTabController); + mainScrollBody = HomeAssistant().ui.build(context, _viewsTabController); } } @@ -882,6 +888,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker //flutterWebviewPlugin.dispose(); _viewsTabController?.dispose(); _stateSubscription?.cancel(); + _lovelaceSubscription?.cancel(); _settingsSubscription?.cancel(); _serviceCallSubscription?.cancel(); _showPopupDialogSubscription?.cancel(); diff --git a/lib/types/event_bus_events.dart b/lib/types/event_bus_events.dart index cbaf68e..ae29238 100644 --- a/lib/types/event_bus_events.dart +++ b/lib/types/event_bus_events.dart @@ -12,6 +12,8 @@ class StateChangedEvent { }); } +class LovelaceChangedEvent {} + class SettingsChangedEvent { bool reconnect; diff --git a/lib/ui.dart b/lib/ui.dart index 003793d..35099dc 100644 --- a/lib/ui.dart +++ b/lib/ui.dart @@ -6,8 +6,24 @@ class HomeAssistantUI { bool get isEmpty => views == null || views.isEmpty; - HomeAssistantUI() { + HomeAssistantUI(rawLovelaceConfig) { views = []; + Logger.d("--Title: ${rawLovelaceConfig["title"]}"); + title = rawLovelaceConfig["title"]; + int viewCounter = 0; + Logger.d("--Views count: ${rawLovelaceConfig['views'].length}"); + rawLovelaceConfig["views"].forEach((rawView){ + Logger.d("----view id: ${rawView['id']}"); + HAView view = HAView( + count: viewCounter, + rawData: rawView + ); + + views.add( + view + ); + viewCounter += 1; + }); } Widget build(BuildContext context, TabController tabController) { diff --git a/lib/view.class.dart b/lib/view.class.dart index a4ba4fa..24c4d94 100644 --- a/lib/view.class.dart +++ b/lib/view.class.dart @@ -4,72 +4,166 @@ class HAView { List cards = []; List badges = []; Entity linkedEntity; - final String name; - final String id; - final String iconName; + String name; + String id; + String iconName; final int count; - final bool panel; + bool isPanel; - HAView({ - this.name, - this.id, - this.count, - this.iconName, - this.panel: false, - List childEntities - }) { - if (childEntities != null) { - _fillView(childEntities); - } + HAView({@required this.count, @required rawData}) { + id = "${rawData['id']}"; + name = rawData['title']; + iconName = rawData['icon']; + isPanel = rawData['panel'] ?? false; + + if (rawData['badges'] != null && rawData['badges'] is List) { + rawData['badges'].forEach((entity) { + if (entity is String) { + if (HomeAssistant().entities.isExist(entity)) { + Entity e = HomeAssistant().entities.get(entity); + badges.add(e); + } + } else { + String eId = '${entity['entity']}'; + if (HomeAssistant().entities.isExist(eId)) { + Entity e = HomeAssistant().entities.get(eId); + badges.add(e); + } + } + }); + } + + cards.addAll(_createLovelaceCards(rawData["cards"] ?? [])); } - void _fillView(List childEntities) { - List autoGeneratedCards = []; - badges.addAll(childEntities.where((entity){ return entity.isBadge;})); - childEntities.where((entity){ return entity.domain == "media_player";}).forEach((e){ - HACard card = HACard( - name: e.displayName, - id: e.entityId, - linkedEntityWrapper: EntityWrapper(entity: e), - type: CardType.MEDIA_CONTROL - ); - cards.add(card); - }); - childEntities.where((e){return (!e.isBadge && e.domain != "media_player");}).forEach((entity) { - if (!entity.isGroup) { - String groupIdToAdd = "${entity.domain}.${entity.domain}$count"; - if (autoGeneratedCards.every((HACard card) => card.id != groupIdToAdd )) { - HACard card = HACard( - id: groupIdToAdd, - name: entity.domain, - type: CardType.ENTITIES - ); - card.entities.add(EntityWrapper(entity: entity)); - autoGeneratedCards.add(card); - } else { - autoGeneratedCards.firstWhere((card) => card.id == groupIdToAdd).entities.add(EntityWrapper(entity: entity)); - } - } else { + List _createLovelaceCards(List rawCards) { + List result = []; + rawCards.forEach((rawCard){ + try { + //bool isThereCardOptionsInside = rawCard["card"] != null; + var rawCardInfo = rawCard["card"] ?? rawCard; HACard card = HACard( - name: entity.displayName, - id: entity.entityId, - linkedEntityWrapper: EntityWrapper(entity: entity), - type: CardType.ENTITIES + id: "card", + name: rawCardInfo["title"] ?? rawCardInfo["name"], + type: rawCardInfo['type'] ?? CardType.ENTITIES, + columnsCount: rawCardInfo['columns'] ?? 4, + showName: (rawCardInfo['show_name'] ?? rawCard['show_name']) ?? true, + showHeaderToggle: (rawCardInfo['show_header_toggle'] ?? rawCard['show_header_toggle']) ?? true, + showState: (rawCardInfo['show_state'] ?? rawCard['show_state']) ?? true, + showEmpty: (rawCardInfo['show_empty'] ?? rawCard['show_empty']) ?? true, + stateFilter: (rawCard['state_filter'] ?? rawCardInfo['state_filter']) ?? [], + states: rawCardInfo['states'], + conditions: rawCard['conditions'] ?? [], + content: rawCardInfo['content'], + min: rawCardInfo['min'] ?? 0, + max: rawCardInfo['max'] ?? 100, + unit: rawCardInfo['unit'], + severity: rawCardInfo['severity'] ); - card.entities.addAll(entity.childEntities.where((entity) {return entity.domain != "media_player";}).map((e) {return EntityWrapper(entity: e);})); - entity.childEntities.where((entity) {return entity.domain == "media_player";}).forEach((entity){ - HACard mediaCard = HACard( - name: entity.displayName, - id: entity.entityId, - linkedEntityWrapper: EntityWrapper(entity: entity), - type: CardType.MEDIA_CONTROL - ); - cards.add(mediaCard); + if (rawCardInfo["cards"] != null) { + card.childCards = _createLovelaceCards(rawCardInfo["cards"]); + } + var rawEntities = rawCard["entities"] ?? rawCardInfo["entities"]; + rawEntities?.forEach((rawEntity) { + if (rawEntity is String) { + if (HomeAssistant().entities.isExist(rawEntity)) { + card.entities.add(EntityWrapper(entity: HomeAssistant().entities.get(rawEntity))); + } else { + card.entities.add(EntityWrapper(entity: Entity.missed(rawEntity))); + } + } else { + if (rawEntity["type"] == "divider") { + card.entities.add(EntityWrapper(entity: Entity.divider())); + } else if (rawEntity["type"] == "section") { + card.entities.add(EntityWrapper(entity: Entity.section(rawEntity["label"] ?? ""))); + } else if (rawEntity["type"] == "call-service") { + Map uiActionData = { + "tap_action": { + "action": EntityUIAction.callService, + "service": rawEntity["service"], + "service_data": rawEntity["service_data"] + }, + "hold_action": EntityUIAction.none + }; + card.entities.add(EntityWrapper( + entity: Entity.callService( + icon: rawEntity["icon"], + name: rawEntity["name"], + service: rawEntity["service"], + actionName: rawEntity["action_name"] + ), + uiAction: EntityUIAction(rawEntityData: uiActionData) + ) + ); + } else if (rawEntity["type"] == "weblink") { + Map uiActionData = { + "tap_action": { + "action": EntityUIAction.navigate, + "service": rawEntity["url"] + }, + "hold_action": EntityUIAction.none + }; + card.entities.add(EntityWrapper( + entity: Entity.weblink( + icon: rawEntity["icon"], + name: rawEntity["name"], + url: rawEntity["url"] + ), + uiAction: EntityUIAction(rawEntityData: uiActionData) + ) + ); + } else if (HomeAssistant().entities.isExist(rawEntity["entity"])) { + Entity e = HomeAssistant().entities.get(rawEntity["entity"]); + card.entities.add( + EntityWrapper( + entity: e, + overrideName: rawEntity["name"], + overrideIcon: rawEntity["icon"], + stateFilter: rawEntity['state_filter'] ?? [], + uiAction: EntityUIAction(rawEntityData: rawEntity) + ) + ); + } else { + card.entities.add(EntityWrapper(entity: Entity.missed(rawEntity["entity"]))); + } + } }); - cards.add(card); + var rawSingleEntity = rawCard["entity"] ?? rawCardInfo["entity"]; + if (rawSingleEntity != null) { + var en = rawSingleEntity; + if (en is String) { + if (HomeAssistant().entities.isExist(en)) { + Entity e = HomeAssistant().entities.get(en); + card.linkedEntityWrapper = EntityWrapper( + entity: e, + overrideIcon: rawCardInfo["icon"], + overrideName: rawCardInfo["name"], + uiAction: EntityUIAction(rawEntityData: rawCard) + ); + } else { + card.linkedEntityWrapper = EntityWrapper(entity: Entity.missed(en)); + } + } else { + if (HomeAssistant().entities.isExist(en["entity"])) { + Entity e = HomeAssistant().entities.get(en["entity"]); + card.linkedEntityWrapper = EntityWrapper( + entity: e, + overrideIcon: en["icon"], + overrideName: en["name"], + stateFilter: en['state_filter'] ?? [], + uiAction: EntityUIAction(rawEntityData: rawCard) + ); + } else { + card.linkedEntityWrapper = EntityWrapper(entity: Entity.missed(en["entity"])); + } + } + } + result.add(card); + } catch (e) { + Logger.e("There was an error parsing card: ${e.toString()}"); } }); - cards.addAll(autoGeneratedCards); + return result; } Widget buildTab() { diff --git a/lib/viewWidget.widget.dart b/lib/viewWidget.widget.dart index ab620d7..8c78023 100644 --- a/lib/viewWidget.widget.dart +++ b/lib/viewWidget.widget.dart @@ -10,7 +10,7 @@ class ViewWidget extends StatelessWidget { @override Widget build(BuildContext context) { - if (this.view.panel) { + if (this.view.isPanel) { return FractionallySizedBox( widthFactor: 1, heightFactor: 1,