Resolves #208 Gauge card

This commit is contained in:
estevez-dev 2019-09-07 15:47:09 +03:00
parent 9160dbf7f2
commit 6650c5c145
11 changed files with 237 additions and 44 deletions

View File

@ -15,6 +15,10 @@ class HACard {
List states; List states;
List conditions; List conditions;
String content; String content;
String unit;
int min;
int max;
Map severity;
HACard({ HACard({
this.name, this.name,
@ -28,6 +32,10 @@ class HACard {
this.content, this.content,
this.states, this.states,
this.conditions: const [], this.conditions: const [],
this.unit,
this.min,
this.max,
this.severity,
@required this.type @required this.type
}) { }) {
if (this.columnsCount <= 0) { if (this.columnsCount <= 0) {

View File

@ -1,4 +1,4 @@
part of '../main.dart'; part of '../../main.dart';
class ButtonEntityContainer extends StatelessWidget { class ButtonEntityContainer extends StatelessWidget {

View File

@ -1,4 +1,4 @@
part of '../main.dart'; part of '../../main.dart';
class CardWidget extends StatelessWidget { class CardWidget extends StatelessWidget {
@ -42,31 +42,35 @@ class CardWidget extends StatelessWidget {
switch (card.type) { switch (card.type) {
case CardType.entities: { case CardType.ENTITIES: {
return _buildEntitiesCard(context); return _buildEntitiesCard(context);
} }
case CardType.glance: { case CardType.GLANCE: {
return _buildGlanceCard(context); return _buildGlanceCard(context);
} }
case CardType.mediaControl: { case CardType.MEDIA_CONTROL: {
return _buildMediaControlsCard(context); return _buildMediaControlsCard(context);
} }
case CardType.entityButton: { case CardType.ENTITY_BUTTON: {
return _buildEntityButtonCard(context); return _buildEntityButtonCard(context);
} }
case CardType.markdown: { case CardType.GAUGE: {
return _buildGaugeCard(context);
}
case CardType.MARKDOWN: {
return _buildMarkdownCard(context); return _buildMarkdownCard(context);
} }
case CardType.alarmPanel: { case CardType.ALARM_PANEL: {
return _buildAlarmPanelCard(context); return _buildAlarmPanelCard(context);
} }
case CardType.horizontalStack: { case CardType.HORIZONTAL_STACK: {
if (card.childCards.isNotEmpty) { if (card.childCards.isNotEmpty) {
List<Widget> children = []; List<Widget> children = [];
card.childCards.forEach((card) { card.childCards.forEach((card) {
@ -89,7 +93,7 @@ class CardWidget extends StatelessWidget {
return Container(height: 0.0, width: 0.0,); return Container(height: 0.0, width: 0.0,);
} }
case CardType.verticalStack: { case CardType.VERTICAL_STACK: {
if (card.childCards.isNotEmpty) { if (card.childCards.isNotEmpty) {
List<Widget> children = []; List<Widget> children = [];
card.childCards.forEach((card) { card.childCards.forEach((card) {
@ -272,6 +276,23 @@ class CardWidget extends StatelessWidget {
); );
} }
Widget _buildGaugeCard(BuildContext context) {
card.linkedEntityWrapper.displayName = card.name ??
card.linkedEntityWrapper.displayName;
return Card(
child: EntityModel(
entityWrapper: card.linkedEntityWrapper,
child: GaugeCardBody(
min: card.min,
max: card.max,
unit: card.unit ?? card.linkedEntityWrapper.entity.unitOfMeasurement,
severity: card.severity,
),
handleTap: true
)
);
}
Widget _buildUnsupportedCard(BuildContext context) { Widget _buildUnsupportedCard(BuildContext context) {
List<Widget> body = []; List<Widget> body = [];
body.add(CardHeaderWidget(name: card.name ?? "")); body.add(CardHeaderWidget(name: card.name ?? ""));

View File

@ -0,0 +1,149 @@
part of '../../main.dart';
class GaugeCardBody extends StatefulWidget {
final int min;
final int max;
final String unit;
final Map severity;
GaugeCardBody({Key key, this.min, this.max, this.unit, this.severity}) : super(key: key);
@override
_GaugeCardBodyState createState() => _GaugeCardBodyState();
}
class _GaugeCardBodyState extends State<GaugeCardBody> {
List<charts.Series> seriesList;
List<charts.Series<GaugeSegment, String>> _createData(double value) {
double fixedValue;
if (value > widget.max) {
fixedValue = widget.max.toDouble();
} else if (value < widget.min) {
fixedValue = widget.min.toDouble();
} else {
fixedValue = value;
}
double toShow = ((fixedValue - widget.min) / (widget.max - widget.min)) * 100;
Color mainColor;
if (widget.severity != null) {
if (widget.severity["red"] is int && fixedValue >= widget.severity["red"]) {
mainColor = Colors.red;
} else if (widget.severity["yellow"] is int && fixedValue >= widget.severity["yellow"]) {
mainColor = Colors.amber;
} else {
mainColor = Colors.green;
}
} else {
mainColor = Colors.green;
}
final data = [
GaugeSegment('Main', toShow, mainColor),
GaugeSegment('Rest', 100 - toShow, Colors.black45),
];
return [
charts.Series<GaugeSegment, String>(
id: 'Segments',
domainFn: (GaugeSegment segment, _) => segment.segment,
measureFn: (GaugeSegment segment, _) => segment.value,
colorFn: (GaugeSegment segment, _) => segment.color,
// Set a label accessor to control the text of the arc label.
labelAccessorFn: (GaugeSegment segment, _) =>
segment.segment == 'Main' ? '${segment.value}' : null,
data: data,
)
];
}
@override
Widget build(BuildContext context) {
EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
return InkWell(
onTap: () => entityWrapper.handleTap(),
onLongPress: () => entityWrapper.handleHold(),
child: AspectRatio(
aspectRatio: 1.5,
child: Stack(
fit: StackFit.expand,
overflow: Overflow.clip,
children: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double verticalOffset;
if(constraints.maxWidth > 150.0) {
verticalOffset = 0.2;
} else if (constraints.maxWidth > 100.0) {
verticalOffset = 0.3;
} else {
verticalOffset = 0.3;
}
return FractionallySizedBox(
heightFactor: 2,
widthFactor: 1,
alignment: FractionalOffset(0,verticalOffset),
child: charts.PieChart(
_createData(entityWrapper.entity.doubleState),
animate: false,
defaultRenderer: charts.ArcRendererConfig(
arcRatio: 0.4,
startAngle: pi,
arcLength: pi,
),
),
);
}
),
Align(
alignment: Alignment.bottomCenter,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double fontSize = constraints.maxHeight / 7;
return Padding(
padding: EdgeInsets.only(bottom: 2*fontSize),
child: Text(
'${entityWrapper.entity.doubleState}${widget.unit}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: fontSize),
),
);
}
),
),
Align(
alignment: Alignment.bottomCenter,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double fontSize = constraints.maxHeight / 7;
return Padding(
padding: EdgeInsets.only(bottom: fontSize),
child: Text(
'${entityWrapper.displayName}',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: fontSize),
),
);
}
),
)
]
)
),
);
}
}
class GaugeSegment {
final String segment;
final double value;
final charts.Color color;
GaugeSegment(this.segment, this.value, Color color)
: this.color = charts.Color(
r: color.red, g: color.green, b: color.blue, a: color.alpha);
}

View File

@ -1,4 +1,4 @@
part of '../main.dart'; part of '../../main.dart';
class GlanceEntityContainer extends StatelessWidget { class GlanceEntityContainer extends StatelessWidget {

View File

@ -77,23 +77,23 @@ class EntityUIAction {
} }
class CardType { class CardType {
static const horizontalStack = "horizontal-stack"; static const HORIZONTAL_STACK = "horizontal-stack";
static const verticalStack = "vertical-stack"; static const VERTICAL_STACK = "vertical-stack";
static const entities = "entities"; static const ENTITIES = "entities";
static const glance = "glance"; static const GLANCE = "glance";
static const mediaControl = "media-control"; static const MEDIA_CONTROL = "media-control";
static const weatherForecast = "weather-forecast"; static const WEATHER_FORECAST = "weather-forecast";
static const thermostat = "thermostat"; static const THERMOSTAT = "thermostat";
static const sensor = "sensor"; static const SENSOR = "sensor";
static const plantStatus = "plant-status"; static const PLANT_STATUS = "plant-status";
static const pictureEntity = "picture-entity"; static const PICTURE_ENTITY = "picture-entity";
static const pictureElements = "picture-elements"; static const PICTURE_ELEMENTS = "picture-elements";
static const picture = "picture"; static const PICTURE = "picture";
static const map = "map"; static const MAP = "map";
static const iframe = "iframe"; static const IFRAME = "iframe";
static const gauge = "gauge"; static const GAUGE = "gauge";
static const entityButton = "entity-button"; static const ENTITY_BUTTON = "entity-button";
static const conditional = "conditional"; static const CONDITIONAL = "conditional";
static const alarmPanel = "alarm-panel"; static const ALARM_PANEL = "alarm-panel";
static const markdown = "markdown"; static const MARKDOWN = "markdown";
} }

View File

@ -191,7 +191,7 @@ class HomeAssistant {
HACard card = HACard( HACard card = HACard(
id: "card", id: "card",
name: rawCardInfo["title"] ?? rawCardInfo["name"], name: rawCardInfo["title"] ?? rawCardInfo["name"],
type: rawCardInfo['type'] ?? CardType.entities, type: rawCardInfo['type'] ?? CardType.ENTITIES,
columnsCount: rawCardInfo['columns'] ?? 4, columnsCount: rawCardInfo['columns'] ?? 4,
showName: rawCardInfo['show_name'] ?? true, showName: rawCardInfo['show_name'] ?? true,
showState: rawCardInfo['show_state'] ?? true, showState: rawCardInfo['show_state'] ?? true,
@ -199,7 +199,11 @@ class HomeAssistant {
stateFilter: rawCardInfo['state_filter'] ?? [], stateFilter: rawCardInfo['state_filter'] ?? [],
states: rawCardInfo['states'], states: rawCardInfo['states'],
conditions: rawCard['conditions'] ?? [], conditions: rawCard['conditions'] ?? [],
content: rawCardInfo['content'] content: rawCardInfo['content'],
min: rawCardInfo['min'] ?? 0,
max: rawCardInfo['max'] ?? 100,
unit: rawCardInfo['unit'],
severity: rawCardInfo['severity']
); );
if (rawCardInfo["cards"] != null) { if (rawCardInfo["cards"] != null) {
card.childCards = _createLovelaceCards(rawCardInfo["cards"]); card.childCards = _createLovelaceCards(rawCardInfo["cards"]);

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -21,6 +22,7 @@ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:device_info/device_info.dart'; import 'package:device_info/device_info.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:in_app_purchase/in_app_purchase.dart'; import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:auto_size_text/auto_size_text.dart';
part 'const.dart'; part 'const.dart';
part 'utils/launcher.dart'; part 'utils/launcher.dart';
@ -49,8 +51,8 @@ part 'entity_widgets/common/badge.dart';
part 'entity_widgets/model_widgets.dart'; part 'entity_widgets/model_widgets.dart';
part 'entity_widgets/default_entity_container.dart'; part 'entity_widgets/default_entity_container.dart';
part 'entity_widgets/missed_entity.dart'; part 'entity_widgets/missed_entity.dart';
part 'entity_widgets/glance_entity_container.dart'; part 'cards/widgets/glance_entity_container.dart';
part 'entity_widgets/button_entity_container.dart'; part 'cards/widgets/button_entity_container.dart';
part 'entity_widgets/common/entity_attributes_list.dart'; part 'entity_widgets/common/entity_attributes_list.dart';
part 'entity_widgets/entity_icon.dart'; part 'entity_widgets/entity_icon.dart';
part 'entity_widgets/entity_name.dart'; part 'entity_widgets/entity_name.dart';
@ -106,17 +108,18 @@ part 'managers/device_info_manager.class.dart';
part 'managers/startup_user_messages_manager.class.dart'; part 'managers/startup_user_messages_manager.class.dart';
part 'ui_class/ui.dart'; part 'ui_class/ui.dart';
part 'ui_class/view.class.dart'; part 'ui_class/view.class.dart';
part 'ui_class/card.class.dart'; part 'cards/card.class.dart';
part 'ui_class/sizes_class.dart'; part 'ui_class/sizes_class.dart';
part 'ui_class/panel_class.dart'; part 'ui_class/panel_class.dart';
part 'ui_widgets/view.dart'; part 'ui_widgets/view.dart';
part 'ui_widgets/card_widget.dart'; part 'cards/widgets/card_widget.dart';
part 'ui_widgets/card_header_widget.dart'; part 'ui_widgets/card_header_widget.dart';
part 'panels/config_panel_widget.dart'; part 'panels/config_panel_widget.dart';
part 'panels/widgets/link_to_web_config.dart'; part 'panels/widgets/link_to_web_config.dart';
part 'utils/logger.dart'; part 'utils/logger.dart';
part 'types/ha_error.dart'; part 'types/ha_error.dart';
part 'types/event_bus_events.dart'; part 'types/event_bus_events.dart';
part 'cards/widgets/gauge_card_body.dart';
EventBus eventBus = new EventBus(); EventBus eventBus = new EventBus();

View File

@ -29,7 +29,7 @@ class HAView {
name: e.displayName, name: e.displayName,
id: e.entityId, id: e.entityId,
linkedEntityWrapper: EntityWrapper(entity: e), linkedEntityWrapper: EntityWrapper(entity: e),
type: CardType.mediaControl type: CardType.MEDIA_CONTROL
); );
cards.add(card); cards.add(card);
}); });
@ -40,7 +40,7 @@ class HAView {
HACard card = HACard( HACard card = HACard(
id: groupIdToAdd, id: groupIdToAdd,
name: entity.domain, name: entity.domain,
type: CardType.entities type: CardType.ENTITIES
); );
card.entities.add(EntityWrapper(entity: entity)); card.entities.add(EntityWrapper(entity: entity));
autoGeneratedCards.add(card); autoGeneratedCards.add(card);
@ -52,7 +52,7 @@ class HAView {
name: entity.displayName, name: entity.displayName,
id: entity.entityId, id: entity.entityId,
linkedEntityWrapper: EntityWrapper(entity: entity), linkedEntityWrapper: EntityWrapper(entity: entity),
type: CardType.entities type: CardType.ENTITIES
); );
card.entities.addAll(entity.childEntities.where((entity) {return entity.domain != "media_player";}).map((e) {return EntityWrapper(entity: e);})); card.entities.addAll(entity.childEntities.where((entity) {return entity.domain != "media_player";}).map((e) {return EntityWrapper(entity: e);}));
entity.childEntities.where((entity) {return entity.domain == "media_player";}).forEach((entity){ entity.childEntities.where((entity) {return entity.domain == "media_player";}).forEach((entity){
@ -60,7 +60,7 @@ class HAView {
name: entity.displayName, name: entity.displayName,
id: entity.entityId, id: entity.entityId,
linkedEntityWrapper: EntityWrapper(entity: entity), linkedEntityWrapper: EntityWrapper(entity: entity),
type: CardType.mediaControl type: CardType.MEDIA_CONTROL
); );
cards.add(mediaCard); cards.add(mediaCard);
}); });

View File

@ -22,6 +22,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
auto_size_text:
dependency: "direct main"
description:
name: auto_size_text
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -105,7 +112,7 @@ packages:
name: firebase_messaging name: firebase_messaging
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.1.4" version: "5.1.5"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -248,7 +255,7 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
pedantic: pedantic:
dependency: transitive dependency: transitive
description: description:

View File

@ -16,9 +16,9 @@ dependencies:
cached_network_image: any cached_network_image: any
url_launcher: any url_launcher: any
date_format: any date_format: any
charts_flutter: any charts_flutter: ^0.8.0
flutter_markdown: any flutter_markdown: any
in_app_purchase: ^0.2.1+2 in_app_purchase: ^0.2.1+3
# flutter_svg: ^0.10.3 # flutter_svg: ^0.10.3
flutter_custom_tabs: ^0.6.0 flutter_custom_tabs: ^0.6.0
firebase_messaging: ^5.1.4 firebase_messaging: ^5.1.4
@ -26,6 +26,7 @@ dependencies:
flutter_secure_storage: ^3.2.1+1 flutter_secure_storage: ^3.2.1+1
device_info: ^0.4.0+2 device_info: ^0.4.0+2
flutter_local_notifications: ^0.8.2 flutter_local_notifications: ^0.8.2
auto_size_text: ^2.1.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: