Settings loading refactored. WIP #338

This commit is contained in:
estevez-dev 2019-03-19 23:07:40 +02:00
parent d70ba0a55a
commit ccb88884a7
30 changed files with 163 additions and 85 deletions

View File

@ -15,7 +15,8 @@
<application
android:name="io.flutter.app.FlutterApplication"
android:label="HA Client"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/ic_launcher"
android:networkSecurityConfig="@xml/network_security_config">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">homemade.systems</domain>
</domain-config>
</network-security-config>

View File

@ -1,7 +1,8 @@
part of '../main.dart';
class AlarmControlPanelEntity extends Entity {
AlarmControlPanelEntity(Map rawData) : super(rawData);
AlarmControlPanelEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildAdditionalControlsForPage(BuildContext context) {

View File

@ -1,7 +1,8 @@
part of '../main.dart';
class AutomationEntity extends Entity {
AutomationEntity(Map rawData) : super(rawData);
AutomationEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {

View File

@ -1,7 +1,8 @@
part of '../main.dart';
class ButtonEntity extends Entity {
ButtonEntity(Map rawData) : super(rawData);
ButtonEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {

View File

@ -4,7 +4,7 @@ class CameraEntity extends Entity {
static const SUPPORT_ON_OFF = 1;
CameraEntity(Map rawData) : super(rawData);
CameraEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportOnOff => ((supportedFeatures &
CameraEntity.SUPPORT_ON_OFF) ==

View File

@ -23,6 +23,8 @@ class ClimateEntity extends Entity {
static const SUPPORT_AUX_HEAT = 2048;
static const SUPPORT_ON_OFF = 4096;
ClimateEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportTargetTemperature => ((supportedFeatures &
ClimateEntity.SUPPORT_TARGET_TEMPERATURE) ==
ClimateEntity.SUPPORT_TARGET_TEMPERATURE);
@ -88,11 +90,9 @@ class ClimateEntity extends Entity {
bool get isOff => state == EntityState.off;
bool get auxHeat => attributes['aux_heat'] == "on";
ClimateEntity(Map rawData) : super(rawData);
@override
void update(Map rawData) {
super.update(rawData);
void update(Map rawData, String webHost) {
super.update(rawData, webHost);
if (supportTargetTemperature) {
historyConfig.numericAttributesToShow.add("temperature");
}

View File

@ -11,6 +11,8 @@ class CoverEntity extends Entity {
static const SUPPORT_STOP_TILT = 64;
static const SUPPORT_SET_TILT_POSITION = 128;
CoverEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportOpen => ((supportedFeatures &
CoverEntity.SUPPORT_OPEN) ==
CoverEntity.SUPPORT_OPEN);
@ -45,8 +47,6 @@ class CoverEntity extends Entity {
bool get canTiltBeOpened => currentTiltPosition < 100;
bool get canTiltBeClosed => currentTiltPosition > 0;
CoverEntity(Map rawData) : super(rawData);
@override
Widget _buildStatePart(BuildContext context) {
return CoverStateWidget();

View File

@ -1,6 +1,8 @@
part of '../main.dart';
class DateTimeEntity extends Entity {
DateTimeEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get hasDate => attributes["has_date"] ?? false;
bool get hasTime => attributes["has_time"] ?? false;
int get year => attributes["year"] ?? 1970;
@ -12,8 +14,6 @@ class DateTimeEntity extends Entity {
String get formattedState => _getFormattedState();
DateTime get dateTimeState => _getDateTimeState();
DateTimeEntity(Map rawData) : super(rawData);
@override
Widget _buildStatePart(BuildContext context) {
return DateTimeStateWidget();

View File

@ -73,6 +73,7 @@ class Entity {
Map attributes;
String domain;
String entityId;
String entityPicture;
String state;
String displayState;
DateTime _lastUpdated;
@ -94,7 +95,6 @@ class Entity {
bool get isBadge => Entity.badgeDomains.contains(domain);
String get icon => attributes["icon"] ?? "";
bool get isOn => state == EntityState.on;
String get entityPicture => _getEntityPictureUrl();
String get unitOfMeasurement => attributes["unit_of_measurement"] ?? "";
List get childEntityIds => attributes["entity_id"] ?? [];
String get lastUpdated => _getLastUpdatedFormatted();
@ -102,21 +102,21 @@ class Entity {
double get doubleState => double.tryParse(state) ?? 0.0;
int get supportedFeatures => attributes["supported_features"] ?? 0;
String _getEntityPictureUrl() {
String _getEntityPictureUrl(String webHost) {
String result = attributes["entity_picture"];
if (result == null) return result;
if (!result.startsWith("http")) {
if (result.startsWith("/")) {
result = "$homeAssistantWebHost$result";
result = "$webHost$result";
} else {
result = "$homeAssistantWebHost/$result";
result = "$webHost/$result";
}
}
return result;
}
Entity(Map rawData) {
update(rawData);
Entity(Map rawData, String webHost) {
update(rawData, webHost);
}
Entity.missed(String entityId) {
@ -148,7 +148,7 @@ class Entity {
attributes = {"hidden": false, "friendly_name": "${name ?? url}", "icon": "${icon ?? 'mdi:link'}"};
}
void update(Map rawData) {
void update(Map rawData, String webHost) {
attributes = rawData["attributes"] ?? {};
domain = rawData["entity_id"].split(".")[0];
entityId = rawData["entity_id"];
@ -156,6 +156,7 @@ class Entity {
state = rawData["state"];
displayState = Entity.StateByDeviceClass["$deviceClass.$state"] ?? state;
_lastUpdated = DateTime.tryParse(rawData["last_updated"]);
entityPicture = _getEntityPictureUrl(webHost);
}
double _getDoubleAttributeValue(String attributeName) {

View File

@ -6,7 +6,7 @@ class FanEntity extends Entity {
static const SUPPORT_OSCILLATE = 2;
static const SUPPORT_DIRECTION = 4;
FanEntity(Map rawData) : super(rawData);
FanEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportSetSpeed => ((supportedFeatures &
FanEntity.SUPPORT_SET_SPEED) ==

View File

@ -1,12 +1,13 @@
part of '../main.dart';
class GroupEntity extends Entity {
GroupEntity(Map rawData) : super(rawData);
final List<String> _domainsForSwitchableGroup = ["switch", "light", "automation", "input_boolean"];
String mutualDomain;
bool switchable = false;
GroupEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {
if (switchable) {
@ -19,8 +20,8 @@ class GroupEntity extends Entity {
}
@override
void update(Map rawData) {
super.update(rawData);
void update(Map rawData, String webHost) {
super.update(rawData, webHost);
if (_isOneDomain()) {
mutualDomain = attributes['entity_id'][0].split(".")[0];
switchable = _domainsForSwitchableGroup.contains(mutualDomain);

View File

@ -42,7 +42,7 @@ class LightEntity extends Entity {
bool get isAdditionalControls => ((supportedFeatures != null) && (supportedFeatures != 0));
List<String> get effectList => getStringListAttributeValue("effect_list");
LightEntity(Map rawData) : super(rawData);
LightEntity(Map rawData, String webHost) : super(rawData, webHost);
HSVColor _getColor() {
List hs = attributes["hs_color"];

View File

@ -1,7 +1,7 @@
part of '../main.dart';
class LockEntity extends Entity {
LockEntity(Map rawData) : super(rawData);
LockEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get isLocked => state == "locked";

View File

@ -20,7 +20,7 @@ class MediaPlayerEntity extends Entity {
static const SUPPORT_SHUFFLE_SET = 32768;
static const SUPPORT_SELECT_SOUND_MODE = 65536;
MediaPlayerEntity(Map rawData) : super(rawData);
MediaPlayerEntity(Map rawData, String webHost) : super(rawData, webHost);
bool get supportPause => ((supportedFeatures &
MediaPlayerEntity.SUPPORT_PAUSE) ==

View File

@ -1,7 +1,7 @@
part of '../main.dart';
class SunEntity extends Entity {
SunEntity(Map rawData) : super(rawData);
SunEntity(Map rawData, String webHost) : super(rawData, webHost);
}
class SensorEntity extends Entity {
@ -12,6 +12,6 @@ class SensorEntity extends Entity {
numericState: true
);
SensorEntity(Map rawData) : super(rawData);
SensorEntity(Map rawData, String webHost) : super(rawData, webHost);
}

View File

@ -5,7 +5,7 @@ class SelectEntity extends Entity {
? (attributes["options"] as List).cast<String>()
: [];
SelectEntity(Map rawData) : super(rawData);
SelectEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {

View File

@ -1,7 +1,7 @@
part of '../main.dart';
class SliderEntity extends Entity {
SliderEntity(Map rawData) : super(rawData);
SliderEntity(Map rawData, String webHost) : super(rawData, webHost);
double get minValue => _getDoubleAttributeValue("min") ?? 0.0;
double get maxValue =>_getDoubleAttributeValue("max") ?? 100.0;

View File

@ -1,7 +1,7 @@
part of '../main.dart';
class SwitchEntity extends Entity {
SwitchEntity(Map rawData) : super(rawData);
SwitchEntity(Map rawData, String webHost) : super(rawData, webHost);
@override
Widget _buildStatePart(BuildContext context) {

View File

@ -1,7 +1,7 @@
part of '../main.dart';
class TextEntity extends Entity {
TextEntity(Map rawData) : super(rawData);
TextEntity(Map rawData, String webHost) : super(rawData, webHost);
int get valueMinLength => attributes["min"] ?? -1;
int get valueMaxLength => attributes["max"] ?? -1;

View File

@ -1,13 +1,13 @@
part of '../main.dart';
class TimerEntity extends Entity {
TimerEntity(Map rawData) : super(rawData);
TimerEntity(Map rawData, String webHost) : super(rawData, webHost);
Duration duration;
@override
void update(Map rawData) {
super.update(rawData);
void update(Map rawData, String webHost) {
super.update(rawData, webHost);
String durationSource = "${attributes["duration"]}";
if (durationSource != null && durationSource.isNotEmpty) {
try {

View File

@ -2,13 +2,15 @@ part of 'main.dart';
class EntityCollection {
final homeAssistantWebHost;
Map<String, Entity> _allEntities;
//Map<String, Entity> views;
bool get isEmpty => _allEntities.isEmpty;
List<Entity> get viewEntities => _allEntities.values.where((entity) => entity.isView).toList();
EntityCollection() {
EntityCollection(this.homeAssistantWebHost) {
_allEntities = {};
//views = {};
}
@ -36,67 +38,67 @@ class EntityCollection {
Entity _createEntityInstance(rawEntityData) {
switch (rawEntityData["entity_id"].split(".")[0]) {
case 'sun': {
return SunEntity(rawEntityData);
return SunEntity(rawEntityData, homeAssistantWebHost);
}
case "media_player": {
return MediaPlayerEntity(rawEntityData);
return MediaPlayerEntity(rawEntityData, homeAssistantWebHost);
}
case 'sensor': {
return SensorEntity(rawEntityData);
return SensorEntity(rawEntityData, homeAssistantWebHost);
}
case 'lock': {
return LockEntity(rawEntityData);
return LockEntity(rawEntityData, homeAssistantWebHost);
}
case "automation": {
return AutomationEntity(rawEntityData);
return AutomationEntity(rawEntityData, homeAssistantWebHost);
}
case "input_boolean":
case "switch": {
return SwitchEntity(rawEntityData);
return SwitchEntity(rawEntityData, homeAssistantWebHost);
}
case "light": {
return LightEntity(rawEntityData);
return LightEntity(rawEntityData, homeAssistantWebHost);
}
case "group": {
return GroupEntity(rawEntityData);
return GroupEntity(rawEntityData, homeAssistantWebHost);
}
case "script":
case "scene": {
return ButtonEntity(rawEntityData);
return ButtonEntity(rawEntityData, homeAssistantWebHost);
}
case "input_datetime": {
return DateTimeEntity(rawEntityData);
return DateTimeEntity(rawEntityData, homeAssistantWebHost);
}
case "input_select": {
return SelectEntity(rawEntityData);
return SelectEntity(rawEntityData, homeAssistantWebHost);
}
case "input_number": {
return SliderEntity(rawEntityData);
return SliderEntity(rawEntityData, homeAssistantWebHost);
}
case "input_text": {
return TextEntity(rawEntityData);
return TextEntity(rawEntityData, homeAssistantWebHost);
}
case "climate": {
return ClimateEntity(rawEntityData);
return ClimateEntity(rawEntityData, homeAssistantWebHost);
}
case "cover": {
return CoverEntity(rawEntityData);
return CoverEntity(rawEntityData, homeAssistantWebHost);
}
case "fan": {
return FanEntity(rawEntityData);
return FanEntity(rawEntityData, homeAssistantWebHost);
}
case "camera": {
return CameraEntity(rawEntityData);
return CameraEntity(rawEntityData, homeAssistantWebHost);
}
case "alarm_control_panel": {
return AlarmControlPanelEntity(rawEntityData);
return AlarmControlPanelEntity(rawEntityData, homeAssistantWebHost);
}
case "timer": {
return TimerEntity(rawEntityData);
return TimerEntity(rawEntityData, homeAssistantWebHost);
}
default: {
return Entity(rawEntityData);
return Entity(rawEntityData, homeAssistantWebHost);
}
}
}
@ -121,7 +123,7 @@ class EntityCollection {
}
void updateFromRaw(Map rawEntityData) {
get("${rawEntityData["entity_id"]}")?.update(rawEntityData);
get("${rawEntityData["entity_id"]}")?.update(rawEntityData, homeAssistantWebHost);
}
Entity get(String entityId) {

View File

@ -16,6 +16,7 @@ class _CameraStreamViewState extends State<CameraStreamView> {
}
CameraEntity _entity;
String _webHost;
http.Client client;
http.StreamedResponse response;
@ -28,7 +29,7 @@ class _CameraStreamViewState extends State<CameraStreamView> {
void _connect() async {
started = true;
timeToStop = false;
String streamUrl = '$homeAssistantWebHost/api/camera_proxy_stream/${_entity.entityId}?token=${_entity.attributes['access_token']}';
String streamUrl = '$_webHost/api/camera_proxy_stream/${_entity.entityId}?token=${_entity.attributes['access_token']}';
client = new http.Client(); // create a client to make api calls
http.Request request = new http.Request("GET", Uri.parse(streamUrl)); // create get request
Logger.d("[Sending] ==> $streamUrl");
@ -130,6 +131,7 @@ class _CameraStreamViewState extends State<CameraStreamView> {
.of(context)
.entityWrapper
.entity;
_webHost = HomeAssistantModel.of(context).homeAssistant.httpAPIEndpoint;
_connect();
}

View File

@ -73,7 +73,7 @@ class MediaPlayerWidget extends StatelessWidget {
Widget _buildImage(MediaPlayerEntity entity) {
String state = entity.state;
if (homeAssistantWebHost != null && entity.entityPicture != null && state != EntityState.off && state != EntityState.unavailable && state != EntityState.idle) {
if (entity.entityPicture != null && state != EntityState.off && state != EntityState.unavailable && state != EntityState.idle) {
return Container(
color: Colors.black,
child: Row(

View File

@ -2,8 +2,10 @@ part of 'main.dart';
class HomeAssistant {
String _webSocketAPIEndpoint;
String httpAPIEndpoint;
String _password;
bool _useLovelace = false;
bool isSettingsLoaded = false;
IOWebSocketChannel _hassioChannel;
SendMessageQueue _messageQueue;
@ -15,6 +17,7 @@ class HomeAssistant {
HomeAssistantUI ui;
Map _instanceConfig = {};
String _userName;
String hostname;
HSVColor savedColor;
Map _rawLovelaceData;
@ -45,10 +48,27 @@ class HomeAssistant {
//int get viewsCount => entities.views.length ?? 0;
HomeAssistant() {
entities = EntityCollection();
_messageQueue = SendMessageQueue(messageExpirationTime);
}
Future loadConnectionSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String domain = prefs.getString('hassio-domain');
String port = prefs.getString('hassio-port');
hostname = "$domain:$port";
_webSocketAPIEndpoint = "${prefs.getString('hassio-protocol')}://$domain:$port/api/websocket";
httpAPIEndpoint = "${prefs.getString('hassio-res-protocol')}://$domain:$port";
_password = prefs.getString('hassio-password');
_useLovelace = prefs.getBool('use-lovelace') ?? true;
if ((domain == null) || (port == null) || (_password == null) ||
(domain.length == 0) || (port.length == 0) || (_password.length == 0)) {
throw("Check connection settings");
} else {
isSettingsLoaded = true;
entities = EntityCollection(httpAPIEndpoint);
}
}
void updateSettings(String url, String password, bool useLovelace) {
_webSocketAPIEndpoint = url;
_password = password;
@ -555,7 +575,7 @@ class HomeAssistant {
}
}
Widget buildViews(BuildContext context, bool lovelace, TabController tabController) {
Widget buildViews(BuildContext context, TabController tabController) {
return ui.build(context, tabController);
}
@ -563,7 +583,7 @@ class HomeAssistant {
DateTime now = DateTime.now();
//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 url = "$homeAssistantWebHost/api/history/period/$startTime?&filter_entity_id=$entityId";
String url = "$httpAPIEndpoint/api/history/period/$startTime?&filter_entity_id=$entityId";
Logger.d("[Sending] ==> $url");
http.Response historyResponse;
historyResponse = await http.get(url, headers: {
@ -580,7 +600,7 @@ class HomeAssistant {
}
Future sendHTTPRequest(String data) async {
String url = "$homeAssistantWebHost/api/notify.fcm-android";
String url = "$httpAPIEndpoint/api/notify.fcm-android";
Logger.d("[Sending] ==> $url");
http.Response response;
response = await http.post(

View File

@ -18,6 +18,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
part 'entity_class/const.dart';
part 'entity_class/entity.class.dart';
@ -103,7 +104,7 @@ EventBus eventBus = new EventBus();
const String appName = "HA Client";
const appVersion = "0.5.2";
String homeAssistantWebHost;
//String homeAssistantWebHost;
void main() {
FlutterError.onError = (errorDetails) {
@ -156,29 +157,28 @@ class MainPage extends StatefulWidget {
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
HomeAssistant _homeAssistant;
//Map _instanceConfig;
String _webSocketApiEndpoint;
String _password;
//String _webSocketApiEndpoint;
//String _password;
//int _uiViewsCount = 0;
String _instanceHost;
//String _instanceHost;
StreamSubscription _stateSubscription;
StreamSubscription _settingsSubscription;
StreamSubscription _serviceCallSubscription;
StreamSubscription _showEntityPageSubscription;
StreamSubscription _showErrorSubscription;
bool _settingsLoaded = false;
//bool _settingsLoaded = false;
bool _accountMenuExpanded = false;
bool _useLovelaceUI;
//bool _useLovelaceUI;
int _previousViewCount;
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
@override
void initState() {
super.initState();
_settingsLoaded = false;
WidgetsBinding.instance.addObserver(this);
Logger.d("<!!!> Creating new HomeAssistant instance");
_homeAssistant = HomeAssistant();
//_settingsLoaded = false;
WidgetsBinding.instance.addObserver(this);
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
Logger.d("Settings change event: reconnect=${event.reconnect}");
@ -193,7 +193,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
}
void _initialLoad() {
_loadConnectionSettings().then((_){
_homeAssistant.loadConnectionSettings().then((_){
_subscribe();
_refreshData();
}, onError: (_) {
@ -203,13 +203,13 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
Logger.d("$state");
if (state == AppLifecycleState.resumed && _settingsLoaded) {
//Logger.d("$state");
if (state == AppLifecycleState.resumed && _homeAssistant.isSettingsLoaded) {
_refreshData();
}
}
_loadConnectionSettings() async {
/*_loadConnectionSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String domain = prefs.getString('hassio-domain');
String port = prefs.getString('hassio-port');
@ -224,7 +224,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
} else {
_settingsLoaded = true;
}
}
}*/
_subscribe() {
if (_stateSubscription == null) {
@ -258,7 +258,8 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
});
}
/*_firebaseMessaging.getToken().then((String token) {
_firebaseMessaging.getToken().then((String token) {
//Logger.d("FCM token: $token");
_homeAssistant.sendHTTPRequest('{"token": "$token"}');
});
_firebaseMessaging.configure(
@ -271,11 +272,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
onResume: (data) {
Logger.d("Notification [onResume]: $data");
}
);*/
);
}
_refreshData() async {
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
//_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
_hideBottomBar();
_showInfoBottomBar(progress: true,);
await _homeAssistant.fetch().then((result) {
@ -342,7 +343,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
menuItems.add(
UserAccountsDrawerHeader(
accountName: Text(_homeAssistant.userName),
accountEmail: Text(_instanceHost ?? "Not configured"),
accountEmail: Text(_homeAssistant.hostname ?? "Not configured"),
onDetailsPressed: () {
setState(() {
_accountMenuExpanded = !_accountMenuExpanded;
@ -387,7 +388,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
new ListTile(
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:home-assistant")),
title: Text("Open Web UI"),
onTap: () => HAUtils.launchURL(homeAssistantWebHost),
onTap: () => HAUtils.launchURL(_homeAssistant.httpAPIEndpoint),
),
Divider()
]);
@ -631,7 +632,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
),
)
:
_homeAssistant.buildViews(context, _useLovelaceUI, _viewsTabController),
_homeAssistant.buildViews(context, _viewsTabController),
);
}

View File

@ -21,10 +21,21 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
bool _useLovelace = true;
bool _newUseLovelace = true;
String oauthUrl;
final flutterWebviewPlugin = new FlutterWebviewPlugin();
@override
void initState() {
super.initState();
_loadSettings();
flutterWebviewPlugin.onUrlChanged.listen((String url) {
Logger.d("Launched url: $url");
if (url.startsWith("http://ha-client.homemade.systems/service/auth_callback.html")) {
String authCode = url.split("=")[1];
Logger.d("Auth code: $authCode");
flutterWebviewPlugin.close();
}
});
}
_loadSettings() async {
@ -40,6 +51,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
} catch (e) {
_useLovelace = _newUseLovelace = true;
}
oauthUrl = "${ _newSocketProtocol == "wss" ? "https" : "http"}://$_newHassioDomain:${_newHassioPort ?? ''}/auth/authorize?client_id=${Uri.encodeComponent('http://ha-client.homemade.systems/')}&redirect_uri=${Uri.encodeComponent('http://ha-client.homemade.systems/service/auth_callback.html')}";
Logger.d("OAuth url: $oauthUrl");
});
}
@ -67,6 +80,24 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
@override
Widget build(BuildContext context) {
Widget webViewButton;
if (oauthUrl != null) {
webViewButton = FlatButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => WebviewScaffold(
url: oauthUrl,
appBar: new AppBar(
title: new Text("Login"),
)
)
));
},
child: Text("Login with Home Assistant")
);
} else {
webViewButton = Container(height: 0.0,);
}
return new Scaffold(
appBar: new AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
@ -149,6 +180,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
"Try ports 80 and 443 if default is not working and you don't know why.",
style: TextStyle(color: Colors.grey),
),
webViewButton,
new TextField(
decoration: InputDecoration(
labelText: "Access token"

View File

@ -36,7 +36,8 @@ class Panel {
)
);
} else {
String url = "$homeAssistantWebHost/$urlPath";
HomeAssistantModel haModel = HomeAssistantModel.of(context);
String url = "${haModel.homeAssistant.httpAPIEndpoint}/$urlPath";
Logger.d("Launching custom tab with $url");
HAUtils.launchURLInCustomTab(context, url);
}

View File

@ -153,6 +153,13 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_webview_plugin:
dependency: "direct main"
description:
name: flutter_webview_plugin
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.1"
http:
dependency: transitive
description:

View File

@ -21,6 +21,7 @@ dependencies:
flutter_svg: ^0.10.3
flutter_custom_tabs: ^0.6.0
firebase_messaging: ^4.0.0+1
flutter_webview_plugin: ^0.3.1
dev_dependencies:
flutter_test: