Project structure changes

This commit is contained in:
estevez-dev
2019-09-09 18:50:35 +03:00
parent 56a333a852
commit d5baabdd53
25 changed files with 34 additions and 34 deletions

View File

@ -0,0 +1,241 @@
part of '../../main.dart';
class CombinedHistoryChartWidget extends StatefulWidget {
final rawHistory;
final EntityHistoryConfig config;
const CombinedHistoryChartWidget({Key key, @required this.rawHistory, @required this.config}) : super(key: key);
@override
State<StatefulWidget> createState() {
return new _CombinedHistoryChartWidgetState();
}
}
class _CombinedHistoryChartWidgetState extends State<CombinedHistoryChartWidget> {
int _selectedId = -1;
List<charts.Series<EntityHistoryMoment, DateTime>> _parsedHistory;
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
_parsedHistory = _parseHistory();
DateTime selectedTime;
List<String> selectedStates = [];
List<int> colorIndexes = [];
if ((_selectedId > -1) && (_parsedHistory != null) && (_parsedHistory.first.data.length >= (_selectedId + 1))) {
selectedTime = _parsedHistory.first.data[_selectedId].startTime;
_parsedHistory.where((item) { return item.id == "state"; }).forEach((item) {
selectedStates.add(item.data[_selectedId].state);
colorIndexes.add(item.data[_selectedId].colorId);
});
_parsedHistory.where((item) { return item.id == "value"; }).forEach((item) {
selectedStates.add("${item.data[_selectedId].value ?? '-'}");
colorIndexes.add(item.data[_selectedId].colorId);
});
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
HistoryControlWidget(
selectedTimeStart: selectedTime,
selectedStates: selectedStates,
onPrevTap: () => _selectPrev(),
onNextTap: () => _selectNext(),
colorIndexes: colorIndexes,
),
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(
includeArea: false,
includePoints: true
),
selectionModels: [
new charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: (model) => _onSelectionChanged(model),
)
],
customSeriesRenderers: [
new charts.SymbolAnnotationRendererConfig(
customRendererId: "stateBars"
)
],
),
)
],
);
}
double _parseToDouble(temp1) {
if (temp1 is int) {
return temp1.toDouble();
} else if (temp1 is double) {
return temp1;
} else {
return double.tryParse("$temp1");
}
}
List<charts.Series<EntityHistoryMoment, DateTime>> _parseHistory() {
Logger.d(" parsing history...");
Map<String, List<EntityHistoryMoment>> numericDataLists = {};
int colorIdCounter = 0;
widget.config.numericAttributesToShow.forEach((String attrName) {
Logger.d(" parsing attribute $attrName");
List<EntityHistoryMoment> data = [];
DateTime now = DateTime.now();
for (var i = 0; i < widget.rawHistory.length; i++) {
var stateData = widget.rawHistory[i];
DateTime startTime = DateTime.tryParse(stateData["last_updated"])?.toLocal();
DateTime endTime;
bool hiddenLine;
double value;
double previousValue = 0.0;
value = _parseToDouble(stateData["attributes"]["$attrName"]);
bool hiddenDot = (value == null);
if (hiddenDot && i > 0) {
previousValue = data[i-1].value ?? data[i-1].previousValue;
}
if (i < (widget.rawHistory.length - 1)) {
endTime = DateTime.tryParse(widget.rawHistory[i+1]["last_updated"])?.toLocal();
double nextValue = _parseToDouble(widget.rawHistory[i+1]["attributes"]["$attrName"]);
hiddenLine = (nextValue == null || hiddenDot);
} else {
hiddenLine = hiddenDot;
endTime = now;
}
data.add(EntityHistoryMoment(
value: value,
previousValue: previousValue,
hiddenDot: hiddenDot,
hiddenLine: hiddenLine,
state: stateData["state"],
startTime: startTime,
endTime: endTime,
id: i,
colorId: colorIdCounter
));
}
data.add(EntityHistoryMoment(
value: data.last.value,
previousValue: data.last.previousValue,
hiddenDot: data.last.hiddenDot,
hiddenLine: data.last.hiddenLine,
state: data.last.state,
startTime: now,
id: widget.rawHistory.length,
colorId: colorIdCounter
));
numericDataLists.addAll({attrName: data});
colorIdCounter += 1;
});
if ((_selectedId == -1) && (numericDataLists.isNotEmpty)) {
_selectedId = numericDataLists.length -1;
}
List<charts.Series<EntityHistoryMoment, DateTime>> result = [];
numericDataLists.forEach((attrName, dataList) {
Logger.d(" adding ${dataList.length} data values");
result.add(
new charts.Series<EntityHistoryMoment, DateTime>(
id: "value",
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor("_", historyMoment.colorId),
radiusPxFn: (EntityHistoryMoment historyMoment, __) {
if (historyMoment.hiddenDot) {
return 0.0;
} else if (historyMoment.id == _selectedId) {
return 5.0;
} else {
return 1.0;
}
},
strokeWidthPxFn: (EntityHistoryMoment historyMoment, __) => historyMoment.hiddenLine ? 0.0 : 2.0,
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
measureFn: (EntityHistoryMoment historyMoment, _) => historyMoment.value ?? historyMoment.previousValue,
data: dataList,
/*domainLowerBoundFn: (CombinedEntityStateHistoryMoment historyMoment, _) => historyMoment.time.subtract(Duration(hours: 1)),
domainUpperBoundFn: (CombinedEntityStateHistoryMoment historyMoment, _) => historyMoment.time.add(Duration(hours: 1)),*/
)
);
});
result.add(
new charts.Series<EntityHistoryMoment, DateTime>(
id: 'state',
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 4.0,
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
domainLowerBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
domainUpperBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
// No measure values are needed for symbol annotations.
measureFn: (_, __) => null,
data: numericDataLists[numericDataLists.keys.first],
)
// Configure our custom symbol annotation renderer for this series.
..setAttribute(charts.rendererIdKey, 'stateBars')
// Optional radius for the annotation shape. If not specified, this will
// default to the same radius as the points.
//..setAttribute(charts.boundsLineRadiusPxKey, 3.5)
);
return result;
}
void _selectPrev() {
if (_selectedId > 0) {
setState(() {
_selectedId -= 1;
});
}
else {
setState(() {
_selectedId = _parsedHistory.first.data.length - 1;
});
}
}
void _selectNext() {
if (_selectedId < (_parsedHistory.first.data.length - 1)) {
setState(() {
_selectedId += 1;
});
}
else {
setState(() {
_selectedId = 0;
});
}
}
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
int selectedId;
if (selectedDatum.isNotEmpty) {
selectedId = selectedDatum.first.datum.id;
setState(() {
_selectedId = selectedId;
});
} else {
setState(() {
});
}
}
}

View File

@ -0,0 +1,144 @@
part of '../../main.dart';
class EntityHistoryWidgetType {
static const int simple = 0;
static const int numericState = 1;
static const int numericAttributes = 2;
}
class EntityHistoryConfig {
final int chartType;
final List<String> numericAttributesToShow;
final bool numericState;
EntityHistoryConfig({this.chartType, this.numericAttributesToShow, this.numericState: true});
}
class EntityHistoryWidget extends StatefulWidget {
final EntityHistoryConfig config;
const EntityHistoryWidget({Key key, @required this.config}) : super(key: key);
@override
_EntityHistoryWidgetState createState() {
return new _EntityHistoryWidgetState();
}
}
class _EntityHistoryWidgetState extends State<EntityHistoryWidget> {
List _history;
bool _needToUpdateHistory;
DateTime _historyLastUpdated;
bool _disposed = false;
@override
void initState() {
super.initState();
_needToUpdateHistory = true;
}
void _loadHistory(String entityId) {
DateTime now = DateTime.now();
if (_historyLastUpdated != null) {
Logger.d("History was updated ${now.difference(_historyLastUpdated).inSeconds} seconds ago");
}
if (_historyLastUpdated == null || now.difference(_historyLastUpdated).inSeconds > 30) {
_historyLastUpdated = now;
ConnectionManager().getHistory(entityId).then((history){
if (!_disposed) {
setState(() {
_history = history.isNotEmpty ? history[0] : [];
_needToUpdateHistory = false;
});
}
}).catchError((e) {
Logger.e("Error loading $entityId history: $e");
if (!_disposed) {
setState(() {
_history = [];
_needToUpdateHistory = false;
});
}
});
}
}
@override
Widget build(BuildContext context) {
final EntityModel entityModel = EntityModel.of(context);
final Entity entity = entityModel.entityWrapper.entity;
if (!_needToUpdateHistory) {
_needToUpdateHistory = true;
} else {
_loadHistory(entity.entityId);
}
return _buildChart();
}
Widget _buildChart() {
List<Widget> children = [];
if (_history == null) {
children.add(
Text("Loading history...")
);
} else if (_history.isEmpty) {
children.add(
Text("No history")
);
} else {
children.add(
_selectChartWidget()
);
}
children.add(Divider());
return Padding(
padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, Sizes.rowPadding),
child: Column(
children: children,
),
);
}
Widget _selectChartWidget() {
switch (widget.config.chartType) {
case EntityHistoryWidgetType.simple: {
return SimpleStateHistoryChartWidget(
rawHistory: _history,
);
}
case EntityHistoryWidgetType.numericState: {
return NumericStateHistoryChartWidget(
rawHistory: _history,
config: widget.config,
);
}
case EntityHistoryWidgetType.numericAttributes: {
return CombinedHistoryChartWidget(
rawHistory: _history,
config: widget.config,
);
}
default: {
Logger.d(" Simple selected as default");
return SimpleStateHistoryChartWidget(
rawHistory: _history,
);
}
}
}
@override
void dispose() {
_disposed = true;
super.dispose();
}
}

