2018-09-25 22:47:06 +03:00
part of ' main.dart ' ;
2018-09-27 14:51:57 +03:00
class HomeAssistant {
2019-03-26 00:18:30 +02:00
2020-03-20 00:59:53 +02:00
static const DEFAULT_DASHBOARD = ' lovelace ' ;
2019-06-15 18:07:11 +03:00
static final HomeAssistant _instance = HomeAssistant . _internal ( ) ;
factory HomeAssistant ( ) {
return _instance ;
}
2018-10-25 00:05:29 +03:00
EntityCollection entities ;
2018-10-27 00:54:05 +03:00
HomeAssistantUI ui ;
2018-09-25 22:47:06 +03:00
Map _instanceConfig = { } ;
2018-10-07 20:18:14 +03:00
String _userName ;
2020-05-14 19:44:50 +03:00
String currentDashboardPath ;
2019-03-10 23:28:23 +02:00
HSVColor savedColor ;
2019-09-15 17:29:49 +03:00
int savedPlayerPosition ;
2019-09-15 18:38:02 +03:00
String sendToPlayerId ;
2019-09-15 20:27:49 +03:00
String sendFromPlayerId ;
2020-03-15 15:47:51 +02:00
Map services ;
2020-03-20 12:14:14 +02:00
bool autoUi = false ;
2018-09-27 14:51:57 +03:00
2018-10-27 00:54:05 +03:00
Map _rawLovelaceData ;
2020-03-15 15:47:51 +02:00
var _rawStates ;
var _rawUserInfo ;
var _rawPanels ;
2018-10-27 00:54:05 +03:00
2019-03-13 16:39:23 +02:00
List < Panel > panels = [ ] ;
2018-10-08 23:11:56 +03:00
Duration fetchTimeout = Duration ( seconds: 30 ) ;
2018-10-02 18:05:50 +03:00
2018-11-04 21:02:12 +02:00
String get locationName {
2020-03-20 12:14:14 +02:00
if ( ! autoUi ) {
2020-04-03 01:04:56 +03:00
return ui ? . title ? ? " Home " ;
2018-11-04 21:02:12 +02:00
} else {
2020-04-03 01:04:56 +03:00
return _instanceConfig [ " location_name " ] ? ? " Home " ;
2018-11-04 21:02:12 +02:00
}
}
2020-05-03 02:02:18 +03:00
String get userName = > _userName ? ? ' ' ;
2018-10-08 23:11:56 +03:00
String get userAvatarText = > userName . length > 0 ? userName [ 0 ] : " " ;
2019-03-21 14:08:07 +02:00
bool get isNoEntities = > entities = = null | | entities . isEmpty ;
bool get isNoViews = > ui = = null | | ui . isEmpty ;
2018-09-27 14:51:57 +03:00
2019-06-15 18:07:11 +03:00
HomeAssistant . _internal ( ) {
2019-08-31 22:10:07 +03:00
ConnectionManager ( ) . onStateChangeCallback = _handleEntityStateChange ;
2020-03-20 01:16:59 +02:00
ConnectionManager ( ) . onLovelaceUpdatedCallback = _handleLovelaceUpdate ;
2019-08-31 22:10:07 +03:00
DeviceInfoManager ( ) . loadDeviceInfo ( ) ;
2018-09-25 22:47:06 +03:00
}
2019-03-26 00:18:30 +02:00
Completer _fetchCompleter ;
2018-10-08 23:11:56 +03:00
2020-03-22 01:11:00 +02:00
Future fetchData ( bool uiOnly ) {
2019-03-26 00:18:30 +02:00
if ( _fetchCompleter ! = null & & ! _fetchCompleter . isCompleted ) {
Logger . w ( " Previous data fetch is not completed yet " ) ;
return _fetchCompleter . future ;
2018-10-08 23:11:56 +03:00
}
2019-03-26 00:18:30 +02:00
_fetchCompleter = Completer ( ) ;
2018-10-07 18:27:10 +03:00
List < Future > futures = [ ] ;
2020-03-22 01:11:00 +02:00
if ( ! uiOnly ) {
2020-05-13 15:46:25 +03:00
if ( entities = = null ) entities = EntityCollection ( AppSettings ( ) . httpWebHost ) ;
2020-03-22 01:11:00 +02:00
futures . add ( _getStates ( null ) ) ;
futures . add ( _getConfig ( null ) ) ;
futures . add ( _getUserInfo ( null ) ) ;
futures . add ( _getPanels ( null ) ) ;
futures . add ( _getServices ( null ) ) ;
}
2020-03-20 12:14:14 +02:00
if ( ! autoUi ) {
2020-03-15 15:47:51 +02:00
futures . add ( _getLovelace ( null ) ) ;
2018-10-27 00:54:05 +03:00
}
2019-03-26 00:18:30 +02:00
Future . wait ( futures ) . then ( ( _ ) {
2020-04-15 17:03:31 +03:00
if ( isComponentEnabled ( ' mobile_app ' ) ) {
2020-03-15 17:26:03 +02:00
_createUI ( ) ;
2019-04-19 21:43:52 +03:00
_fetchCompleter . complete ( ) ;
} else {
2020-05-01 16:47:41 +03:00
_fetchCompleter . completeError ( HACException ( " Mobile app component not found " , actions: [ HAErrorAction . tryAgain ( ) , HAErrorAction ( type: HAErrorActionType . URL , title: " Help " , url: " http://ha-client.app/docs#mobile-app-integration " ) ] ) ) ;
2019-04-19 21:43:52 +03:00
}
2019-03-22 14:04:20 +02:00
} ) . catchError ( ( e ) {
2019-03-26 00:18:30 +02:00
_fetchCompleter . completeError ( e ) ;
2019-03-22 14:04:20 +02:00
} ) ;
2019-03-26 00:18:30 +02:00
return _fetchCompleter . future ;
2018-09-25 22:47:06 +03:00
}
2020-03-15 17:26:03 +02:00
Future < void > fetchDataFromCache ( ) async {
Logger . d ( ' Loading cached data ' ) ;
2020-03-15 15:47:51 +02:00
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
2020-03-15 17:26:03 +02:00
bool cached = prefs . getBool ( ' cached ' ) ;
if ( cached ! = null & & cached ) {
2020-05-13 15:46:25 +03:00
if ( entities = = null ) entities = EntityCollection ( AppSettings ( ) . httpWebHost ) ;
2020-03-15 15:47:51 +02:00
try {
_getStates ( prefs ) ;
2020-03-20 12:14:14 +02:00
if ( ! autoUi ) {
2020-03-15 15:47:51 +02:00
_getLovelace ( prefs ) ;
}
_getConfig ( prefs ) ;
_getUserInfo ( prefs ) ;
_getPanels ( prefs ) ;
_getServices ( prefs ) ;
2020-04-15 17:03:31 +03:00
if ( isComponentEnabled ( ' mobile_app ' ) ) {
2020-03-15 17:26:03 +02:00
_createUI ( ) ;
}
2020-03-15 15:47:51 +02:00
} catch ( e ) {
Logger . d ( ' Didnt get cached data: $ e ' ) ;
}
}
}
void saveCache ( ) async {
Logger . d ( ' Saving data to cache... ' ) ;
SharedPreferences prefs = await SharedPreferences . getInstance ( ) ;
try {
await prefs . setString ( ' cached_states ' , json . encode ( _rawStates ) ) ;
await prefs . setString ( ' cached_lovelace ' , json . encode ( _rawLovelaceData ) ) ;
await prefs . setString ( ' cached_user ' , json . encode ( _rawUserInfo ) ) ;
await prefs . setString ( ' cached_config ' , json . encode ( _instanceConfig ) ) ;
await prefs . setString ( ' cached_panels ' , json . encode ( _rawPanels ) ) ;
await prefs . setString ( ' cached_services ' , json . encode ( services ) ) ;
await prefs . setBool ( ' cached ' , true ) ;
2020-05-01 17:34:31 +03:00
} catch ( e , stacktrace ) {
2020-03-15 15:47:51 +02:00
await prefs . setBool ( ' cached ' , false ) ;
2020-05-01 17:34:31 +03:00
Logger . e ( ' Error saving cache: $ e ' , stacktrace: stacktrace ) ;
2020-03-15 15:47:51 +02:00
}
Logger . d ( ' Done saving cache ' ) ;
}
2019-03-20 23:38:57 +02:00
Future logout ( ) async {
2019-03-21 14:08:07 +02:00
Logger . d ( " Logging out... " ) ;
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . logout ( ) . then ( ( _ ) {
2019-03-26 00:18:30 +02:00
ui ? . clear ( ) ;
entities ? . clear ( ) ;
2019-04-05 11:59:13 +03:00
panels ? . clear ( ) ;
2019-03-26 00:18:30 +02:00
} ) ;
2019-03-20 23:05:25 +02:00
}
2020-03-15 15:47:51 +02:00
Future _getConfig ( SharedPreferences sharedPrefs ) async {
2020-05-01 17:06:49 +03:00
_instanceConfig ? . clear ( ) ;
2020-05-06 20:51:06 +03:00
if ( sharedPrefs ! = null & & sharedPrefs . containsKey ( ' cached_config ' ) ) {
2020-03-15 15:47:51 +02:00
try {
2020-05-06 20:51:06 +03:00
var data = json . decode ( sharedPrefs . getString ( ' cached_config ' ) ) ;
2020-03-15 15:47:51 +02:00
_parseConfig ( data ) ;
2020-05-01 17:06:49 +03:00
} catch ( e , stacktrace ) {
2020-05-01 17:34:31 +03:00
Logger . e ( ' Error gettong config from cache: $ e ' , stacktrace: stacktrace ) ;
2020-03-15 15:47:51 +02:00
}
} else {
await ConnectionManager ( ) . sendSocketMessage ( type: " get_config " ) . then ( ( data ) = > _parseConfig ( data ) ) . catchError ( ( e ) {
2020-05-01 17:34:31 +03:00
Logger . e ( ' get_config error: $ e ' ) ;
2020-05-01 16:47:41 +03:00
throw HACException ( " Error getting config: $ e " ) ;
2020-03-15 15:47:51 +02:00
} ) ;
}
2018-09-25 22:47:06 +03:00
}
2020-03-15 15:47:51 +02:00
void _parseConfig ( data ) {
2020-05-01 17:06:49 +03:00
_instanceConfig = data ;
2019-03-20 19:01:30 +02:00
}
2020-03-15 15:47:51 +02:00
Future _getStates ( SharedPreferences sharedPrefs ) async {
2020-05-06 20:51:06 +03:00
if ( sharedPrefs ! = null & & sharedPrefs . containsKey ( ' cached_states ' ) ) {
2020-03-15 15:47:51 +02:00
try {
2020-05-06 20:51:06 +03:00
var data = json . decode ( sharedPrefs . getString ( ' cached_states ' ) ) ;
2020-05-07 18:33:45 +03:00
_parseStates ( data ? ? [ ] ) ;
2020-05-01 17:06:49 +03:00
} catch ( e , stacktrace ) {
2020-05-01 17:42:45 +03:00
Logger . e ( ' Error getting cached states: $ e ' , stacktrace: stacktrace ) ;
2020-03-15 15:47:51 +02:00
}
} else {
await ConnectionManager ( ) . sendSocketMessage ( type: " get_states " ) . then (
( data ) = > _parseStates ( data )
) . catchError ( ( e ) {
2020-05-06 20:51:06 +03:00
Logger . e ( ' get_states error: $ e ' ) ;
2020-05-01 16:47:41 +03:00
throw HACException ( " Error getting states: $ e " ) ;
2020-03-15 15:47:51 +02:00
} ) ;
}
}
2019-12-10 23:19:29 +02:00
2020-03-15 15:47:51 +02:00
void _parseStates ( data ) {
_rawStates = data ;
entities . parse ( data ) ;
}
Future _getLovelace ( SharedPreferences sharedPrefs ) {
2020-05-06 20:51:06 +03:00
if ( sharedPrefs ! = null & & sharedPrefs . containsKey ( ' cached_lovelace ' ) ) {
2020-03-15 15:47:51 +02:00
try {
2020-05-06 20:51:06 +03:00
var data = json . decode ( sharedPrefs . getString ( ' cached_lovelace ' ) ) ;
2020-03-15 15:47:51 +02:00
_rawLovelaceData = data ;
} catch ( e ) {
2020-03-20 12:14:14 +02:00
autoUi = true ;
2019-11-08 22:14:34 +02:00
}
2020-03-15 15:47:51 +02:00
return Future . value ( ) ;
} else {
Completer completer = Completer ( ) ;
2020-04-04 16:17:01 +03:00
var additionalData ;
2020-05-14 19:44:50 +03:00
if ( AppSettings ( ) . haVersion > = 107 & & currentDashboardPath ! = HomeAssistant . DEFAULT_DASHBOARD ) {
2020-04-04 16:17:01 +03:00
additionalData = {
2020-05-14 19:44:50 +03:00
' url_path ' : currentDashboardPath
2020-04-04 16:17:01 +03:00
} ;
}
2020-03-20 00:59:53 +02:00
ConnectionManager ( ) . sendSocketMessage (
type: ' lovelace/config ' ,
2020-04-04 16:17:01 +03:00
additionalData: additionalData
2020-03-20 00:59:53 +02:00
) . then ( ( data ) {
2020-03-15 15:47:51 +02:00
_rawLovelaceData = data ;
completer . complete ( ) ;
} ) . catchError ( ( e ) {
2020-05-01 17:06:49 +03:00
if ( " $ e " . contains ( " config_not_found " ) ) {
2020-03-20 12:14:14 +02:00
autoUi = true ;
2020-04-03 01:04:56 +03:00
_rawLovelaceData = null ;
2020-03-15 15:47:51 +02:00
completer . complete ( ) ;
} else {
2020-05-01 17:34:31 +03:00
Logger . e ( ' lovelace/config error: $ e ' ) ;
2020-05-01 16:47:41 +03:00
completer . completeError ( HACException ( " Error getting lovelace config: $ e " ) ) ;
2020-03-15 15:47:51 +02:00
}
} ) ;
return completer . future ;
}
2018-10-27 00:54:05 +03:00
}
2020-05-01 17:06:49 +03:00
Future _getServices ( SharedPreferences prefs ) async {
services ? . clear ( ) ;
2020-05-06 20:51:06 +03:00
if ( prefs ! = null & & prefs . containsKey ( ' cached_services ' ) ) {
2020-03-15 15:47:51 +02:00
try {
2020-05-06 20:51:06 +03:00
var data = json . decode ( prefs . getString ( ' cached_services ' ) ) ;
2020-05-07 18:31:18 +03:00
_parseServices ( data ? ? { } ) ;
2020-05-01 17:06:49 +03:00
} catch ( e , stacktrace ) {
2020-06-03 21:42:26 +03:00
Logger . e ( e , stacktrace: stacktrace , skipCrashlytics: true ) ;
2020-03-15 15:47:51 +02:00
}
}
await ConnectionManager ( ) . sendSocketMessage ( type: " get_services " ) . then ( ( data ) = > _parseServices ( data ) ) . catchError ( ( e ) {
2020-05-01 17:34:31 +03:00
Logger . e ( ' get_services error: $ e ' ) ;
2020-03-15 15:47:51 +02:00
} ) ;
}
2020-02-20 16:33:03 +02:00
2020-03-15 15:47:51 +02:00
void _parseServices ( data ) {
services = data ;
2020-02-20 16:33:03 +02:00
}
2020-03-15 15:47:51 +02:00
Future _getUserInfo ( SharedPreferences sharedPrefs ) async {
2019-03-02 18:00:25 +02:00
_userName = null ;
2020-05-06 20:51:06 +03:00
if ( sharedPrefs ! = null & & sharedPrefs . containsKey ( ' cached_user ' ) ) {
try {
var data = json . decode ( sharedPrefs . getString ( ' cached_user ' ) ) ;
2020-05-07 18:31:18 +03:00
_parseUserInfo ( data ? ? { } ) ;
2020-05-06 20:51:06 +03:00
} catch ( e , stacktrace ) {
Logger . e ( ' Error getting cached user info: $ e ' , stacktrace: stacktrace ) ;
}
return Future . value ( ) ;
} else {
await ConnectionManager ( ) . sendSocketMessage ( type: " auth/current_user " ) . then ( ( data ) = > _parseUserInfo ( data ) ) . catchError ( ( e ) {
Logger . e ( ' auth/current_user error: $ e ' ) ;
} ) ;
}
2018-10-07 18:21:55 +03:00
}
2020-03-15 15:47:51 +02:00
void _parseUserInfo ( data ) {
_rawUserInfo = data ;
_userName = data [ " name " ] ;
}
Future _getPanels ( SharedPreferences sharedPrefs ) async {
2020-05-06 20:51:06 +03:00
if ( sharedPrefs ! = null & & sharedPrefs . containsKey ( ' cached_panels ' ) ) {
2020-03-15 15:47:51 +02:00
try {
2020-05-06 20:51:06 +03:00
var data = json . decode ( sharedPrefs . getString ( ' cached_panels ' ) ) ;
2020-05-07 18:38:38 +03:00
_parsePanels ( data ? ? { } ) ;
2020-05-01 16:47:41 +03:00
} catch ( e , stacktrace ) {
2020-06-03 21:42:26 +03:00
Logger . e ( e , stacktrace: stacktrace , skipCrashlytics: true ) ;
2020-05-01 19:05:07 +03:00
panels . clear ( ) ;
2020-03-15 15:47:51 +02:00
}
} else {
2020-05-01 16:47:41 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " get_panels " ) . then ( ( data ) = > _parsePanels ( data ) ) . catchError ( ( e , stacktrace ) {
2020-05-01 19:05:07 +03:00
Logger . e ( ' get_panels error: $ e ' , stacktrace: stacktrace ) ;
panels . clear ( ) ;
throw HACException ( ' Can \' t get panels: $ e ' ) ;
2020-03-15 15:47:51 +02:00
} ) ;
}
}
void _parsePanels ( data ) {
2020-05-01 19:05:07 +03:00
panels . clear ( ) ;
2020-03-15 15:47:51 +02:00
_rawPanels = data ;
2020-03-20 00:59:53 +02:00
List < Panel > dashboards = [ ] ;
2020-03-15 15:47:51 +02:00
data . forEach ( ( k , v ) {
2019-03-22 14:04:20 +02:00
String title = v [ ' title ' ] = = null ? " ${ k [ 0 ] . toUpperCase ( ) } ${ k . substring ( 1 ) } " : " ${ v [ ' title ' ] [ 0 ] . toUpperCase ( ) } ${ v [ ' title ' ] . substring ( 1 ) } " ;
2020-03-20 00:59:53 +02:00
if ( v [ ' component_name ' ] ! = null & & v [ ' component_name ' ] = = ' lovelace ' ) {
dashboards . add (
Panel (
id: k ,
2020-03-20 12:14:14 +02:00
componentName: v [ ' component_name ' ] ,
2020-03-20 00:59:53 +02:00
title: title ,
2020-03-20 12:14:14 +02:00
urlPath: v [ ' url_path ' ] ,
config: v [ ' config ' ] ,
icon: ( v [ ' icon ' ] = = null | | v [ ' icon ' ] = = ' hass:view-dashboard ' ) ? ' mdi:view-dashboard ' : v [ ' icon ' ]
2020-03-20 00:59:53 +02:00
)
) ;
} else {
panels . add (
Panel (
id: k ,
2020-03-20 12:14:14 +02:00
componentName: v [ ' component_name ' ] ,
2020-03-20 00:59:53 +02:00
title: title ,
2020-03-20 12:14:14 +02:00
urlPath: v [ ' url_path ' ] ,
config: v [ ' config ' ] ,
icon: v [ ' icon ' ]
2020-03-20 00:59:53 +02:00
)
) ;
}
} ) ;
panels . insertAll ( 0 , dashboards ) ;
2020-03-15 15:47:51 +02:00
}
Future getCameraStream ( String entityId ) {
Completer completer = Completer ( ) ;
ConnectionManager ( ) . sendSocketMessage ( type: " camera/stream " , additionalData: { " entity_id " : entityId } ) . then ( ( data ) {
completer . complete ( data ) ;
2019-03-22 14:04:20 +02:00
} ) . catchError ( ( e ) {
2020-03-15 15:47:51 +02:00
completer . completeError ( e ) ;
2018-10-02 15:46:24 +03:00
} ) ;
2020-03-15 15:47:51 +02:00
return completer . future ;
2018-09-25 22:47:06 +03:00
}
2020-04-15 17:03:31 +03:00
bool isComponentEnabled ( String name ) {
return _instanceConfig [ " components " ] ! = null & & ( _instanceConfig [ " components " ] as List ) . contains ( " $ name " ) ;
}
2020-03-20 01:16:59 +02:00
void _handleLovelaceUpdate ( ) {
if ( _fetchCompleter ! = null & & _fetchCompleter . isCompleted ) {
2020-03-22 01:11:00 +02:00
eventBus . fire ( new LovelaceChangedEvent ( ) ) ;
2020-03-20 01:16:59 +02:00
}
}
2018-09-25 22:47:06 +03:00
void _handleEntityStateChange ( Map eventData ) {
2019-12-11 00:04:24 +02:00
if ( _fetchCompleter ! = null & & _fetchCompleter . isCompleted ) {
2019-04-19 14:38:02 +03:00
Map data = Map . from ( eventData ) ;
eventBus . fire ( new StateChangedEvent (
entityId: data [ " entity_id " ] ,
needToRebuildUI: entities . updateState ( data )
) ) ;
}
2018-09-25 22:47:06 +03:00
}
2020-04-01 20:04:32 +03:00
bool isServiceExist ( String service ) {
return services ! = null & &
services . isNotEmpty & &
services . containsKey ( service ) ;
}
2018-10-27 00:54:05 +03:00
void _createUI ( ) {
2020-04-03 01:04:56 +03:00
Logger . d ( " Creating Lovelace UI " ) ;
ui = HomeAssistantUI ( rawLovelaceConfig: _rawLovelaceData ) ;
2018-10-25 00:54:20 +03:00
}
2018-10-02 17:23:19 +03:00
}