WIP #120 Numeric state charts
This commit is contained in:
parent
e16338c3f2
commit
fcd4ac7292
@ -5,15 +5,19 @@ class EntityColors {
|
||||
"on": Colors.amber,
|
||||
"auto": Colors.amber,
|
||||
"idle": Colors.amber,
|
||||
"off": Color.fromRGBO(68, 115, 158, 1.0),
|
||||
"default": Color.fromRGBO(68, 115, 158, 1.0),
|
||||
"heat": Colors.redAccent,
|
||||
"cool": Colors.lightBlue,
|
||||
"unavailable": Colors.black26,
|
||||
"unknown": Colors.black26,
|
||||
"playing": Colors.amber,
|
||||
"above_horizon": Colors.amber,
|
||||
"home": Colors.amber,
|
||||
"open": Colors.amber,
|
||||
"off": Color.fromRGBO(68, 115, 158, 1.0),
|
||||
"closed": Color.fromRGBO(68, 115, 158, 1.0),
|
||||
"default": Color.fromRGBO(68, 115, 158, 1.0),
|
||||
"heat": Colors.redAccent,
|
||||
"cool": Colors.lightBlue,
|
||||
"closing": Colors.cyan,
|
||||
"opening": Colors.purple,
|
||||
"unavailable": Colors.black26,
|
||||
"unknown": Colors.black26,
|
||||
};
|
||||
|
||||
static Color stateColor(String state) {
|
||||
@ -69,8 +73,8 @@ class Entity {
|
||||
DateTime _lastUpdated;
|
||||
|
||||
List<Entity> childEntities = [];
|
||||
|
||||
List<String> attributesToShow = ["all"];
|
||||
int historyWidgetType = EntityHistoryWidgetType.simple;
|
||||
|
||||
String get displayName =>
|
||||
attributes["friendly_name"] ?? (attributes["name"] ?? "_");
|
||||
@ -88,6 +92,7 @@ class Entity {
|
||||
List get childEntityIds => attributes["entity_id"] ?? [];
|
||||
String get lastUpdated => _getLastUpdatedFormatted();
|
||||
bool get isHidden => attributes["hidden"] ?? false;
|
||||
double get doubleState => double.tryParse(state) ?? 0.0;
|
||||
|
||||
Entity(Map rawData) {
|
||||
update(rawData);
|
||||
@ -167,7 +172,7 @@ class Entity {
|
||||
|
||||
Widget buildHistoryWidget() {
|
||||
return EntityHistoryWidget(
|
||||
type: EntityHistoryWidgetType.simplest,
|
||||
type: historyWidgetType,
|
||||
);
|
||||
}
|
||||
|
||||
|
14
lib/entity_class/other_entity.class.dart
Normal file
14
lib/entity_class/other_entity.class.dart
Normal file
@ -0,0 +1,14 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class SunEntity extends Entity {
|
||||
SunEntity(Map rawData) : super(rawData);
|
||||
}
|
||||
|
||||
class SensorEntity extends Entity {
|
||||
|
||||
@override
|
||||
int historyWidgetType = EntityHistoryWidgetType.valueToTime;
|
||||
|
||||
SensorEntity(Map rawData) : super(rawData);
|
||||
|
||||
}
|
@ -1,16 +1,11 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class SunEntity extends Entity {
|
||||
SunEntity(Map rawData) : super(rawData);
|
||||
}
|
||||
|
||||
class SliderEntity extends Entity {
|
||||
SliderEntity(Map rawData) : super(rawData);
|
||||
|
||||
double get minValue => attributes["min"] ?? 0.0;
|
||||
double get maxValue => attributes["max"] ?? 100.0;
|
||||
double get valueStep => attributes["step"] ?? 1.0;
|
||||
double get doubleState => double.tryParse(state) ?? 0.0;
|
||||
double get minValue => _getDoubleAttributeValue("min") ?? 0.0;
|
||||
double get maxValue =>_getDoubleAttributeValue("max") ?? 100.0;
|
||||
double get valueStep => _getDoubleAttributeValue("step") ?? 1.0;
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
@ -38,6 +38,9 @@ class EntityCollection {
|
||||
case 'sun': {
|
||||
return SunEntity(rawEntityData);
|
||||
}
|
||||
case 'sensor': {
|
||||
return SensorEntity(rawEntityData);
|
||||
}
|
||||
case "automation":
|
||||
case "input_boolean":
|
||||
case "switch": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
part of '../../main.dart';
|
||||
|
||||
class EntityHistoryWidgetType {
|
||||
static const int simplest = 0;
|
||||
static const int simple = 0;
|
||||
static const int valueToTime = 1;
|
||||
static const int randomColors = 2;
|
||||
}
|
||||
|
||||
class EntityHistoryWidget extends StatefulWidget {
|
||||
@ -69,9 +70,7 @@ class _EntityHistoryWidgetState extends State<EntityHistoryWidget> {
|
||||
);
|
||||
} else {
|
||||
children.add(
|
||||
SimpleStateHistoryChartWidget(
|
||||
rawHistory: _history
|
||||
)
|
||||
_selectChartWidget()
|
||||
);
|
||||
}
|
||||
children.add(Divider());
|
||||
@ -83,4 +82,27 @@ class _EntityHistoryWidgetState extends State<EntityHistoryWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _selectChartWidget() {
|
||||
switch (widget.type) {
|
||||
case EntityHistoryWidgetType.simple: {
|
||||
return SimpleStateHistoryChartWidget(
|
||||
rawHistory: _history,
|
||||
);
|
||||
}
|
||||
|
||||
case EntityHistoryWidgetType.valueToTime: {
|
||||
return NumericStateHistoryChartWidget(
|
||||
rawHistory: _history,
|
||||
);
|
||||
}
|
||||
|
||||
default: {
|
||||
return SimpleStateHistoryChartWidget(
|
||||
rawHistory: _history,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
part of '../../main.dart';
|
||||
|
||||
class NumericStateHistoryChartWidget extends StatefulWidget {
|
||||
final rawHistory;
|
||||
|
||||
const NumericStateHistoryChartWidget({Key key, this.rawHistory}) : super(key: key);
|
||||
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return new _NumericStateHistoryChartWidgetState();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class _NumericStateHistoryChartWidgetState extends State<NumericStateHistoryChartWidget> {
|
||||
|
||||
int _selectedId = -1;
|
||||
List<charts.Series<NumericEntityStateHistoryMoment, DateTime>> _parsedHistory;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
_parsedHistory = _parseHistory();
|
||||
DateTime selectedTime;
|
||||
double selectedState;
|
||||
if ((_selectedId > -1) && (_parsedHistory != null) && (_parsedHistory.first.data.length >= (_selectedId + 1))) {
|
||||
selectedTime = _parsedHistory.first.data[_selectedId].time;
|
||||
selectedState = _parsedHistory.first.data[_selectedId].value;
|
||||
}
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
HistoryControlWidget(
|
||||
selectedTimeStart: selectedTime,
|
||||
selectedState: "$selectedState",
|
||||
onPrevTap: () => _selectPrev(),
|
||||
onNextTap: () => _selectNext(),
|
||||
),
|
||||
SizedBox(
|
||||
height: 150.0,
|
||||
child: charts.TimeSeriesChart(
|
||||
_parsedHistory,
|
||||
animate: false,
|
||||
primaryMeasureAxis: new charts.NumericAxisSpec(
|
||||
tickProviderSpec:
|
||||
new charts.BasicNumericTickProviderSpec(zeroBound: false)),
|
||||
dateTimeFactory: const charts.LocalDateTimeFactory(),
|
||||
defaultRenderer: charts.LineRendererConfig(),
|
||||
customSeriesRenderers: [
|
||||
new charts.PointRendererConfig(
|
||||
// ID used to link series to this renderer.
|
||||
customRendererId: 'valuePoints')
|
||||
],
|
||||
/*primaryMeasureAxis: charts.NumericAxisSpec(
|
||||
renderSpec: charts.NoneRenderSpec()
|
||||
),*/
|
||||
selectionModels: [
|
||||
new charts.SelectionModelConfig(
|
||||
type: charts.SelectionModelType.info,
|
||||
listener: (model) => _onSelectionChanged(model),
|
||||
)
|
||||
],
|
||||
behaviors: [
|
||||
charts.PanAndZoomBehavior(),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<charts.Series<NumericEntityStateHistoryMoment, DateTime>> _parseHistory() {
|
||||
List<NumericEntityStateHistoryMoment> data = [];
|
||||
DateTime now = DateTime.now();
|
||||
for (var i = 0; i < widget.rawHistory.length; i++) {
|
||||
var stateData = widget.rawHistory[i];
|
||||
DateTime time = DateTime.tryParse(stateData["last_updated"])?.toLocal();
|
||||
data.add(NumericEntityStateHistoryMoment(double.tryParse(stateData["state"]), time, i));
|
||||
}
|
||||
data.add(NumericEntityStateHistoryMoment(data.last.value, now, widget.rawHistory.length));
|
||||
return [
|
||||
new charts.Series<NumericEntityStateHistoryMoment, DateTime>(
|
||||
id: 'State',
|
||||
colorFn: (NumericEntityStateHistoryMoment historyMoment, __) => EntityColors.historyStateColor("unavailable"),
|
||||
domainFn: (NumericEntityStateHistoryMoment historyMoment, _) => historyMoment.time,
|
||||
measureFn: (NumericEntityStateHistoryMoment historyMoment, _) => historyMoment.value,
|
||||
data: data,
|
||||
),
|
||||
new charts.Series<NumericEntityStateHistoryMoment, DateTime>(
|
||||
id: 'State',
|
||||
radiusPxFn: (NumericEntityStateHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 4.0 : 2.0,
|
||||
colorFn: (NumericEntityStateHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? EntityColors.historyStateColor("on") : EntityColors.historyStateColor("off"),
|
||||
domainFn: (NumericEntityStateHistoryMoment historyMoment, _) => historyMoment.time,
|
||||
measureFn: (NumericEntityStateHistoryMoment historyMoment, _) => historyMoment.value,
|
||||
data: data,
|
||||
)..setAttribute(charts.rendererIdKey, 'valuePoints')
|
||||
];
|
||||
}
|
||||
|
||||
void _selectPrev() {
|
||||
if (_selectedId > 0) {
|
||||
setState(() {
|
||||
_selectedId -= 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _selectNext() {
|
||||
if (_selectedId < (_parsedHistory.first.data.length - 2)) {
|
||||
setState(() {
|
||||
_selectedId += 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onSelectionChanged(charts.SelectionModel model) {
|
||||
final selectedDatum = model.selectedDatum;
|
||||
|
||||
int selectedId;
|
||||
|
||||
if (selectedDatum.isNotEmpty) {
|
||||
selectedId = selectedDatum.first.datum.id;
|
||||
setState(() {
|
||||
_selectedId = selectedId;
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NumericEntityStateHistoryMoment {
|
||||
final DateTime time;
|
||||
final double value;
|
||||
final int id;
|
||||
|
||||
NumericEntityStateHistoryMoment(this.value, this.time, this.id);
|
||||
}
|
@ -162,15 +162,7 @@ class HistoryControlWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text("${formatDate(selectedTimeStart, [M, ' ', d, ', ', HH, ':', nn, ':', ss])}", textAlign: TextAlign.left,),
|
||||
Text("${formatDate(selectedTimeEnd ?? selectedTimeStart, [M, ' ', d, ', ', HH, ':', nn, ':', ss])}", textAlign: TextAlign.left,),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildTime(),
|
||||
IconButton(
|
||||
icon: Icon(Icons.chevron_right),
|
||||
padding: EdgeInsets.all(0.0),
|
||||
@ -185,6 +177,24 @@ class HistoryControlWidget extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildTime() {
|
||||
List<Widget> children = [];
|
||||
children.add(
|
||||
Text("${formatDate(selectedTimeStart, [M, ' ', d, ', ', HH, ':', nn, ':', ss])}", textAlign: TextAlign.left,)
|
||||
);
|
||||
if (selectedTimeEnd != null) {
|
||||
children.add(
|
||||
Text("${formatDate(selectedTimeEnd, [M, ' ', d, ', ', HH, ':', nn, ':', ss])}", textAlign: TextAlign.left,)
|
||||
);
|
||||
}
|
||||
return Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleEntityStateHistoryMoment {
|
||||
|
@ -24,7 +24,8 @@ part 'entity_class/cover_entity.class.dart';
|
||||
part 'entity_class/date_time_entity.class.dart';
|
||||
part 'entity_class/light_entity.class.dart';
|
||||
part 'entity_class/select_entity.class.dart';
|
||||
part 'entity_class/sun_entity.class.dart';
|
||||
part 'entity_class/other_entity.class.dart';
|
||||
part 'entity_class/slider_entity.dart';
|
||||
part 'entity_widgets/badge.dart';
|
||||
part 'entity_widgets/model_widgets.dart';
|
||||
part 'entity_widgets/default_entity_container.dart';
|
||||
@ -37,6 +38,7 @@ part 'entity_widgets/mode_selector.dart';
|
||||
part 'entity_widgets/entity_page_container.dart';
|
||||
part 'entity_widgets/history_chart/entity_history.dart';
|
||||
part 'entity_widgets/history_chart/simple_state_history_chart.dart';
|
||||
part 'entity_widgets/history_chart/numeric_state_history_chart.dart';
|
||||
part 'entity_widgets/state/switch_state.dart';
|
||||
part 'entity_widgets/state/slider_state.dart';
|
||||
part 'entity_widgets/state/text_input_state.dart';
|
||||
|
Reference in New Issue
Block a user