Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
67e885e76a | |||
594bce0b8d | |||
7f6569e0db | |||
1c829c8364 | |||
7ca4b02e6d | |||
fadfefd836 | |||
37155901ef | |||
fbbb96409d | |||
5126c54914 | |||
916d0b7e3c | |||
0815840a9c | |||
bc237796b2 | |||
7f44800f64 | |||
85ac746e9d | |||
8215175098 | |||
39ee8b1799 | |||
c76d3d68c8 |
1232
android/hs_err_pid766.log
Normal file
1232
android/hs_err_pid766.log
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,6 @@ class AuthManager {
|
|||||||
Completer completer = Completer();
|
Completer completer = Completer();
|
||||||
final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
final flutterWebviewPlugin = new FlutterWebviewPlugin();
|
||||||
flutterWebviewPlugin.onUrlChanged.listen((String url) {
|
flutterWebviewPlugin.onUrlChanged.listen((String url) {
|
||||||
Logger.d("Webview url changed to $url");
|
|
||||||
if (url.startsWith("http://ha-client.homemade.systems/service/auth_callback.html")) {
|
if (url.startsWith("http://ha-client.homemade.systems/service/auth_callback.html")) {
|
||||||
String authCode = url.split("=")[1];
|
String authCode = url.split("=")[1];
|
||||||
Logger.d("We have auth code. Getting temporary access token...");
|
Logger.d("We have auth code. Getting temporary access token...");
|
||||||
@ -24,7 +23,7 @@ class AuthManager {
|
|||||||
includeAuthHeader: false,
|
includeAuthHeader: false,
|
||||||
data: "grant_type=authorization_code&code=$authCode&client_id=${Uri.encodeComponent('http://ha-client.homemade.systems/')}"
|
data: "grant_type=authorization_code&code=$authCode&client_id=${Uri.encodeComponent('http://ha-client.homemade.systems/')}"
|
||||||
).then((response) {
|
).then((response) {
|
||||||
Logger.d("Gottemp token");
|
Logger.d("Got temp token");
|
||||||
String tempToken = json.decode(response)['access_token'];
|
String tempToken = json.decode(response)['access_token'];
|
||||||
Logger.d("Closing webview...");
|
Logger.d("Closing webview...");
|
||||||
//flutterWebviewPlugin.close();
|
//flutterWebviewPlugin.close();
|
||||||
@ -38,7 +37,7 @@ class AuthManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Logger.d("Launching OAuth: $oauthUrl");
|
Logger.d("Launching OAuth");
|
||||||
eventBus.fire(StartAuthEvent(oauthUrl, true));
|
eventBus.fire(StartAuthEvent(oauthUrl, true));
|
||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
@ -104,13 +104,11 @@ class Connection {
|
|||||||
completer?.completeError(HAError("Connection timeout"));
|
completer?.completeError(HAError("Connection timeout"));
|
||||||
});
|
});
|
||||||
}).then((_) {
|
}).then((_) {
|
||||||
Logger.d("doConnect is finished 1");
|
|
||||||
completer?.complete();
|
completer?.complete();
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
completer?.completeError(e);
|
completer?.completeError(e);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Logger.d("doConnect is finished 2");
|
|
||||||
completer?.complete();
|
completer?.complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +122,7 @@ class Connection {
|
|||||||
} else {
|
} else {
|
||||||
connecting = Completer();
|
connecting = Completer();
|
||||||
_disconnect().then((_) {
|
_disconnect().then((_) {
|
||||||
Logger.d("Socket connecting: $_webSocketAPIEndpoint...");
|
Logger.d("Socket connecting...");
|
||||||
_socket = IOWebSocketChannel.connect(
|
_socket = IOWebSocketChannel.connect(
|
||||||
_webSocketAPIEndpoint, pingInterval: Duration(seconds: 15));
|
_webSocketAPIEndpoint, pingInterval: Duration(seconds: 15));
|
||||||
_socketSubscription = _socket.stream.listen(
|
_socketSubscription = _socket.stream.listen(
|
||||||
@ -303,7 +301,10 @@ class Connection {
|
|||||||
_tempToken = null;
|
_tempToken = null;
|
||||||
final storage = new FlutterSecureStorage();
|
final storage = new FlutterSecureStorage();
|
||||||
storage.write(key: "hacl_llt", value: "$_token").then((_) {
|
storage.write(key: "hacl_llt", value: "$_token").then((_) {
|
||||||
completer.complete();
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
prefs.setBool("oauth-used", true);
|
||||||
|
completer.complete();
|
||||||
|
});
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
@ -334,13 +335,13 @@ class Connection {
|
|||||||
_connect().timeout(connectTimeout, onTimeout: (){
|
_connect().timeout(connectTimeout, onTimeout: (){
|
||||||
_completer.completeError(HAError("No connection to Home Assistant", actions: [HAErrorAction.reconnect()]));
|
_completer.completeError(HAError("No connection to Home Assistant", actions: [HAErrorAction.reconnect()]));
|
||||||
}).then((_) {
|
}).then((_) {
|
||||||
Logger.d("[Sending] ==> $rawMessage");
|
Logger.d("[Sending] ==> ${auth ? "type="+dataObject['type'] : rawMessage}");
|
||||||
_socket.sink.add(rawMessage);
|
_socket.sink.add(rawMessage);
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
_completer.completeError(e);
|
_completer.completeError(e);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Logger.d("[Sending] ==> $rawMessage");
|
Logger.d("[Sending] ==> ${auth ? "type="+dataObject['type'] : rawMessage}");
|
||||||
_socket.sink.add(rawMessage);
|
_socket.sink.add(rawMessage);
|
||||||
}
|
}
|
||||||
return _completer.future;
|
return _completer.future;
|
||||||
@ -369,7 +370,7 @@ class Connection {
|
|||||||
//String endTime = formatDate(now, [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
//String endTime = formatDate(now, [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
||||||
String startTime = formatDate(now.subtract(Duration(hours: 24)), [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
String startTime = formatDate(now.subtract(Duration(hours: 24)), [yyyy, '-', mm, '-', dd, 'T', HH, ':', nn, ':', ss, z]);
|
||||||
String url = "$httpWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
|
String url = "$httpWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
|
||||||
Logger.d("[Sending] ==> $url");
|
Logger.d("[Sending] ==> HTTP /api/history/period/$startTime?&filter_entity_id=$entityId");
|
||||||
http.Response historyResponse;
|
http.Response historyResponse;
|
||||||
historyResponse = await http.get(url, headers: {
|
historyResponse = await http.get(url, headers: {
|
||||||
"authorization": "Bearer $_token",
|
"authorization": "Bearer $_token",
|
||||||
@ -377,7 +378,7 @@ class Connection {
|
|||||||
});
|
});
|
||||||
var history = json.decode(historyResponse.body);
|
var history = json.decode(historyResponse.body);
|
||||||
if (history is List) {
|
if (history is List) {
|
||||||
Logger.d( "[Received] <== ${history.first.length} history recors");
|
Logger.d( "[Received] <== HTTP ${history.first.length} history recors");
|
||||||
return history;
|
return history;
|
||||||
} else {
|
} else {
|
||||||
return [];
|
return [];
|
||||||
@ -387,7 +388,7 @@ class Connection {
|
|||||||
Future sendHTTPPost({String endPoint, String data, String contentType: "application/json", bool includeAuthHeader: true}) async {
|
Future sendHTTPPost({String endPoint, String data, String contentType: "application/json", bool includeAuthHeader: true}) async {
|
||||||
Completer completer = Completer();
|
Completer completer = Completer();
|
||||||
String url = "$httpWebHost$endPoint";
|
String url = "$httpWebHost$endPoint";
|
||||||
Logger.d("[Sending] ==> $url");
|
Logger.d("[Sending] ==> HTTP $endPoint");
|
||||||
Map<String, String> headers = {};
|
Map<String, String> headers = {};
|
||||||
if (contentType != null) {
|
if (contentType != null) {
|
||||||
headers["Content-Type"] = contentType;
|
headers["Content-Type"] = contentType;
|
||||||
@ -400,7 +401,7 @@ class Connection {
|
|||||||
headers: headers,
|
headers: headers,
|
||||||
body: data
|
body: data
|
||||||
).then((response) {
|
).then((response) {
|
||||||
Logger.d("[Received] <== ${response.statusCode}, ${response.body}");
|
Logger.d("[Received] <== HTTP ${response.statusCode}");
|
||||||
if (response.statusCode >= 200 && response.statusCode < 300 ) {
|
if (response.statusCode >= 200 && response.statusCode < 300 ) {
|
||||||
completer.complete(response.body);
|
completer.complete(response.body);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of 'main.dart';
|
||||||
|
|
||||||
class EntityState {
|
class EntityState {
|
||||||
static const on = 'on';
|
static const on = 'on';
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class AlarmControlPanelEntity extends Entity {
|
class AlarmControlPanelEntity extends Entity {
|
||||||
AlarmControlPanelEntity(Map rawData, String webHost) : super(rawData, webHost);
|
AlarmControlPanelEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class AlarmControlPanelControlsWidget extends StatefulWidget {
|
class AlarmControlPanelControlsWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class AutomationEntity extends Entity {
|
class AutomationEntity extends Entity {
|
||||||
AutomationEntity(Map rawData, String webHost) : super(rawData, webHost);
|
AutomationEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class ButtonEntity extends Entity {
|
class ButtonEntity extends Entity {
|
||||||
ButtonEntity(Map rawData, String webHost) : super(rawData, webHost);
|
ButtonEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class CameraEntity extends Entity {
|
class CameraEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class ClimateEntity extends Entity {
|
class ClimateEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class ClimateControlWidget extends StatefulWidget {
|
class ClimateControlWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class ClimateStateWidget extends StatelessWidget {
|
class ClimateStateWidget extends StatelessWidget {
|
||||||
@override
|
@override
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class CoverEntity extends Entity {
|
class CoverEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class CoverControlWidget extends StatefulWidget {
|
class CoverControlWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class CoverStateWidget extends StatelessWidget {
|
class CoverStateWidget extends StatelessWidget {
|
||||||
void _open(CoverEntity entity) {
|
void _open(CoverEntity entity) {
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class DateTimeEntity extends Entity {
|
class DateTimeEntity extends Entity {
|
||||||
DateTimeEntity(Map rawData, String webHost) : super(rawData, webHost);
|
DateTimeEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class DateTimeStateWidget extends StatelessWidget {
|
class DateTimeStateWidget extends StatelessWidget {
|
||||||
@override
|
@override
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class FanEntity extends Entity {
|
class FanEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class FanControlsWidget extends StatefulWidget {
|
class FanControlsWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class GroupEntity extends Entity {
|
class GroupEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class LightEntity extends Entity {
|
class LightEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class LightControlsWidget extends StatefulWidget {
|
class LightControlsWidget extends StatefulWidget {
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
String _tmpEffect;
|
String _tmpEffect;
|
||||||
|
|
||||||
void _resetState(LightEntity entity) {
|
void _resetState(LightEntity entity) {
|
||||||
_tmpBrightness = entity.brightness ?? 0;
|
_tmpBrightness = entity.brightness ?? 1;
|
||||||
_tmpWhiteValue = entity.whiteValue ?? 0;
|
_tmpWhiteValue = entity.whiteValue ?? 0;
|
||||||
_tmpColorTemp = entity.colorTemp ?? entity.minMireds?.toInt();
|
_tmpColorTemp = entity.colorTemp ?? entity.minMireds?.toInt();
|
||||||
_tmpColor = entity.color ?? _tmpColor;
|
_tmpColor = entity.color ?? _tmpColor;
|
||||||
@ -28,15 +28,9 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_tmpBrightness = value.round();
|
_tmpBrightness = value.round();
|
||||||
_changedHere = true;
|
_changedHere = true;
|
||||||
if (_tmpBrightness > 0) {
|
eventBus.fire(new ServiceCallEvent(
|
||||||
eventBus.fire(new ServiceCallEvent(
|
entity.domain, "turn_on", entity.entityId,
|
||||||
entity.domain, "turn_on", entity.entityId,
|
{"brightness": _tmpBrightness}));
|
||||||
{"brightness": _tmpBrightness}));
|
|
||||||
} else {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "turn_off", entity.entityId,
|
|
||||||
null));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,10 +108,10 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
_tmpBrightness = value.round();
|
_tmpBrightness = value.round();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
min: 0.0,
|
min: 1.0,
|
||||||
max: 255.0,
|
max: 255.0,
|
||||||
onChangeEnd: (value) => _setBrightness(entity, value),
|
onChangeEnd: (value) => _setBrightness(entity, value),
|
||||||
value: _tmpBrightness == null ? 0.0 : _tmpBrightness.toDouble(),
|
value: _tmpBrightness == null ? 1.0 : _tmpBrightness.toDouble(),
|
||||||
leading: Icon(Icons.brightness_5),
|
leading: Icon(Icons.brightness_5),
|
||||||
title: "Brightness",
|
title: "Brightness",
|
||||||
);
|
);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class LockEntity extends Entity {
|
class LockEntity extends Entity {
|
||||||
LockEntity(Map rawData, String webHost) : super(rawData, webHost);
|
LockEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class LockStateWidget extends StatelessWidget {
|
class LockStateWidget extends StatelessWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class MediaPlayerEntity extends Entity {
|
class MediaPlayerEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class MediaPlayerWidget extends StatelessWidget {
|
class MediaPlayerWidget extends StatelessWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class SelectEntity extends Entity {
|
class SelectEntity extends Entity {
|
||||||
List<String> get listOptions => attributes["options"] != null
|
List<String> get listOptions => attributes["options"] != null
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class SelectStateWidget extends StatefulWidget {
|
class SelectStateWidget extends StatefulWidget {
|
||||||
|
|
@ -1,8 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class SunEntity extends Entity {
|
|
||||||
SunEntity(Map rawData, String webHost) : super(rawData, webHost);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SensorEntity extends Entity {
|
class SensorEntity extends Entity {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class SliderEntity extends Entity {
|
class SliderEntity extends Entity {
|
||||||
SliderEntity(Map rawData, String webHost) : super(rawData, webHost);
|
SliderEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class SliderControlsWidget extends StatefulWidget {
|
class SliderControlsWidget extends StatefulWidget {
|
||||||
|
|
5
lib/entities/sun/sun_entity.class.dart
Normal file
5
lib/entities/sun/sun_entity.class.dart
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class SunEntity extends Entity {
|
||||||
|
SunEntity(Map rawData, String webHost) : super(rawData, webHost);
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class SwitchEntity extends Entity {
|
class SwitchEntity extends Entity {
|
||||||
SwitchEntity(Map rawData, String webHost) : super(rawData, webHost);
|
SwitchEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class SwitchStateWidget extends StatefulWidget {
|
class SwitchStateWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class TextEntity extends Entity {
|
class TextEntity extends Entity {
|
||||||
TextEntity(Map rawData, String webHost) : super(rawData, webHost);
|
TextEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class TextInputStateWidget extends StatefulWidget {
|
class TextInputStateWidget extends StatefulWidget {
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class TimerEntity extends Entity {
|
class TimerEntity extends Entity {
|
||||||
TimerEntity(Map rawData, String webHost) : super(rawData, webHost);
|
TimerEntity(Map rawData, String webHost) : super(rawData, webHost);
|
@ -1,4 +1,4 @@
|
|||||||
part of '../../main.dart';
|
part of '../../../main.dart';
|
||||||
|
|
||||||
class TimerState extends StatefulWidget {
|
class TimerState extends StatefulWidget {
|
||||||
//final bool expanded;
|
//final bool expanded;
|
@ -91,7 +91,6 @@ class HomeAssistant {
|
|||||||
"device_name": "$userName's ${Device().model}",
|
"device_name": "$userName's ${Device().model}",
|
||||||
"manufacturer": Device().manufacturer,
|
"manufacturer": Device().manufacturer,
|
||||||
"model": Device().model,
|
"model": Device().model,
|
||||||
"os_name": Device().osName,
|
|
||||||
"os_version": Device().osVersion,
|
"os_version": Device().osVersion,
|
||||||
"app_data": {
|
"app_data": {
|
||||||
"push_token": "$fcmToken",
|
"push_token": "$fcmToken",
|
||||||
@ -108,6 +107,7 @@ class HomeAssistant {
|
|||||||
registrationData.addAll({
|
registrationData.addAll({
|
||||||
"app_id": "ha_client",
|
"app_id": "ha_client",
|
||||||
"app_name": "$appName",
|
"app_name": "$appName",
|
||||||
|
"os_name": Device().osName,
|
||||||
"supports_encryption": false,
|
"supports_encryption": false,
|
||||||
});
|
});
|
||||||
Connection().sendHTTPPost(
|
Connection().sendHTTPPost(
|
||||||
@ -121,7 +121,7 @@ class HomeAssistant {
|
|||||||
prefs.setString("app-webhook-id", responseObject["webhook_id"]);
|
prefs.setString("app-webhook-id", responseObject["webhook_id"]);
|
||||||
Connection().webhookId = responseObject["webhook_id"];
|
Connection().webhookId = responseObject["webhook_id"];
|
||||||
completer.complete();
|
completer.complete();
|
||||||
eventBus.fire(ShowDialogEvent(
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
title: "Mobile app Integration was created",
|
title: "Mobile app Integration was created",
|
||||||
body: "HA Client was registered as MobileApp in your Home Assistant. To start using notifications you need to restart your Home Assistant",
|
body: "HA Client was registered as MobileApp in your Home Assistant. To start using notifications you need to restart your Home Assistant",
|
||||||
positiveText: "Restart now",
|
positiveText: "Restart now",
|
||||||
@ -147,45 +147,37 @@ class HomeAssistant {
|
|||||||
includeAuthHeader: false,
|
includeAuthHeader: false,
|
||||||
data: json.encode(updateData)
|
data: json.encode(updateData)
|
||||||
).then((response) {
|
).then((response) {
|
||||||
Logger.d("App registration works fine");
|
if (response == null || response.isEmpty) {
|
||||||
if (showOkDialog) {
|
Logger.d("No registration data in response. MobileApp integration was removed");
|
||||||
eventBus.fire(ShowDialogEvent(
|
_askToRegisterApp();
|
||||||
title: "All good",
|
} else {
|
||||||
body: "HA Client integration with your Home Assistant server works fine",
|
Logger.d("App registration works fine");
|
||||||
positiveText: "Nice!",
|
if (showOkDialog) {
|
||||||
negativeText: "Ok"
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
));
|
title: "All good",
|
||||||
|
body: "HA Client integration with your Home Assistant server works fine",
|
||||||
|
positiveText: "Nice!",
|
||||||
|
negativeText: "Ok"
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
completer.complete();
|
completer.complete();
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
if (e['code'] != null && e['code'] == 410) {
|
if (e['code'] != null && e['code'] == 410) {
|
||||||
Logger.e("MobileApp integration was removed");
|
Logger.e("MobileApp integration was removed");
|
||||||
eventBus.fire(ShowDialogEvent(
|
_askToRegisterApp();
|
||||||
title: "App integration was removed",
|
|
||||||
body: "Looks like app integration was removed from your Home Assistant. HA Client needs to be registered on your Home Assistant server to make it possible to use notifications and other useful stuff.",
|
|
||||||
positiveText: "Register now",
|
|
||||||
negativeText: "Cancel",
|
|
||||||
onPositive: () {
|
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
|
||||||
prefs.remove("app-webhook-id");
|
|
||||||
Connection().webhookId = null;
|
|
||||||
HomeAssistant().checkAppRegistration();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
));
|
|
||||||
} else {
|
} else {
|
||||||
Logger.e("Error updating app registration: ${e.toString()}");
|
Logger.e("Error updating app registration: ${e.toString()}");
|
||||||
eventBus.fire(ShowDialogEvent(
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
title: "App integration is not working properly",
|
title: "App integration is not working properly",
|
||||||
body: "Something wrong with HA Client integration on your Home Assistant server. Try to remove current app integration from Configuration -> Integrationds using web UI, restart your Home Assistant and go back to the app. NOTE that after clicking 'Ok' current integration data will be removed from the app and new integration wll be created on Home Assistant side on next app launch.",
|
body: "Something wrong with HA Client integration on your Home Assistant server. Please report this issue.",
|
||||||
positiveText: "Ok",
|
positiveText: "Report to GitHub",
|
||||||
negativeText: "I'll handle it",
|
negativeText: "Report to Discord",
|
||||||
onPositive: () {
|
onPositive: () {
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
HAUtils.launchURL("https://github.com/estevez-dev/ha_client/issues/new");
|
||||||
prefs.remove("app-webhook-id");
|
},
|
||||||
Connection().webhookId = null;
|
onNegative: () {
|
||||||
HAUtils.launchURL(Connection().httpWebHost+"/config/integrations/dashboard");
|
HAUtils.launchURL("https://discord.gg/AUzEvwn");
|
||||||
});
|
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -195,6 +187,22 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _askToRegisterApp() {
|
||||||
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
|
title: "App integration was removed",
|
||||||
|
body: "Looks like app integration was removed from your Home Assistant. HA Client needs to be registered on your Home Assistant server to make it possible to use notifications and other useful stuff.",
|
||||||
|
positiveText: "Register now",
|
||||||
|
negativeText: "Cancel",
|
||||||
|
onPositive: () {
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
prefs.remove("app-webhook-id");
|
||||||
|
Connection().webhookId = null;
|
||||||
|
HomeAssistant().checkAppRegistration();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Future _getConfig() async {
|
Future _getConfig() async {
|
||||||
await Connection().sendSocketMessage(type: "get_config").then((data) {
|
await Connection().sendSocketMessage(type: "get_config").then((data) {
|
||||||
_instanceConfig = Map.from(data);
|
_instanceConfig = Map.from(data);
|
||||||
@ -296,31 +304,25 @@ class HomeAssistant {
|
|||||||
List<HACard> result = [];
|
List<HACard> result = [];
|
||||||
rawCards.forEach((rawCard){
|
rawCards.forEach((rawCard){
|
||||||
try {
|
try {
|
||||||
bool isThereCardOptionsInside = rawCard["card"] != null;
|
//bool isThereCardOptionsInside = rawCard["card"] != null;
|
||||||
|
var rawCardInfo = rawCard["card"] ?? rawCard;
|
||||||
HACard card = HACard(
|
HACard card = HACard(
|
||||||
id: "card",
|
id: "card",
|
||||||
name: isThereCardOptionsInside ? rawCard["card"]["title"] ??
|
name: rawCardInfo["title"] ?? rawCardInfo["name"],
|
||||||
rawCard["card"]["name"] : rawCard["title"] ?? rawCard["name"],
|
type: rawCardInfo['type'],
|
||||||
type: isThereCardOptionsInside
|
columnsCount: rawCardInfo['columns'] ?? 4,
|
||||||
? rawCard["card"]['type']
|
showName: rawCardInfo['show_name'] ?? true,
|
||||||
: rawCard['type'],
|
showState: rawCardInfo['show_state'] ?? true,
|
||||||
columnsCount: isThereCardOptionsInside
|
showEmpty: rawCardInfo['show_empty'] ?? true,
|
||||||
? rawCard["card"]['columns'] ?? 4
|
stateFilter: rawCardInfo['state_filter'] ?? [],
|
||||||
: rawCard['columns'] ?? 4,
|
states: rawCardInfo['states'],
|
||||||
showName: isThereCardOptionsInside ? rawCard["card"]['show_name'] ??
|
conditions: rawCard['conditions'] ?? [],
|
||||||
true : rawCard['show_name'] ?? true,
|
content: rawCardInfo['content']
|
||||||
showState: isThereCardOptionsInside
|
|
||||||
? rawCard["card"]['show_state'] ?? true
|
|
||||||
: rawCard['show_state'] ?? true,
|
|
||||||
showEmpty: rawCard['show_empty'] ?? true,
|
|
||||||
stateFilter: rawCard['state_filter'] ?? [],
|
|
||||||
states: rawCard['states'],
|
|
||||||
content: rawCard['content']
|
|
||||||
);
|
);
|
||||||
if (rawCard["cards"] != null) {
|
if (rawCardInfo["cards"] != null) {
|
||||||
card.childCards = _createLovelaceCards(rawCard["cards"]);
|
card.childCards = _createLovelaceCards(rawCardInfo["cards"]);
|
||||||
}
|
}
|
||||||
rawCard["entities"]?.forEach((rawEntity) {
|
rawCardInfo["entities"]?.forEach((rawEntity) {
|
||||||
if (rawEntity is String) {
|
if (rawEntity is String) {
|
||||||
if (entities.isExist(rawEntity)) {
|
if (entities.isExist(rawEntity)) {
|
||||||
card.entities.add(EntityWrapper(entity: entities.get(rawEntity)));
|
card.entities.add(EntityWrapper(entity: entities.get(rawEntity)));
|
||||||
@ -383,15 +385,15 @@ class HomeAssistant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (rawCard["entity"] != null) {
|
if (rawCardInfo["entity"] != null) {
|
||||||
var en = rawCard["entity"];
|
var en = rawCardInfo["entity"];
|
||||||
if (en is String) {
|
if (en is String) {
|
||||||
if (entities.isExist(en)) {
|
if (entities.isExist(en)) {
|
||||||
Entity e = entities.get(en);
|
Entity e = entities.get(en);
|
||||||
card.linkedEntityWrapper = EntityWrapper(
|
card.linkedEntityWrapper = EntityWrapper(
|
||||||
entity: e,
|
entity: e,
|
||||||
icon: rawCard["icon"],
|
icon: rawCardInfo["icon"],
|
||||||
displayName: rawCard["name"],
|
displayName: rawCardInfo["name"],
|
||||||
uiAction: EntityUIAction(rawEntityData: rawCard)
|
uiAction: EntityUIAction(rawEntityData: rawCard)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
160
lib/main.dart
160
lib/main.dart
@ -22,28 +22,30 @@ import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
|
|||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:device_info/device_info.dart';
|
import 'package:device_info/device_info.dart';
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
|
|
||||||
part 'entity_class/const.dart';
|
part 'const.dart';
|
||||||
part 'entity_class/entity.class.dart';
|
part 'entities/entity.class.dart';
|
||||||
part 'entity_class/entity_wrapper.class.dart';
|
part 'entities/entity_wrapper.class.dart';
|
||||||
part 'entity_class/timer_entity.dart';
|
part 'entities/timer/timer_entity.class.dart';
|
||||||
part 'entity_class/switch_entity.class.dart';
|
part 'entities/switch/switch_entity.class.dart';
|
||||||
part 'entity_class/button_entity.class.dart';
|
part 'entities/button/button_entity.class.dart';
|
||||||
part 'entity_class/text_entity.class.dart';
|
part 'entities/text/text_entity.class.dart';
|
||||||
part 'entity_class/climate_entity.class.dart';
|
part 'entities/climate/climate_entity.class.dart';
|
||||||
part 'entity_class/cover_entity.class.dart';
|
part 'entities/cover/cover_entity.class.dart';
|
||||||
part 'entity_class/date_time_entity.class.dart';
|
part 'entities/date_time/date_time_entity.class.dart';
|
||||||
part 'entity_class/light_entity.class.dart';
|
part 'entities/light/light_entity.class.dart';
|
||||||
part 'entity_class/select_entity.class.dart';
|
part 'entities/select/select_entity.class.dart';
|
||||||
part 'entity_class/other_entity.class.dart';
|
part 'entities/sun/sun_entity.class.dart';
|
||||||
part 'entity_class/slider_entity.dart';
|
part 'entities/sensor/sensor_entity.class.dart';
|
||||||
part 'entity_class/media_player_entity.class.dart';
|
part 'entities/slider/slider_entity.dart';
|
||||||
part 'entity_class/lock_entity.class.dart';
|
part 'entities/media_player/media_player_entity.class.dart';
|
||||||
part 'entity_class/group_entity.class.dart';
|
part 'entities/lock/lock_entity.class.dart';
|
||||||
part 'entity_class/fan_entity.class.dart';
|
part 'entities/group/group_entity.class.dart';
|
||||||
part 'entity_class/automation_entity.dart';
|
part 'entities/fan/fan_entity.class.dart';
|
||||||
part 'entity_class/camera_entity.class.dart';
|
part 'entities/automation/automation_entity.class.dart';
|
||||||
part 'entity_class/alarm_control_panel.class.dart';
|
part 'entities/camera/camera_entity.class.dart';
|
||||||
|
part 'entities/alarm_control_panel/alarm_control_panel_entity.class.dart';
|
||||||
part 'entity_widgets/common/badge.dart';
|
part 'entity_widgets/common/badge.dart';
|
||||||
part 'entity_widgets/model_widgets.dart';
|
part 'entity_widgets/model_widgets.dart';
|
||||||
part 'entity_widgets/default_entity_container.dart';
|
part 'entity_widgets/default_entity_container.dart';
|
||||||
@ -68,27 +70,31 @@ part 'entity_widgets/history_chart/numeric_state_history_chart.dart';
|
|||||||
part 'entity_widgets/history_chart/combined_history_chart.dart';
|
part 'entity_widgets/history_chart/combined_history_chart.dart';
|
||||||
part 'entity_widgets/history_chart/history_control_widget.dart';
|
part 'entity_widgets/history_chart/history_control_widget.dart';
|
||||||
part 'entity_widgets/history_chart/entity_history_moment.dart';
|
part 'entity_widgets/history_chart/entity_history_moment.dart';
|
||||||
part 'entity_widgets/state/switch_state.dart';
|
part 'entities/switch/widget/switch_state.dart';
|
||||||
part 'entity_widgets/controls/slider_controls.dart';
|
part 'entities/slider/widgets/slider_controls.dart';
|
||||||
part 'entity_widgets/state/text_input_state.dart';
|
part 'entities/text/widgets/text_input_state.dart';
|
||||||
part 'entity_widgets/state/select_state.dart';
|
part 'entities/select/widgets/select_state.dart';
|
||||||
part 'entity_widgets/state/simple_state.dart';
|
part 'entity_widgets/common/simple_state.dart';
|
||||||
part 'entity_widgets/state/timer_state.dart';
|
part 'entities/timer/widgets/timer_state.dart';
|
||||||
part 'entity_widgets/state/climate_state.dart';
|
part 'entities/climate/widgets/climate_state.widget.dart';
|
||||||
part 'entity_widgets/state/cover_state.dart';
|
part 'entities/cover/widgets/cover_state.dart';
|
||||||
part 'entity_widgets/state/date_time_state.dart';
|
part 'entities/date_time/widgets/date_time_state.dart';
|
||||||
part 'entity_widgets/state/lock_state.dart';
|
part 'entities/lock/widgets/lock_state.dart';
|
||||||
part 'entity_widgets/controls/climate_controls.dart';
|
part 'entities/climate/widgets/climate_controls.dart';
|
||||||
part 'entity_widgets/controls/cover_controls.dart';
|
part 'entities/cover/widgets/cover_controls.widget.dart';
|
||||||
part 'entity_widgets/controls/light_controls.dart';
|
part 'entities/light/widgets/light_controls.dart';
|
||||||
part 'entity_widgets/controls/media_player_widgets.dart';
|
part 'entities/media_player/widgets/media_player_widgets.dart';
|
||||||
part 'entity_widgets/controls/fan_controls.dart';
|
part 'entities/fan/widgets/fan_controls.dart';
|
||||||
part 'entity_widgets/controls/alarm_control_panel_controls.dart';
|
part 'entities/alarm_control_panel/widgets/alarm_control_panel_controls.widget.dart';
|
||||||
part 'settings.page.dart';
|
part 'pages/settings.page.dart';
|
||||||
part 'panel.page.dart';
|
part 'pages/purchase.page.dart';
|
||||||
|
part 'pages/widgets/product_purchase.widget.dart';
|
||||||
|
part 'pages/widgets/page_loading_indicator.dart';
|
||||||
|
part 'pages/widgets/page_loading_error.dart';
|
||||||
|
part 'pages/panel.page.dart';
|
||||||
part 'home_assistant.class.dart';
|
part 'home_assistant.class.dart';
|
||||||
part 'log.page.dart';
|
part 'pages/log.page.dart';
|
||||||
part 'entity.page.dart';
|
part 'pages/entity.page.dart';
|
||||||
part 'utils.class.dart';
|
part 'utils.class.dart';
|
||||||
part 'mdi.class.dart';
|
part 'mdi.class.dart';
|
||||||
part 'entity_collection.class.dart';
|
part 'entity_collection.class.dart';
|
||||||
@ -110,7 +116,7 @@ EventBus eventBus = new EventBus();
|
|||||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
|
||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
const appVersion = "0.6.0";
|
const appVersion = "0.6.3";
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FlutterError.onError = (errorDetails) {
|
FlutterError.onError = (errorDetails) {
|
||||||
@ -147,15 +153,25 @@ class HAClientApp extends StatelessWidget {
|
|||||||
"/": (context) => MainPage(title: 'HA Client', homeAssistant: homeAssistant,),
|
"/": (context) => MainPage(title: 'HA Client', homeAssistant: homeAssistant,),
|
||||||
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
||||||
"/configuration": (context) => PanelPage(title: "Configuration"),
|
"/configuration": (context) => PanelPage(title: "Configuration"),
|
||||||
|
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
||||||
"/log-view": (context) => LogViewPage(title: "Log"),
|
"/log-view": (context) => LogViewPage(title: "Log"),
|
||||||
"/login": (_) => WebviewScaffold(
|
"/login": (context) => WebviewScaffold(
|
||||||
url: "${Connection().oauthUrl}",
|
url: "${Connection().oauthUrl}",
|
||||||
appBar: new AppBar(
|
appBar: new AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.help),
|
icon: Icon(Icons.help),
|
||||||
onPressed: () => HAUtils.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/docs#authentication")
|
onPressed: () => HAUtils.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/docs#authentication")
|
||||||
),
|
),
|
||||||
title: new Text("Login to your Home Assistant"),
|
title: new Text("Login with HA"),
|
||||||
|
actions: <Widget>[
|
||||||
|
FlatButton(
|
||||||
|
child: Text("Manual", style: TextStyle(color: Colors.white)),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pushNamed("/connection-settings");
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -175,19 +191,26 @@ class MainPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
|
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
|
||||||
|
|
||||||
|
StreamSubscription<List<PurchaseDetails>> _subscription;
|
||||||
StreamSubscription _stateSubscription;
|
StreamSubscription _stateSubscription;
|
||||||
StreamSubscription _settingsSubscription;
|
StreamSubscription _settingsSubscription;
|
||||||
StreamSubscription _serviceCallSubscription;
|
StreamSubscription _serviceCallSubscription;
|
||||||
StreamSubscription _showEntityPageSubscription;
|
StreamSubscription _showEntityPageSubscription;
|
||||||
StreamSubscription _showErrorSubscription;
|
StreamSubscription _showErrorSubscription;
|
||||||
StreamSubscription _startAuthSubscription;
|
StreamSubscription _startAuthSubscription;
|
||||||
StreamSubscription _showDialogSubscription;
|
StreamSubscription _showPopupDialogSubscription;
|
||||||
|
StreamSubscription _showPopupMessageSubscription;
|
||||||
StreamSubscription _reloadUISubscription;
|
StreamSubscription _reloadUISubscription;
|
||||||
int _previousViewCount;
|
int _previousViewCount;
|
||||||
bool _showLoginButton = false;
|
bool _showLoginButton = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
final Stream purchaseUpdates =
|
||||||
|
InAppPurchaseConnection.instance.purchaseUpdatedStream;
|
||||||
|
_subscription = purchaseUpdates.listen((purchases) {
|
||||||
|
_handlePurchaseUpdates(purchases);
|
||||||
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
@ -294,6 +317,20 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
_quickLoad();
|
_quickLoad();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handlePurchaseUpdates(purchase) {
|
||||||
|
if (purchase is List<PurchaseDetails>) {
|
||||||
|
if (purchase[0].status == PurchaseStatus.purchased) {
|
||||||
|
eventBus.fire(ShowPopupMessageEvent(
|
||||||
|
title: "Thanks a lot!",
|
||||||
|
body: "Thank you for supporting HA Client development!",
|
||||||
|
buttonText: "Ok"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.e("Something wrong with purchase handling. Got: $purchase");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future _subscribe() {
|
Future _subscribe() {
|
||||||
Completer completer = Completer();
|
Completer completer = Completer();
|
||||||
@ -312,9 +349,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
_quickLoad();
|
_quickLoad();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (_showDialogSubscription == null) {
|
if (_showPopupDialogSubscription == null) {
|
||||||
_showDialogSubscription = eventBus.on<ShowDialogEvent>().listen((event){
|
_showPopupDialogSubscription = eventBus.on<ShowPopupDialogEvent>().listen((event){
|
||||||
_showDialog(
|
_showPopupDialog(
|
||||||
title: event.title,
|
title: event.title,
|
||||||
body: event.body,
|
body: event.body,
|
||||||
onPositive: event.onPositive,
|
onPositive: event.onPositive,
|
||||||
@ -324,6 +361,17 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (_showPopupMessageSubscription == null) {
|
||||||
|
_showPopupMessageSubscription = eventBus.on<ShowPopupMessageEvent>().listen((event){
|
||||||
|
_showPopupDialog(
|
||||||
|
title: event.title,
|
||||||
|
body: event.body,
|
||||||
|
onPositive: event.onButtonClick,
|
||||||
|
positiveText: event.buttonText,
|
||||||
|
negativeText: null
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
if (_serviceCallSubscription == null) {
|
if (_serviceCallSubscription == null) {
|
||||||
_serviceCallSubscription =
|
_serviceCallSubscription =
|
||||||
eventBus.on<ServiceCallEvent>().listen((event) {
|
eventBus.on<ServiceCallEvent>().listen((event) {
|
||||||
@ -366,7 +414,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showOAuth() {
|
void _showOAuth() {
|
||||||
Logger.d("_showOAuth: ${Connection().oauthUrl}");
|
|
||||||
Navigator.of(context).pushNamed('/login');
|
Navigator.of(context).pushNamed('/login');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,7 +427,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showDialog({String title, String body, var onPositive, var onNegative, String positiveText, String negativeText}) {
|
void _showPopupDialog({String title, String body, var onPositive, var onNegative, String positiveText, String negativeText}) {
|
||||||
List<Widget> buttons = [];
|
List<Widget> buttons = [];
|
||||||
buttons.add(FlatButton(
|
buttons.add(FlatButton(
|
||||||
child: new Text("$positiveText"),
|
child: new Text("$positiveText"),
|
||||||
@ -521,6 +568,15 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
|
new ListTile(
|
||||||
|
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:food")),
|
||||||
|
title: Text("Support app development"),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pushNamed('/putchase');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Divider(),
|
||||||
new ListTile(
|
new ListTile(
|
||||||
leading: Icon(Icons.help),
|
leading: Icon(Icons.help),
|
||||||
title: Text("Help"),
|
title: Text("Help"),
|
||||||
@ -857,10 +913,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
_stateSubscription?.cancel();
|
_stateSubscription?.cancel();
|
||||||
_settingsSubscription?.cancel();
|
_settingsSubscription?.cancel();
|
||||||
_serviceCallSubscription?.cancel();
|
_serviceCallSubscription?.cancel();
|
||||||
_showDialogSubscription?.cancel();
|
_showPopupDialogSubscription?.cancel();
|
||||||
|
_showPopupMessageSubscription?.cancel();
|
||||||
_showEntityPageSubscription?.cancel();
|
_showEntityPageSubscription?.cancel();
|
||||||
_showErrorSubscription?.cancel();
|
_showErrorSubscription?.cancel();
|
||||||
_startAuthSubscription?.cancel();
|
_startAuthSubscription?.cancel();
|
||||||
|
_subscription?.cancel();
|
||||||
_reloadUISubscription?.cancel();
|
_reloadUISubscription?.cancel();
|
||||||
//TODO disconnect
|
//TODO disconnect
|
||||||
//widget.homeAssistant?.disconnect();
|
//widget.homeAssistant?.disconnect();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
part of 'main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class EntityViewPage extends StatefulWidget {
|
class EntityViewPage extends StatefulWidget {
|
||||||
EntityViewPage({Key key, @required this.entityId, @required this.homeAssistant }) : super(key: key);
|
EntityViewPage({Key key, @required this.entityId, @required this.homeAssistant }) : super(key: key);
|
@ -1,4 +1,4 @@
|
|||||||
part of 'main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class LogViewPage extends StatefulWidget {
|
class LogViewPage extends StatefulWidget {
|
||||||
LogViewPage({Key key, this.title}) : super(key: key);
|
LogViewPage({Key key, this.title}) : super(key: key);
|
@ -1,4 +1,4 @@
|
|||||||
part of 'main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class PanelPage extends StatefulWidget {
|
class PanelPage extends StatefulWidget {
|
||||||
PanelPage({Key key, this.title, this.panel}) : super(key: key);
|
PanelPage({Key key, this.title, this.panel}) : super(key: key);
|
107
lib/pages/purchase.page.dart
Normal file
107
lib/pages/purchase.page.dart
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class PurchasePage extends StatefulWidget {
|
||||||
|
PurchasePage({Key key, this.title}) : super(key: key);
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
|
||||||
|
@override
|
||||||
|
_PurchasePageState createState() => new _PurchasePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PurchasePageState extends State<PurchasePage> {
|
||||||
|
|
||||||
|
bool _loaded = false;
|
||||||
|
String _error = "";
|
||||||
|
List<ProductDetails> _products;
|
||||||
|
List<PurchaseDetails> _purchases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_loadProducts();
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadProducts() async {
|
||||||
|
final bool available = await InAppPurchaseConnection.instance.isAvailable();
|
||||||
|
if (!available) {
|
||||||
|
setState(() {
|
||||||
|
_error = "Error connecting to store";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const Set<String> _kIds = {'just_few_bucks_per_year', 'app_fan_support_per_year', 'grateful_user_support_per_year'};
|
||||||
|
final ProductDetailsResponse response = await InAppPurchaseConnection.instance.queryProductDetails(_kIds);
|
||||||
|
if (!response.notFoundIDs.isEmpty) {
|
||||||
|
Logger.d("Products not found: ${response.notFoundIDs}");
|
||||||
|
}
|
||||||
|
_products = response.productDetails;
|
||||||
|
_loadPreviousPurchases();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadPreviousPurchases() async {
|
||||||
|
final QueryPurchaseDetailsResponse response = await InAppPurchaseConnection.instance.queryPastPurchases();
|
||||||
|
if (response.error != null) {
|
||||||
|
setState(() {
|
||||||
|
_error = "Error loading previous purchases";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_purchases = response.pastPurchases;
|
||||||
|
for (PurchaseDetails purchase in _purchases) {
|
||||||
|
Logger.d("Previous purchase: ${purchase.status}");
|
||||||
|
}
|
||||||
|
if (_products.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_error = "No data found in store";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildProducts() {
|
||||||
|
List<Widget> productWidgets = [];
|
||||||
|
for (ProductDetails product in _products) {
|
||||||
|
productWidgets.add(
|
||||||
|
ProductPurchase(
|
||||||
|
product: product,
|
||||||
|
onBuy: (product) => _buyProduct(product),
|
||||||
|
purchased: _purchases.any((purchase) { return purchase.productID == product.id;}),)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return ListView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
|
children: productWidgets
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _buyProduct(ProductDetails product) {
|
||||||
|
Logger.d("Starting purchase of ${product.id}");
|
||||||
|
final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
|
||||||
|
InAppPurchaseConnection.instance.buyNonConsumable(purchaseParam: purchaseParam);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
Widget body;
|
||||||
|
if (!_loaded) {
|
||||||
|
body = _error.isEmpty ? PageLoadingIndicator() : PageLoadingError(errorText: _error);
|
||||||
|
} else {
|
||||||
|
body = _buildProducts();
|
||||||
|
}
|
||||||
|
return new Scaffold(
|
||||||
|
appBar: new AppBar(
|
||||||
|
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
|
||||||
|
Navigator.pop(context);
|
||||||
|
}),
|
||||||
|
title: new Text(widget.title),
|
||||||
|
),
|
||||||
|
body: body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
part of 'main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class ConnectionSettingsPage extends StatefulWidget {
|
class ConnectionSettingsPage extends StatefulWidget {
|
||||||
ConnectionSettingsPage({Key key, this.title}) : super(key: key);
|
ConnectionSettingsPage({Key key, this.title}) : super(key: key);
|
||||||
@ -16,10 +16,13 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
String _newHassioPort = "";
|
String _newHassioPort = "";
|
||||||
String _socketProtocol = "wss";
|
String _socketProtocol = "wss";
|
||||||
String _newSocketProtocol = "wss";
|
String _newSocketProtocol = "wss";
|
||||||
|
String _longLivedToken = "";
|
||||||
|
String _newLongLivedToken = "";
|
||||||
bool _useLovelace = true;
|
bool _useLovelace = true;
|
||||||
bool _newUseLovelace = true;
|
bool _newUseLovelace = true;
|
||||||
|
|
||||||
String oauthUrl;
|
String oauthUrl;
|
||||||
|
bool useOAuth = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -30,6 +33,23 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
|
|
||||||
_loadSettings() async {
|
_loadSettings() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
final storage = new FlutterSecureStorage();
|
||||||
|
|
||||||
|
try {
|
||||||
|
useOAuth = prefs.getBool("oauth-used") ?? true;
|
||||||
|
} catch (e) {
|
||||||
|
useOAuth = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useOAuth) {
|
||||||
|
try {
|
||||||
|
_longLivedToken = _newLongLivedToken =
|
||||||
|
await storage.read(key: "hacl_llt");
|
||||||
|
} catch (e) {
|
||||||
|
_longLivedToken = _newLongLivedToken = "";
|
||||||
|
await storage.delete(key: "hacl_llt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? "";
|
_hassioDomain = _newHassioDomain = prefs.getString("hassio-domain")?? "";
|
||||||
@ -48,7 +68,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
(_newHassioPort != _hassioPort) ||
|
(_newHassioPort != _hassioPort) ||
|
||||||
(_newHassioDomain != _hassioDomain) ||
|
(_newHassioDomain != _hassioDomain) ||
|
||||||
(_newSocketProtocol != _socketProtocol) ||
|
(_newSocketProtocol != _socketProtocol) ||
|
||||||
(_newUseLovelace != _useLovelace));
|
(_newUseLovelace != _useLovelace) ||
|
||||||
|
(_newLongLivedToken != _longLivedToken));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +77,15 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
if (_newHassioDomain.indexOf("http") == 0 && _newHassioDomain.indexOf("//") > 0) {
|
if (_newHassioDomain.indexOf("http") == 0 && _newHassioDomain.indexOf("//") > 0) {
|
||||||
_newHassioDomain = _newHassioDomain.split("//")[1];
|
_newHassioDomain = _newHassioDomain.split("//")[1];
|
||||||
}
|
}
|
||||||
|
_newHassioDomain = _newHassioDomain.split("/")[0];
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
final storage = new FlutterSecureStorage();
|
||||||
|
if (_newLongLivedToken.isNotEmpty) {
|
||||||
|
prefs.setBool("oauth-used", false);
|
||||||
|
await storage.write(key: "hacl_llt", value: _newLongLivedToken);
|
||||||
|
} else if (!useOAuth) {
|
||||||
|
await storage.delete(key: "hacl_llt");
|
||||||
|
}
|
||||||
prefs.setString("hassio-domain", _newHassioDomain);
|
prefs.setString("hassio-domain", _newHassioDomain);
|
||||||
prefs.setString("hassio-port", _newHassioPort);
|
prefs.setString("hassio-port", _newHassioPort);
|
||||||
prefs.setString("hassio-protocol", _newSocketProtocol);
|
prefs.setString("hassio-protocol", _newSocketProtocol);
|
||||||
@ -171,6 +200,33 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Text(
|
||||||
|
"Authentication settings",
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.black45,
|
||||||
|
fontSize: 20.0
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(height: 10.0,),
|
||||||
|
Text(
|
||||||
|
"You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.",
|
||||||
|
style: TextStyle(color: Colors.redAccent),
|
||||||
|
),
|
||||||
|
new TextField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Long-lived token"
|
||||||
|
),
|
||||||
|
controller: new TextEditingController.fromValue(
|
||||||
|
new TextEditingValue(
|
||||||
|
text: _newLongLivedToken ?? '',
|
||||||
|
selection:
|
||||||
|
new TextSelection.collapsed(offset: _newLongLivedToken != null ? _newLongLivedToken.length : 0)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
onChanged: (value) {
|
||||||
|
_newLongLivedToken = value;
|
||||||
|
}
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
32
lib/pages/widgets/page_loading_error.dart
Normal file
32
lib/pages/widgets/page_loading_error.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class PageLoadingError extends StatelessWidget {
|
||||||
|
|
||||||
|
final String errorText;
|
||||||
|
|
||||||
|
const PageLoadingError({Key key, this.errorText: "Error"}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
||||||
|
child: Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.redAccent,
|
||||||
|
size: 48.0
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Text(this.errorText, style: TextStyle(color: Colors.black45))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
23
lib/pages/widgets/page_loading_indicator.dart
Normal file
23
lib/pages/widgets/page_loading_indicator.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class PageLoadingIndicator extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
||||||
|
child: CircularProgressIndicator()
|
||||||
|
),
|
||||||
|
Text("Loading...", style: TextStyle(color: Colors.black45))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
68
lib/pages/widgets/product_purchase.widget.dart
Normal file
68
lib/pages/widgets/product_purchase.widget.dart
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class ProductPurchase extends StatelessWidget {
|
||||||
|
|
||||||
|
final ProductDetails product;
|
||||||
|
final onBuy;
|
||||||
|
final purchased;
|
||||||
|
|
||||||
|
const ProductPurchase({Key key, @required this.product, @required this.onBuy, this.purchased}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
String period = "/ ";
|
||||||
|
Color priceColor;
|
||||||
|
if (product.id.contains("year")) {
|
||||||
|
period += "year";
|
||||||
|
priceColor = Colors.amber;
|
||||||
|
} else {
|
||||||
|
period += "month";
|
||||||
|
priceColor = Colors.deepOrangeAccent;
|
||||||
|
}
|
||||||
|
return Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(Sizes.leftWidgetPadding),
|
||||||
|
child: Flex(
|
||||||
|
direction: Axis.horizontal,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
flex: 5,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.only(right: Sizes.rightWidgetPadding),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
"${product.title}",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: 16.0
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(height: Sizes.rowPadding,),
|
||||||
|
Text(
|
||||||
|
"${product.description}",
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 4,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
Container(height: Sizes.rowPadding,),
|
||||||
|
Text("${product.price} $period", style: TextStyle(color: priceColor)),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: RaisedButton(
|
||||||
|
child: Text(this.purchased ? "Bought" : "Buy", style: TextStyle(color: Colors.white)),
|
||||||
|
color: Colors.blue,
|
||||||
|
onPressed: this.purchased ? null : () => this.onBuy(this.product),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ class HACard {
|
|||||||
int columnsCount;
|
int columnsCount;
|
||||||
List stateFilter;
|
List stateFilter;
|
||||||
List states;
|
List states;
|
||||||
|
List conditions;
|
||||||
String content;
|
String content;
|
||||||
|
|
||||||
HACard({
|
HACard({
|
||||||
@ -26,6 +27,7 @@ class HACard {
|
|||||||
this.showEmpty: true,
|
this.showEmpty: true,
|
||||||
this.content,
|
this.content,
|
||||||
this.states,
|
this.states,
|
||||||
|
this.conditions,
|
||||||
@required this.type
|
@required this.type
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -24,6 +24,22 @@ class CardWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (card.conditions.isNotEmpty) {
|
||||||
|
bool showCardByConditions = false;
|
||||||
|
for (var condition in card.conditions) {
|
||||||
|
Entity conditionEntity = HomeAssistant().entities.get(condition['entity']);
|
||||||
|
if (conditionEntity != null &&
|
||||||
|
(condition['state'] != null && conditionEntity.state == condition['state']) ||
|
||||||
|
(condition['state_not'] != null && conditionEntity.state != condition['state_not'])
|
||||||
|
) {
|
||||||
|
showCardByConditions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!showCardByConditions) {
|
||||||
|
return Container(width: 0.0, height: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (card.type) {
|
switch (card.type) {
|
||||||
|
|
||||||
case CardType.entities: {
|
case CardType.entities: {
|
||||||
|
@ -256,7 +256,7 @@ class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
restart() {
|
restart() {
|
||||||
eventBus.fire(ShowDialogEvent(
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
title: "Are you sure you want to restart Home Assistant?",
|
title: "Are you sure you want to restart Home Assistant?",
|
||||||
body: "This will restart your Home Assistant server.",
|
body: "This will restart your Home Assistant server.",
|
||||||
positiveText: "Sure. Make it so",
|
positiveText: "Sure. Make it so",
|
||||||
@ -268,7 +268,7 @@ class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
eventBus.fire(ShowDialogEvent(
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
title: "Are you sure you wanr to STOP Home Assistant?",
|
title: "Are you sure you wanr to STOP Home Assistant?",
|
||||||
body: "This will STOP your Home Assistant server. It means that your web interface as well as HA Client will not work untill you'll find a way to start your server using ssh or something.",
|
body: "This will STOP your Home Assistant server. It means that your web interface as well as HA Client will not work untill you'll find a way to start your server using ssh or something.",
|
||||||
positiveText: "Sure. Make it so",
|
positiveText: "Sure. Make it so",
|
||||||
@ -284,7 +284,7 @@ class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resetRegistration() {
|
resetRegistration() {
|
||||||
eventBus.fire(ShowDialogEvent(
|
eventBus.fire(ShowPopupDialogEvent(
|
||||||
title: "Waaaait",
|
title: "Waaaait",
|
||||||
body: "If you don't whant to have duplicate integrations and entities in your HA for your current device, first you need to remove MobileApp integration from Integration settings in HA and restart server.",
|
body: "If you don't whant to have duplicate integrations and entities in your HA for your current device, first you need to remove MobileApp integration from Integration settings in HA and restart server.",
|
||||||
positiveText: "Done it already",
|
positiveText: "Done it already",
|
||||||
|
@ -173,7 +173,7 @@ class ServiceCallEvent {
|
|||||||
ServiceCallEvent(this.domain, this.service, this.entityId, this.additionalParams);
|
ServiceCallEvent(this.domain, this.service, this.entityId, this.additionalParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShowDialogEvent {
|
class ShowPopupDialogEvent {
|
||||||
final String title;
|
final String title;
|
||||||
final String body;
|
final String body;
|
||||||
final String positiveText;
|
final String positiveText;
|
||||||
@ -181,7 +181,16 @@ class ShowDialogEvent {
|
|||||||
final onPositive;
|
final onPositive;
|
||||||
final onNegative;
|
final onNegative;
|
||||||
|
|
||||||
ShowDialogEvent({this.title, this.body, this.positiveText: "Ok", this.negativeText: "Cancel", this.onPositive, this.onNegative});
|
ShowPopupDialogEvent({this.title, this.body, this.positiveText: "Ok", this.negativeText: "Cancel", this.onPositive, this.onNegative});
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShowPopupMessageEvent {
|
||||||
|
final String title;
|
||||||
|
final String body;
|
||||||
|
final String buttonText;
|
||||||
|
final onButtonClick;
|
||||||
|
|
||||||
|
ShowPopupMessageEvent({this.title, this.body, this.buttonText: "Ok", this.onButtonClick});
|
||||||
}
|
}
|
||||||
|
|
||||||
class ShowEntityPageEvent {
|
class ShowEntityPageEvent {
|
||||||
|
20
pubspec.lock
20
pubspec.lock
@ -105,7 +105,7 @@ packages:
|
|||||||
name: firebase_messaging
|
name: firebase_messaging
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0+4"
|
version: "5.1.4"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -138,7 +138,7 @@ packages:
|
|||||||
name: flutter_local_notifications
|
name: flutter_local_notifications
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.1+3"
|
version: "0.8.2"
|
||||||
flutter_markdown:
|
flutter_markdown:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -186,6 +186,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.4"
|
version: "2.1.4"
|
||||||
|
in_app_purchase:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: in_app_purchase
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.1"
|
||||||
intl:
|
intl:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -193,6 +200,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.15.8"
|
version: "0.15.8"
|
||||||
|
json_annotation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: json_annotation
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -379,7 +393,7 @@ packages:
|
|||||||
name: xml
|
name: xml
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.5"
|
version: "3.5.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
13
pubspec.yaml
13
pubspec.yaml
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 0.6.0+606
|
version: 0.6.3+630
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
||||||
@ -18,13 +18,14 @@ dependencies:
|
|||||||
date_format: any
|
date_format: any
|
||||||
charts_flutter: any
|
charts_flutter: any
|
||||||
flutter_markdown: any
|
flutter_markdown: any
|
||||||
|
in_app_purchase: ^0.2.1
|
||||||
# flutter_svg: ^0.10.3
|
# flutter_svg: ^0.10.3
|
||||||
flutter_custom_tabs: ^0.6.0
|
flutter_custom_tabs: ^0.6.0
|
||||||
firebase_messaging: ^4.0.0+1
|
firebase_messaging: ^5.1.4
|
||||||
flutter_webview_plugin: ^0.3.1
|
flutter_webview_plugin: ^0.3.7
|
||||||
flutter_secure_storage: ^3.2.0
|
flutter_secure_storage: ^3.2.1+1
|
||||||
device_info: ^0.4.0+1
|
device_info: ^0.4.0+2
|
||||||
flutter_local_notifications: ^0.7.1+3
|
flutter_local_notifications: ^0.8.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user