Compare commits
24 Commits
beta/0.8.0
...
beta/0.8.2
Author | SHA1 | Date | |
---|---|---|---|
89513ca4e5 | |||
a934ee2335 | |||
49aeea634f | |||
e18b9ebe14 | |||
08ee3f3d80 | |||
62d07bf8b9 | |||
ab398cbdc3 | |||
007d12719c | |||
524d195800 | |||
405de64249 | |||
f53554702e | |||
379e1a4a7e | |||
d6f7096055 | |||
37c721e4f6 | |||
d94235ef6d | |||
eb4184713f | |||
a0a0cb4612 | |||
f448a20784 | |||
36eff26862 | |||
5b2a1163b9 | |||
e627a8b963 | |||
4432124e8c | |||
b8ba3c59e9 | |||
c40a496b6b |
2
.gitignore
vendored
2
.gitignore
vendored
@ -18,5 +18,5 @@ flutter_export_environment.sh
|
|||||||
.flutter-plugins-dependencies
|
.flutter-plugins-dependencies
|
||||||
|
|
||||||
key.properties
|
key.properties
|
||||||
premium_features_manager.class.dart
|
.secrets.dart
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
|
@ -3,7 +3,7 @@ image:
|
|||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- before: |
|
- before: |
|
||||||
export PATH=$FLUTTER_HOME/bin:$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH
|
export PATH=$FLUTTER_HOME/bin:$FLUTTER_HOME/bin/cache/dart-sdk/bin:$ANDROID_HOME/bin:$ANDROID_HOME/platform-tools:$PATH
|
||||||
mkdir -p /home/gitpod/.android
|
mkdir -p /home/gitpod/.android
|
||||||
touch /home/gitpod/.android/repositories.cfg
|
touch /home/gitpod/.android/repositories.cfg
|
||||||
init: |
|
init: |
|
||||||
|
@ -6,7 +6,7 @@ Visit [ha-client.app](http://ha-client.app/) for more info.
|
|||||||
|
|
||||||
Download the app from [Google Play](https://play.google.com/apps/testing/com.keyboardcrumbs.haclient)
|
Download the app from [Google Play](https://play.google.com/apps/testing/com.keyboardcrumbs.haclient)
|
||||||
|
|
||||||
Discuss it on [Spectrum.chat](https://spectrum.chat/ha-client) or at [Home Assistant community](https://community.home-assistant.io/c/mobile-apps/ha-client-android)
|
Discuss it on [Discord](https://discord.gg/nd6FZQ) or at [Home Assistant community](https://community.home-assistant.io/c/mobile-apps/ha-client-android)
|
||||||
|
|
||||||
[](https://gitpod.io/#https://github.com/estevez-dev/ha_client)
|
[](https://gitpod.io/#https://github.com/estevez-dev/ha_client)
|
||||||
|
|
||||||
|
61
assets/html/cameraLiveView.html
Normal file
61
assets/html/cameraLiveView.html
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
widows: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
video {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
var messageChannel = '{{message_channel}}';
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<video id="screen" width="100%" controls></video>
|
||||||
|
<script>
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
var video = document.getElementById('screen');
|
||||||
|
var hls = new Hls();
|
||||||
|
hls.on(Hls.Events.ERROR, function (event, data) {
|
||||||
|
if (data.fatal) {
|
||||||
|
switch(data.type) {
|
||||||
|
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||||
|
// try to recover network error
|
||||||
|
console.log("fatal network error encountered, try to recover");
|
||||||
|
hls.startLoad();
|
||||||
|
break;
|
||||||
|
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||||
|
console.log("fatal media error encountered, try to recover");
|
||||||
|
hls.recoverMediaError();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// cannot recover
|
||||||
|
hls.destroy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// bind them together
|
||||||
|
hls.attachMedia(video);
|
||||||
|
hls.on(Hls.Events.MEDIA_ATTACHED, function () {
|
||||||
|
console.log("video and hls.js are now bound together !");
|
||||||
|
hls.loadSource("{{stream_url}}");
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
||||||
|
console.log("manifest loaded, found " + data.levels.length + " quality level");
|
||||||
|
video.play();
|
||||||
|
video.onloadedmetadata = function() {
|
||||||
|
window[messageChannel].postMessage(document.body.clientWidth / video.offsetHeight);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -198,9 +198,6 @@ class CardWidget extends StatelessWidget {
|
|||||||
body.add(CardHeader(
|
body.add(CardHeader(
|
||||||
name: card.name ?? "",
|
name: card.name ?? "",
|
||||||
subtitle: Text("${card.linkedEntityWrapper.entity.displayState}",
|
subtitle: Text("${card.linkedEntityWrapper.entity.displayState}",
|
||||||
style: TextStyle(
|
|
||||||
color: Colors.grey
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -18,7 +18,7 @@ class CardHeader extends StatelessWidget {
|
|||||||
title: Text("$name",
|
title: Text("$name",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
|
style: Theme.of(context).textTheme.headline),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = new Container(width: 0.0, height: 0.0);
|
result = new Container(width: 0.0, height: 0.0);
|
||||||
|
@ -48,8 +48,7 @@ class EntityButtonCardBody extends StatelessWidget {
|
|||||||
textOverflow: TextOverflow.ellipsis,
|
textOverflow: TextOverflow.ellipsis,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
wordsWrap: true,
|
wordsWrap: true,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center
|
||||||
fontSize: Sizes.nameFontSize,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return Container(width: 0, height: 0);
|
return Container(width: 0, height: 0);
|
||||||
|
@ -14,10 +14,11 @@ class GaugeCardBody extends StatefulWidget {
|
|||||||
|
|
||||||
class _GaugeCardBodyState extends State<GaugeCardBody> {
|
class _GaugeCardBodyState extends State<GaugeCardBody> {
|
||||||
|
|
||||||
List<charts.Series> seriesList;
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
List<charts.Series<GaugeSegment, String>> _createData(double value) {
|
EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
||||||
double fixedValue;
|
double fixedValue;
|
||||||
|
double value = entityWrapper.entity.doubleState;
|
||||||
if (value > widget.max) {
|
if (value > widget.max) {
|
||||||
fixedValue = widget.max.toDouble();
|
fixedValue = widget.max.toDouble();
|
||||||
} else if (value < widget.min) {
|
} else if (value < widget.min) {
|
||||||
@ -25,130 +26,151 @@ class _GaugeCardBodyState extends State<GaugeCardBody> {
|
|||||||
} else {
|
} else {
|
||||||
fixedValue = value;
|
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 [
|
List<GaugeRange> ranges;
|
||||||
charts.Series<GaugeSegment, String>(
|
if (widget.severity != null && widget.severity["green"] is int && widget.severity["red"] is int && widget.severity["yellow"] is int) {
|
||||||
id: 'Segments',
|
List<RangeContainer> rangesList = <RangeContainer>[
|
||||||
domainFn: (GaugeSegment segment, _) => segment.segment,
|
RangeContainer(widget.severity["green"], HAClientTheme().getGreenGaugeColor()),
|
||||||
measureFn: (GaugeSegment segment, _) => segment.value,
|
RangeContainer(widget.severity["red"], HAClientTheme().getRedGaugeColor()),
|
||||||
colorFn: (GaugeSegment segment, _) => segment.color,
|
RangeContainer(widget.severity["yellow"], HAClientTheme().getYellowGaugeColor())
|
||||||
// Set a label accessor to control the text of the arc label.
|
];
|
||||||
labelAccessorFn: (GaugeSegment segment, _) =>
|
rangesList.sort((current, next) {
|
||||||
segment.segment == 'Main' ? '${segment.value}' : null,
|
if (current.startFrom > next.startFrom) {
|
||||||
data: data,
|
return 1;
|
||||||
|
}
|
||||||
|
if (current.startFrom < next.startFrom) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
});
|
||||||
|
ranges = [
|
||||||
|
GaugeRange(
|
||||||
|
startValue: rangesList[0].startFrom.toDouble(),
|
||||||
|
endValue: rangesList[1].startFrom.toDouble(),
|
||||||
|
color: fixedValue < rangesList[1].startFrom ? rangesList[0].color : rangesList[0].color.withOpacity(0.1),
|
||||||
|
sizeUnit: GaugeSizeUnit.factor,
|
||||||
|
endWidth: 0.3,
|
||||||
|
startWidth: 0.3
|
||||||
|
),
|
||||||
|
GaugeRange(
|
||||||
|
startValue: rangesList[1].startFrom.toDouble(),
|
||||||
|
endValue: rangesList[2].startFrom.toDouble(),
|
||||||
|
color: (fixedValue < rangesList[2].startFrom && fixedValue >= rangesList[1].startFrom) ? rangesList[1].color : rangesList[1].color.withOpacity(0.1),
|
||||||
|
sizeUnit: GaugeSizeUnit.factor,
|
||||||
|
endWidth: 0.3,
|
||||||
|
startWidth: 0.3
|
||||||
|
),
|
||||||
|
GaugeRange(
|
||||||
|
startValue: rangesList[2].startFrom.toDouble(),
|
||||||
|
endValue: widget.max.toDouble(),
|
||||||
|
color: fixedValue >= rangesList[2].startFrom ? rangesList[2].color : rangesList[2].color.withOpacity(0.1),
|
||||||
|
sizeUnit: GaugeSizeUnit.factor,
|
||||||
|
endWidth: 0.3,
|
||||||
|
startWidth: 0.3
|
||||||
|
)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if (ranges == null) {
|
||||||
|
ranges = <GaugeRange>[
|
||||||
|
GaugeRange(
|
||||||
|
startValue: widget.min.toDouble(),
|
||||||
|
endValue: widget.max.toDouble(),
|
||||||
|
color: Theme.of(context).primaryColorDark,
|
||||||
|
sizeUnit: GaugeSizeUnit.factor,
|
||||||
|
endWidth: 0.3,
|
||||||
|
startWidth: 0.3
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () => entityWrapper.handleTap(),
|
onTap: () => entityWrapper.handleTap(),
|
||||||
onLongPress: () => entityWrapper.handleHold(),
|
onLongPress: () => entityWrapper.handleHold(),
|
||||||
onDoubleTap: () => entityWrapper.handleDoubleTap(),
|
onDoubleTap: () => entityWrapper.handleDoubleTap(),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: 1.5,
|
aspectRatio: 2,
|
||||||
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(
|
child: LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
double fontSize = constraints.maxHeight / 7;
|
double fontSizeFactor;
|
||||||
return Padding(
|
if (constraints.maxWidth > 300.0) {
|
||||||
padding: EdgeInsets.only(bottom: 2*fontSize),
|
fontSizeFactor = 1.6;
|
||||||
child: SimpleEntityState(
|
} else if (constraints.maxWidth > 150.0) {
|
||||||
//textAlign: TextAlign.center,
|
fontSizeFactor = 1;
|
||||||
|
} else if (constraints.maxWidth > 100.0) {
|
||||||
|
fontSizeFactor = 0.6;
|
||||||
|
} else {
|
||||||
|
fontSizeFactor = 0.4;
|
||||||
|
}
|
||||||
|
return SfRadialGauge(
|
||||||
|
axes: <RadialAxis>[
|
||||||
|
RadialAxis(
|
||||||
|
maximum: widget.max.toDouble(),
|
||||||
|
minimum: widget.min.toDouble(),
|
||||||
|
showLabels: false,
|
||||||
|
showTicks: false,
|
||||||
|
canScaleToFit: true,
|
||||||
|
ranges: ranges,
|
||||||
|
annotations: <GaugeAnnotation>[
|
||||||
|
GaugeAnnotation(
|
||||||
|
angle: -90,
|
||||||
|
positionFactor: 1.3,
|
||||||
|
//verticalAlignment: GaugeAlignment.far,
|
||||||
|
widget: EntityName(
|
||||||
|
textStyle: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
fontSize: Theme.of(context).textTheme.body1.fontSize * fontSizeFactor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GaugeAnnotation(
|
||||||
|
angle: 180,
|
||||||
|
positionFactor: 0,
|
||||||
|
verticalAlignment: GaugeAlignment.center,
|
||||||
|
widget: SimpleEntityState(
|
||||||
expanded: false,
|
expanded: false,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
bold: true,
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
padding: EdgeInsets.all(0.0),
|
textStyle: Theme.of(context).textTheme.title.copyWith(
|
||||||
fontSize: fontSize,
|
fontSize: Theme.of(context).textTheme.title.fontSize * fontSizeFactor,
|
||||||
//padding: EdgeInsets.only(top: Sizes.rowPadding),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Align(
|
)
|
||||||
alignment: Alignment.bottomCenter,
|
],
|
||||||
child: LayoutBuilder(
|
axisLineStyle: AxisLineStyle(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
thickness: 0.3,
|
||||||
double fontSize = constraints.maxHeight / 7;
|
thicknessUnit: GaugeSizeUnit.factor
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: fontSize),
|
|
||||||
child: EntityName(
|
|
||||||
fontSize: fontSize,
|
|
||||||
maxLines: 1,
|
|
||||||
padding: EdgeInsets.all(0.0),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textOverflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
|
startAngle: 180,
|
||||||
|
endAngle: 0,
|
||||||
|
pointers: <GaugePointer>[
|
||||||
|
NeedlePointer(
|
||||||
|
value: fixedValue,
|
||||||
|
lengthUnit: GaugeSizeUnit.factor,
|
||||||
|
needleLength: 0.9,
|
||||||
|
needleColor: Theme.of(context).accentColor,
|
||||||
|
enableAnimation: true,
|
||||||
|
needleStartWidth: 1,
|
||||||
|
animationType: AnimationType.bounceOut,
|
||||||
|
needleEndWidth: 3,
|
||||||
|
knobStyle: KnobStyle(
|
||||||
|
sizeUnit: GaugeSizeUnit.factor,
|
||||||
|
color: Theme.of(context).buttonColor,
|
||||||
|
knobRadius: 0.1
|
||||||
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GaugeSegment {
|
class RangeContainer {
|
||||||
final String segment;
|
final int startFrom;
|
||||||
final double value;
|
Color color;
|
||||||
final charts.Color color;
|
|
||||||
|
|
||||||
GaugeSegment(this.segment, this.value, Color color)
|
RangeContainer(this.startFrom, this.color);
|
||||||
: this.color = charts.Color(
|
|
||||||
r: color.red, g: color.green, b: color.blue, a: color.alpha);
|
|
||||||
}
|
}
|
@ -6,7 +6,6 @@ class GlanceCardEntityContainer extends StatelessWidget {
|
|||||||
final bool showState;
|
final bool showState;
|
||||||
final bool nameInTheBottom;
|
final bool nameInTheBottom;
|
||||||
final double iconSize;
|
final double iconSize;
|
||||||
final double nameFontSize;
|
|
||||||
final bool wordsWrapInName;
|
final bool wordsWrapInName;
|
||||||
|
|
||||||
GlanceCardEntityContainer({
|
GlanceCardEntityContainer({
|
||||||
@ -15,7 +14,6 @@ class GlanceCardEntityContainer extends StatelessWidget {
|
|||||||
@required this.showState,
|
@required this.showState,
|
||||||
this.nameInTheBottom: false,
|
this.nameInTheBottom: false,
|
||||||
this.iconSize: Sizes.iconSize,
|
this.iconSize: Sizes.iconSize,
|
||||||
this.nameFontSize: Sizes.smallFontSize,
|
|
||||||
this.wordsWrapInName: false
|
this.wordsWrapInName: false
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -31,7 +29,7 @@ class GlanceCardEntityContainer extends StatelessWidget {
|
|||||||
List<Widget> result = [];
|
List<Widget> result = [];
|
||||||
if (!nameInTheBottom) {
|
if (!nameInTheBottom) {
|
||||||
if (showName) {
|
if (showName) {
|
||||||
result.add(_buildName());
|
result.add(_buildName(context));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (showState) {
|
if (showState) {
|
||||||
@ -49,7 +47,7 @@ class GlanceCardEntityContainer extends StatelessWidget {
|
|||||||
result.add(_buildState());
|
result.add(_buildState());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result.add(_buildName());
|
result.add(_buildName(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Center(
|
return Center(
|
||||||
@ -65,13 +63,13 @@ class GlanceCardEntityContainer extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildName() {
|
Widget _buildName(BuildContext context) {
|
||||||
return EntityName(
|
return EntityName(
|
||||||
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
|
padding: EdgeInsets.only(bottom: Sizes.rowPadding),
|
||||||
textOverflow: TextOverflow.ellipsis,
|
textOverflow: TextOverflow.ellipsis,
|
||||||
wordsWrap: wordsWrapInName,
|
wordsWrap: wordsWrapInName,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
fontSize: nameFontSize,
|
textStyle: Theme.of(context).textTheme.body1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,58 +34,5 @@ class _LightCardBodyState extends State<LightCardBody> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return InkWell(
|
|
||||||
onTap: () => entityWrapper.handleTap(),
|
|
||||||
onLongPress: () => entityWrapper.handleHold(),
|
|
||||||
onDoubleTap: () => entityWrapper.handleDoubleTap(),
|
|
||||||
child: AspectRatio(
|
|
||||||
aspectRatio: 1.5,
|
|
||||||
child: Stack(
|
|
||||||
fit: StackFit.expand,
|
|
||||||
overflow: Overflow.clip,
|
|
||||||
children: [
|
|
||||||
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: SimpleEntityState(
|
|
||||||
//textAlign: TextAlign.center,
|
|
||||||
expanded: false,
|
|
||||||
maxLines: 1,
|
|
||||||
bold: true,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
padding: EdgeInsets.all(0.0),
|
|
||||||
fontSize: fontSize,
|
|
||||||
//padding: EdgeInsets.only(top: Sizes.rowPadding),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Align(
|
|
||||||
alignment: Alignment.bottomCenter,
|
|
||||||
child: LayoutBuilder(
|
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
|
||||||
double fontSize = constraints.maxHeight / 7;
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(bottom: fontSize),
|
|
||||||
child: EntityName(
|
|
||||||
fontSize: fontSize,
|
|
||||||
maxLines: 1,
|
|
||||||
padding: EdgeInsets.all(0.0),
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
textOverflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -126,10 +126,10 @@ class Sizes {
|
|||||||
static const extendedWidgetHeight = 50.0;
|
static const extendedWidgetHeight = 50.0;
|
||||||
static const iconSize = 28.0;
|
static const iconSize = 28.0;
|
||||||
static const largeIconSize = 46.0;
|
static const largeIconSize = 46.0;
|
||||||
static const stateFontSize = 15.0;
|
//static const stateFontSize = 15.0;
|
||||||
static const nameFontSize = 15.0;
|
//static const nameFontSize = 15.0;
|
||||||
static const smallFontSize = 14.0;
|
//static const smallFontSize = 14.0;
|
||||||
static const largeFontSize = 24.0;
|
//static const largeFontSize = 24.0;
|
||||||
static const inputWidth = 160.0;
|
static const inputWidth = 160.0;
|
||||||
static const rowPadding = 10.0;
|
static const rowPadding = 10.0;
|
||||||
static const doubleRowPadding = rowPadding*2;
|
static const doubleRowPadding = rowPadding*2;
|
||||||
|
@ -248,7 +248,9 @@ class _AlarmControlPanelControlsWidgetWidgetState extends State<AlarmControlPane
|
|||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text(
|
child: Text(
|
||||||
"TRIGGER",
|
"TRIGGER",
|
||||||
style: TextStyle(color: Colors.redAccent)
|
style: Theme.of(context).textTheme.subhead.copyWith(
|
||||||
|
color: Theme.of(context).errorColor
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onPressed: () => _askToTrigger(entity),
|
onPressed: () => _askToTrigger(entity),
|
||||||
)
|
)
|
||||||
|
@ -7,8 +7,7 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
double iconSize = 26.0;
|
double iconSize = 26.0;
|
||||||
Widget badgeIcon;
|
Widget badgeIcon;
|
||||||
String onBadgeTextValue;
|
String onBadgeTextValue;
|
||||||
Color iconColor = EntityColor.badgeColors[entityModel.entityWrapper.entity.domain] ??
|
Color iconColor = HAClientTheme().getBadgeColor(entityModel.entityWrapper.entity.domain);
|
||||||
EntityColor.badgeColors["default"];
|
|
||||||
switch (entityModel.entityWrapper.entity.domain) {
|
switch (entityModel.entityWrapper.entity.domain) {
|
||||||
case "sun":
|
case "sun":
|
||||||
{
|
{
|
||||||
@ -30,7 +29,7 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
badgeIcon = EntityIcon(
|
badgeIcon = EntityIcon(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
color: Colors.black
|
color: Theme.of(context).textTheme.body1.color
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -40,7 +39,7 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
badgeIcon = EntityIcon(
|
badgeIcon = EntityIcon(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
color: Colors.black
|
color: Theme.of(context).textTheme.body1.color
|
||||||
);
|
);
|
||||||
onBadgeTextValue = entityModel.entityWrapper.entity.displayState;
|
onBadgeTextValue = entityModel.entityWrapper.entity.displayState;
|
||||||
break;
|
break;
|
||||||
@ -64,7 +63,9 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
overflow: TextOverflow.fade,
|
overflow: TextOverflow.fade,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: stateFontSize),
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
fontSize: stateFontSize
|
||||||
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
@ -77,7 +78,9 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
onBadgeText = Container(
|
onBadgeText = Container(
|
||||||
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
||||||
child: Text("$onBadgeTextValue",
|
child: Text("$onBadgeTextValue",
|
||||||
style: TextStyle(fontSize: 12.0, color: Colors.white),
|
style: Theme.of(context).textTheme.overline.copyWith(
|
||||||
|
color: HAClientTheme().getOnBadgeTextColor()
|
||||||
|
),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
overflow: TextOverflow.fade),
|
overflow: TextOverflow.fade),
|
||||||
@ -98,7 +101,7 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
decoration: new BoxDecoration(
|
decoration: new BoxDecoration(
|
||||||
// Circle shape
|
// Circle shape
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
color: Colors.white,
|
color: Theme.of(context).cardColor,
|
||||||
// The border you want
|
// The border you want
|
||||||
border: new Border.all(
|
border: new Border.all(
|
||||||
width: 2.0,
|
width: 2.0,
|
||||||
@ -131,7 +134,7 @@ class BadgeWidget extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
"${entityModel.entityWrapper.displayName}",
|
"${entityModel.entityWrapper.displayName}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(fontSize: 12.0),
|
style: Theme.of(context).textTheme.caption,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
|
@ -14,8 +14,6 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
|
|
||||||
CameraEntity _entity;
|
CameraEntity _entity;
|
||||||
String _streamUrl = "";
|
String _streamUrl = "";
|
||||||
VideoPlayerController _videoPlayerController;
|
|
||||||
Timer _monitorTimer;
|
|
||||||
bool _isLoaded = false;
|
bool _isLoaded = false;
|
||||||
double _aspectRatio = 1.33;
|
double _aspectRatio = 1.33;
|
||||||
String _webViewHtml;
|
String _webViewHtml;
|
||||||
@ -41,11 +39,15 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
if (_entity.supportStream) {
|
if (_entity.supportStream) {
|
||||||
HomeAssistant().getCameraStream(_entity.entityId)
|
HomeAssistant().getCameraStream(_entity.entityId)
|
||||||
.then((data) {
|
.then((data) {
|
||||||
if (_videoPlayerController != null) {
|
_jsMessageChannelName = 'HA_${_entity.entityId.replaceAll('.', '_')}';
|
||||||
_videoPlayerController.dispose().then((_) => createPlayer(data));
|
rootBundle.loadString('assets/html/cameraLiveView.html').then((file) {
|
||||||
} else {
|
_webViewHtml = Uri.dataFromString(
|
||||||
createPlayer(data);
|
file.replaceFirst('{{stream_url}}', '${ConnectionManager().httpWebHost}${data["url"]}').replaceFirst('{{message_channel}}', _jsMessageChannelName),
|
||||||
}
|
mimeType: 'text/html',
|
||||||
|
encoding: Encoding.getByName('utf-8')
|
||||||
|
).toString();
|
||||||
|
_loading.complete();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catchError((e) {
|
.catchError((e) {
|
||||||
_loading.completeError(e);
|
_loading.completeError(e);
|
||||||
@ -67,40 +69,6 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
return _loading.future;
|
return _loading.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
void createPlayer(data) {
|
|
||||||
_videoPlayerController = VideoPlayerController.network("${ConnectionManager().httpWebHost}${data["url"]}");
|
|
||||||
_videoPlayerController.initialize().then((_) {
|
|
||||||
setState((){
|
|
||||||
_aspectRatio = _videoPlayerController.value.aspectRatio;
|
|
||||||
});
|
|
||||||
_loading.complete();
|
|
||||||
autoPlay();
|
|
||||||
startMonitor();
|
|
||||||
}).catchError((e) {
|
|
||||||
_loading.completeError(e);
|
|
||||||
Logger.e("[Camera Player] Error player init. Retrying");
|
|
||||||
_loadResources();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void autoPlay() {
|
|
||||||
if (!_videoPlayerController.value.isPlaying) {
|
|
||||||
_videoPlayerController.play();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startMonitor() {
|
|
||||||
_monitorTimer?.cancel();
|
|
||||||
_monitorTimer = Timer.periodic(Duration(milliseconds: 500), (timer) {
|
|
||||||
if (_videoPlayerController.value.hasError) {
|
|
||||||
timer.cancel();
|
|
||||||
setState(() {
|
|
||||||
_isLoaded = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildScreen() {
|
Widget _buildScreen() {
|
||||||
Widget screenWidget;
|
Widget screenWidget;
|
||||||
if (!_isLoaded) {
|
if (!_isLoaded) {
|
||||||
@ -109,16 +77,6 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else if (_entity.supportStream) {
|
|
||||||
if (_videoPlayerController.value.initialized) {
|
|
||||||
screenWidget = VideoPlayer(_videoPlayerController);
|
|
||||||
} else {
|
|
||||||
screenWidget = Center(
|
|
||||||
child: EntityPicture(
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
screenWidget = WebView(
|
screenWidget = WebView(
|
||||||
initialUrl: _webViewHtml,
|
initialUrl: _webViewHtml,
|
||||||
@ -130,6 +88,7 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
JavascriptChannel(
|
JavascriptChannel(
|
||||||
name: _jsMessageChannelName,
|
name: _jsMessageChannelName,
|
||||||
onMessageReceived: ((message) {
|
onMessageReceived: ((message) {
|
||||||
|
Logger.d('[Camera Player] Message from page: $message');
|
||||||
setState((){
|
setState((){
|
||||||
_aspectRatio = double.tryParse(message.message) ?? 1.33;
|
_aspectRatio = double.tryParse(message.message) ?? 1.33;
|
||||||
});
|
});
|
||||||
@ -145,28 +104,6 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildControls() {
|
Widget _buildControls() {
|
||||||
Widget playControl;
|
|
||||||
if (_entity.supportStream) {
|
|
||||||
playControl = Center(
|
|
||||||
child: IconButton(
|
|
||||||
icon: Icon((_videoPlayerController != null && _videoPlayerController.value.isPlaying) ? Icons.pause_circle_outline : Icons.play_circle_outline),
|
|
||||||
iconSize: 60,
|
|
||||||
color: Colors.amberAccent,
|
|
||||||
onPressed: (_videoPlayerController == null || _videoPlayerController.value.hasError || !_isLoaded) ? null :
|
|
||||||
() {
|
|
||||||
setState(() {
|
|
||||||
if (_videoPlayerController != null && _videoPlayerController.value.isPlaying) {
|
|
||||||
_videoPlayerController.pause();
|
|
||||||
} else {
|
|
||||||
_videoPlayerController.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
playControl = Container();
|
|
||||||
}
|
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@ -175,7 +112,7 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.refresh),
|
icon: Icon(Icons.refresh),
|
||||||
iconSize: 40,
|
iconSize: 40,
|
||||||
color: Colors.amberAccent,
|
color: Theme.of(context).accentColor,
|
||||||
onPressed: _isLoaded ? () {
|
onPressed: _isLoaded ? () {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoaded = false;
|
_isLoaded = false;
|
||||||
@ -183,14 +120,13 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
} : null,
|
} : null,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: playControl,
|
child: Container(),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.fullscreen),
|
icon: Icon(Icons.fullscreen),
|
||||||
iconSize: 40,
|
iconSize: 40,
|
||||||
color: Colors.amberAccent,
|
color: Theme.of(context).accentColor,
|
||||||
onPressed: _isLoaded ? () {
|
onPressed: _isLoaded ? () {
|
||||||
_videoPlayerController?.pause();
|
|
||||||
eventBus.fire(ShowEntityPageEvent());
|
eventBus.fire(ShowEntityPageEvent());
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
@ -239,8 +175,6 @@ class _CameraStreamViewState extends State<CameraStreamView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_monitorTimer?.cancel();
|
|
||||||
_videoPlayerController?.dispose();
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -204,20 +204,20 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
//_buildOnOffControl(entity),
|
//_buildOnOffControl(entity),
|
||||||
_buildTemperatureControls(entity),
|
_buildTemperatureControls(entity, context),
|
||||||
_buildTargetTemperatureControls(entity),
|
_buildTargetTemperatureControls(entity, context),
|
||||||
_buildHumidityControls(entity),
|
_buildHumidityControls(entity, context),
|
||||||
_buildOperationControl(entity),
|
_buildOperationControl(entity, context),
|
||||||
_buildFanControl(entity),
|
_buildFanControl(entity, context),
|
||||||
_buildSwingControl(entity),
|
_buildSwingControl(entity, context),
|
||||||
_buildPresetModeControl(entity),
|
_buildPresetModeControl(entity, context),
|
||||||
_buildAuxHeatControl(entity)
|
_buildAuxHeatControl(entity, context)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildPresetModeControl(ClimateEntity entity) {
|
Widget _buildPresetModeControl(ClimateEntity entity, BuildContext context) {
|
||||||
if (entity.supportPresetMode) {
|
if (entity.supportPresetMode) {
|
||||||
return ModeSelectorWidget(
|
return ModeSelectorWidget(
|
||||||
options: entity.presetModes,
|
options: entity.presetModes,
|
||||||
@ -242,7 +242,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
Widget _buildAuxHeatControl(ClimateEntity entity) {
|
Widget _buildAuxHeatControl(ClimateEntity entity, BuildContext context) {
|
||||||
if (entity.supportAuxHeat ) {
|
if (entity.supportAuxHeat ) {
|
||||||
return ModeSwitchWidget(
|
return ModeSwitchWidget(
|
||||||
caption: "Aux heat",
|
caption: "Aux heat",
|
||||||
@ -254,7 +254,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildOperationControl(ClimateEntity entity) {
|
Widget _buildOperationControl(ClimateEntity entity, BuildContext context) {
|
||||||
if (entity.hvacModes != null) {
|
if (entity.hvacModes != null) {
|
||||||
return ModeSelectorWidget(
|
return ModeSelectorWidget(
|
||||||
onChange: (mode) => _setHVACMode(entity, mode),
|
onChange: (mode) => _setHVACMode(entity, mode),
|
||||||
@ -267,7 +267,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildFanControl(ClimateEntity entity) {
|
Widget _buildFanControl(ClimateEntity entity, BuildContext context) {
|
||||||
if (entity.supportFanMode) {
|
if (entity.supportFanMode) {
|
||||||
return ModeSelectorWidget(
|
return ModeSelectorWidget(
|
||||||
options: entity.fanModes,
|
options: entity.fanModes,
|
||||||
@ -280,7 +280,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSwingControl(ClimateEntity entity) {
|
Widget _buildSwingControl(ClimateEntity entity, BuildContext context) {
|
||||||
if (entity.supportSwingMode) {
|
if (entity.supportSwingMode) {
|
||||||
return ModeSelectorWidget(
|
return ModeSelectorWidget(
|
||||||
onChange: (mode) => _setSwingMode(entity, mode),
|
onChange: (mode) => _setSwingMode(entity, mode),
|
||||||
@ -293,17 +293,15 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTemperatureControls(ClimateEntity entity) {
|
Widget _buildTemperatureControls(ClimateEntity entity, BuildContext context) {
|
||||||
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Target temperature", style: TextStyle(
|
Text("Target temperature", style: Theme.of(context).textTheme.body1),
|
||||||
fontSize: Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTemperature,
|
value: _tmpTemperature,
|
||||||
fontColor: _temperaturePending ? Colors.red : Colors.black,
|
active: _temperaturePending,
|
||||||
onDec: () => _temperatureDown(entity),
|
onDec: () => _temperatureDown(entity),
|
||||||
onInc: () => _temperatureUp(entity),
|
onInc: () => _temperatureUp(entity),
|
||||||
)
|
)
|
||||||
@ -314,13 +312,13 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTargetTemperatureControls(ClimateEntity entity) {
|
Widget _buildTargetTemperatureControls(ClimateEntity entity, BuildContext context) {
|
||||||
List<Widget> controls = [];
|
List<Widget> controls = [];
|
||||||
if ((entity.supportTargetTemperatureRange) && (entity.targetLow != null)) {
|
if ((entity.supportTargetTemperatureRange) && (entity.targetLow != null)) {
|
||||||
controls.addAll(<Widget>[
|
controls.addAll(<Widget>[
|
||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTargetLow,
|
value: _tmpTargetLow,
|
||||||
fontColor: _temperaturePending ? Colors.red : Colors.black,
|
active: _temperaturePending,
|
||||||
onDec: () => _targetLowDown(entity),
|
onDec: () => _targetLowDown(entity),
|
||||||
onInc: () => _targetLowUp(entity),
|
onInc: () => _targetLowUp(entity),
|
||||||
),
|
),
|
||||||
@ -333,7 +331,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
controls.add(
|
controls.add(
|
||||||
TemperatureControlWidget(
|
TemperatureControlWidget(
|
||||||
value: _tmpTargetHigh,
|
value: _tmpTargetHigh,
|
||||||
fontColor: _temperaturePending ? Colors.red : Colors.black,
|
active: _temperaturePending,
|
||||||
onDec: () => _targetHighDown(entity),
|
onDec: () => _targetHighDown(entity),
|
||||||
onInc: () => _targetHighUp(entity),
|
onInc: () => _targetHighUp(entity),
|
||||||
)
|
)
|
||||||
@ -343,9 +341,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Target temperature range", style: TextStyle(
|
Text("Target temperature range", style: Theme.of(context).textTheme.body1),
|
||||||
fontSize: Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
Row(
|
Row(
|
||||||
children: controls,
|
children: controls,
|
||||||
)
|
)
|
||||||
@ -356,13 +352,13 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildHumidityControls(ClimateEntity entity) {
|
Widget _buildHumidityControls(ClimateEntity entity, BuildContext context) {
|
||||||
List<Widget> result = [];
|
List<Widget> result = [];
|
||||||
if (entity.supportTargetHumidity) {
|
if (entity.supportTargetHumidity) {
|
||||||
result.addAll(<Widget>[
|
result.addAll(<Widget>[
|
||||||
Text(
|
Text(
|
||||||
"$_tmpTargetHumidity%",
|
"$_tmpTargetHumidity%",
|
||||||
style: TextStyle(fontSize: Sizes.largeFontSize),
|
style: Theme.of(context).textTheme.display1,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Slider(
|
child: Slider(
|
||||||
@ -387,9 +383,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
||||||
child: Text("Target humidity", style: TextStyle(
|
child: Text("Target humidity", style: Theme.of(context).textTheme.body1),
|
||||||
fontSize: Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
@ -33,23 +33,16 @@ class ClimateStateWidget extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("$displayState",
|
Text("$displayState",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: Theme.of(context).textTheme.body2),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: Sizes.stateFontSize,
|
|
||||||
)),
|
|
||||||
Text(" $targetTemp",
|
Text(" $targetTemp",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: Theme.of(context).textTheme.body1)
|
||||||
fontSize: Sizes.stateFontSize,
|
|
||||||
))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
entity.currentTemperature != null ?
|
entity.currentTemperature != null ?
|
||||||
Text("Currently: ${entity.currentTemperature}",
|
Text("Currently: ${entity.currentTemperature}",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: new TextStyle(
|
style: Theme.of(context).textTheme.subtitle
|
||||||
fontSize: Sizes.stateFontSize,
|
|
||||||
color: Colors.black45)
|
|
||||||
) :
|
) :
|
||||||
Container(height: 0.0,)
|
Container(height: 0.0,)
|
||||||
],
|
],
|
||||||
|
@ -5,8 +5,6 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
final String caption;
|
final String caption;
|
||||||
final List options;
|
final List options;
|
||||||
final String value;
|
final String value;
|
||||||
final double captionFontSize;
|
|
||||||
final double valueFontSize;
|
|
||||||
final onChange;
|
final onChange;
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
|
||||||
@ -16,8 +14,6 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
@required this.options,
|
@required this.options,
|
||||||
this.value,
|
this.value,
|
||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
this.captionFontSize,
|
|
||||||
this.valueFontSize,
|
|
||||||
this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0),
|
this.padding: const EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, 0.0),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -28,9 +24,7 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("$caption", style: TextStyle(
|
Text("$caption", style: Theme.of(context).textTheme.body1),
|
||||||
fontSize: captionFontSize ?? Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -40,10 +34,7 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
value: value,
|
value: value,
|
||||||
iconSize: 30.0,
|
iconSize: 30.0,
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.title,
|
||||||
fontSize: valueFontSize ?? Sizes.largeFontSize,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
hint: Text("Select ${caption.toLowerCase()}"),
|
hint: Text("Select ${caption.toLowerCase()}"),
|
||||||
items: options.map((value) {
|
items: options.map((value) {
|
||||||
return new DropdownMenuItem<String>(
|
return new DropdownMenuItem<String>(
|
||||||
|
@ -4,7 +4,6 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
|
|
||||||
final String caption;
|
final String caption;
|
||||||
final onChange;
|
final onChange;
|
||||||
final double captionFontSize;
|
|
||||||
final bool value;
|
final bool value;
|
||||||
final bool expanded;
|
final bool expanded;
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
@ -13,7 +12,6 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
@required this.caption,
|
@required this.caption,
|
||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
this.captionFontSize,
|
|
||||||
this.value,
|
this.value,
|
||||||
this.expanded: true,
|
this.expanded: true,
|
||||||
this.padding: const EdgeInsets.only(left: Sizes.leftWidgetPadding, right: Sizes.rightWidgetPadding)
|
this.padding: const EdgeInsets.only(left: Sizes.leftWidgetPadding, right: Sizes.rightWidgetPadding)
|
||||||
@ -25,7 +23,7 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
padding: this.padding,
|
padding: this.padding,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildCaption(),
|
_buildCaption(context),
|
||||||
Switch(
|
Switch(
|
||||||
onChanged: (value) => onChange(value),
|
onChanged: (value) => onChange(value),
|
||||||
value: value ?? false,
|
value: value ?? false,
|
||||||
@ -35,12 +33,10 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildCaption() {
|
Widget _buildCaption(BuildContext context) {
|
||||||
Widget captionWidget = Text(
|
Widget captionWidget = Text(
|
||||||
"$caption",
|
"$caption",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1,
|
||||||
fontSize: captionFontSize ?? Sizes.stateFontSize
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
return Expanded(
|
return Expanded(
|
||||||
|
@ -2,8 +2,7 @@ part of '../../../main.dart';
|
|||||||
|
|
||||||
class TemperatureControlWidget extends StatelessWidget {
|
class TemperatureControlWidget extends StatelessWidget {
|
||||||
final double value;
|
final double value;
|
||||||
final double fontSize;
|
final bool active;
|
||||||
final Color fontColor;
|
|
||||||
final onInc;
|
final onInc;
|
||||||
final onDec;
|
final onDec;
|
||||||
|
|
||||||
@ -12,8 +11,9 @@ class TemperatureControlWidget extends StatelessWidget {
|
|||||||
@required this.value,
|
@required this.value,
|
||||||
@required this.onInc,
|
@required this.onInc,
|
||||||
@required this.onDec,
|
@required this.onDec,
|
||||||
this.fontSize,
|
//this.fontSize,
|
||||||
this.fontColor})
|
this.active: false
|
||||||
|
})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -23,10 +23,7 @@ class TemperatureControlWidget extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"$value",
|
"$value",
|
||||||
style: TextStyle(
|
style: active ? Theme.of(context).textTheme.display2 : Theme.of(context).textTheme.display1,
|
||||||
fontSize: fontSize ?? 24.0,
|
|
||||||
color: fontColor ?? Colors.black
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Column(
|
Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -64,9 +64,7 @@ class _CoverControlWidgetState extends State<CoverControlWidget> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
||||||
child: Text("Position", style: TextStyle(
|
child: Text("Position"),
|
||||||
fontSize: Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
Slider(
|
Slider(
|
||||||
value: _tmpPosition,
|
value: _tmpPosition,
|
||||||
@ -118,9 +116,7 @@ class _CoverControlWidgetState extends State<CoverControlWidget> {
|
|||||||
controls.insert(0, Padding(
|
controls.insert(0, Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
0.0, Sizes.rowPadding, 0.0, Sizes.rowPadding),
|
||||||
child: Text("Tilt position", style: TextStyle(
|
child: Text("Tilt position"),
|
||||||
fontSize: Sizes.stateFontSize
|
|
||||||
)),
|
|
||||||
));
|
));
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -9,10 +9,8 @@ class DateTimeStateWidget extends StatelessWidget {
|
|||||||
padding: EdgeInsets.fromLTRB(0.0, 0.0, Sizes.rightWidgetPadding, 0.0),
|
padding: EdgeInsets.fromLTRB(0.0, 0.0, Sizes.rightWidgetPadding, 0.0),
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
child: Text("${entity.formattedState}",
|
child: Text("${entity.formattedState}",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right
|
||||||
style: new TextStyle(
|
),
|
||||||
fontSize: Sizes.stateFontSize,
|
|
||||||
)),
|
|
||||||
onTap: () => _handleStateTap(context, entity),
|
onTap: () => _handleStateTap(context, entity),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,19 @@ class DefaultEntityContainer extends StatelessWidget {
|
|||||||
return MissedEntityWidget();
|
return MissedEntityWidget();
|
||||||
}
|
}
|
||||||
if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.DIVIDER) {
|
if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.DIVIDER) {
|
||||||
return Divider(
|
return Divider();
|
||||||
color: Colors.black45,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.SECTION) {
|
if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.SECTION) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Divider(
|
Divider(),
|
||||||
color: Colors.black45,
|
|
||||||
),
|
|
||||||
Text(
|
Text(
|
||||||
"${entityModel.entityWrapper.entity.displayName}",
|
"${entityModel.entityWrapper.entity.displayName}",
|
||||||
style: TextStyle(color: Colors.blue),
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
color: Theme.of(context).primaryColor
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
part of '../main.dart';
|
|
||||||
|
|
||||||
class EntityColor {
|
|
||||||
|
|
||||||
static const defaultStateColor = Color.fromRGBO(68, 115, 158, 1.0);
|
|
||||||
|
|
||||||
static const badgeColors = {
|
|
||||||
"default": Color.fromRGBO(223, 76, 30, 1.0),
|
|
||||||
"binary_sensor": Color.fromRGBO(3, 155, 229, 1.0)
|
|
||||||
};
|
|
||||||
|
|
||||||
static const _stateColors = {
|
|
||||||
EntityState.on: Colors.amber,
|
|
||||||
"auto": Colors.amber,
|
|
||||||
EntityState.active: Colors.amber,
|
|
||||||
EntityState.playing: Colors.amber,
|
|
||||||
EntityState.paused: Colors.amber,
|
|
||||||
"above_horizon": Colors.amber,
|
|
||||||
EntityState.home: Colors.amber,
|
|
||||||
EntityState.open: Colors.amber,
|
|
||||||
EntityState.cleaning: Colors.amber,
|
|
||||||
EntityState.returning: Colors.amber,
|
|
||||||
EntityState.off: defaultStateColor,
|
|
||||||
EntityState.closed: defaultStateColor,
|
|
||||||
"below_horizon": defaultStateColor,
|
|
||||||
"default": defaultStateColor,
|
|
||||||
EntityState.idle: defaultStateColor,
|
|
||||||
"heat": Colors.redAccent,
|
|
||||||
"cool": Colors.lightBlue,
|
|
||||||
EntityState.unavailable: Colors.black26,
|
|
||||||
EntityState.unknown: Colors.black26,
|
|
||||||
EntityState.alarm_disarmed: Colors.green,
|
|
||||||
EntityState.alarm_armed_away: Colors.redAccent,
|
|
||||||
EntityState.alarm_armed_custom_bypass: Colors.redAccent,
|
|
||||||
EntityState.alarm_armed_home: Colors.redAccent,
|
|
||||||
EntityState.alarm_armed_night: Colors.redAccent,
|
|
||||||
EntityState.alarm_triggered: Colors.redAccent,
|
|
||||||
EntityState.alarm_arming: Colors.amber,
|
|
||||||
EntityState.alarm_disarming: Colors.amber,
|
|
||||||
EntityState.alarm_pending: Colors.amber,
|
|
||||||
};
|
|
||||||
|
|
||||||
static Color stateColor(String state) {
|
|
||||||
return _stateColors[state] ?? _stateColors["default"];
|
|
||||||
}
|
|
||||||
|
|
||||||
static charts.Color chartHistoryStateColor(String state, int id) {
|
|
||||||
Color c = _stateColors[state];
|
|
||||||
if (c != null) {
|
|
||||||
return charts.Color(
|
|
||||||
r: c.red,
|
|
||||||
g: c.green,
|
|
||||||
b: c.blue,
|
|
||||||
a: c.alpha
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
double r = id.toDouble() % 10;
|
|
||||||
return charts.MaterialPalette.getOrderedPalettes(10)[r.round()].shadeDefault;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Color historyStateColor(String state, int id) {
|
|
||||||
Color c = _stateColors[state];
|
|
||||||
if (c != null) {
|
|
||||||
return c;
|
|
||||||
} else {
|
|
||||||
if (id > -1) {
|
|
||||||
double r = id.toDouble() % 10;
|
|
||||||
charts.Color c1 = charts.MaterialPalette.getOrderedPalettes(10)[r.round()].shadeDefault;
|
|
||||||
return Color.fromARGB(c1.a, c1.r, c1.g, c1.b);
|
|
||||||
} else {
|
|
||||||
return _stateColors[EntityState.on];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -67,7 +67,7 @@ class EntityIcon extends StatelessWidget {
|
|||||||
padding: padding,
|
padding: padding,
|
||||||
child: buildIcon(
|
child: buildIcon(
|
||||||
entityWrapper,
|
entityWrapper,
|
||||||
color ?? EntityColor.stateColor(entityWrapper.entity.state)
|
color ?? HAClientTheme().getColorByEntityState(entityWrapper.entity.state, context)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -5,18 +5,24 @@ class EntityName extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
final TextOverflow textOverflow;
|
final TextOverflow textOverflow;
|
||||||
final bool wordsWrap;
|
final bool wordsWrap;
|
||||||
final double fontSize;
|
|
||||||
final TextAlign textAlign;
|
final TextAlign textAlign;
|
||||||
final int maxLines;
|
final int maxLines;
|
||||||
|
final TextStyle textStyle;
|
||||||
|
|
||||||
const EntityName({Key key, this.maxLines, this.padding: const EdgeInsets.only(right: 10.0), this.textOverflow: TextOverflow.ellipsis, this.wordsWrap: true, this.fontSize: Sizes.nameFontSize, this.textAlign: TextAlign.left}) : super(key: key);
|
const EntityName({Key key, this.maxLines, this.padding: const EdgeInsets.only(right: 10.0), this.textOverflow: TextOverflow.ellipsis, this.textStyle, this.wordsWrap: true, this.textAlign: TextAlign.left}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
||||||
TextStyle textStyle = TextStyle(fontSize: fontSize);
|
TextStyle tStyle;
|
||||||
|
if (textStyle == null) {
|
||||||
if (entityWrapper.entity.statelessType == StatelessEntityType.WEBLINK) {
|
if (entityWrapper.entity.statelessType == StatelessEntityType.WEBLINK) {
|
||||||
textStyle = textStyle.apply(color: Colors.blue, decoration: TextDecoration.underline);
|
tStyle = HAClientTheme().getLinkTextStyle(context);
|
||||||
|
} else {
|
||||||
|
tStyle = Theme.of(context).textTheme.body1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tStyle = textStyle;
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
@ -25,7 +31,7 @@ class EntityName extends StatelessWidget {
|
|||||||
overflow: textOverflow,
|
overflow: textOverflow,
|
||||||
softWrap: wordsWrap,
|
softWrap: wordsWrap,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
style: textStyle,
|
style: tStyle,
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -16,7 +16,7 @@ class EntityPageLayout extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
showClose ?
|
showClose ?
|
||||||
Container(
|
Container(
|
||||||
color: Colors.blue[300],
|
color: Theme.of(context).primaryColor,
|
||||||
height: 40,
|
height: 40,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -25,18 +25,14 @@ class EntityPageLayout extends StatelessWidget {
|
|||||||
padding: EdgeInsets.only(left: 8),
|
padding: EdgeInsets.only(left: 8),
|
||||||
child: Text(
|
child: Text(
|
||||||
entity.displayName,
|
entity.displayName,
|
||||||
style: TextStyle(
|
style: Theme.of(context).primaryTextTheme.headline
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
fontSize: 22
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
padding: EdgeInsets.all(0),
|
padding: EdgeInsets.all(0),
|
||||||
icon: Icon(Icons.close),
|
icon: Icon(Icons.close),
|
||||||
color: Colors.white,
|
color: Theme.of(context).primaryTextTheme.headline.color,
|
||||||
iconSize: 36.0,
|
iconSize: 36.0,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
eventBus.fire(ShowEntityPageEvent());
|
eventBus.fire(ShowEntityPageEvent());
|
||||||
|
@ -22,7 +22,7 @@ class EntityPicture extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildIcon(EntityWrapper data) {
|
Widget buildIcon(EntityWrapper data, BuildContext context) {
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ class EntityPicture extends StatelessWidget {
|
|||||||
child: Icon(
|
child: Icon(
|
||||||
IconData(iconCode, fontFamily: 'Material Design Icons'),
|
IconData(iconCode, fontFamily: 'Material Design Icons'),
|
||||||
size: Sizes.largeIconSize,
|
size: Sizes.largeIconSize,
|
||||||
color: EntityColor.defaultStateColor,
|
color: HAClientTheme().getOffStateColor(context),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -63,7 +63,8 @@ class EntityPicture extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: buildIcon(
|
child: buildIcon(
|
||||||
entityWrapper
|
entityWrapper,
|
||||||
|
context
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ class FlatServiceButton extends StatelessWidget {
|
|||||||
final String serviceName;
|
final String serviceName;
|
||||||
final String entityId;
|
final String entityId;
|
||||||
final String text;
|
final String text;
|
||||||
final double fontSize;
|
|
||||||
|
|
||||||
FlatServiceButton({
|
FlatServiceButton({
|
||||||
Key key,
|
Key key,
|
||||||
@ -14,7 +13,6 @@ class FlatServiceButton extends StatelessWidget {
|
|||||||
@required this.serviceName,
|
@required this.serviceName,
|
||||||
@required this.entityId,
|
@required this.entityId,
|
||||||
@required this.text,
|
@required this.text,
|
||||||
this.fontSize: Sizes.stateFontSize
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
void _setNewState() {
|
void _setNewState() {
|
||||||
@ -24,7 +22,7 @@ class FlatServiceButton extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: fontSize*2.5,
|
height: Theme.of(context).textTheme.subhead.fontSize*2.5,
|
||||||
child: FlatButton(
|
child: FlatButton(
|
||||||
onPressed: (() {
|
onPressed: (() {
|
||||||
_setNewState();
|
_setNewState();
|
||||||
@ -32,8 +30,7 @@ class FlatServiceButton extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
text,
|
text,
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style:
|
style: HAClientTheme().getActionTextStyle(context),
|
||||||
new TextStyle(fontSize: fontSize, color: Colors.blue),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -183,7 +183,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
}
|
}
|
||||||
return UniversalSlider(
|
return UniversalSlider(
|
||||||
title: "Color temperature",
|
title: "Color temperature",
|
||||||
leading: Text("Cold", style: TextStyle(color: Colors.lightBlue),),
|
leading: Text("Cold", style: Theme.of(context).textTheme.body1.copyWith(color: Colors.lightBlue)),
|
||||||
value: val,
|
value: val,
|
||||||
onChangeEnd: (value) => _setColorTemp(entity, value),
|
onChangeEnd: (value) => _setColorTemp(entity, value),
|
||||||
max: entity.maxMireds,
|
max: entity.maxMireds,
|
||||||
@ -194,7 +194,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
_tmpColorTemp = value.round();
|
_tmpColorTemp = value.round();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
closing: Text("Warm", style: TextStyle(color: Colors.amberAccent),),
|
closing: Text("Warm", style: Theme.of(context).textTheme.body1.copyWith(color: Colors.amberAccent),),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Container(width: 0.0, height: 0.0);
|
return Container(width: 0.0, height: 0.0);
|
||||||
@ -224,7 +224,7 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
color: savedColor?.toColor() ?? Colors.transparent,
|
color: savedColor?.toColor() ?? Theme.of(context).backgroundColor,
|
||||||
child: Text('Paste color'),
|
child: Text('Paste color'),
|
||||||
onPressed: savedColor == null ? null : () {
|
onPressed: savedColor == null ? null : () {
|
||||||
_setColor(entity, savedColor);
|
_setColor(entity, savedColor);
|
||||||
|
@ -28,8 +28,7 @@ class LockStateWidget extends StatelessWidget {
|
|||||||
onPressed: () => _unlock(entity),
|
onPressed: () => _unlock(entity),
|
||||||
child: Text("UNLOCK",
|
child: Text("UNLOCK",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style:
|
style: HAClientTheme().getActionTextStyle(context)
|
||||||
new TextStyle(fontSize: Sizes.stateFontSize, color: Colors.blue),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -39,8 +38,7 @@ class LockStateWidget extends StatelessWidget {
|
|||||||
onPressed: () => _lock(entity),
|
onPressed: () => _lock(entity),
|
||||||
child: Text("LOCK",
|
child: Text("LOCK",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style:
|
style: HAClientTheme().getActionTextStyle(context),
|
||||||
new TextStyle(fontSize: Sizes.stateFontSize, color: Colors.blue),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -56,8 +54,7 @@ class LockStateWidget extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
entity.isLocked ? "UNLOCK" : "LOCK",
|
entity.isLocked ? "UNLOCK" : "LOCK",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style:
|
style: HAClientTheme().getActionTextStyle(context),
|
||||||
new TextStyle(fontSize: Sizes.stateFontSize, color: Colors.blue),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -33,7 +33,7 @@ class _MediaPlayerProgressBarState extends State<MediaPlayerProgressBar> {
|
|||||||
return LinearProgressIndicator(
|
return LinearProgressIndicator(
|
||||||
value: progress,
|
value: progress,
|
||||||
backgroundColor: Colors.black45,
|
backgroundColor: Colors.black45,
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(EntityColor.stateColor(EntityState.on)),
|
valueColor: AlwaysStoppedAnimation<Color>(HAClientTheme().getOnStateColor(context)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,6 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
|||||||
double _currentPosition = 0;
|
double _currentPosition = 0;
|
||||||
int _savedPosition = 0;
|
int _savedPosition = 0;
|
||||||
|
|
||||||
final TextStyle _seekTextStyle = TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
color: Colors.blue,
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -53,8 +47,7 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
|||||||
buttons.add(
|
buttons.add(
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
child: Text("Jump to ${Duration(seconds: _savedPosition).toString().split('.')[0]}"),
|
child: Text("Jump to ${Duration(seconds: _savedPosition).toString().split('.')[0]}"),
|
||||||
color: Colors.orange,
|
color: Theme.of(context).accentColor,
|
||||||
focusColor: Colors.white,
|
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
ConnectionManager().callService(
|
ConnectionManager().callService(
|
||||||
domain: "media_player",
|
domain: "media_player",
|
||||||
@ -79,7 +72,13 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("00:00"),
|
Text("00:00"),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text("${Duration(seconds: _currentPosition.toInt()).toString().split(".")[0]}",textAlign: TextAlign.center, style: _seekTextStyle),
|
child: Text(
|
||||||
|
"${Duration(seconds: _currentPosition.toInt()).toString().split(".")[0]}",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.title.copyWith(
|
||||||
|
color: Colors.blue
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Text("${Duration(seconds: entity.durationSeconds).toString().split(".")[0]}")
|
Text("${Duration(seconds: entity.durationSeconds).toString().split(".")[0]}")
|
||||||
],
|
],
|
||||||
@ -87,8 +86,7 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
|||||||
Container(height: 10,),
|
Container(height: 10,),
|
||||||
Slider(
|
Slider(
|
||||||
min: 0,
|
min: 0,
|
||||||
activeColor: Colors.amber,
|
activeColor: Theme.of(context).accentColor,
|
||||||
inactiveColor: Colors.black26,
|
|
||||||
max: entity.durationSeconds.toDouble(),
|
max: entity.durationSeconds.toDouble(),
|
||||||
value: _currentPosition,
|
value: _currentPosition,
|
||||||
onChangeStart: (val) {
|
onChangeStart: (val) {
|
||||||
|
@ -12,14 +12,14 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
Stack(
|
Stack(
|
||||||
alignment: AlignmentDirectional.topEnd,
|
alignment: AlignmentDirectional.topEnd,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildImage(entity),
|
_buildImage(entity, context),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0.0,
|
bottom: 0.0,
|
||||||
left: 0.0,
|
left: 0.0,
|
||||||
right: 0.0,
|
right: 0.0,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.black45,
|
color: Colors.black45,
|
||||||
child: _buildState(entity),
|
child: _buildState(entity, context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
@ -35,12 +35,9 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildState(MediaPlayerEntity entity) {
|
Widget _buildState(MediaPlayerEntity entity, BuildContext context) {
|
||||||
TextStyle style = TextStyle(
|
TextStyle style = Theme.of(context).textTheme.body1.copyWith(
|
||||||
fontSize: 14.0,
|
color: Colors.white
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
height: 1.2
|
|
||||||
);
|
);
|
||||||
List<Widget> states = [];
|
List<Widget> states = [];
|
||||||
states.add(Text("${entity.displayName}", style: style));
|
states.add(Text("${entity.displayName}", style: style));
|
||||||
@ -71,7 +68,7 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildImage(MediaPlayerEntity entity) {
|
Widget _buildImage(MediaPlayerEntity entity, BuildContext context) {
|
||||||
String state = entity.state;
|
String state = entity.state;
|
||||||
if (entity.entityPicture != null && state != EntityState.off && state != EntityState.unavailable && state != EntityState.idle) {
|
if (entity.entityPicture != null && state != EntityState.off && state != EntityState.unavailable && state != EntityState.idle) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -97,7 +94,7 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
Icon(
|
Icon(
|
||||||
MaterialDesignIcons.getIconDataFromIconName("mdi:movie"),
|
MaterialDesignIcons.getIconDataFromIconName("mdi:movie"),
|
||||||
size: 150.0,
|
size: 150.0,
|
||||||
color: EntityColor.stateColor("$state"),
|
color: HAClientTheme().getColorByEntityState("$state", context),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -7,10 +7,10 @@ class SimpleEntityState extends StatelessWidget {
|
|||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
final int maxLines;
|
final int maxLines;
|
||||||
final String customValue;
|
final String customValue;
|
||||||
final double fontSize;
|
final TextStyle textStyle;
|
||||||
final bool bold;
|
//final bool bold;
|
||||||
|
|
||||||
const SimpleEntityState({Key key,this.bold: false, this.maxLines: 10, this.fontSize: Sizes.stateFontSize, this.expanded: true, this.textAlign: TextAlign.right, this.padding: const EdgeInsets.fromLTRB(0.0, 0.0, Sizes.rightWidgetPadding, 0.0), this.customValue}) : super(key: key);
|
const SimpleEntityState({Key key,/*this.bold: false,*/ this.maxLines: 10, this.expanded: true, this.textAlign: TextAlign.right, this.textStyle, this.padding: const EdgeInsets.fromLTRB(0.0, 0.0, Sizes.rightWidgetPadding, 0.0), this.customValue}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -22,16 +22,19 @@ class SimpleEntityState extends StatelessWidget {
|
|||||||
} else {
|
} else {
|
||||||
state = customValue;
|
state = customValue;
|
||||||
}
|
}
|
||||||
TextStyle textStyle = TextStyle(
|
TextStyle tStyle;
|
||||||
fontSize: this.fontSize,
|
if (textStyle != null) {
|
||||||
fontWeight: FontWeight.normal
|
tStyle = textStyle;
|
||||||
|
} else if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.CALL_SERVICE) {
|
||||||
|
tStyle = Theme.of(context).textTheme.subhead.copyWith(
|
||||||
|
color: Colors.blue
|
||||||
);
|
);
|
||||||
if (entityModel.entityWrapper.entity.statelessType == StatelessEntityType.CALL_SERVICE) {
|
} else {
|
||||||
textStyle = textStyle.apply(color: Colors.blue);
|
tStyle = Theme.of(context).textTheme.body1;
|
||||||
}
|
}
|
||||||
if (this.bold) {
|
/*if (this.bold) {
|
||||||
textStyle = textStyle.apply(fontWeightDelta: 100);
|
textStyle = textStyle.apply(fontWeightDelta: 100);
|
||||||
}
|
}*/
|
||||||
while (state.contains(" ")){
|
while (state.contains(" ")){
|
||||||
state = state.replaceAll(" ", " ");
|
state = state.replaceAll(" ", " ");
|
||||||
}
|
}
|
||||||
@ -43,7 +46,7 @@ class SimpleEntityState extends StatelessWidget {
|
|||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: textStyle
|
style: tStyle
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
|
@ -62,8 +62,7 @@ class _SliderControlsWidgetState extends State<SliderControlsWidget> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"$_newValue",
|
"$_newValue",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.display1.copyWith(
|
||||||
fontSize: Sizes.largeFontSize,
|
|
||||||
color: Colors.blue
|
color: Colors.blue
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -40,10 +40,7 @@ class UniversalSlider extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Text(
|
Text("$title"),
|
||||||
"$title",
|
|
||||||
style: TextStyle(fontSize: Sizes.stateFontSize),
|
|
||||||
),
|
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -10,7 +10,7 @@ class VacuumControls extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
_buildStatusAndBattery(entity),
|
_buildStatusAndBattery(entity, context),
|
||||||
_buildCommands(entity),
|
_buildCommands(entity),
|
||||||
_buildFanSpeed(entity),
|
_buildFanSpeed(entity),
|
||||||
_buildAdditionalInfo(entity)
|
_buildAdditionalInfo(entity)
|
||||||
@ -19,12 +19,12 @@ class VacuumControls extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStatusAndBattery(VacuumEntity entity) {
|
Widget _buildStatusAndBattery(VacuumEntity entity, BuildContext context) {
|
||||||
List<Widget> result = [];
|
List<Widget> result = [];
|
||||||
if (entity.supportStatus) {
|
if (entity.supportStatus) {
|
||||||
result.addAll(
|
result.addAll(
|
||||||
<Widget>[
|
<Widget>[
|
||||||
Text("Status:", style: TextStyle(fontSize: Sizes.stateFontSize),),
|
Text("Status:"),
|
||||||
Container(width: 6,),
|
Container(width: 6,),
|
||||||
Expanded(
|
Expanded(
|
||||||
//flex: 1,
|
//flex: 1,
|
||||||
@ -33,10 +33,7 @@ class VacuumControls extends StatelessWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body2,
|
||||||
fontSize: Sizes.stateFontSize,
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
@ -48,7 +45,7 @@ class VacuumControls extends StatelessWidget {
|
|||||||
result.addAll(<Widget>[
|
result.addAll(<Widget>[
|
||||||
Icon(MaterialDesignIcons.getIconDataFromIconName(iconName)),
|
Icon(MaterialDesignIcons.getIconDataFromIconName(iconName)),
|
||||||
Container(width: 6,),
|
Container(width: 6,),
|
||||||
Text("$batteryLevel %", style: TextStyle(fontSize: Sizes.stateFontSize))
|
Text("$batteryLevel %")
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -172,7 +169,7 @@ class VacuumControls extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Vacuum cleaner commands:", style: TextStyle(fontSize: Sizes.stateFontSize)),
|
Text("Vacuum cleaner commands:"),
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
@ -27,10 +27,7 @@ class VacuumStateButton extends StatelessWidget {
|
|||||||
text: "RETURN TO DOCK"
|
text: "RETURN TO DOCK"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = Text(entity.state.toUpperCase(), style: TextStyle(
|
result = Text(entity.state.toUpperCase(), style: Theme.of(context).textTheme.subhead);
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.grey
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.only(right: 15),
|
padding: EdgeInsets.only(right: 15),
|
||||||
|
@ -191,11 +191,15 @@ class HomeAssistant {
|
|||||||
return Future.value();
|
return Future.value();
|
||||||
} else {
|
} else {
|
||||||
Completer completer = Completer();
|
Completer completer = Completer();
|
||||||
|
var additionalData;
|
||||||
|
if (_lovelaceDashbordUrl != HomeAssistant.DEFAULT_DASHBOARD) {
|
||||||
|
additionalData = {
|
||||||
|
'url_path': _lovelaceDashbordUrl
|
||||||
|
};
|
||||||
|
}
|
||||||
ConnectionManager().sendSocketMessage(
|
ConnectionManager().sendSocketMessage(
|
||||||
type: 'lovelace/config',
|
type: 'lovelace/config',
|
||||||
additionalData: {
|
additionalData: additionalData
|
||||||
'url_path': _lovelaceDashbordUrl == HomeAssistant.DEFAULT_DASHBOARD ? null : _lovelaceDashbordUrl
|
|
||||||
}
|
|
||||||
).then((data) {
|
).then((data) {
|
||||||
_rawLovelaceData = data;
|
_rawLovelaceData = data;
|
||||||
completer.complete();
|
completer.complete();
|
||||||
@ -327,7 +331,7 @@ class HomeAssistant {
|
|||||||
void _createUI() {
|
void _createUI() {
|
||||||
Logger.d("Creating Lovelace UI");
|
Logger.d("Creating Lovelace UI");
|
||||||
ui = HomeAssistantUI(rawLovelaceConfig: _rawLovelaceData);
|
ui = HomeAssistantUI(rawLovelaceConfig: _rawLovelaceData);
|
||||||
if (isServiceExist('zha_map')) {
|
/*if (isServiceExist('zha_map')) {
|
||||||
panels.add(
|
panels.add(
|
||||||
Panel(
|
Panel(
|
||||||
id: 'haclient_zha',
|
id: 'haclient_zha',
|
||||||
@ -337,7 +341,7 @@ class HomeAssistant {
|
|||||||
icon: 'mdi:zigbee'
|
icon: 'mdi:zigbee'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:math';
|
|
||||||
import 'dart:io';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -10,7 +8,6 @@ import 'package:web_socket_channel/io.dart';
|
|||||||
import 'package:event_bus/event_bus.dart';
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart' as urlLauncher;
|
import 'package:url_launcher/url_launcher.dart' as urlLauncher;
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:date_format/date_format.dart';
|
import 'package:date_format/date_format.dart';
|
||||||
@ -33,9 +30,11 @@ import 'package:battery/battery.dart';
|
|||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview;
|
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview;
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:syncfusion_flutter_core/core.dart';
|
||||||
|
import 'package:syncfusion_flutter_gauges/gauges.dart';
|
||||||
|
|
||||||
import 'utils/logger.dart';
|
import 'utils/logger.dart';
|
||||||
|
import '.secrets.dart';
|
||||||
|
|
||||||
part 'const.dart';
|
part 'const.dart';
|
||||||
part 'utils/launcher.dart';
|
part 'utils/launcher.dart';
|
||||||
@ -76,7 +75,6 @@ part 'entities/universal_slider.widget.dart';
|
|||||||
part 'entities/flat_service_button.widget.dart';
|
part 'entities/flat_service_button.widget.dart';
|
||||||
part 'entities/light/widgets/light_color_picker.dart';
|
part 'entities/light/widgets/light_color_picker.dart';
|
||||||
part 'entities/camera/widgets/camera_stream_view.dart';
|
part 'entities/camera/widgets/camera_stream_view.dart';
|
||||||
part 'entities/entity_colors.class.dart';
|
|
||||||
part 'plugins/history_chart/entity_history.dart';
|
part 'plugins/history_chart/entity_history.dart';
|
||||||
part 'plugins/history_chart/simple_state_history_chart.dart';
|
part 'plugins/history_chart/simple_state_history_chart.dart';
|
||||||
part 'plugins/history_chart/numeric_state_history_chart.dart';
|
part 'plugins/history_chart/numeric_state_history_chart.dart';
|
||||||
@ -124,6 +122,7 @@ part 'managers/mobile_app_integration_manager.class.dart';
|
|||||||
part 'managers/connection_manager.class.dart';
|
part 'managers/connection_manager.class.dart';
|
||||||
part 'managers/device_info_manager.class.dart';
|
part 'managers/device_info_manager.class.dart';
|
||||||
part 'managers/startup_user_messages_manager.class.dart';
|
part 'managers/startup_user_messages_manager.class.dart';
|
||||||
|
part 'managers/theme_manager.dart';
|
||||||
part 'ui.dart';
|
part 'ui.dart';
|
||||||
part 'view.class.dart';
|
part 'view.class.dart';
|
||||||
part 'cards/card.class.dart';
|
part 'cards/card.class.dart';
|
||||||
@ -148,7 +147,7 @@ EventBus eventBus = new EventBus();
|
|||||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||||
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
|
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
|
||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
const appVersionNumber = "0.8.0";
|
const appVersionNumber = "0.8.2";
|
||||||
const appVersionAdd = "";
|
const appVersionAdd = "";
|
||||||
const appVersion = "$appVersionNumber$appVersionAdd";
|
const appVersion = "$appVersionNumber$appVersionAdd";
|
||||||
|
|
||||||
@ -164,6 +163,7 @@ Future<void> _reportError(dynamic error, dynamic stackTrace) async {
|
|||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
Crashlytics.instance.enableInDevMode = false;
|
Crashlytics.instance.enableInDevMode = false;
|
||||||
|
SyncfusionLicense.registerLicense(secrets['syncfusion_license_key']);
|
||||||
|
|
||||||
FlutterError.onError = (FlutterErrorDetails details) {
|
FlutterError.onError = (FlutterErrorDetails details) {
|
||||||
Logger.e(" Caut Flutter runtime error: ${details.exception}");
|
Logger.e(" Caut Flutter runtime error: ${details.exception}");
|
||||||
@ -226,9 +226,9 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new MaterialApp(
|
return new MaterialApp(
|
||||||
title: appName,
|
title: appName,
|
||||||
theme: new ThemeData(
|
theme: HAClientTheme().lightTheme,
|
||||||
primarySwatch: Colors.blue,
|
darkTheme: HAClientTheme().darkTheme,
|
||||||
),
|
debugShowCheckedModeBanner: false,
|
||||||
initialRoute: "/",
|
initialRoute: "/",
|
||||||
routes: {
|
routes: {
|
||||||
"/": (context) => MainPage(title: 'HA Client'),
|
"/": (context) => MainPage(title: 'HA Client'),
|
||||||
@ -262,7 +262,9 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
title: new Text("Login with HA"),
|
title: new Text("Login with HA"),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text("Manual", style: TextStyle(color: Colors.white)),
|
child: Text("Manual", style: Theme.of(context).textTheme.button.copyWith(
|
||||||
|
decoration: TextDecoration.underline
|
||||||
|
)),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
eventBus.fire(ShowPageEvent(path: "/connection-settings", goBackFirst: true));
|
eventBus.fire(ShowPageEvent(path: "/connection-settings", goBackFirst: true));
|
||||||
},
|
},
|
||||||
|
@ -223,16 +223,6 @@ class ConnectionManager {
|
|||||||
onLovelaceUpdatedCallback();
|
onLovelaceUpdatedCallback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if ((data["event"] != null) && (data["event"]["event_type"] == "state_changed")) {
|
|
||||||
Logger.d("[Received] <== ${data['type']}.${data["event"]["event_type"]}: ${data["event"]["data"]["entity_id"]}");
|
|
||||||
onStateChangeCallback(data["event"]["data"]);
|
|
||||||
} else if (data["event"] != null) {
|
|
||||||
Logger.w("Unhandled event type: ${data["event"]["event_type"]}");
|
|
||||||
} else {
|
|
||||||
Logger.e("Event is null: $data");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Logger.d("[Received unhandled] <== ${data.toString()}");
|
Logger.d("[Received unhandled] <== ${data.toString()}");
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class StartupUserMessagesManager {
|
|||||||
bool _supportAppDevelopmentMessageShown;
|
bool _supportAppDevelopmentMessageShown;
|
||||||
bool _whatsNewMessageShown;
|
bool _whatsNewMessageShown;
|
||||||
static final _supportAppDevelopmentMessageKey = "user-message-shown-support-development_3";
|
static final _supportAppDevelopmentMessageKey = "user-message-shown-support-development_3";
|
||||||
static final _whatsNewMessageKey = "user-message-shown-whats-new-884";
|
static final _whatsNewMessageKey = "user-message-shown-whats-new-887";
|
||||||
|
|
||||||
void checkMessagesToShow() async {
|
void checkMessagesToShow() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
223
lib/managers/theme_manager.dart
Normal file
223
lib/managers/theme_manager.dart
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class HAClientTheme {
|
||||||
|
|
||||||
|
static const TextTheme textTheme = TextTheme(
|
||||||
|
display1: TextStyle(fontSize: 34, fontWeight: FontWeight.normal),
|
||||||
|
display2: TextStyle(fontSize: 34, fontWeight: FontWeight.normal),
|
||||||
|
headline: TextStyle(fontSize: 24, fontWeight: FontWeight.normal),
|
||||||
|
title: TextStyle(fontSize: 20, fontWeight: FontWeight.w700),
|
||||||
|
subhead: TextStyle(fontSize: 16, fontWeight: FontWeight.normal),
|
||||||
|
body1: TextStyle(fontSize: 15, fontWeight: FontWeight.normal),
|
||||||
|
body2: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||||
|
subtitle: TextStyle(fontSize: 15, fontWeight: FontWeight.w500),
|
||||||
|
caption: TextStyle(fontSize: 12, fontWeight: FontWeight.normal),
|
||||||
|
overline: TextStyle(
|
||||||
|
fontSize: 10,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
letterSpacing: 1,
|
||||||
|
),
|
||||||
|
button: TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
|
||||||
|
);
|
||||||
|
|
||||||
|
static const offEntityStates = [
|
||||||
|
EntityState.off,
|
||||||
|
EntityState.closed,
|
||||||
|
"below_horizon",
|
||||||
|
"default",
|
||||||
|
EntityState.idle,
|
||||||
|
EntityState.alarm_disarmed,
|
||||||
|
];
|
||||||
|
|
||||||
|
static const onEntityStates = [
|
||||||
|
EntityState.on,
|
||||||
|
"auto",
|
||||||
|
EntityState.active,
|
||||||
|
EntityState.playing,
|
||||||
|
EntityState.paused,
|
||||||
|
"above_horizon",
|
||||||
|
EntityState.home,
|
||||||
|
EntityState.open,
|
||||||
|
EntityState.cleaning,
|
||||||
|
EntityState.returning,
|
||||||
|
"cool",
|
||||||
|
EntityState.alarm_arming,
|
||||||
|
EntityState.alarm_disarming,
|
||||||
|
EntityState.alarm_pending,
|
||||||
|
];
|
||||||
|
|
||||||
|
static const disabledEntityStates = [
|
||||||
|
EntityState.unavailable,
|
||||||
|
EntityState.unknown,
|
||||||
|
];
|
||||||
|
|
||||||
|
static const alarmEntityStates = [
|
||||||
|
EntityState.alarm_armed_away,
|
||||||
|
EntityState.alarm_armed_custom_bypass,
|
||||||
|
EntityState.alarm_armed_home,
|
||||||
|
EntityState.alarm_armed_night,
|
||||||
|
EntityState.alarm_triggered,
|
||||||
|
"heat",
|
||||||
|
];
|
||||||
|
|
||||||
|
static const defaultStateColor = Color.fromRGBO(68, 115, 158, 1.0);
|
||||||
|
|
||||||
|
static const badgeColors = {
|
||||||
|
"default": Color.fromRGBO(223, 76, 30, 1.0),
|
||||||
|
"binary_sensor": Color.fromRGBO(3, 155, 229, 1.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
static final HAClientTheme _instance = HAClientTheme
|
||||||
|
._internal();
|
||||||
|
|
||||||
|
factory HAClientTheme() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
HAClientTheme._internal();
|
||||||
|
|
||||||
|
final ThemeData lightTheme = ThemeData.from(
|
||||||
|
colorScheme: ColorScheme(
|
||||||
|
primary: Color.fromRGBO(112, 154, 193, 1),
|
||||||
|
primaryVariant: Color.fromRGBO(68, 115, 158, 1),
|
||||||
|
secondary: Color.fromRGBO(253, 216, 53, 1),
|
||||||
|
secondaryVariant: Color.fromRGBO(222, 181, 2, 1),
|
||||||
|
background: Color.fromRGBO(250, 250, 250, 1),
|
||||||
|
surface: Colors.white,
|
||||||
|
error: Colors.red,
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSecondary: Colors.black87,
|
||||||
|
onBackground: Colors.black87,
|
||||||
|
onSurface: Colors.black87,
|
||||||
|
onError: Colors.white,
|
||||||
|
brightness: Brightness.light
|
||||||
|
),
|
||||||
|
textTheme: ThemeData.light().textTheme.copyWith(
|
||||||
|
display1: textTheme.display1.copyWith(color: Colors.black54),
|
||||||
|
display2: textTheme.display2.copyWith(color: Colors.redAccent),
|
||||||
|
headline: textTheme.headline.copyWith(color: Colors.black87),
|
||||||
|
title: textTheme.title.copyWith(color: Colors.black87),
|
||||||
|
subhead: textTheme.subhead.copyWith(color: Colors.black54),
|
||||||
|
body1: textTheme.body1.copyWith(color: Colors.black87),
|
||||||
|
body2: textTheme.body2.copyWith(color: Colors.black87),
|
||||||
|
subtitle: textTheme.subtitle.copyWith(color: Colors.black45),
|
||||||
|
caption: textTheme.caption.copyWith(color: Colors.black45),
|
||||||
|
overline: textTheme.overline.copyWith(color: Colors.black26),
|
||||||
|
button: textTheme.button.copyWith(color: Colors.white),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
final ThemeData darkTheme = ThemeData.from(
|
||||||
|
colorScheme: ColorScheme(
|
||||||
|
primary: Color.fromRGBO(112, 154, 193, 1),
|
||||||
|
primaryVariant: Color.fromRGBO(68, 115, 158, 1),
|
||||||
|
secondary: Color.fromRGBO(253, 216, 53, 1),
|
||||||
|
secondaryVariant: Color.fromRGBO(222, 181, 2, 1),
|
||||||
|
background: Color.fromRGBO(47, 49, 54, 1),
|
||||||
|
surface: Color.fromRGBO(54, 57, 63, 1),
|
||||||
|
error: Color.fromRGBO(183, 109, 109, 1),
|
||||||
|
onPrimary: Colors.white,
|
||||||
|
onSecondary: Colors.black87,
|
||||||
|
onBackground: Color.fromRGBO(220, 221, 222, 1),
|
||||||
|
onSurface: Colors.white,
|
||||||
|
onError: Colors.white,
|
||||||
|
brightness: Brightness.dark
|
||||||
|
),
|
||||||
|
textTheme: textTheme
|
||||||
|
);
|
||||||
|
|
||||||
|
Color getOnStateColor(BuildContext context) {
|
||||||
|
return Theme.of(context).colorScheme.secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getOffStateColor(BuildContext context) {
|
||||||
|
return Theme.of(context).colorScheme.primaryVariant;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getDisabledStateColor(BuildContext context) {
|
||||||
|
return Theme.of(context).disabledColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getAlertStateColor(BuildContext context) {
|
||||||
|
return Theme.of(context).colorScheme.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getColorByEntityState(String state, BuildContext context) {
|
||||||
|
if (onEntityStates.contains(state)) {
|
||||||
|
return getOnStateColor(context);
|
||||||
|
} else if (disabledEntityStates.contains(state)) {
|
||||||
|
return getDisabledStateColor(context);
|
||||||
|
} else if (alarmEntityStates.contains(state)) {
|
||||||
|
return getAlertStateColor(context);
|
||||||
|
} else {
|
||||||
|
return getOffStateColor(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getGreenGaugeColor() {
|
||||||
|
return Colors.green;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getYellowGaugeColor() {
|
||||||
|
return Colors.yellow;
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getRedGaugeColor() {
|
||||||
|
return Colors.red;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle getLinkTextStyle(BuildContext context) {
|
||||||
|
ThemeData theme = Theme.of(context);
|
||||||
|
return theme.textTheme.body1.copyWith(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration: TextDecoration.underline
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextStyle getActionTextStyle(BuildContext context) {
|
||||||
|
ThemeData theme = Theme.of(context);
|
||||||
|
return theme.textTheme.subhead.copyWith(
|
||||||
|
color: Colors.blue
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getBadgeColor(String entityDomain) {
|
||||||
|
return badgeColors[entityDomain] ??
|
||||||
|
badgeColors["default"];
|
||||||
|
}
|
||||||
|
|
||||||
|
Color getOnBadgeTextColor() {
|
||||||
|
return Colors.white;
|
||||||
|
}
|
||||||
|
|
||||||
|
charts.Color chartHistoryStateColor(String state, int id, BuildContext context) {
|
||||||
|
Color c = getColorByEntityState(state, context);
|
||||||
|
if (c != null) {
|
||||||
|
return charts.Color(
|
||||||
|
r: c.red,
|
||||||
|
g: c.green,
|
||||||
|
b: c.blue,
|
||||||
|
a: c.alpha
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
double r = id.toDouble() % 10;
|
||||||
|
return charts.MaterialPalette.getOrderedPalettes(10)[r.round()].shadeDefault;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Color historyStateColor(String state, int id, BuildContext context) {
|
||||||
|
Color c = getColorByEntityState(state, context);
|
||||||
|
if (c != null) {
|
||||||
|
return c;
|
||||||
|
} else {
|
||||||
|
if (id > -1) {
|
||||||
|
double r = id.toDouble() % 10;
|
||||||
|
charts.Color c1 = charts.MaterialPalette.getOrderedPalettes(10)[r.round()].shadeDefault;
|
||||||
|
return Color.fromARGB(c1.a, c1.r, c1.g, c1.b);
|
||||||
|
} else {
|
||||||
|
return getOnStateColor(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -115,15 +115,14 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Location tracking", style: TextStyle(fontSize: Sizes.largeFontSize-2)),
|
Text("Location tracking", style: Theme.of(context).textTheme.title),
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"),
|
onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"),
|
||||||
child: Text(
|
child: Text(
|
||||||
"Please read documentation!",
|
"Please read documentation!",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.subhead.copyWith(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
fontSize: 16,
|
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -153,21 +152,24 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
//Expanded(child: Container(),),
|
//Expanded(child: Container(),),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
child: Text("-", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
child: Text("-", style: Theme.of(context).textTheme.title),
|
||||||
onPressed: () => decLocationInterval(),
|
onPressed: () => decLocationInterval(),
|
||||||
),
|
),
|
||||||
Text("$_locationInterval", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
Text("$_locationInterval", style: Theme.of(context).textTheme.title),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
child: Text("+", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
child: Text("+", style: Theme.of(context).textTheme.title),
|
||||||
onPressed: () => incLocationInterval(),
|
onPressed: () => incLocationInterval(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Divider(),
|
Divider(),
|
||||||
Text("Integration status", style: TextStyle(fontSize: Sizes.largeFontSize-2)),
|
Text("Integration status", style: Theme.of(context).textTheme.title),
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Text("${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}"),
|
Text(
|
||||||
|
"${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}",
|
||||||
|
style: Theme.of(context).textTheme.subtitle,
|
||||||
|
),
|
||||||
Container(height: 6.0,),
|
Container(height: 6.0,),
|
||||||
Text("Here you can manually check if HA Client integration with your Home Assistant works fine. As mobileApp integration in Home Assistant is still in development, this is not 100% correct check."),
|
Text("Here you can manually check if HA Client integration with your Home Assistant works fine. As mobileApp integration in Home Assistant is still in development, this is not 100% correct check."),
|
||||||
//Divider(),
|
//Divider(),
|
||||||
@ -177,13 +179,13 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
RaisedButton(
|
RaisedButton(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: () => updateRegistration(),
|
onPressed: () => updateRegistration(),
|
||||||
child: Text("Check integration", style: TextStyle(color: Colors.white))
|
child: Text("Check integration", style: Theme.of(context).textTheme.button)
|
||||||
),
|
),
|
||||||
Container(width: 10.0,),
|
Container(width: 10.0,),
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
color: Colors.redAccent,
|
color: Colors.redAccent,
|
||||||
onPressed: () => resetRegistration(),
|
onPressed: () => resetRegistration(),
|
||||||
child: Text("Reset integration", style: TextStyle(color: Colors.white))
|
child: Text("Reset integration", style: Theme.of(context).textTheme.button)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -347,11 +347,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
accountName: Text(HomeAssistant().userName),
|
accountName: Text(HomeAssistant().userName),
|
||||||
accountEmail: Text(HomeAssistant().locationName ?? ""),
|
accountEmail: Text(HomeAssistant().locationName ?? ""),
|
||||||
currentAccountPicture: CircleAvatar(
|
currentAccountPicture: CircleAvatar(
|
||||||
|
backgroundColor: Theme.of(context).backgroundColor,
|
||||||
child: Text(
|
child: Text(
|
||||||
HomeAssistant().userAvatarText,
|
HomeAssistant().userAvatarText,
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.display1
|
||||||
fontSize: 32.0
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -360,21 +359,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
HomeAssistant().panels.forEach((Panel panel) {
|
HomeAssistant().panels.forEach((Panel panel) {
|
||||||
if (!panel.isHidden) {
|
if (!panel.isHidden) {
|
||||||
menuItems.add(
|
menuItems.add(
|
||||||
new ListTile(
|
panel.getMenuItemWidget(context)
|
||||||
leading: Icon(MaterialDesignIcons.getIconDataFromIconName(panel.icon)),
|
|
||||||
title: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text("${panel.title}"),
|
|
||||||
Container(width: 4.0,),
|
|
||||||
panel.isWebView ? Text("webview", style: TextStyle(fontSize: 8.0, color: Colors.black45),) : Container(width: 1.0,)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
panel.handleOpen(context);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -435,11 +420,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
new ListTile(
|
||||||
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:forum")),
|
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:discord")),
|
||||||
title: Text("Contacts/Discussion"),
|
title: Text("Contacts/Discussion"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Launcher.launchURL("https://spectrum.chat/ha-client");
|
Launcher.launchURL("https://discord.gg/nd6FZQ");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
new ListTile(
|
||||||
@ -458,9 +443,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"ha-client.app",
|
"ha-client.app",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -474,9 +459,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Terms and Conditions",
|
"Terms and Conditions",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -490,9 +475,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Privacy Policy",
|
"Privacy Policy",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -519,13 +504,13 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
bool _showBottomBar = false;
|
bool _showBottomBar = false;
|
||||||
String _bottomBarText;
|
String _bottomBarText;
|
||||||
bool _bottomBarProgress;
|
bool _bottomBarProgress;
|
||||||
Color _bottomBarColor;
|
bool _bottomBarErrorColor;
|
||||||
Timer _bottomBarTimer;
|
Timer _bottomBarTimer;
|
||||||
|
|
||||||
void _showInfoBottomBar({String message, bool progress: false, Duration duration}) {
|
void _showInfoBottomBar({String message, bool progress: false, Duration duration}) {
|
||||||
_bottomBarTimer?.cancel();
|
_bottomBarTimer?.cancel();
|
||||||
_bottomBarAction = Container(height: 0.0, width: 0.0,);
|
_bottomBarAction = Container(height: 0.0, width: 0.0,);
|
||||||
_bottomBarColor = Colors.grey.shade50;
|
_bottomBarErrorColor = false;
|
||||||
setState(() {
|
setState(() {
|
||||||
_bottomBarText = message;
|
_bottomBarText = message;
|
||||||
_bottomBarProgress = progress;
|
_bottomBarProgress = progress;
|
||||||
@ -539,11 +524,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showErrorBottomBar(HAError error) {
|
void _showErrorBottomBar(HAError error) {
|
||||||
TextStyle textStyle = TextStyle(
|
TextStyle textStyle = Theme.of(context).textTheme.button.copyWith(
|
||||||
color: Colors.blue,
|
decoration: TextDecoration.underline
|
||||||
fontSize: Sizes.nameFontSize
|
|
||||||
);
|
);
|
||||||
_bottomBarColor = Colors.red.shade100;
|
_bottomBarErrorColor = true;
|
||||||
List<Widget> actions = [];
|
List<Widget> actions = [];
|
||||||
error.actions.forEach((HAErrorAction action) {
|
error.actions.forEach((HAErrorAction action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -649,9 +633,9 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
activePlayers.map((entity) => PopupMenuItem<String>(
|
activePlayers.map((entity) => PopupMenuItem<String>(
|
||||||
child: Text(
|
child: Text(
|
||||||
"${entity.displayName}",
|
"${entity.displayName}",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
color: EntityColor.stateColor(entity.state)
|
color: HAClientTheme().getColorByEntityState(entity.state, context)
|
||||||
),
|
)
|
||||||
),
|
),
|
||||||
value: "${entity.entityId}",
|
value: "${entity.entityId}",
|
||||||
)).toList()
|
)).toList()
|
||||||
@ -680,7 +664,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text("$playersCount", style: TextStyle(fontSize: 12)),
|
child: Text(
|
||||||
|
"$playersCount",
|
||||||
|
style: Theme.of(context).textTheme.caption.copyWith(
|
||||||
|
color: Colors.white
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -698,7 +687,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
FlatButton(
|
FlatButton(
|
||||||
child: Text("Login with Home Assistant", style: TextStyle(fontSize: 16.0, color: Colors.white)),
|
child: Text("Login with Home Assistant", style: Theme.of(context).textTheme.button),
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: () => _fullLoad(),
|
onPressed: () => _fullLoad(),
|
||||||
)
|
)
|
||||||
@ -833,16 +822,16 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
bottomBarChildren.add(
|
bottomBarChildren.add(
|
||||||
CollectionScaleTransition(
|
CollectionScaleTransition(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.on),),
|
Icon(Icons.stop, size: 10.0, color: HAClientTheme().getOnStateColor(context),),
|
||||||
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.unavailable),),
|
Icon(Icons.stop, size: 10.0, color: HAClientTheme().getDisabledStateColor(context),),
|
||||||
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.off),),
|
Icon(Icons.stop, size: 10.0, color: HAClientTheme().getOffStateColor(context),),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (bottomBarChildren.isNotEmpty) {
|
if (bottomBarChildren.isNotEmpty) {
|
||||||
bottomBar = Container(
|
bottomBar = Container(
|
||||||
color: _bottomBarColor,
|
color: _bottomBarErrorColor ? Theme.of(context).errorColor : Theme.of(context).primaryColorLight,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
@ -135,7 +135,9 @@ class _PlayMediaPageState extends State<PlayMediaPage> {
|
|||||||
if (_validationMessage.isNotEmpty) {
|
if (_validationMessage.isNotEmpty) {
|
||||||
children.add(Text(
|
children.add(Text(
|
||||||
"$_validationMessage",
|
"$_validationMessage",
|
||||||
style: TextStyle(color: Colors.red)
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
color: Theme.of(context).errorColor
|
||||||
|
)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
children.addAll(<Widget>[
|
children.addAll(<Widget>[
|
||||||
@ -193,7 +195,7 @@ class _PlayMediaPageState extends State<PlayMediaPage> {
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"How?",
|
"How?",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
decoration: TextDecoration.underline
|
decoration: TextDecoration.underline
|
||||||
),
|
),
|
||||||
|
@ -138,10 +138,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"Connection settings",
|
"Connection settings",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.headline,
|
||||||
color: Colors.black45,
|
|
||||||
fontSize: 20.0
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
new Row(
|
new Row(
|
||||||
children: [
|
children: [
|
||||||
@ -176,16 +173,13 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
),
|
),
|
||||||
new Text(
|
new Text(
|
||||||
"Try ports 80 and 443 if default is not working and you don't know why.",
|
"Try ports 80 and 443 if default is not working and you don't know why.",
|
||||||
style: TextStyle(color: Colors.grey),
|
style: Theme.of(context).textTheme.caption,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20.0),
|
padding: EdgeInsets.only(top: 20.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
"UI",
|
"UI",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.headline,
|
||||||
color: Colors.black45,
|
|
||||||
fontSize: 20.0
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
new Row(
|
new Row(
|
||||||
@ -203,15 +197,14 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"Authentication settings",
|
"Authentication settings",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.headline,
|
||||||
color: Colors.black45,
|
|
||||||
fontSize: 20.0
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Container(height: 10.0,),
|
Container(height: 10.0,),
|
||||||
Text(
|
Text(
|
||||||
"You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.",
|
"You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.",
|
||||||
style: TextStyle(color: Colors.redAccent),
|
style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
color: Colors.redAccent
|
||||||
|
),
|
||||||
),
|
),
|
||||||
new TextField(
|
new TextField(
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
|
@ -24,7 +24,7 @@ class _WhatsNewPageState extends State<WhatsNewPage> {
|
|||||||
error = "";
|
error = "";
|
||||||
});
|
});
|
||||||
http.Response response;
|
http.Response response;
|
||||||
response = await http.get("http://ha-client.app/service/whats_new_0.8.md");
|
response = await http.get("http://ha-client.app/service/whats_new_0.8.2.md");
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
setState(() {
|
setState(() {
|
||||||
data = response.body;
|
data = response.body;
|
||||||
|
@ -10,8 +10,7 @@ class LastUpdatedWidget extends StatelessWidget {
|
|||||||
child: Text(
|
child: Text(
|
||||||
'${entityModel.entityWrapper.entity.lastUpdated}',
|
'${entityModel.entityWrapper.entity.lastUpdated}',
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.caption
|
||||||
fontSize: Sizes.smallFontSize, color: Colors.black26),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ class PageLoadingError extends StatelessWidget {
|
|||||||
size: 48.0
|
size: 48.0
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
Text(this.errorText, style: TextStyle(color: Colors.black45))
|
Text(this.errorText, style: Theme.of(context).textTheme.subtitle)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -14,7 +14,7 @@ class PageLoadingIndicator extends StatelessWidget {
|
|||||||
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
padding: EdgeInsets.only(top: 40.0, bottom: 20.0),
|
||||||
child: CircularProgressIndicator()
|
child: CircularProgressIndicator()
|
||||||
),
|
),
|
||||||
Text("Loading...", style: TextStyle(color: Colors.black45))
|
Text("Loading...", style: Theme.of(context).textTheme.subtitle)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -40,10 +40,7 @@ class ProductPurchase extends StatelessWidget {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
"${product.title}",
|
"${product.title}",
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.body2,
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 16.0
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Text(
|
Text(
|
||||||
@ -53,7 +50,9 @@ class ProductPurchase extends StatelessWidget {
|
|||||||
softWrap: true,
|
softWrap: true,
|
||||||
),
|
),
|
||||||
Container(height: Sizes.rowPadding,),
|
Container(height: Sizes.rowPadding,),
|
||||||
Text("${product.price} $period", style: TextStyle(color: priceColor)),
|
Text("${product.price} $period", style: Theme.of(context).textTheme.body1.copyWith(
|
||||||
|
color: priceColor
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
@ -61,7 +60,7 @@ class ProductPurchase extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: RaisedButton(
|
child: RaisedButton(
|
||||||
child: Text(this.purchased ? buttonTextInactive : buttonText, style: TextStyle(color: Colors.white)),
|
child: Text(this.purchased ? buttonTextInactive : buttonText, style: Theme.of(context).textTheme.button),
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
onPressed: this.purchased ? null : () => this.onBuy(this.product),
|
onPressed: this.purchased ? null : () => this.onBuy(this.product),
|
||||||
),
|
),
|
||||||
|
@ -56,7 +56,7 @@ class Panel {
|
|||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("${this.title}"),
|
Text("${this.title}"),
|
||||||
Container(width: 4.0,),
|
Container(width: 4.0,),
|
||||||
isWebView ? Text("webview", style: TextStyle(fontSize: 8.0, color: Colors.black45),) : Container(width: 1.0,)
|
isWebView ? Text("webview", style: Theme.of(context).textTheme.overline) : Container(width: 1.0,)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -16,7 +16,7 @@ class LinkToWebConfig extends StatelessWidget {
|
|||||||
title: Text("${this.name}",
|
title: Text("${this.name}",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
|
style: Theme.of(context).textTheme.headline),
|
||||||
subtitle: Text("Tap to open web version"),
|
subtitle: Text("Tap to open web version"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Launcher.launchAuthenticatedWebView(context: context, url: this.url, title: this.name);
|
Launcher.launchAuthenticatedWebView(context: context, url: this.url, title: this.name);
|
||||||
|
@ -156,7 +156,8 @@ class _CombinedHistoryChartWidgetState extends State<CombinedHistoryChartWidget>
|
|||||||
result.add(
|
result.add(
|
||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: "value",
|
id: "value",
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor("_", historyMoment.colorId),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor("_", historyMoment.colorId, context),
|
||||||
radiusPxFn: (EntityHistoryMoment historyMoment, __) {
|
radiusPxFn: (EntityHistoryMoment historyMoment, __) {
|
||||||
if (historyMoment.hiddenDot) {
|
if (historyMoment.hiddenDot) {
|
||||||
return 0.0;
|
return 0.0;
|
||||||
@ -179,7 +180,8 @@ class _CombinedHistoryChartWidgetState extends State<CombinedHistoryChartWidget>
|
|||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: 'state',
|
id: 'state',
|
||||||
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 4.0,
|
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 4.0,
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor(historyMoment.state, historyMoment.colorId, context),
|
||||||
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
||||||
domainLowerBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
domainLowerBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
||||||
domainUpperBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
|
domainUpperBoundFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
|
||||||
|
@ -28,7 +28,7 @@ class HistoryControlWidget extends StatelessWidget {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(right: 10.0),
|
padding: EdgeInsets.only(right: 10.0),
|
||||||
child: _buildStates(),
|
child: _buildStates(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
_buildTime(),
|
_buildTime(),
|
||||||
@ -46,18 +46,16 @@ class HistoryControlWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildStates() {
|
Widget _buildStates(BuildContext context) {
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
for (int i = 0; i < selectedStates.length; i++) {
|
for (int i = 0; i < selectedStates.length; i++) {
|
||||||
children.add(
|
children.add(
|
||||||
Text(
|
Text(
|
||||||
"${selectedStates[i] ?? '-'}",
|
"${selectedStates[i] ?? '-'}",
|
||||||
textAlign: TextAlign.right,
|
textAlign: TextAlign.right,
|
||||||
style: TextStyle(
|
style: Theme.of(context).textTheme.title.copyWith(
|
||||||
fontWeight: FontWeight.bold,
|
color: HAClientTheme().historyStateColor(selectedStates[i], colorIndexes[i], context)
|
||||||
color: EntityColor.historyStateColor(selectedStates[i], colorIndexes[i]),
|
)
|
||||||
fontSize: 22.0
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,8 @@ class _NumericStateHistoryChartWidgetState extends State<NumericStateHistoryChar
|
|||||||
return [
|
return [
|
||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: 'State',
|
id: 'State',
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(EntityState.on, -1),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor(EntityState.on, -1, context),
|
||||||
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
||||||
measureFn: (EntityHistoryMoment historyMoment, _) => historyMoment.value ?? historyMoment.previousValue,
|
measureFn: (EntityHistoryMoment historyMoment, _) => historyMoment.value ?? historyMoment.previousValue,
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -107,7 +107,8 @@ class _SimpleStateHistoryChartWidgetState extends State<SimpleStateHistoryChartW
|
|||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: 'State',
|
id: 'State',
|
||||||
strokeWidthPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 6.0 : 3.0,
|
strokeWidthPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 6.0 : 3.0,
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor(historyMoment.state, historyMoment.colorId, context),
|
||||||
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
||||||
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
||||||
data: data,
|
data: data,
|
||||||
@ -115,7 +116,8 @@ class _SimpleStateHistoryChartWidgetState extends State<SimpleStateHistoryChartW
|
|||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: 'State',
|
id: 'State',
|
||||||
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
|
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor(historyMoment.state, historyMoment.colorId, context),
|
||||||
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.startTime,
|
||||||
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
||||||
data: data,
|
data: data,
|
||||||
@ -123,7 +125,8 @@ class _SimpleStateHistoryChartWidgetState extends State<SimpleStateHistoryChartW
|
|||||||
new charts.Series<EntityHistoryMoment, DateTime>(
|
new charts.Series<EntityHistoryMoment, DateTime>(
|
||||||
id: 'State',
|
id: 'State',
|
||||||
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
|
radiusPxFn: (EntityHistoryMoment historyMoment, __) => (historyMoment.id == _selectedId) ? 5.0 : 3.0,
|
||||||
colorFn: (EntityHistoryMoment historyMoment, __) => EntityColor.chartHistoryStateColor(historyMoment.state, historyMoment.colorId),
|
colorFn: (EntityHistoryMoment historyMoment, __) =>
|
||||||
|
HAClientTheme().chartHistoryStateColor(historyMoment.state, historyMoment.colorId, context),
|
||||||
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
|
domainFn: (EntityHistoryMoment historyMoment, _) => historyMoment.endTime ?? DateTime.now(),
|
||||||
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
measureFn: (EntityHistoryMoment historyMoment, _) => 10,
|
||||||
data: data,
|
data: data,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 0.8.0+885
|
version: 0.8.2+887
|
||||||
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -32,7 +32,8 @@ dependencies:
|
|||||||
workmanager: ^0.2.2
|
workmanager: ^0.2.2
|
||||||
battery: ^0.3.1+7
|
battery: ^0.3.1+7
|
||||||
firebase_crashlytics: ^0.1.3+3
|
firebase_crashlytics: ^0.1.3+3
|
||||||
video_player: ^0.10.7
|
syncfusion_flutter_core: ^18.1.43
|
||||||
|
syncfusion_flutter_gauges: ^18.1.43
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
@ -53,6 +54,7 @@ flutter:
|
|||||||
- images/hassio-192x192.png
|
- images/hassio-192x192.png
|
||||||
- assets/js/externalAuth.js
|
- assets/js/externalAuth.js
|
||||||
- assets/html/cameraView.html
|
- assets/html/cameraView.html
|
||||||
|
- assets/html/cameraLiveView.html
|
||||||
|
|
||||||
fonts:
|
fonts:
|
||||||
- family: "Material Design Icons"
|
- family: "Material Design Icons"
|
||||||
|
11
tool/secrets.dart
Normal file
11
tool/secrets.dart
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
Future<void> main() async {
|
||||||
|
final config = {
|
||||||
|
'syncfusion_license_key': Platform.environment['SYNCFUSION_LICENSE_KEY'],
|
||||||
|
};
|
||||||
|
|
||||||
|
final filename = 'lib/.secrets.dart';
|
||||||
|
File(filename).writeAsString('final secrets = ${json.encode(config)};');
|
||||||
|
}
|
Reference in New Issue
Block a user