ui build refactoring 1

This commit is contained in:
Yegor Vialov 2020-03-21 23:11:00 +00:00
parent 56d8e389db
commit 2f517a3ad5
6 changed files with 199 additions and 281 deletions

View File

@ -56,27 +56,29 @@ class HomeAssistant {
Completer _fetchCompleter; Completer _fetchCompleter;
Future fetchData() { Future fetchData(bool uiOnly) {
if (_fetchCompleter != null && !_fetchCompleter.isCompleted) { if (_fetchCompleter != null && !_fetchCompleter.isCompleted) {
Logger.w("Previous data fetch is not completed yet"); Logger.w("Previous data fetch is not completed yet");
return _fetchCompleter.future; return _fetchCompleter.future;
} }
if (entities == null) entities = EntityCollection(ConnectionManager().httpWebHost);
_fetchCompleter = Completer(); _fetchCompleter = Completer();
List<Future> futures = []; List<Future> 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) { if (!autoUi) {
futures.add(_getLovelace(null)); futures.add(_getLovelace(null));
} }
futures.add(_getConfig(null));
futures.add(_getUserInfo(null));
futures.add(_getPanels(null));
futures.add(_getServices(null));
Future.wait(futures).then((_) { Future.wait(futures).then((_) {
if (isMobileAppEnabled) { if (isMobileAppEnabled) {
_createUI(); _createUI();
_fetchCompleter.complete(); _fetchCompleter.complete();
MobileAppIntegrationManager.checkAppRegistration(); if (!uiOnly) MobileAppIntegrationManager.checkAppRegistration();
} else { } 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")])); _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() { void _handleLovelaceUpdate() {
if (_fetchCompleter != null && _fetchCompleter.isCompleted) { if (_fetchCompleter != null && _fetchCompleter.isCompleted) {
eventBus.fire(new StateChangedEvent( eventBus.fire(new LovelaceChangedEvent());
needToRebuildUI: true
));
} }
} }
@ -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<HACard> _createLovelaceCards(List rawCards) {
List<HACard> 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() { void _createUI() {
ui = HomeAssistantUI();
if (!autoUi && (_rawLovelaceData != null)) { if (!autoUi && (_rawLovelaceData != null)) {
Logger.d("Creating Lovelace UI"); Logger.d("Creating Lovelace UI");
_parseLovelace(); ui = HomeAssistantUI(_rawLovelaceData);
} else { } else {
Logger.d("Creating group-based UI"); Logger.e("No lovelace config!!!!");
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;
});
} }
} }
Widget buildViews(BuildContext context, TabController tabController) {
return ui.build(context, tabController);
}
} }
/* /*

View File

@ -12,6 +12,7 @@ class MainPage extends StatefulWidget {
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin { class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
StreamSubscription _stateSubscription; StreamSubscription _stateSubscription;
StreamSubscription _lovelaceSubscription;
StreamSubscription _settingsSubscription; StreamSubscription _settingsSubscription;
StreamSubscription _serviceCallSubscription; StreamSubscription _serviceCallSubscription;
StreamSubscription _showEntityPageSubscription; StreamSubscription _showEntityPageSubscription;
@ -106,23 +107,23 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
}); });
} }
void _quickLoad() { void _quickLoad({bool uiOnly: false}) {
_hideBottomBar(); _hideBottomBar();
_showInfoBottomBar(progress: true,); _showInfoBottomBar(progress: true,);
ConnectionManager().init(loadSettings: false, forceReconnect: false).then((_){ ConnectionManager().init(loadSettings: false, forceReconnect: false).then((_){
_fetchData(useCache: false); _fetchData(useCache: false, uiOnly: uiOnly);
}, onError: (e) { }, onError: (e) {
_setErrorState(e); _setErrorState(e);
}); });
} }
_fetchData({useCache: false}) async { _fetchData({useCache: false, uiOnly: false}) async {
if (useCache) { if (useCache && !uiOnly) {
HomeAssistant().fetchDataFromCache().then((_) { HomeAssistant().fetchDataFromCache().then((_) {
setState((){}); setState((){});
}); });
} }
await HomeAssistant().fetchData().then((_) { await HomeAssistant().fetchData(uiOnly).then((_) {
_hideBottomBar(); _hideBottomBar();
if (_entityToShow != null) { if (_entityToShow != null) {
_entityToShow = HomeAssistant().entities.get(_entityToShow.entityId); _entityToShow = HomeAssistant().entities.get(_entityToShow.entityId);
@ -160,9 +161,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
} }
}); });
} }
if (_lovelaceSubscription == null) {
_lovelaceSubscription = eventBus.on<LovelaceChangedEvent>().listen((event) {
_quickLoad();
});
}
if (_reloadUISubscription == null) { if (_reloadUISubscription == null) {
_reloadUISubscription = eventBus.on<ReloadUIEvent>().listen((event){ _reloadUISubscription = eventBus.on<ReloadUIEvent>().listen((event){
_quickLoad(); _quickLoad(uiOnly: true);
}); });
} }
if (_showPopupDialogSubscription == null) { if (_showPopupDialogSubscription == null) {
@ -708,7 +714,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
direction: Axis.horizontal, direction: Axis.horizontal,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: HomeAssistant().buildViews(context, _viewsTabController), child: HomeAssistant().ui.build(context, _viewsTabController),
), ),
Container( Container(
width: Sizes.mainPageScreenSeparatorWidth, width: Sizes.mainPageScreenSeparatorWidth,
@ -723,7 +729,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
} else if (_entityToShow != null) { } else if (_entityToShow != null) {
mainScrollBody = EntityPageLayout(entity: _entityToShow, showClose: true,); mainScrollBody = EntityPageLayout(entity: _entityToShow, showClose: true,);
} else { } else {
mainScrollBody = HomeAssistant().buildViews(context, _viewsTabController); mainScrollBody = HomeAssistant().ui.build(context, _viewsTabController);
} }
} }
@ -882,6 +888,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
//flutterWebviewPlugin.dispose(); //flutterWebviewPlugin.dispose();
_viewsTabController?.dispose(); _viewsTabController?.dispose();
_stateSubscription?.cancel(); _stateSubscription?.cancel();
_lovelaceSubscription?.cancel();
_settingsSubscription?.cancel(); _settingsSubscription?.cancel();
_serviceCallSubscription?.cancel(); _serviceCallSubscription?.cancel();
_showPopupDialogSubscription?.cancel(); _showPopupDialogSubscription?.cancel();

View File

@ -12,6 +12,8 @@ class StateChangedEvent {
}); });
} }
class LovelaceChangedEvent {}
class SettingsChangedEvent { class SettingsChangedEvent {
bool reconnect; bool reconnect;

View File

@ -6,8 +6,24 @@ class HomeAssistantUI {
bool get isEmpty => views == null || views.isEmpty; bool get isEmpty => views == null || views.isEmpty;
HomeAssistantUI() { HomeAssistantUI(rawLovelaceConfig) {
views = []; 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) { Widget build(BuildContext context, TabController tabController) {

View File

@ -4,72 +4,166 @@ class HAView {
List<HACard> cards = []; List<HACard> cards = [];
List<Entity> badges = []; List<Entity> badges = [];
Entity linkedEntity; Entity linkedEntity;
final String name; String name;
final String id; String id;
final String iconName; String iconName;
final int count; final int count;
final bool panel; bool isPanel;
HAView({ HAView({@required this.count, @required rawData}) {
this.name, id = "${rawData['id']}";
this.id, name = rawData['title'];
this.count, iconName = rawData['icon'];
this.iconName, isPanel = rawData['panel'] ?? false;
this.panel: false,
List<Entity> childEntities if (rawData['badges'] != null && rawData['badges'] is List) {
}) { rawData['badges'].forEach((entity) {
if (childEntities != null) { if (entity is String) {
_fillView(childEntities); 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<Entity> childEntities) { List<HACard> _createLovelaceCards(List rawCards) {
List<HACard> autoGeneratedCards = []; List<HACard> result = [];
badges.addAll(childEntities.where((entity){ return entity.isBadge;})); rawCards.forEach((rawCard){
childEntities.where((entity){ return entity.domain == "media_player";}).forEach((e){ try {
HACard card = HACard( //bool isThereCardOptionsInside = rawCard["card"] != null;
name: e.displayName, var rawCardInfo = rawCard["card"] ?? rawCard;
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 {
HACard card = HACard( HACard card = HACard(
name: entity.displayName, id: "card",
id: entity.entityId, name: rawCardInfo["title"] ?? rawCardInfo["name"],
linkedEntityWrapper: EntityWrapper(entity: entity), type: rawCardInfo['type'] ?? CardType.ENTITIES,
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);})); if (rawCardInfo["cards"] != null) {
entity.childEntities.where((entity) {return entity.domain == "media_player";}).forEach((entity){ card.childCards = _createLovelaceCards(rawCardInfo["cards"]);
HACard mediaCard = HACard( }
name: entity.displayName, var rawEntities = rawCard["entities"] ?? rawCardInfo["entities"];
id: entity.entityId, rawEntities?.forEach((rawEntity) {
linkedEntityWrapper: EntityWrapper(entity: entity), if (rawEntity is String) {
type: CardType.MEDIA_CONTROL if (HomeAssistant().entities.isExist(rawEntity)) {
); card.entities.add(EntityWrapper(entity: HomeAssistant().entities.get(rawEntity)));
cards.add(mediaCard); } 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() { Widget buildTab() {

View File

@ -10,7 +10,7 @@ class ViewWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (this.view.panel) { if (this.view.isPanel) {
return FractionallySizedBox( return FractionallySizedBox(
widthFactor: 1, widthFactor: 1,
heightFactor: 1, heightFactor: 1,