View File

@ -0,0 +1,25 @@
part of '../../main.dart';
class EntityHistoryMoment {
final DateTime startTime;
final DateTime endTime;
final double value;
final double previousValue;
final int id;
final int colorId;
final String state;
final bool hiddenDot;
final bool hiddenLine;
EntityHistoryMoment({
this.value,
this.previousValue,
this.hiddenDot,
this.hiddenLine,
this.state,
@required this.startTime,
this.endTime,
@required this.id,
this.colorId
});
}

View File

@ -0,0 +1,86 @@
part of '../../main.dart';
class HistoryControlWidget extends StatelessWidget {
final Function onPrevTap;
final Function onNextTap;
final DateTime selectedTimeStart;
final DateTime selectedTimeEnd;
final List<String> selectedStates;
final List<int> colorIndexes;
const HistoryControlWidget({Key key, this.onPrevTap, this.onNextTap, this.selectedTimeStart, this.selectedTimeEnd, this.selectedStates, @ required this.colorIndexes}) : super(key: key);
@override
Widget build(BuildContext context) {
if (selectedTimeStart != null) {
return
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.chevron_left),
padding: EdgeInsets.all(0.0),
iconSize: 40.0,
onPressed: onPrevTap,
),
Expanded(
child: Padding(
padding: EdgeInsets.only(right: 10.0),
child: _buildStates(),
),
),
_buildTime(),
IconButton(
icon: Icon(Icons.chevron_right),
padding: EdgeInsets.all(0.0),
iconSize: 40.0,
onPressed: onNextTap,
),
],
);
} else {
return Container(height: 48.0);
}
}
Widget _buildStates() {
List<Widget> children = [];
for (int i = 0; i < selectedStates.length; i++) {
children.add(
Text(
"${selectedStates[i] ?? '-'}",
textAlign: TextAlign.right,
style: TextStyle(
fontWeight: FontWeight.bold,
color: EntityColor.historyStateColor(selectedStates[i], colorIndexes[i]),
fontSize: 22.0
),
)
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: children,
);
}
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 Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: children,
);
}
}

View File

@ -0,0 +1,171 @@
part of '../../main.dart';
class NumericStateHistoryChartWidget extends StatefulWidget {
final rawHistory;
final EntityHistoryConfig config;
const NumericStateHistoryChartWidget({Key key, @required this.rawHistory, @required this.config}) : super(key: key);
@override
State<StatefulWidget> createState() {
return new _NumericStateHistoryChartWidgetState();
}
}
class _NumericStateHistoryChartWidgetState extends State<NumericStateHistoryChartWidget> {
int _selectedId = -1;
List<charts.Series<EntityHistoryMoment, 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].startTime;
selectedState = _parsedHistory.first.data[_selectedId].value;
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
HistoryControlWidget(
selectedTimeStart: selectedTime,
selectedStates: ["${selectedState ?? '-'}"],
onPrevTap: () => _selectPrev(),
onNextTap: () => _selectNext(),
colorIndexes: [-1],
),
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(
includePoints: true
),
/*primaryMeasureAxis: charts.NumericAxisSpec(
renderSpec: charts.NoneRenderSpec()
),*/
selectionModels: [
new charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: (model) => _onSelectionChanged(model),
)
],
),
)
],
);
}
List<charts.Series<EntityHistoryMoment, DateTime>> _parseHistory() {
List<EntityHistoryMoment> 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();
double value = double.tryParse(stateData["state"]);
double previousValue = 0.0;
bool hiddenDot = (value == null);
bool hiddenLine;
if (hiddenDot && i > 0) {
previousValue = data[i-1].value ?? data[i-1].previousValue;
}
if (i < (widget.rawHistory.length - 1)) {
double nextValue = double.tryParse(widget.rawHistory[i+1]["state"]);
hiddenLine = (nextValue == null || hiddenDot);
} else {
hiddenLine = hiddenDot;
}
data.add(EntityHistoryMoment(
value: value,
previousValue: previousValue,
hiddenDot: hiddenDot,
hiddenLine: hiddenLine,
startTime: time,
id: i
));
}
data.add(EntityHistoryMoment(
value: data.last.value,
previousValue: data.last.previousValue,
hiddenDot: data.last.hiddenDot,
hiddenLine: data.last.hiddenLine,
startTime: now,
id: widget.rawHistory.length
));
if (_selectedId == -1) {
_selectedId = data.length - 1;
}
return [
new charts.Series<EntityHistoryMoment, DateTime>(
id: 'State',
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(EntityState.on, -1),
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
measureFn: (EntityHistoryMoment historyMoment, _) => historyMoment.value ?? historyMoment.previousValue,
data: data,
strokeWidthPxFn: (EntityHistoryMoment historyMoment, __) => historyMoment.hiddenLine ? 0.0 : 2.0,
radiusPxFn: (EntityHistoryMoment historyMoment, __) {
if (historyMoment.hiddenDot) {
return 0.0;
} else if (historyMoment.id == _selectedId) {
return 5.0;
} else {
return 1.0;
}
},
)
];
}
void _selectPrev() {
if (_selectedId > 0) {
setState(() {
_selectedId -= 1;
});
}
else {
setState(() {
_selectedId = _parsedHistory.first.data.length - 1;
});
}
}
void _selectNext() {
if (_selectedId < (_parsedHistory.first.data.length - 1)) {
setState(() {
_selectedId += 1;
});
}
else {
setState(() {
_selectedId = 0;
});
}
}
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
int selectedId;
if (selectedDatum.isNotEmpty) {
selectedId = selectedDatum.first.datum.id;
setState(() {
_selectedId = selectedId;
});
} else {
setState(() {
});
}
}
}

View File

