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 <application
android:name="io.flutter.app.FlutterApplication" android:name="io.flutter.app.FlutterApplication"
android:label="HA Client" android:label="HA Client"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" 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'; part of '../main.dart';
class AlarmControlPanelEntity extends Entity { class AlarmControlPanelEntity extends Entity {
AlarmControlPanelEntity(Map rawData) : super(rawData); AlarmControlPanelEntity(Map rawData, String webHost) : super(rawData, webHost);
@override @override
Widget _buildAdditionalControlsForPage(BuildContext context) { Widget _buildAdditionalControlsForPage(BuildContext context) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
part of '../main.dart'; part of '../main.dart';
class SunEntity extends Entity { class SunEntity extends Entity {
SunEntity(Map rawData) : super(rawData); SunEntity(Map rawData, String webHost) : super(rawData, webHost);
} }
class SensorEntity extends Entity { class SensorEntity extends Entity {
@ -12,6 +12,6 @@ class SensorEntity extends Entity {
numericState: true 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>() ? (attributes["options"] as List).cast<String>()
: []; : [];
SelectEntity(Map rawData) : super(rawData); SelectEntity(Map rawData, String webHost) : super(rawData, webHost);
@override @override
Widget _buildStatePart(BuildContext context) { Widget _buildStatePart(BuildContext context) {

View File

@ -1,7 +1,7 @@
part of '../main.dart'; part of '../main.dart';
class SliderEntity extends Entity { 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 minValue => _getDoubleAttributeValue("min") ?? 0.0;
double get maxValue =>_getDoubleAttributeValue("max") ?? 100.0; double get maxValue =>_getDoubleAttributeValue("max") ?? 100.0;

View File

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

View File

@ -1,7 +1,7 @@
part of '../main.dart'; part of '../main.dart';
class TextEntity extends Entity { 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 valueMinLength => attributes["min"] ?? -1;
int get valueMaxLength => attributes["max"] ?? -1; int get valueMaxLength => attributes["max"] ?? -1;

View File

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

View File

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

View File

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

View File

@ -73,7 +73,7 @@ class MediaPlayerWidget extends StatelessWidget {
Widget _buildImage(MediaPlayerEntity entity) { Widget _buildImage(MediaPlayerEntity entity) {
String state = entity.state; 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( return Container(
color: Colors.black, color: Colors.black,
child: Row( child: Row(

View File

@ -2,8 +2,10 @@ part of 'main.dart';
class HomeAssistant { class HomeAssistant {
String _webSocketAPIEndpoint; String _webSocketAPIEndpoint;
String httpAPIEndpoint;
String _password; String _password;
bool _useLovelace = false; bool _useLovelace = false;
bool isSettingsLoaded = false;
IOWebSocketChannel _hassioChannel; IOWebSocketChannel _hassioChannel;
SendMessageQueue _messageQueue; SendMessageQueue _messageQueue;
@ -15,6 +17,7 @@ class HomeAssistant {
HomeAssistantUI ui; HomeAssistantUI ui;
Map _instanceConfig = {}; Map _instanceConfig = {};
String _userName; String _userName;
String hostname;
HSVColor savedColor; HSVColor savedColor;
Map _rawLovelaceData; Map _rawLovelaceData;
@ -45,10 +48,27 @@ class HomeAssistant {
//int get viewsCount => entities.views.length ?? 0; //int get viewsCount => entities.views.length ?? 0;
HomeAssistant() { HomeAssistant() {
entities = EntityCollection();
_messageQueue = SendMessageQueue(messageExpirationTime); _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) { void updateSettings(String url, String password, bool useLovelace) {
_webSocketAPIEndpoint = url; _webSocketAPIEndpoint = url;
_password = password; _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); return ui.build(context, tabController);
} }
@ -563,7 +583,7 @@ class HomeAssistant {
DateTime now = DateTime.now(); DateTime now = DateTime.now();
//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 = "$homeAssistantWebHost/api/history/period/$startTime?&filter_entity_id=$entityId"; String url = "$httpAPIEndpoint/api/history/period/$startTime?&filter_entity_id=$entityId";
Logger.d("[Sending] ==> $url"); Logger.d("[Sending] ==> $url");
http.Response historyResponse; http.Response historyResponse;
historyResponse = await http.get(url, headers: { historyResponse = await http.get(url, headers: {
@ -580,7 +600,7 @@ class HomeAssistant {
} }
Future sendHTTPRequest(String data) async { Future sendHTTPRequest(String data) async {
String url = "$homeAssistantWebHost/api/notify.fcm-android"; String url = "$httpAPIEndpoint/api/notify.fcm-android";
Logger.d("[Sending] ==> $url"); Logger.d("[Sending] ==> $url");
http.Response response; http.Response response;
response = await http.post( 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_svg/flutter_svg.dart';
import 'package:flutter_custom_tabs/flutter_custom_tabs.dart'; import 'package:flutter_custom_tabs/flutter_custom_tabs.dart';
import 'package:firebase_messaging/firebase_messaging.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/const.dart';
part 'entity_class/entity.class.dart'; part 'entity_class/entity.class.dart';
@ -103,7 +104,7 @@ EventBus eventBus = new EventBus();
const String appName = "HA Client"; const String appName = "HA Client";
const appVersion = "0.5.2"; const appVersion = "0.5.2";
String homeAssistantWebHost; //String homeAssistantWebHost;
void main() { void main() {
FlutterError.onError = (errorDetails) { FlutterError.onError = (errorDetails) {
@ -156,29 +157,28 @@ class MainPage extends StatefulWidget {
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin { class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
HomeAssistant _homeAssistant; HomeAssistant _homeAssistant;
//Map _instanceConfig; //Map _instanceConfig;
String _webSocketApiEndpoint; //String _webSocketApiEndpoint;
String _password; //String _password;
//int _uiViewsCount = 0; //int _uiViewsCount = 0;
String _instanceHost; //String _instanceHost;
StreamSubscription _stateSubscription; StreamSubscription _stateSubscription;
StreamSubscription _settingsSubscription; StreamSubscription _settingsSubscription;
StreamSubscription _serviceCallSubscription; StreamSubscription _serviceCallSubscription;
StreamSubscription _showEntityPageSubscription; StreamSubscription _showEntityPageSubscription;
StreamSubscription _showErrorSubscription; StreamSubscription _showErrorSubscription;
bool _settingsLoaded = false; //bool _settingsLoaded = false;
bool _accountMenuExpanded = false; bool _accountMenuExpanded = false;
bool _useLovelaceUI; //bool _useLovelaceUI;
int _previousViewCount; int _previousViewCount;
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging(); final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_settingsLoaded = false;
WidgetsBinding.instance.addObserver(this);
Logger.d("<!!!> Creating new HomeAssistant instance"); Logger.d("<!!!> Creating new HomeAssistant instance");
_homeAssistant = HomeAssistant(); _homeAssistant = HomeAssistant();
//_settingsLoaded = false;
WidgetsBinding.instance.addObserver(this);
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) { _settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
Logger.d("Settings change event: reconnect=${event.reconnect}"); Logger.d("Settings change event: reconnect=${event.reconnect}");
@ -193,7 +193,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
} }
void _initialLoad() { void _initialLoad() {
_loadConnectionSettings().then((_){ _homeAssistant.loadConnectionSettings().then((_){
_subscribe(); _subscribe();
_refreshData(); _refreshData();
}, onError: (_) { }, onError: (_) {
@ -203,13 +203,13 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
@override @override
void didChangeAppLifecycleState(AppLifecycleState state) { void didChangeAppLifecycleState(AppLifecycleState state) {
Logger.d("$state"); //Logger.d("$state");
if (state == AppLifecycleState.resumed && _settingsLoaded) { if (state == AppLifecycleState.resumed && _homeAssistant.isSettingsLoaded) {
_refreshData(); _refreshData();
} }
} }
_loadConnectionSettings() async { /*_loadConnectionSettings() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); SharedPreferences prefs = await SharedPreferences.getInstance();
String domain = prefs.getString('hassio-domain'); String domain = prefs.getString('hassio-domain');
String port = prefs.getString('hassio-port'); String port = prefs.getString('hassio-port');
@ -224,7 +224,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
} else { } else {
_settingsLoaded = true; _settingsLoaded = true;
} }
} }*/
_subscribe() { _subscribe() {
if (_stateSubscription == null) { 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"}'); _homeAssistant.sendHTTPRequest('{"token": "$token"}');
}); });
_firebaseMessaging.configure( _firebaseMessaging.configure(
@ -271,11 +272,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
onResume: (data) { onResume: (data) {
Logger.d("Notification [onResume]: $data"); Logger.d("Notification [onResume]: $data");
} }
);*/ );
} }
_refreshData() async { _refreshData() async {
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI); //_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _useLovelaceUI);
_hideBottomBar(); _hideBottomBar();
_showInfoBottomBar(progress: true,); _showInfoBottomBar(progress: true,);
await _homeAssistant.fetch().then((result) { await _homeAssistant.fetch().then((result) {
@ -342,7 +343,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
menuItems.add( menuItems.add(
UserAccountsDrawerHeader( UserAccountsDrawerHeader(
accountName: Text(_homeAssistant.userName), accountName: Text(_homeAssistant.userName),
accountEmail: Text(_instanceHost ?? "Not configured"), accountEmail: Text(_homeAssistant.hostname ?? "Not configured"),
onDetailsPressed: () { onDetailsPressed: () {
setState(() { setState(() {
_accountMenuExpanded = !_accountMenuExpanded; _accountMenuExpanded = !_accountMenuExpanded;
@ -387,7 +388,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
new ListTile( new ListTile(
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:home-assistant")), leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:home-assistant")),
title: Text("Open Web UI"), title: Text("Open Web UI"),
onTap: () => HAUtils.launchURL(homeAssistantWebHost), onTap: () => HAUtils.launchURL(_homeAssistant.httpAPIEndpoint),
), ),
Divider() 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 _useLovelace = true;
bool _newUseLovelace = true; bool _newUseLovelace = true;
String oauthUrl;
final flutterWebviewPlugin = new FlutterWebviewPlugin();
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_loadSettings(); _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 { _loadSettings() async {
@ -40,6 +51,8 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
} catch (e) { } catch (e) {
_useLovelace = _newUseLovelace = true; _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 @override
Widget build(BuildContext context) { 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( return new Scaffold(
appBar: new AppBar( appBar: new AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ 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.", "Try ports 80 and 443 if default is not working and you don't know why.",
style: TextStyle(color: Colors.grey), style: TextStyle(color: Colors.grey),
), ),
webViewButton,
new TextField( new TextField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: "Access token" labelText: "Access token"

View File

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

View File

@ -153,6 +153,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" 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: http:
dependency: transitive dependency: transitive
description: description:

View File

@ -21,6 +21,7 @@ dependencies:
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: ^4.0.0+1
flutter_webview_plugin: ^0.3.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: