From c0a9b89d40293d83a7a85d35be30186526ccd874 Mon Sep 17 00:00:00 2001 From: estevez Date: Sat, 29 Sep 2018 16:19:01 +0300 Subject: [PATCH] Resolves #26 Entity view page --- lib/entity.class.dart | 160 ++++++++++++++++++++++++++----- lib/entity.page.dart | 48 ++++++++++ lib/entity_collection.class.dart | 1 + lib/log.page.dart | 5 - lib/main.dart | 18 ++++ lib/utils.class.dart | 6 ++ pubspec.lock | 7 ++ pubspec.yaml | 1 + 8 files changed, 219 insertions(+), 27 deletions(-) create mode 100644 lib/entity.page.dart diff --git a/lib/entity.class.dart b/lib/entity.class.dart index 5f74719..c261677 100644 --- a/lib/entity.class.dart +++ b/lib/entity.class.dart @@ -1,18 +1,24 @@ part of 'main.dart'; class Entity { - static Map stateIconColors = { + static const STATE_ICONS_COLORS = { "on": Colors.amber, "off": Color.fromRGBO(68, 115, 158, 1.0), "unavailable": Colors.black12, "unknown": Colors.black12, "playing": Colors.amber }; + static const RIGTH_WIDGET_PADDING = 14.0; + static const LEFT_WIDGET_PADDING = 8.0; + static const EXTENDED_WIDGET_HEIGHT = 50.0; + static const WIDGET_HEIGHT = 34.0; + Map _attributes; String _domain; String _entityId; String _state; String _entityPicture; + DateTime _lastUpdated; String get displayName => _attributes["friendly_name"] ?? (_attributes["name"] ?? "_"); String get domain => _domain; @@ -34,6 +40,7 @@ class Entity { String get entityPicture => _attributes["entity_picture"]; String get unitOfMeasurement => _attributes["unit_of_measurement"] ?? ""; List get childEntities => _attributes["entity_id"] ?? []; + String get lastUpdated => _getLastUpdatedFormatted(); Entity(Map rawData) { update(rawData); @@ -48,25 +55,34 @@ class Entity { _domain = rawData["entity_id"].split(".")[0]; _entityId = rawData["entity_id"]; _state = rawData["state"]; + _lastUpdated = DateTime.tryParse(rawData["last_updated"]); + } + + String _getLastUpdatedFormatted() { + if (_lastUpdated == null) { + return "-"; + } else { + return formatDate(_lastUpdated, [yy, '-', M, '-', d, ' ', HH, ':', nn, ':', ss]); + } + } + + void openEntityPage() { + eventBus.fire(new ShowEntityPageEvent(this)); } Widget buildWidget() { return SizedBox( - height: 34.0, + height: Entity.WIDGET_HEIGHT, child: Row( children: [ - Padding( - padding: EdgeInsets.fromLTRB(8.0, 0.0, 12.0, 0.0), - child: MaterialDesignIcons.createIconWidgetFromEntityData(this, 28.0, Entity.stateIconColors[_state] ?? Colors.blueGrey), + GestureDetector( + child: _buildIconWidget(), + onTap: openEntityPage, ), Expanded( - child: Text( - "${this.displayName}", - overflow: TextOverflow.fade, - softWrap: false, - style: TextStyle( - fontSize: 16.0 - ), + child: GestureDetector( + child: _buildNameWidget(), + onTap: openEntityPage, ), ), _buildActionWidget() @@ -75,18 +91,78 @@ class Entity { ); } + Widget buildExtendedWidget() { + return Row( + children: [ + _buildIconWidget(), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: _buildNameWidget(), + ), + _buildExtendedActionWidget() + ], + ), + _buildLastUpdatedWidget() + ], + ), + ) + ], + ); + } + + Widget _buildIconWidget() { + return Padding( + padding: EdgeInsets.fromLTRB(Entity.LEFT_WIDGET_PADDING, 0.0, 12.0, 0.0), + child: MaterialDesignIcons.createIconWidgetFromEntityData(this, 28.0, Entity.STATE_ICONS_COLORS[_state] ?? Colors.blueGrey), + ); + } + + Widget _buildLastUpdatedWidget() { + return Text( + '${this.lastUpdated}', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 12.0, + color: Colors.black26 + ), + ); + } + + Widget _buildNameWidget() { + return Text( + "${this.displayName}", + overflow: TextOverflow.fade, + softWrap: false, + style: TextStyle( + fontSize: 16.0 + ), + ); + } + Widget _buildActionWidget() { return Padding( - padding: EdgeInsets.fromLTRB(0.0, 0.0, 14.0, 0.0), - child: Text( - "${_state}${this.unitOfMeasurement}", - textAlign: TextAlign.right, - style: new TextStyle( - fontSize: 16.0, - ) + padding: EdgeInsets.fromLTRB(0.0, 0.0, Entity.RIGTH_WIDGET_PADDING, 0.0), + child: GestureDetector( + child: Text( + "$_state${this.unitOfMeasurement}", + textAlign: TextAlign.right, + style: new TextStyle( + fontSize: 16.0, + ) + ), + onTap: openEntityPage, ) ); } + + Widget _buildExtendedActionWidget() { + return _buildActionWidget(); + } } class SwitchEntity extends Entity { @@ -129,6 +205,30 @@ class InputEntity extends Entity { InputEntity(Map rawData) : super(rawData); + @override + Widget buildExtendedWidget() { + return Column( + children: [ + SizedBox( + height: Entity.EXTENDED_WIDGET_HEIGHT, + child: Row( + children: [ + _buildIconWidget(), + Expanded( + child: _buildNameWidget(), + ), + _buildLastUpdatedWidget() + ], + ), + ), + SizedBox( + height: Entity.EXTENDED_WIDGET_HEIGHT, + child: _buildExtendedActionWidget(), + ) + ], + ); + } + @override Widget _buildActionWidget() { if (this.isSlider) { @@ -146,7 +246,7 @@ class InputEntity extends Entity { eventBus.fire(new StateChangedEvent(_entityId, (value.roundToDouble() / 10).toString(), true)); }, onChangeEnd: (value) { - eventBus.fire(new ServiceCallEvent(_domain, "set_value", _entityId,{"value": "${_state}"})); + eventBus.fire(new ServiceCallEvent(_domain, "set_value", _entityId,{"value": "$_state"})); }, ), ), @@ -164,9 +264,25 @@ class InputEntity extends Entity { ), ); } else { - //TODO draw box instead of slider - return Text("Not implemented"); + return super._buildActionWidget(); } } + @override + Widget _buildExtendedActionWidget() { + return Padding( + padding: EdgeInsets.fromLTRB(Entity.LEFT_WIDGET_PADDING, 0.0, Entity.RIGTH_WIDGET_PADDING, 0.0), + child: Container( + child: TextField( + controller: TextEditingController( + text: _state, + ), + onChanged: (value) { + eventBus.fire(new ServiceCallEvent(_domain, "set_value", _entityId,{"value": "$value"})); + }, + ), + ) + ); + } + } \ No newline at end of file diff --git a/lib/entity.page.dart b/lib/entity.page.dart new file mode 100644 index 0000000..7cb17b0 --- /dev/null +++ b/lib/entity.page.dart @@ -0,0 +1,48 @@ +part of 'main.dart'; + +class EntityViewPage extends StatefulWidget { + EntityViewPage({Key key, this.entity}) : super(key: key); + + Entity entity; + + @override + _EntityViewPageState createState() => new _EntityViewPageState(); +} + +class _EntityViewPageState extends State { + String _title; + Entity _entity; + + @override + void initState() { + super.initState(); + _entity = widget.entity; + _prepareData(); + } + + _prepareData() async { + _title = _entity.displayName; + } + + @override + Widget build(BuildContext context) { + return new Scaffold( + appBar: new AppBar( + leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ + Navigator.pop(context); + }), + // Here we take the value from the MyHomePage object that was created by + // the App.build method, and use it to set our appbar title. + title: new Text(_title), + ), + body: Padding( + padding: EdgeInsets.all(10.0), + child: ListView( + children: [ + _entity.buildExtendedWidget() + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/entity_collection.class.dart b/lib/entity_collection.class.dart index cdd6ba4..1aae3b6 100644 --- a/lib/entity_collection.class.dart +++ b/lib/entity_collection.class.dart @@ -40,6 +40,7 @@ class EntityCollection { return ButtonEntity(rawEntityData); } + case "input_text": case "input_number": { return InputEntity(rawEntityData); } diff --git a/lib/log.page.dart b/lib/log.page.dart index 4355e82..ca142ce 100644 --- a/lib/log.page.dart +++ b/lib/log.page.dart @@ -10,11 +10,6 @@ class LogViewPage extends StatefulWidget { } class _LogViewPageState extends State { - String _hassioDomain = ""; - String _hassioPort = "8123"; - String _hassioPassword = ""; - String _socketProtocol = "wss"; - String _authType = "access_token"; String _logData; @override diff --git a/lib/main.dart b/lib/main.dart index acae0da..9bebc03 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,10 +10,12 @@ import 'package:flutter/widgets.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter/services.dart'; +import 'package:date_format/date_format.dart'; part 'settings.page.dart'; part 'home_assistant.class.dart'; part 'log.page.dart'; +part 'entity.page.dart'; part 'utils.class.dart'; part 'mdi.class.dart'; part 'entity.class.dart'; @@ -86,6 +88,7 @@ class _MainPageState extends State with WidgetsBindingObserver { StreamSubscription _stateSubscription; StreamSubscription _settingsSubscription; StreamSubscription _serviceCallSubscription; + StreamSubscription _showEntityPageSubscription; bool _isLoading = true; Map _badgeColors = { @@ -152,6 +155,11 @@ class _MainPageState extends State with WidgetsBindingObserver { _serviceCallSubscription = eventBus.on().listen((event) { _callService(event.domain, event.service, event.entityId, event.additionalParams); }); + + if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel(); + _showEntityPageSubscription = eventBus.on().listen((event) { + _showEntityPage(event.entity); + }); } _refreshData() async { @@ -192,6 +200,15 @@ class _MainPageState extends State with WidgetsBindingObserver { }).catchError((e) => _setErrorState(e)); } + void _showEntityPage(Entity entity) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => EntityViewPage(entity: entity), + ) + ); + } + List _buildViews() { List result = []; if ((_entities != null) && (!_homeAssistant.uiBuilder.isEmpty)) { @@ -602,6 +619,7 @@ class _MainPageState extends State with WidgetsBindingObserver { if (_stateSubscription != null) _stateSubscription.cancel(); if (_settingsSubscription != null) _settingsSubscription.cancel(); if (_serviceCallSubscription != null) _serviceCallSubscription.cancel(); + if (_showEntityPageSubscription != null) _showEntityPageSubscription.cancel(); _homeAssistant.closeConnection(); super.dispose(); } diff --git a/lib/utils.class.dart b/lib/utils.class.dart index 3f40b84..8a6486c 100644 --- a/lib/utils.class.dart +++ b/lib/utils.class.dart @@ -63,4 +63,10 @@ class ServiceCallEvent { Map additionalParams; ServiceCallEvent(this.domain, this.service, this.entityId, this.additionalParams); +} + +class ShowEntityPageEvent { + Entity entity; + + ShowEntityPageEvent(this.entity); } \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 0f4e6b7..54e3160 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -87,6 +87,13 @@ packages: url: "https://github.com/MarkOSullivan94/dart_config.git" source: git version: "0.5.0" + date_format: + dependency: "direct main" + description: + name: date_format + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.5" event_bus: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index c44c853..587da90 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: flutter_launcher_icons: ^0.6.1 cached_network_image: ^0.4.1 url_launcher: ^3.0.3 + date_format: ^1.0.5 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.