@ -0,0 +1,187 @@
part of '../../main.dart';
class SimpleStateHistoryChartWidget extends StatefulWidget {
final rawHistory;
const SimpleStateHistoryChartWidget({Key key, this.rawHistory}) : super(key: key);
@override
State<StatefulWidget> createState() {
return new _SimpleStateHistoryChartWidgetState();
}
}
class _SimpleStateHistoryChartWidgetState extends State<SimpleStateHistoryChartWidget> {
int _selectedId = -1;
List<charts.Series<EntityHistoryMoment, DateTime>> _parsedHistory;
@override
Widget build(BuildContext context) {
_parsedHistory = _parseHistory();
DateTime selectedTimeStart;
DateTime selectedTimeEnd;
String selectedState;
if ((_selectedId > -1) && (_parsedHistory != null) && (_parsedHistory.first.data.length >= (_selectedId + 1))) {
selectedTimeStart = _parsedHistory.first.data[_selectedId].startTime;
selectedTimeEnd = _parsedHistory.first.data[_selectedId].endTime;
selectedState = _parsedHistory.first.data[_selectedId].state;
}
return Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
HistoryControlWidget(
selectedTimeStart: selectedTimeStart,
selectedTimeEnd: selectedTimeEnd,
selectedStates: [selectedState],
onPrevTap: () => _selectPrev(),
onNextTap: () => _selectNext(),
colorIndexes: [_parsedHistory.first.data[_selectedId].colorId],
),
SizedBox(
height: 70.0,
child: charts.TimeSeriesChart(
_parsedHistory,
animate: false,
dateTimeFactory: const charts.LocalDateTimeFactory(),
primaryMeasureAxis: charts.NumericAxisSpec(
renderSpec: charts.NoneRenderSpec()
),
selectionModels: [
new charts.SelectionModelConfig(
type: charts.SelectionModelType.info,
changedListener: (model) => _onSelectionChanged(model),
)
],
customSeriesRenderers: [
new charts.PointRendererConfig(
// ID used to link series to this renderer.
customRendererId: 'startValuePoints'),
new charts.PointRendererConfig(
// ID used to link series to this renderer.
customRendererId: 'endValuePoints')
],
),
)
],
);
}
List<charts.Series<EntityHistoryMoment, DateTime>> _parseHistory() {
List<EntityHistoryMoment> data = [];
DateTime now = DateTime.now();
Map<String, int> cachedStates = {};
for (var i = 0; i < widget.rawHistory.length; i++) {
var stateData = widget.rawHistory[i];
DateTime startTime = DateTime.tryParse(stateData["last_updated"])?.toLocal();
DateTime endTime;
if (i < (widget.rawHistory.length - 1)) {
endTime = DateTime.tryParse(widget.rawHistory[i+1]["last_updated"])?.toLocal();
} else {
endTime = now;
}
if (cachedStates[stateData["state"]] == null) {
cachedStates.addAll({"${stateData["state"]}": cachedStates.length});
}
data.add(EntityHistoryMoment(
state: stateData["state"],
startTime: startTime,
endTime: endTime,
id: i,
colorId: cachedStates[stateData["state"]]
));
}
data.add(EntityHistoryMoment(
state: data.last.state,
startTime: now,
id: widget.rawHistory.length,
colorId: data.last.colorId
));
if (_selectedId == -1) {
_selectedId = data.length - 1;
}
return [
new charts.Series<EntityHistoryMoment, DateTime>(
id: 'State',
strokeWidthPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 6.0 : 3.0,
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
data: data,
),
new charts.Series<EntityHistoryMoment, DateTime>(
id: 'State',
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
data: data,
)..setAttribute(charts.rendererIdKey, 'startValuePoints'),
new charts.Series<EntityHistoryMoment, DateTime>(
id: 'State',
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
data: data,
)..setAttribute(charts.rendererIdKey, 'endValuePoints')
];
}
void _selectPrev() {
if (_selectedId > 0) {
setState(() {
_selectedId -= 1;
});
}
else {
setState(() {
_selectedId = _parsedHistory.first.data.length - 1;
});
}
}
void _selectNext() {
if (_selectedId < (_parsedHistory.first.data.length - 1)) {
setState(() {
_selectedId += 1;
});
}
else {
setState(() {
_selectedId = 0;
});
}
}
void _onSelectionChanged(charts.SelectionModel model) {
final selectedDatum = model.selectedDatum;
int selectedId;
if ((selectedDatum.isNotEmpty) &&(selectedDatum.first.datum.endTime != null)) {
selectedId = selectedDatum.first.datum.id;
setState(() {
_selectedId = selectedId;
});
} else {
setState(() {
});
}
}
}
/*
class SimpleEntityStateHistoryMoment {
final DateTime startTime;
final DateTime endTime;
final String state;
final int id;
final int colorId;
SimpleEntityStateHistoryMoment(this.state, this.startTime, this.endTime, this.id, this.colorId);
}*/