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
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 ;
2019-03-10 23:28:23 +02:00
HSVColor savedColor ;
2018-09-27 14:51:57 +03:00
2019-06-16 00:08:13 +03:00
String fcmToken ;
2018-10-27 00:54:05 +03:00
Map _rawLovelaceData ;
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 {
2019-08-31 22:10:07 +03:00
if ( ConnectionManager ( ) . useLovelace ) {
2018-11-04 21:02:12 +02:00
return ui ? . title ? ? " " ;
} else {
return _instanceConfig [ " location_name " ] ? ? " " ;
}
}
2018-10-07 20:18:14 +03:00
String get userName = > _userName ? ? locationName ;
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 ;
2019-04-19 14:38:02 +03:00
bool get isMobileAppEnabled = > _instanceConfig [ " components " ] ! = null & & ( _instanceConfig [ " components " ] as List ) . contains ( " mobile_app " ) ;
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 ;
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
2019-04-05 11:48:41 +03:00
Future fetchData ( ) {
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-08-31 22:10:07 +03:00
if ( entities = = null ) entities = EntityCollection ( ConnectionManager ( ) . httpWebHost ) ;
2019-03-26 00:18:30 +02:00
_fetchCompleter = Completer ( ) ;
2018-10-07 18:27:10 +03:00
List < Future > futures = [ ] ;
futures . add ( _getStates ( ) ) ;
2019-08-31 22:10:07 +03:00
if ( ConnectionManager ( ) . useLovelace ) {
2018-10-27 00:54:05 +03:00
futures . add ( _getLovelace ( ) ) ;
}
2018-10-07 18:27:10 +03:00
futures . add ( _getConfig ( ) ) ;
futures . add ( _getServices ( ) ) ;
2018-10-07 20:18:14 +03:00
futures . add ( _getUserInfo ( ) ) ;
2019-03-13 16:39:23 +02:00
futures . add ( _getPanels ( ) ) ;
2019-08-31 22:10:07 +03:00
futures . add ( ConnectionManager ( ) . sendSocketMessage (
2019-04-19 14:38:02 +03:00
type: " subscribe_events " ,
additionalData: { " event_type " : " state_changed " } ,
) ) ;
2019-03-26 00:18:30 +02:00
Future . wait ( futures ) . then ( ( _ ) {
2019-04-19 21:43:52 +03:00
if ( isMobileAppEnabled ) {
_createUI ( ) ;
_fetchCompleter . complete ( ) ;
2019-08-31 22:06:52 +03:00
MobileAppIntegrationManager . checkAppRegistration ( ) ;
2019-04-19 21:43:52 +03:00
} else {
_fetchCompleter . completeError ( HAError ( " Mobile app component not found " , actions: [ HAErrorAction . tryAgain ( ) , HAErrorAction ( type: HAErrorActionType . URL , title: " Help " , url: " http://ha-client.homemade.systems/docs#mobile-app " ) ] ) ) ;
}
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
}
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
}
2019-03-02 18:00:25 +02:00
Future _getConfig ( ) async {
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " get_config " ) . then ( ( data ) {
2019-03-22 14:04:20 +02:00
_instanceConfig = Map . from ( data ) ;
} ) . catchError ( ( e ) {
2019-04-19 21:43:52 +03:00
throw HAError ( " Error getting config: ${ e } " ) ;
2019-03-21 16:55:25 +02:00
} ) ;
2018-09-25 22:47:06 +03:00
}
2019-03-02 18:00:25 +02:00
Future _getStates ( ) async {
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " get_states " ) . then (
2019-03-22 14:04:20 +02:00
( data ) = > entities . parse ( data )
) . catchError ( ( e ) {
2019-04-19 21:43:52 +03:00
throw HAError ( " Error getting states: $ e " ) ;
2019-03-22 14:04:20 +02:00
} ) ;
2019-03-20 19:01:30 +02:00
}
2019-03-02 18:00:25 +02:00
Future _getLovelace ( ) async {
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " lovelace/config " ) . then ( ( data ) = > _rawLovelaceData = data ) . catchError ( ( e ) {
2019-04-19 21:43:52 +03:00
throw HAError ( " Error getting lovelace config: $ e " ) ;
2019-03-22 14:04:20 +02:00
} ) ;
2018-10-27 00:54:05 +03:00
}
2019-03-02 18:00:25 +02:00
Future _getUserInfo ( ) async {
_userName = null ;
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " auth/current_user " ) . then ( ( data ) = > _userName = data [ " name " ] ) . catchError ( ( e ) {
2019-03-22 14:04:20 +02:00
Logger . w ( " Can't get user info: ${ e } " ) ;
} ) ;
2018-10-07 18:21:55 +03:00
}
2019-03-02 18:00:25 +02:00
Future _getServices ( ) async {
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " get_services " ) . then ( ( data ) = > Logger . d ( " Services received " ) ) . catchError ( ( e ) {
2019-03-22 14:04:20 +02:00
Logger . w ( " Can't get services: ${ e } " ) ;
} ) ;
2018-09-25 22:47:06 +03:00
}
2019-03-13 16:39:23 +02:00
Future _getPanels ( ) async {
panels . clear ( ) ;
2019-08-31 22:10:07 +03:00
await ConnectionManager ( ) . sendSocketMessage ( type: " get_panels " ) . then ( ( data ) {
2019-03-22 14:04:20 +02:00
data . forEach ( ( k , v ) {
String title = v [ ' title ' ] = = null ? " ${ k [ 0 ] . toUpperCase ( ) } ${ k . substring ( 1 ) } " : " ${ v [ ' title ' ] [ 0 ] . toUpperCase ( ) } ${ v [ ' title ' ] . substring ( 1 ) } " ;
panels . add ( Panel (
id: k ,
type: v [ " component_name " ] ,
title: title ,
urlPath: v [ " url_path " ] ,
config: v [ " config " ] ,
icon: v [ " icon " ]
)
) ;
} ) ;
} ) . catchError ( ( e ) {
2019-04-19 21:43:52 +03:00
throw HAError ( " Error getting panels list: $ e " ) ;
2018-10-02 15:46:24 +03:00
} ) ;
2018-09-25 22:47:06 +03:00
}
void _handleEntityStateChange ( Map eventData ) {
2018-10-27 01:24:23 +03:00
//TheLogger.debug( "New state for ${eventData['entity_id']}");
2019-04-19 14:40:05 +03:00
if ( _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
}
2018-10-27 00:54:05 +03:00
void _parseLovelace ( ) {
2018-12-15 14:09:37 +02:00
Logger . d ( " --Title: ${ _rawLovelaceData [ " title " ] } " ) ;
2018-11-04 21:02:12 +02:00
ui . title = _rawLovelaceData [ " title " ] ;
2018-10-27 00:54:05 +03:00
int viewCounter = 0 ;
2018-12-15 14:09:37 +02:00
Logger . d ( " --Views count: ${ _rawLovelaceData [ ' views ' ] . length } " ) ;
2018-10-27 00:54:05 +03:00
_rawLovelaceData [ " views " ] . forEach ( ( rawView ) {
2018-12-15 14:09:37 +02:00
Logger . d ( " ----view id: ${ rawView [ ' id ' ] } " ) ;
2018-10-27 00:54:05 +03:00
HAView view = HAView (
count: viewCounter ,
2018-11-04 18:26:31 +02:00
id: " ${ rawView [ ' id ' ] } " ,
2018-10-27 00:54:05 +03:00
name: rawView [ ' title ' ] ,
iconName: rawView [ ' icon ' ]
) ;
2019-01-25 22:55:41 +02:00
if ( rawView [ ' badges ' ] ! = null & & rawView [ ' badges ' ] is List ) {
rawView [ ' badges ' ] . forEach ( ( entity ) {
if ( entities . isExist ( entity ) ) {
Entity e = entities . get ( entity ) ;
view . badges . add ( e ) ;
}
} ) ;
}
2018-10-27 00:54:05 +03:00
view . cards . addAll ( _createLovelaceCards ( rawView [ " cards " ] ? ? [ ] ) ) ;
ui . views . add (
view
) ;
viewCounter + = 1 ;
} ) ;
}
List < HACard > _createLovelaceCards ( List rawCards ) {
List < HACard > result = [ ] ;
rawCards . forEach ( ( rawCard ) {
2019-01-25 22:41:26 +02:00
try {
2019-08-26 17:03:28 +03:00
//bool isThereCardOptionsInside = rawCard["card"] != null;
var rawCardInfo = rawCard [ " card " ] ? ? rawCard ;
2019-01-25 22:41:26 +02:00
HACard card = HACard (
id: " card " ,
2019-08-26 17:03:28 +03:00
name: rawCardInfo [ " title " ] ? ? rawCardInfo [ " name " ] ,
type: rawCardInfo [ ' type ' ] ,
columnsCount: rawCardInfo [ ' columns ' ] ? ? 4 ,
showName: rawCardInfo [ ' show_name ' ] ? ? true ,
showState: rawCardInfo [ ' show_state ' ] ? ? true ,
showEmpty: rawCardInfo [ ' show_empty ' ] ? ? true ,
stateFilter: rawCardInfo [ ' state_filter ' ] ? ? [ ] ,
states: rawCardInfo [ ' states ' ] ,
conditions: rawCard [ ' conditions ' ] ? ? [ ] ,
content: rawCardInfo [ ' content ' ]
2019-01-25 22:41:26 +02:00
) ;
2019-08-26 17:03:28 +03:00
if ( rawCardInfo [ " cards " ] ! = null ) {
card . childCards = _createLovelaceCards ( rawCardInfo [ " cards " ] ) ;
2018-11-25 20:44:19 +02:00
}
2019-08-26 17:03:28 +03:00
rawCardInfo [ " entities " ] ? . forEach ( ( rawEntity ) {
2019-01-25 22:41:26 +02:00
if ( rawEntity is String ) {
if ( entities . isExist ( rawEntity ) ) {
card . entities . add ( EntityWrapper ( entity: entities . get ( rawEntity ) ) ) ;
2019-03-12 23:35:33 +02:00
} else {
card . entities . add ( EntityWrapper ( entity: Entity . missed ( rawEntity ) ) ) ;
2019-01-25 22:41:26 +02:00
}
} else {
2019-03-13 00:56:57 +02:00
if ( rawEntity [ " type " ] = = " divider " ) {
card . entities . add ( EntityWrapper ( entity: Entity . divider ( ) ) ) ;
} else if ( rawEntity [ " type " ] = = " section " ) {
card . entities . add ( EntityWrapper ( entity: Entity . section ( rawEntity [ " label " ] ? ? " " ) ) ) ;
} else if ( rawEntity [ " type " ] = = " call-service " ) {
Map uiActionData = {
" tap_action " : {
" action " : EntityUIAction . callService ,
" service " : rawEntity [ " service " ] ,
" service_data " : rawEntity [ " service_data " ]
} ,
" hold_action " : EntityUIAction . none
} ;
card . entities . add ( EntityWrapper (
entity: Entity . callService (
icon: rawEntity [ " icon " ] ,
name: rawEntity [ " name " ] ,
service: rawEntity [ " service " ] ,
actionName: rawEntity [ " action_name " ]
) ,
uiAction: EntityUIAction ( rawEntityData: uiActionData )
)
) ;
} else if ( rawEntity [ " type " ] = = " weblink " ) {
Map uiActionData = {
" tap_action " : {
" action " : EntityUIAction . navigate ,
" service " : rawEntity [ " url " ]
} ,
" hold_action " : EntityUIAction . none
} ;
card . entities . add ( EntityWrapper (
entity: Entity . weblink (
icon: rawEntity [ " icon " ] ,
name: rawEntity [ " name " ] ,
url: rawEntity [ " url " ]
) ,
uiAction: EntityUIAction ( rawEntityData: uiActionData )
)
) ;
} else if ( entities . isExist ( rawEntity [ " entity " ] ) ) {
2019-01-25 22:41:26 +02:00
Entity e = entities . get ( rawEntity [ " entity " ] ) ;
card . entities . add (
EntityWrapper (
entity: e ,
2019-01-25 23:30:23 +02:00
displayName: rawEntity [ " name " ] ,
2019-01-25 22:45:54 +02:00
icon: rawEntity [ " icon " ] ,
2019-01-25 22:41:26 +02:00
uiAction: EntityUIAction ( rawEntityData: rawEntity )
)
) ;
2019-03-12 23:35:33 +02:00
} else {
card . entities . add ( EntityWrapper ( entity: Entity . missed ( rawEntity [ " entity " ] ) ) ) ;
2019-01-25 22:41:26 +02:00
}
2018-11-25 20:44:19 +02:00
}
2019-01-25 22:41:26 +02:00
} ) ;
2019-08-26 17:03:28 +03:00
if ( rawCardInfo [ " entity " ] ! = null ) {
var en = rawCardInfo [ " entity " ] ;
2019-01-25 22:41:26 +02:00
if ( en is String ) {
if ( entities . isExist ( en ) ) {
Entity e = entities . get ( en ) ;
card . linkedEntityWrapper = EntityWrapper (
entity: e ,
2019-08-26 17:03:28 +03:00
icon: rawCardInfo [ " icon " ] ,
displayName: rawCardInfo [ " name " ] ,
2019-01-25 22:41:26 +02:00
uiAction: EntityUIAction ( rawEntityData: rawCard )
) ;
2019-03-12 23:35:33 +02:00
} else {
card . linkedEntityWrapper = EntityWrapper ( entity: Entity . missed ( en ) ) ;
2019-01-25 22:41:26 +02:00
}
} else {
if ( entities . isExist ( en [ " entity " ] ) ) {
Entity e = entities . get ( en [ " entity " ] ) ;
card . linkedEntityWrapper = EntityWrapper (
entity: e ,
2019-01-25 22:45:54 +02:00
icon: en [ " icon " ] ,
2019-01-25 23:30:23 +02:00
displayName: en [ " name " ] ,
2019-01-25 22:41:26 +02:00
uiAction: EntityUIAction ( rawEntityData: rawCard )
) ;
2019-03-12 23:35:33 +02:00
} else {
card . linkedEntityWrapper = EntityWrapper ( entity: Entity . missed ( en [ " entity " ] ) ) ;
2019-01-25 22:41:26 +02:00
}
2018-11-15 19:08:47 +02:00
}
2018-10-27 17:28:47 +03:00
}
2019-01-25 22:41:26 +02:00
result . add ( card ) ;
} catch ( e ) {
Logger . e ( " There was an error parsing card: ${ e . toString ( ) } " ) ;
2018-10-27 00:54:05 +03:00
}
} ) ;
return result ;
2018-09-25 22:47:06 +03:00
}
2018-10-27 00:54:05 +03:00
void _createUI ( ) {
2018-11-04 21:02:12 +02:00
ui = HomeAssistantUI ( ) ;
2019-08-31 22:10:07 +03:00
if ( ( ConnectionManager ( ) . useLovelace ) & & ( _rawLovelaceData ! = null ) ) {
2018-12-15 14:09:37 +02:00
Logger . d ( " Creating Lovelace UI " ) ;
2018-10-27 00:54:05 +03:00
_parseLovelace ( ) ;
} else {
2018-12-15 14:09:37 +02:00
Logger . d ( " Creating group-based UI " ) ;
2018-10-27 00:54:05 +03:00
int viewCounter = 0 ;
if ( ! entities . hasDefaultView ) {
HAView view = HAView (
count: viewCounter ,
id: " group.default_view " ,
name: " Home " ,
childEntities: entities . filterEntitiesForDefaultView ( )
) ;
ui . views . add (
view
) ;
viewCounter + = 1 ;
2018-10-25 00:54:20 +03:00
}
2018-10-27 00:54:05 +03:00
entities . viewEntities . forEach ( ( viewEntity ) {
HAView view = HAView (
count: viewCounter ,
id: viewEntity . entityId ,
name: viewEntity . displayName ,
childEntities: viewEntity . childEntities
) ;
view . linkedEntity = viewEntity ;
ui . views . add (
view
) ;
viewCounter + = 1 ;
} ) ;
}
2018-10-25 00:54:20 +03:00
}
2019-03-19 23:07:40 +02:00
Widget buildViews ( BuildContext context , TabController tabController ) {
2019-02-21 15:35:58 +02:00
return ui . build ( context , tabController ) ;
2018-10-07 02:17:14 +03:00
}
2018-10-02 17:23:19 +03:00
}
2019-03-26 00:18:30 +02:00
/ *
2018-10-02 17:23:19 +03:00
class SendMessageQueue {
int _messageTimeout ;
List < HAMessage > _queue = [ ] ;
SendMessageQueue ( this . _messageTimeout ) ;
void add ( String message ) {
_queue . add ( HAMessage ( _messageTimeout , message ) ) ;
}
List < String > getActualMessages ( ) {
_queue . removeWhere ( ( item ) = > item . isExpired ( ) ) ;
List < String > result = [ ] ;
_queue . forEach ( ( haMessage ) {
result . add ( haMessage . message ) ;
} ) ;
this . clear ( ) ;
return result ;
}
void clear ( ) {
_queue . clear ( ) ;
}
}
class HAMessage {
DateTime _timeStamp ;
int _messageTimeout ;
String message ;
HAMessage ( this . _messageTimeout , this . message ) {
_timeStamp = DateTime . now ( ) ;
}
bool isExpired ( ) {
2018-10-07 12:14:48 +03:00
return DateTime . now ( ) . difference ( _timeStamp ) . inSeconds > _messageTimeout ;
2018-10-02 17:23:19 +03:00
}
2019-03-26 00:18:30 +02:00
} * /