This commit is contained in:
Yegor Vialov 2018-10-27 00:54:05 +03:00
parent 54979b583b
commit 8fb0d61a84
4 changed files with 246 additions and 137 deletions

View File

@ -4,6 +4,7 @@ class HomeAssistant {
String _webSocketAPIEndpoint;
String _password;
String _authType;
bool _useLovelace;
IOWebSocketChannel _hassioChannel;
SendMessageQueue _messageQueue;
@ -14,14 +15,18 @@ class HomeAssistant {
int _subscriptionMessageId = 0;
int _configMessageId = 0;
int _userInfoMessageId = 0;
int _lovelaceMessageId = 0;
EntityCollection entities;
GroupBasedUI ui;
HomeAssistantUI ui;
Map _instanceConfig = {};
String _userName;
Map _rawLovelaceData;
Completer _fetchCompleter;
Completer _statesCompleter;
Completer _servicesCompleter;
Completer _lovelaceCompleter;
Completer _configCompleter;
Completer _connectionCompleter;
Completer _userInfoCompleter;
@ -45,10 +50,12 @@ class HomeAssistant {
_messageQueue = SendMessageQueue(messageExpirationTime);
}
void updateConnectionSettings(String url, String password, String authType) {
void updateSettings(String url, String password, String authType, bool useLovelace) {
_webSocketAPIEndpoint = url;
_password = password;
_authType = authType;
_useLovelace = useLovelace;
TheLogger.log("Debug", "Use lovelace is $_useLovelace");
}
Future fetch() {
@ -150,11 +157,15 @@ class HomeAssistant {
_getData() async {
List<Future> futures = [];
futures.add(_getStates());
if (_useLovelace) {
futures.add(_getLovelace());
}
futures.add(_getConfig());
futures.add(_getServices());
futures.add(_getUserInfo());
try {
await Future.wait(futures);
_createUI();
_completeFetching(null);
} catch (error) {
_completeFetching(error);
@ -202,6 +213,8 @@ class HomeAssistant {
_parseConfig(data);
} else if (data["id"] == _statesMessageId) {
_parseEntities(data);
} else if (data["id"] == _lovelaceMessageId) {
_handleLovelace(data);
} else if (data["id"] == _servicesMessageId) {
_parseServices(data);
} else if (data["id"] == _userInfoMessageId) {
@ -247,6 +260,15 @@ class HomeAssistant {
return _statesCompleter.future;
}
Future _getLovelace() {
_lovelaceCompleter = new Completer();
_incrementMessageId();
_lovelaceMessageId = _currentMessageId;
_sendMessageRaw('{"id": $_lovelaceMessageId, "type": "lovelace/config"}', false);
return _lovelaceCompleter.future;
}
Future _getUserInfo() {
_userInfoCompleter = new Completer();
_incrementMessageId();
@ -336,28 +358,66 @@ class HomeAssistant {
void _parseServices(response) {
_servicesCompleter.complete();
/*if (response["success"] == false) {
_servicesCompleter.completeError({"errorCode": 4, "errorMessage": response["error"]["message"]});
return;
}
void _handleLovelace(response) {
if (response["success"] == true) {
_rawLovelaceData = response["result"];
} else {
_rawLovelaceData = null;
}
try {
Map data = response["result"];
Map result = {};
TheLogger.log("Debug","Parsing ${data.length} Home Assistant service domains");
data.forEach((domain, services) {
result[domain] = Map.from(services);
services.forEach((serviceName, serviceData) {
if (_entitiesData.isExist("$domain.$serviceName")) {
result[domain].remove(serviceName);
_lovelaceCompleter.complete();
}
void _parseLovelace() {
ui = HomeAssistantUI();
TheLogger.log("debug","Parsing lovelace config");
TheLogger.log("debug","--Title: ${_rawLovelaceData["title"]}");
int viewCounter = 0;
TheLogger.log("debug","--Views count: ${_rawLovelaceData['views'].length}");
_rawLovelaceData["views"].forEach((rawView){
TheLogger.log("debug","----view id: ${rawView['id']}");
HAView view = HAView(
count: viewCounter,
id: rawView['id'],
name: rawView['title'],
iconName: rawView['icon']
);
view.cards.addAll(_createLovelaceCards(rawView["cards"] ?? []));
ui.views.add(
view
);
viewCounter += 1;
});
}
List<HACard> _createLovelaceCards(List rawCards) {
List<HACard> result = [];
rawCards.forEach((rawCard){
if (rawCard["cards"] != null) {
TheLogger.log("debug","------card: ${rawCard['type']} has child cards");
result.addAll(_createLovelaceCards(rawCard["cards"]));
} else {
TheLogger.log("debug","------card: ${rawCard['type']}");
HACard card = HACard(
id: "card",
name: rawCard["title"]
);
rawCard["entities"]?.forEach((rawEntity) {
if (rawEntity is String) {
if (entities.isExist(rawEntity)) {
card.entities.add(entities.get(rawEntity));
}
} else {
if (entities.isExist(rawEntity["entity"])) {
card.entities.add(entities.get(rawEntity["entity"]));
}
}
});
});
_servicesData = result;
_servicesCompleter.complete();
} catch (e) {
TheLogger.log("Error","Error parsing services. But they are not used :-)");
_servicesCompleter.complete();
}*/
result.add(card);
}
});
return result;
}
void _parseEntities(response) async {
@ -366,80 +426,46 @@ class HomeAssistant {
return;
}
entities.parse(response["result"]);
ui = GroupBasedUI();
int viewCounter = 0;
//TODO add default_view
if (!entities.hasDefaultView) {
TheLogger.log("Debug","--Default view");
HACView view = HACView(
count: viewCounter,
id: "group.default_view",
name: "Home"
);
_createView(view, entities.filterEntitiesForDefaultView(), viewCounter);
ui.views.add(
view
);
viewCounter+=1;
}
entities.viewEntities.forEach((viewEntity) {
TheLogger.log("Debug","--View: ${viewEntity.entityId}");
HACView view = HACView(
count: viewCounter,
id: viewEntity.entityId,
name: viewEntity.displayName
);
view.linkedEntity = viewEntity;
_createView(view, viewEntity.childEntities, viewCounter);
ui.views.add(
view
);
viewCounter += 1;
});
_statesCompleter.complete();
}
void _createView(HACView view, List<Entity> childEntities, int viewCounter) {
List<HACCard> autoGeneratedCards = [];
childEntities.forEach((entity) {
if (entity.isBadge) {
view.badges.add(entity);
TheLogger.log("Debug","----Badge: ${entity.entityId}");
} else {
if (!entity.isGroup) {
String groupIdToAdd = "${entity.domain}.${entity.domain}$viewCounter";
if (autoGeneratedCards.every((HACCard card) => card.id != groupIdToAdd )) {
HACCard card = HACCard(
id: groupIdToAdd,
name: entity.domain
);
TheLogger.log("Debug","----Creating card: $groupIdToAdd");
card.entities.add(entity);
autoGeneratedCards.add(card);
} else {
autoGeneratedCards.firstWhere((card) => card.id == groupIdToAdd).entities.add(entity);
}
} else {
TheLogger.log("Debug","----Card: ${entity.entityId}");
HACCard card = HACCard(
name: entity.displayName,
id: entity.entityId,
linkedEntity: entity
);
card.entities.addAll(entity.childEntities);
view.cards.add(card);
}
void _createUI() {
if ((_useLovelace) && (_rawLovelaceData != null)) {
_parseLovelace();
} else {
ui = HomeAssistantUI();
int viewCounter = 0;
if (!entities.hasDefaultView) {
TheLogger.log("Debug", "--Default view");
HAView view = HAView(
count: viewCounter,
id: "group.default_view",
name: "Home",
childEntities: entities.filterEntitiesForDefaultView()
);
ui.views.add(
view
);
viewCounter += 1;
}
});
view.cards.addAll(autoGeneratedCards);
entities.viewEntities.forEach((viewEntity) {
TheLogger.log("Debug", "--View: ${viewEntity.entityId}");
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) {
//return _viewBuilder.buildWidget(context);
Widget buildViews(BuildContext context, bool lovelace) {
return ui.build(context);
}

View File

@ -24,7 +24,7 @@ part 'entity.page.dart';
part 'utils.class.dart';
part 'mdi.class.dart';
part 'entity_collection.class.dart';
part 'group_based_ui.dart';
part 'ui.dart';
EventBus eventBus = new EventBus();
const String appName = "HA Client";
@ -62,7 +62,7 @@ class HAClientApp extends StatelessWidget {
initialRoute: "/",
routes: {
"/": (context) => MainPage(title: 'HA Client'),
"/connection-settings": (context) => ConnectionSettingsPage(title: "Connection Settings"),
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
"/log-view": (context) => LogViewPage(title: "Log")
},
);
@ -96,6 +96,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
int _isLoading = 1;
bool _settingsLoaded = false;
bool _accountMenuExpanded = false;
bool _useLovelaceUI;
@override
void initState() {
@ -145,6 +146,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
homeAssistantWebHost = "${prefs.getString('hassio-res-protocol')}://$domain:$port";
_password = prefs.getString('hassio-password');
_authType = prefs.getString('hassio-auth-type');
_useLovelaceUI = prefs.getBool('use-lovelace') ?? false;
if ((domain == null) || (port == null) || (_password == null) ||
(domain.length == 0) || (port.length == 0) || (_password.length == 0)) {
throw("Check connection settings");
@ -195,7 +197,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
}
_refreshData() async {
_homeAssistant.updateConnectionSettings(_webSocketApiEndpoint, _password, _authType);
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _authType, _useLovelaceUI);
setState(() {
_hideErrorSnackBar();
_isLoading = 1;
@ -239,33 +241,36 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
List<Tab> buildUIViewTabs() {
List<Tab> result = [];
if (_homeAssistant.ui.views.isNotEmpty) {
_homeAssistant.ui.views.forEach((HACView view) {
if (view.linkedEntity == null) {
result.add(
Tab(
icon:
Icon(
MaterialDesignIcons.createIconDataFromIconName("mdi:home-assistant"),
size: 24.0,
)
)
);
} else {
result.add(
Tab(
icon: MaterialDesignIcons.createIconWidgetFromEntityData(
view.linkedEntity, 24.0, null) ??
Icon(
MaterialDesignIcons.createIconDataFromIconName(
"mdi:home-assistant"),
size: 24.0,
)
)
);
}
});
}
if (_homeAssistant.ui.views.isNotEmpty) {
_homeAssistant.ui.views.forEach((HAView view) {
if (view.linkedEntity == null) {
result.add(
Tab(
icon:
Icon(
MaterialDesignIcons.createIconDataFromIconName(
view.iconName ?? "mdi:home-assistant"),
size: 24.0,
)
)
);
} else {
result.add(
Tab(
icon: MaterialDesignIcons.createIconWidgetFromEntityData(
view.linkedEntity, 24.0, null) ??
Icon(
MaterialDesignIcons.createIconDataFromIconName(
"mdi:home-assistant"),
size: 24.0,
)
)
);
}
});
}
return result;
}
@ -319,7 +324,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
menuItems.addAll([
ListTile(
leading: Icon(Icons.settings),
title: Text("Connection settings"),
title: Text("Settings"),
onTap: () {
Navigator.of(context).pop();
Navigator.of(context).pushNamed('/connection-settings');
@ -452,6 +457,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
});
},
),
primary: true,
bottom: empty ? null : TabBar(
tabs: buildUIViewTabs(),
isScrollable: true,
@ -472,7 +478,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
),
)
:
_homeAssistant.buildViews(context)
_homeAssistant.buildViews(context, _useLovelaceUI)
);
}

View File

@ -20,6 +20,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
String _newSocketProtocol = "wss";
String _authType = "access_token";
String _newAuthType = "access_token";
bool _useLovelace = false;
bool _newUseLovelace = false;
bool _edited = false;
FocusNode _domainFocusNode;
FocusNode _portFocusNode;
@ -46,6 +48,11 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
_hassioPassword = _newHassioPassword = prefs.getString("hassio-password") ?? "";
_socketProtocol = _newSocketProtocol = prefs.getString("hassio-protocol") ?? 'wss';
_authType = _newAuthType = prefs.getString("hassio-auth-type") ?? 'access_token';
try {
_useLovelace = _newUseLovelace = prefs.getBool("use-lovelace") ?? false;
} catch (e) {
_useLovelace = _newUseLovelace = false;
}
});
}
@ -55,7 +62,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
(_newHassioPort != _hassioPort) ||
(_newHassioDomain != _hassioDomain) ||
(_newSocketProtocol != _socketProtocol) ||
(_newAuthType != _authType));
(_newAuthType != _authType) ||
(_newUseLovelace != _useLovelace));
});
}
@ -70,6 +78,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
prefs.setString("hassio-protocol", _newSocketProtocol);
prefs.setString("hassio-res-protocol", _newSocketProtocol == "wss" ? "https" : "http");
prefs.setString("hassio-auth-type", _newAuthType);
prefs.setBool("use-lovelace", _newUseLovelace);
}
@override
@ -95,6 +104,13 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
body: ListView(
padding: const EdgeInsets.all(20.0),
children: <Widget>[
Text(
"Connection settings",
style: TextStyle(
color: Colors.black45,
fontSize: 20.0
),
),
new Row(
children: [
Text("Use ssl (HTTPS)"),
@ -172,7 +188,29 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
},
focusNode: _passwordFocusNode,
onEditingComplete: _checkConfigChanged,
)
),
Padding(
padding: EdgeInsets.only(top: 20.0),
child: Text(
"UI",
style: TextStyle(
color: Colors.black45,
fontSize: 20.0
),
),
),
new Row(
children: [
Text("Use Lovelace UI"),
Switch(
value: _newUseLovelace,
onChanged: (value) {
_newUseLovelace = value;
_checkConfigChanged();
},
)
],
),
],
),
);

View File

@ -1,9 +1,9 @@
part of 'main.dart';
class GroupBasedUI {
List<HACView> views;
class HomeAssistantUI {
List<HAView> views;
GroupBasedUI() {
HomeAssistantUI() {
views = [];
}
@ -14,7 +14,6 @@ class GroupBasedUI {
}
List<Widget> _buildViews(BuildContext context) {
TheLogger.log("Debug", "Building UI");
List<Widget> result = [];
views.forEach((view) {
result.add(
@ -26,19 +25,59 @@ class GroupBasedUI {
}
class HACView {
List<HACCard> cards = [];
class HAView {
List<HACard> cards = [];
List<Entity> badges = [];
Entity linkedEntity;
String name;
String id;
String iconName;
int count;
HACView({
HAView({
this.name,
this.id,
this.count
});
this.count,
this.iconName,
List<Entity> childEntities
}) {
_fillView(childEntities ?? []);
}
void _fillView(List<Entity> childEntities) {
List<HACard> autoGeneratedCards = [];
childEntities.forEach((entity) {
if (entity.isBadge) {
badges.add(entity);
TheLogger.log("Debug","----Badge: ${entity.entityId}");
} else {
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
);
TheLogger.log("Debug","----Creating card: $groupIdToAdd");
card.entities.add(entity);
autoGeneratedCards.add(card);
} else {
autoGeneratedCards.firstWhere((card) => card.id == groupIdToAdd).entities.add(entity);
}
} else {
TheLogger.log("Debug","----Card: ${entity.entityId}");
HACard card = HACard(
name: entity.displayName,
id: entity.entityId,
linkedEntity: entity
);
card.entities.addAll(entity.childEntities);
cards.add(card);
}
}
});
cards.addAll(autoGeneratedCards);
}
Widget build(BuildContext context) {
return NewViewWidget(
@ -48,7 +87,7 @@ class HACView {
}
class NewViewWidget extends StatefulWidget {
final HACView view;
final HAView view;
const NewViewWidget({
Key key,
@ -103,7 +142,7 @@ class NewViewWidgetState extends State<NewViewWidget> {
);
}
widget.view.cards.forEach((HACCard card){
widget.view.cards.forEach((HACard card){
result.add(
card.build(context)
);
@ -141,13 +180,13 @@ class NewViewWidgetState extends State<NewViewWidget> {
}
class HACCard {
class HACard {
List<Entity> entities = [];
Entity linkedEntity;
String name;
String id;
HACCard({
HACard({
this.name,
this.id,
this.linkedEntity
@ -163,7 +202,7 @@ class HACCard {
class NewCardWidget extends StatelessWidget {
final HACCard card;
final HACard card;
const NewCardWidget({
Key key,