Resolves #122
This commit is contained in:
parent
54979b583b
commit
8fb0d61a84
@ -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;
|
||||
}
|
||||
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);
|
||||
|
||||
void _handleLovelace(response) {
|
||||
if (response["success"] == true) {
|
||||
_rawLovelaceData = response["result"];
|
||||
} else {
|
||||
_rawLovelaceData = null;
|
||||
}
|
||||
_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"]));
|
||||
}
|
||||
}
|
||||
});
|
||||
result.add(card);
|
||||
}
|
||||
});
|
||||
_servicesData = result;
|
||||
_servicesCompleter.complete();
|
||||
} catch (e) {
|
||||
TheLogger.log("Error","Error parsing services. But they are not used :-)");
|
||||
_servicesCompleter.complete();
|
||||
}*/
|
||||
return result;
|
||||
}
|
||||
|
||||
void _parseEntities(response) async {
|
||||
@ -366,19 +426,23 @@ class HomeAssistant {
|
||||
return;
|
||||
}
|
||||
entities.parse(response["result"]);
|
||||
_statesCompleter.complete();
|
||||
}
|
||||
|
||||
|
||||
ui = GroupBasedUI();
|
||||
void _createUI() {
|
||||
if ((_useLovelace) && (_rawLovelaceData != null)) {
|
||||
_parseLovelace();
|
||||
} else {
|
||||
ui = HomeAssistantUI();
|
||||
int viewCounter = 0;
|
||||
//TODO add default_view
|
||||
if (!entities.hasDefaultView) {
|
||||
TheLogger.log("Debug", "--Default view");
|
||||
HACView view = HACView(
|
||||
HAView view = HAView(
|
||||
count: viewCounter,
|
||||
id: "group.default_view",
|
||||
name: "Home"
|
||||
name: "Home",
|
||||
childEntities: entities.filterEntitiesForDefaultView()
|
||||
);
|
||||
_createView(view, entities.filterEntitiesForDefaultView(), viewCounter);
|
||||
ui.views.add(
|
||||
view
|
||||
);
|
||||
@ -386,60 +450,22 @@ class HomeAssistant {
|
||||
}
|
||||
entities.viewEntities.forEach((viewEntity) {
|
||||
TheLogger.log("Debug", "--View: ${viewEntity.entityId}");
|
||||
HACView view = HACView(
|
||||
HAView view = HAView(
|
||||
count: viewCounter,
|
||||
id: viewEntity.entityId,
|
||||
name: viewEntity.displayName
|
||||
name: viewEntity.displayName,
|
||||
childEntities: viewEntity.childEntities
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
view.cards.addAll(autoGeneratedCards);
|
||||
}
|
||||
|
||||
Widget buildViews(BuildContext context) {
|
||||
//return _viewBuilder.buildWidget(context);
|
||||
Widget buildViews(BuildContext context, bool lovelace) {
|
||||
return ui.build(context);
|
||||
}
|
||||
|
||||
|
@ -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,14 +241,16 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
||||
|
||||
List<Tab> buildUIViewTabs() {
|
||||
List<Tab> result = [];
|
||||
|
||||
if (_homeAssistant.ui.views.isNotEmpty) {
|
||||
_homeAssistant.ui.views.forEach((HACView view) {
|
||||
_homeAssistant.ui.views.forEach((HAView view) {
|
||||
if (view.linkedEntity == null) {
|
||||
result.add(
|
||||
Tab(
|
||||
icon:
|
||||
Icon(
|
||||
MaterialDesignIcons.createIconDataFromIconName("mdi:home-assistant"),
|
||||
MaterialDesignIcons.createIconDataFromIconName(
|
||||
view.iconName ?? "mdi:home-assistant"),
|
||||
size: 24.0,
|
||||
)
|
||||
)
|
||||
@ -266,6 +270,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,9 +188,31 @@ 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();
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,
|
Reference in New Issue
Block a user