Full ui structure refactoring. InheritedWidget as entity model
This commit is contained in:
parent
b5f9ecf601
commit
ffc053fbe6
@ -45,7 +45,7 @@ class HACard extends StatelessWidget {
|
||||
result.add(
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
|
||||
child: entity.buildWidget(context, EntityWidgetType.regular),
|
||||
child: entity.buildDefaultWidget(context),
|
||||
));
|
||||
});
|
||||
return result;
|
||||
|
@ -53,7 +53,7 @@ class _EntityViewPageState extends State<EntityViewPage> {
|
||||
),
|
||||
body: Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: widget.entity.buildWidget(context, EntityWidgetType.extended)
|
||||
child: widget.entity.buildEntityPageWidget(context)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _ButtonEntityWidgetState extends _EntityWidgetState {
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "turn_on", widget.entity.entityId, null));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
return FlatButton(
|
||||
onPressed: (() {
|
||||
setNewState(null);
|
||||
}),
|
||||
child: Text(
|
||||
"EXECUTE",
|
||||
textAlign: TextAlign.right,
|
||||
style:
|
||||
new TextStyle(fontSize: stateFontSize, color: Colors.blue),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,231 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _ClimateEntityWidgetState extends _EntityWidgetState {
|
||||
|
||||
List<String> _operationList = [];
|
||||
double _temperature1 = 0.0;
|
||||
String _operationMode = "";
|
||||
bool _awayMode = false;
|
||||
bool _showPending;
|
||||
bool _changedHere;
|
||||
double _temperatureStep = 0.2;
|
||||
Timer _resetTimer;
|
||||
|
||||
@override
|
||||
double widgetHeight = 38.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_operationList.clear();
|
||||
if (widget.entity.attributes["operation_list"] != null) {
|
||||
widget.entity.attributes["operation_list"].forEach((value){
|
||||
_operationList.add(value.toString());
|
||||
});
|
||||
}
|
||||
_resetVars();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _resetVars() {
|
||||
var temp1 = widget.entity.attributes['temperature'] ?? widget.entity.attributes['target_temp_low'];
|
||||
if (temp1 is int) {
|
||||
_temperature1 = temp1.toDouble();
|
||||
} else if (temp1 is double) {
|
||||
_temperature1 = temp1;
|
||||
}
|
||||
_operationMode = widget.entity.attributes['operation_mode'];
|
||||
_awayMode = widget.entity.attributes['away_mode'] == "on";
|
||||
_showPending = false;
|
||||
_changedHere = false;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildSecondRowWidget() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
super._buildSecondRowWidget(),
|
||||
_buildAdditionalControls()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _temperatureUp() {
|
||||
_temperature1 += _temperatureStep;
|
||||
_setTemperature();
|
||||
}
|
||||
|
||||
void _temperatureDown() {
|
||||
_temperature1 -= _temperatureStep;
|
||||
_setTemperature();
|
||||
}
|
||||
|
||||
void _setTemperature() {
|
||||
setState(() {
|
||||
_temperature1 = double.parse(_temperature1.toStringAsFixed(1));
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_temperature", widget.entity.entityId,{"temperature": "${_temperature1.toStringAsFixed(1)}"}));
|
||||
_resetStateTimer();
|
||||
});
|
||||
}
|
||||
|
||||
void _setOperationMode(value) {
|
||||
setState(() {
|
||||
_operationMode = value;
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_operation_mode", widget.entity.entityId,{"operation_mode": "$_operationMode"}));
|
||||
_resetStateTimer();
|
||||
});
|
||||
}
|
||||
|
||||
void _setAwayMode(value) {
|
||||
setState(() {
|
||||
_awayMode = value;
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_away_mode", widget.entity.entityId,{"away_mode": "${_awayMode ? 'on' : 'off'}"}));
|
||||
_resetStateTimer();
|
||||
});
|
||||
}
|
||||
|
||||
void _resetStateTimer() {
|
||||
if (_resetTimer!=null) {
|
||||
_resetTimer.cancel();
|
||||
}
|
||||
_resetTimer = Timer(Duration(seconds: 3), () {
|
||||
setState(() {});
|
||||
_resetVars();
|
||||
});
|
||||
}
|
||||
|
||||
_buildAdditionalControls() {
|
||||
if (_changedHere) {
|
||||
_showPending = (_temperature1 != widget.entity.attributes['temperature']);
|
||||
_changedHere = false;
|
||||
} else {
|
||||
_resetTimer?.cancel();
|
||||
_resetVars();
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(leftWidgetPadding, rowPadding, rightWidgetPadding, 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text("Target temperature for ${_operationMode != 'off' ? _operationMode : ''}", style: TextStyle(
|
||||
fontSize: stateFontSize
|
||||
)),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
"$_temperature1",
|
||||
style: TextStyle(
|
||||
fontSize: largeFontSize,
|
||||
color: _showPending ? Colors.red : Colors.black
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.keyboard_arrow_up),
|
||||
iconSize: 30.0,
|
||||
onPressed: () => _temperatureUp(),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.keyboard_arrow_down),
|
||||
iconSize: 30.0,
|
||||
onPressed: () => _temperatureDown(),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Text("Operation", style: TextStyle(
|
||||
fontSize: stateFontSize
|
||||
)),
|
||||
DropdownButton<String>(
|
||||
value: "$_operationMode",
|
||||
iconSize: 30.0,
|
||||
style: TextStyle(
|
||||
fontSize: largeFontSize,
|
||||
color: Colors.black,
|
||||
),
|
||||
items: this._operationList.map((String value) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: new Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (_) => _setOperationMode(_),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: rowPadding),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Away mode",
|
||||
style: TextStyle(
|
||||
fontSize: stateFontSize
|
||||
),
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
onChanged: (value) => _setAwayMode(value),
|
||||
value: _awayMode,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${widget.entity.state}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: stateFontSize,
|
||||
)),
|
||||
Text(
|
||||
" ${widget.entity.attributes["temperature"]}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: stateFontSize,
|
||||
))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
"Currently: ${widget.entity.attributes["current_temperature"]}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: stateFontSize,
|
||||
color: Colors.black45
|
||||
))
|
||||
],
|
||||
),
|
||||
onTap: openEntityPage,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_resetTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _DateTimeEntityWidgetState extends _EntityWidgetState {
|
||||
bool get hasDate => widget.entity.attributes["has_date"] ?? false;
|
||||
bool get hasTime => widget.entity.attributes["has_time"] ?? false;
|
||||
int get year => widget.entity.attributes["year"] ?? 1970;
|
||||
int get month => widget.entity.attributes["month"] ?? 1;
|
||||
int get day => widget.entity.attributes["day"] ?? 1;
|
||||
int get hour => widget.entity.attributes["hour"] ?? 0;
|
||||
int get minute => widget.entity.attributes["minute"] ?? 0;
|
||||
int get second => widget.entity.attributes["second"] ?? 0;
|
||||
String get formattedState => _getFormattedState();
|
||||
DateTime get dateTimeState => _getDateTimeState();
|
||||
|
||||
DateTime _getDateTimeState() {
|
||||
return DateTime(this.year, this.month, this.day, this.hour, this.minute, this.second);
|
||||
}
|
||||
|
||||
String _getFormattedState() {
|
||||
String formattedState = "";
|
||||
if (this.hasDate) {
|
||||
formattedState += formatDate(dateTimeState, [M, ' ', d, ', ', yyyy]);
|
||||
}
|
||||
if (this.hasTime) {
|
||||
formattedState += " "+formatDate(dateTimeState, [HH, ':', nn]);
|
||||
}
|
||||
return formattedState;
|
||||
}
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_datetime", widget.entity.entityId,
|
||||
newValue));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Text(
|
||||
"$formattedState",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: stateFontSize,
|
||||
)),
|
||||
onTap: () => _handleStateTap(context),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStateTap(BuildContext context) {
|
||||
if (hasDate) {
|
||||
_showDatePicker(context).then((date) {
|
||||
if (date != null) {
|
||||
if (hasTime) {
|
||||
_showTimePicker(context).then((time){
|
||||
setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}", "time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
||||
});
|
||||
} else {
|
||||
setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}"});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (hasTime) {
|
||||
_showTimePicker(context).then((time){
|
||||
if (time != null) {
|
||||
setNewState({"time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
TheLogger.log("Warning", "${widget.entity.entityId} has no date and no time");
|
||||
}
|
||||
}
|
||||
|
||||
Future _showDatePicker(BuildContext context) {
|
||||
return showDatePicker(
|
||||
context: context,
|
||||
initialDate: dateTimeState,
|
||||
firstDate: DateTime(1970),
|
||||
lastDate: DateTime(2037) //Unix timestamp will finish at Jan 19, 2038
|
||||
);
|
||||
}
|
||||
|
||||
Future _showTimePicker(BuildContext context) {
|
||||
return showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.fromDateTime(dateTimeState)
|
||||
);
|
||||
}
|
||||
}
|
@ -15,6 +15,18 @@ class Entity {
|
||||
};
|
||||
static List badgeDomains = ["alarm_control_panel", "binary_sensor", "device_tracker", "updater", "sun", "timer", "sensor"];
|
||||
|
||||
double rightWidgetPadding = 14.0;
|
||||
double leftWidgetPadding = 8.0;
|
||||
double extendedWidgetHeight = 50.0;
|
||||
double widgetHeight = 34.0;
|
||||
double iconSize = 28.0;
|
||||
double stateFontSize = 16.0;
|
||||
double nameFontSize = 16.0;
|
||||
double smallFontSize = 14.0;
|
||||
double largeFontSize = 24.0;
|
||||
double inputWidth = 160.0;
|
||||
double rowPadding = 10.0;
|
||||
|
||||
Map attributes;
|
||||
String domain;
|
||||
String entityId;
|
||||
@ -24,11 +36,11 @@ class Entity {
|
||||
|
||||
List<Entity> childEntities = [];
|
||||
|
||||
List<String> attributesToShow = ["all"];
|
||||
|
||||
String get displayName =>
|
||||
attributes["friendly_name"] ?? (attributes["name"] ?? "_");
|
||||
|
||||
|
||||
|
||||
String get deviceClass => attributes["device_class"] ?? null;
|
||||
bool get isView =>
|
||||
(domain == "group") &&
|
||||
@ -55,10 +67,52 @@ class Entity {
|
||||
_lastUpdated = DateTime.tryParse(rawData["last_updated"]);
|
||||
}
|
||||
|
||||
EntityWidget buildWidget(BuildContext context, int widgetType) {
|
||||
return EntityWidget(
|
||||
Widget buildDefaultWidget(BuildContext context) {
|
||||
return EntityModel(
|
||||
entity: this,
|
||||
widgetType: widgetType,
|
||||
child: DefaultEntityContainer(
|
||||
state: _buildStatePart(context)
|
||||
),
|
||||
handleTap: true,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return SimpleEntityState();
|
||||
}
|
||||
|
||||
Widget _buildStatePartForPage(BuildContext context) {
|
||||
return _buildStatePart(context);
|
||||
}
|
||||
|
||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||
return Container(width: 0.0, height: 0.0,);
|
||||
}
|
||||
|
||||
Widget buildEntityPageWidget(BuildContext context) {
|
||||
return EntityModel(
|
||||
entity: this,
|
||||
child: EntityPageContainer(
|
||||
children: <Widget> [
|
||||
DefaultEntityContainer(
|
||||
state: _buildStatePartForPage(context)
|
||||
),
|
||||
LastUpdatedWidget(),
|
||||
Divider(),
|
||||
_buildAdditionalControlsForPage(context),
|
||||
Divider(),
|
||||
EntityAttributesList()
|
||||
]
|
||||
),
|
||||
handleTap: false,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildBadgeWidget(BuildContext context) {
|
||||
return EntityModel(
|
||||
entity: this,
|
||||
child: Badge(),
|
||||
handleTap: true,
|
||||
);
|
||||
}
|
||||
|
||||
@ -100,341 +154,165 @@ class Entity {
|
||||
|
||||
}
|
||||
|
||||
class EntityWidgetType {
|
||||
static final int regular = 1;
|
||||
static final int extended = 2;
|
||||
static final int badge = 3;
|
||||
}
|
||||
|
||||
class EntityWidget extends StatefulWidget {
|
||||
|
||||
EntityWidget({Key key, this.entity, this.widgetType}) : super(key: key);
|
||||
|
||||
final Entity entity;
|
||||
final int widgetType;
|
||||
class SwitchEntity extends Entity {
|
||||
SwitchEntity(Map rawData) : super(rawData);
|
||||
|
||||
@override
|
||||
_EntityWidgetState createState() {
|
||||
switch (entity.domain) {
|
||||
case 'sun': {
|
||||
return _SunEntityWidgetState();
|
||||
}
|
||||
case "automation":
|
||||
case "input_boolean":
|
||||
case "switch":
|
||||
case "light": {
|
||||
return _SwitchEntityWidgetState();
|
||||
}
|
||||
case "script":
|
||||
case "scene": {
|
||||
return _ButtonEntityWidgetState();
|
||||
}
|
||||
case "input_datetime": {
|
||||
return _DateTimeEntityWidgetState();
|
||||
}
|
||||
case "input_select": {
|
||||
return _SelectEntityWidgetState();
|
||||
}
|
||||
case "input_number": {
|
||||
return _SliderEntityWidgetState();
|
||||
}
|
||||
case "input_text": {
|
||||
return _TextEntityWidgetState();
|
||||
}
|
||||
case "climate": {
|
||||
return _ClimateEntityWidgetState();
|
||||
}
|
||||
default: {
|
||||
return _EntityWidgetState();
|
||||
}
|
||||
}
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return SwitchControlWidget();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ButtonEntity extends Entity {
|
||||
ButtonEntity(Map rawData) : super(rawData);
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return ButtonControlWidget();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TextEntity extends Entity {
|
||||
TextEntity(Map rawData) : super(rawData);
|
||||
|
||||
int get valueMinLength => attributes["min"] ?? -1;
|
||||
int get valueMaxLength => attributes["max"] ?? -1;
|
||||
String get valuePattern => attributes["pattern"] ?? null;
|
||||
bool get isTextField => attributes["mode"] == "text";
|
||||
bool get isPasswordField => attributes["mode"] == "password";
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return TextControlWidget();
|
||||
}
|
||||
}
|
||||
|
||||
class _EntityWidgetState extends State<EntityWidget> {
|
||||
class SunEntity extends Entity {
|
||||
SunEntity(Map rawData) : super(rawData);
|
||||
}
|
||||
|
||||
List<String> attributesToShow = ["all"];
|
||||
double rightWidgetPadding = 14.0;
|
||||
double leftWidgetPadding = 8.0;
|
||||
double extendedWidgetHeight = 50.0;
|
||||
double widgetHeight = 34.0;
|
||||
double iconSize = 28.0;
|
||||
double stateFontSize = 16.0;
|
||||
double nameFontSize = 16.0;
|
||||
double smallFontSize = 14.0;
|
||||
double largeFontSize = 24.0;
|
||||
double inputWidth = 160.0;
|
||||
double rowPadding = 10.0;
|
||||
class SliderEntity extends Entity {
|
||||
SliderEntity(Map rawData) : super(rawData);
|
||||
|
||||
double get minValue => attributes["min"] ?? 0.0;
|
||||
double get maxValue => attributes["max"] ?? 100.0;
|
||||
double get valueStep => attributes["step"] ?? 1.0;
|
||||
double get doubleState => double.tryParse(state) ?? 0.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.widgetType == EntityWidgetType.regular) {
|
||||
return _buildMainWidget(context);
|
||||
} else if (widget.widgetType == EntityWidgetType.extended) {
|
||||
return _buildExtendedWidget(context);
|
||||
} else if (widget.widgetType == EntityWidgetType.badge) {
|
||||
return _buildBadgeWidget(context);
|
||||
} else {
|
||||
TheLogger.log("Error", "Unknown entity widget type: ${widget.widgetType}");
|
||||
return Container(width: 0.0, height: 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildExtendedWidget(BuildContext context) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
_buildMainWidget(context),
|
||||
_buildSecondRowWidget(),
|
||||
Divider(),
|
||||
_buildAttributesWidget()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMainWidget(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: widgetHeight,
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return Expanded(
|
||||
//width: 200.0,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
child: _buildIconWidget(),
|
||||
onTap: widget.widgetType == EntityWidgetType.extended ? null : openEntityPage,
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
child: _buildNameWidget(),
|
||||
onTap: widget.widgetType == EntityWidgetType.extended ? null : openEntityPage,
|
||||
),
|
||||
),
|
||||
_buildActionWidget(context)
|
||||
SliderControlWidget(expanded: true,),
|
||||
SimpleEntityState(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttributesWidget() {
|
||||
List<Widget> attrs = [];
|
||||
if (attributesToShow.contains("all")) {
|
||||
widget.entity.attributes.forEach((name, value){
|
||||
attrs.add(
|
||||
_buildAttributeWidget("$name", "$value")
|
||||
);
|
||||
});
|
||||
@override
|
||||
Widget _buildStatePartForPage(BuildContext context) {
|
||||
return SimpleEntityState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||
return SliderControlWidget(expanded: false,);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ClimateEntity extends Entity {
|
||||
|
||||
@override
|
||||
double widgetHeight = 38.0;
|
||||
|
||||
List<String> get operationList => (attributes["operation_list"] as List).cast<String>();
|
||||
double get temperature => _getTemperature();
|
||||
String get operationMode => attributes['operation_mode'] ?? "";
|
||||
bool get awayMode => attributes['away_mode'] == "on";
|
||||
|
||||
ClimateEntity(Map rawData) : super(rawData);
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return ClimateStateWidget();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||
return ClimateControlWidget();
|
||||
}
|
||||
|
||||
double _getTemperature() {
|
||||
var temp1 = attributes['temperature'] ?? attributes['target_temp_low'];
|
||||
if (temp1 is int) {
|
||||
return temp1.toDouble();
|
||||
} else if (temp1 is double) {
|
||||
return temp1;
|
||||
} else {
|
||||
attributesToShow.forEach((String attr) {
|
||||
String attrValue = widget.entity.getAttribute("$attr");
|
||||
if (attrValue != null) {
|
||||
attrs.add(
|
||||
_buildAttributeWidget("$attr", "$attrValue")
|
||||
);
|
||||
}
|
||||
});
|
||||
return 0.0;
|
||||
}
|
||||
return Column(
|
||||
children: attrs,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAttributeWidget(String name, String value) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(leftWidgetPadding, rowPadding, 0.0, 0.0),
|
||||
child: Text(
|
||||
"$name",
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, rowPadding, rightWidgetPadding, 0.0),
|
||||
child: Text(
|
||||
"$value",
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void openEntityPage() {
|
||||
eventBus.fire(new ShowEntityPageEvent(widget.entity));
|
||||
}
|
||||
|
||||
void setNewState(newState) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*Widget buildAdditionalWidget() {
|
||||
return _buildSecondRowWidget();
|
||||
}*/
|
||||
|
||||
Widget _buildIconWidget() {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(leftWidgetPadding, 0.0, 12.0, 0.0),
|
||||
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||
widget.entity,
|
||||
iconSize,
|
||||
Entity.STATE_ICONS_COLORS[widget.entity.state] ?? Entity.STATE_ICONS_COLORS["default"]),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSecondRowWidget() {
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
leftWidgetPadding, smallFontSize, 0.0, 0.0),
|
||||
child: Text(
|
||||
'${widget.entity.lastUpdated}',
|
||||
textAlign: TextAlign.left,
|
||||
style:
|
||||
TextStyle(fontSize: smallFontSize, color: Colors.black26),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNameWidget() {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(right: 10.0),
|
||||
child: Text(
|
||||
"${widget.entity.displayName}",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: TextStyle(fontSize: nameFontSize),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Text(
|
||||
"${widget.entity.state}${widget.entity.unitOfMeasurement}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: stateFontSize,
|
||||
)),
|
||||
onTap: openEntityPage,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBadgeWidget(BuildContext context) {
|
||||
double iconSize = 26.0;
|
||||
Widget badgeIcon;
|
||||
String onBadgeTextValue;
|
||||
Color iconColor = Entity.badgeColors[widget.entity.domain] ?? Entity.badgeColors["default"];
|
||||
switch (widget.entity.domain) {
|
||||
case "sun": {
|
||||
badgeIcon = widget.entity.state == "below_horizon" ?
|
||||
Icon(
|
||||
MaterialDesignIcons.createIconDataFromIconCode(0xf0dc),
|
||||
size: iconSize,
|
||||
) :
|
||||
Icon(
|
||||
MaterialDesignIcons.createIconDataFromIconCode(0xf5a8),
|
||||
size: iconSize,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "sensor": {
|
||||
onBadgeTextValue = widget.entity.unitOfMeasurement;
|
||||
badgeIcon = Center(
|
||||
child: Text(
|
||||
"${widget.entity.state}",
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 17.0),
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "device_tracker": {
|
||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(widget.entity, iconSize,Colors.black);
|
||||
onBadgeTextValue = widget.entity.state;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(widget.entity, iconSize,Colors.black);
|
||||
}
|
||||
}
|
||||
Widget onBadgeText;
|
||||
if (onBadgeTextValue == null || onBadgeTextValue.length == 0) {
|
||||
onBadgeText = Container(width: 0.0, height: 0.0);
|
||||
} else {
|
||||
onBadgeText = Container(
|
||||
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
||||
child: Text("$onBadgeTextValue",
|
||||
style: TextStyle(fontSize: 12.0, color: Colors.white),
|
||||
textAlign: TextAlign.center, softWrap: false, overflow: TextOverflow.fade),
|
||||
decoration: new BoxDecoration(
|
||||
// Circle shape
|
||||
//shape: BoxShape.circle,
|
||||
color: iconColor,
|
||||
borderRadius: BorderRadius.circular(9.0),
|
||||
)
|
||||
);
|
||||
}
|
||||
return GestureDetector(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
|
||||
width: 50.0,
|
||||
height: 50.0,
|
||||
decoration: new BoxDecoration(
|
||||
// Circle shape
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
// The border you want
|
||||
border: new Border.all(
|
||||
width: 2.0,
|
||||
color: iconColor,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
overflow: Overflow.visible,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
width: 46.0,
|
||||
height: 46.0,
|
||||
top: 0.0,
|
||||
left: 0.0,
|
||||
child: badgeIcon,
|
||||
),
|
||||
Positioned(
|
||||
//width: 50.0,
|
||||
bottom: -9.0,
|
||||
left: -10.0,
|
||||
right: -10.0,
|
||||
child: Center(
|
||||
child: onBadgeText,
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 60.0,
|
||||
child: Text(
|
||||
"${widget.entity.displayName}",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
softWrap: true,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: openEntityPage,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SelectEntity extends Entity {
|
||||
|
||||
List<String> get listOptions => attributes["options"]!= null ? (attributes["options"] as List).cast<String>() : [];
|
||||
|
||||
SelectEntity(Map rawData) : super(rawData);
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return SelectControlWidget();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DateTimeEntity extends Entity {
|
||||
|
||||
bool get hasDate => attributes["has_date"] ?? false;
|
||||
bool get hasTime => attributes["has_time"] ?? false;
|
||||
int get year => attributes["year"] ?? 1970;
|
||||
int get month => attributes["month"] ?? 1;
|
||||
int get day => attributes["day"] ?? 1;
|
||||
int get hour => attributes["hour"] ?? 0;
|
||||
int get minute => attributes["minute"] ?? 0;
|
||||
int get second => attributes["second"] ?? 0;
|
||||
String get formattedState => _getFormattedState();
|
||||
DateTime get dateTimeState => _getDateTimeState();
|
||||
|
||||
DateTimeEntity(Map rawData) : super(rawData);
|
||||
|
||||
@override
|
||||
Widget _buildStatePart(BuildContext context) {
|
||||
return DateTimeStateWidget();
|
||||
}
|
||||
|
||||
DateTime _getDateTimeState() {
|
||||
return DateTime(this.year, this.month, this.day, this.hour, this.minute, this.second);
|
||||
}
|
||||
|
||||
String _getFormattedState() {
|
||||
String formattedState = "";
|
||||
if (this.hasDate) {
|
||||
formattedState += formatDate(dateTimeState, [M, ' ', d, ', ', yyyy]);
|
||||
}
|
||||
if (this.hasTime) {
|
||||
formattedState += " "+formatDate(dateTimeState, [HH, ':', nn]);
|
||||
}
|
||||
return formattedState;
|
||||
}
|
||||
|
||||
void setNewState(newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(domain, "set_datetime",entityId,
|
||||
newValue));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,42 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _SelectEntityWidgetState extends _EntityWidgetState {
|
||||
List<String> _listOptions = [];
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "select_option", widget.entity.entityId,
|
||||
{"option": "$newValue"}));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
Widget ctrl;
|
||||
_listOptions.clear();
|
||||
if (widget.entity.attributes["options"] != null) {
|
||||
widget.entity.attributes["options"].forEach((value){
|
||||
_listOptions.add(value.toString());
|
||||
});
|
||||
}
|
||||
if (_listOptions.isNotEmpty) {
|
||||
ctrl = DropdownButton<String>(
|
||||
value: widget.entity.state,
|
||||
items: this._listOptions.map((String value) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: new Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (_) {
|
||||
setNewState(_);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
ctrl = Text('---');
|
||||
}
|
||||
return Expanded(
|
||||
//width: Entity.INPUT_WIDTH,
|
||||
child: ctrl,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _SliderEntityWidgetState extends _EntityWidgetState {
|
||||
int _multiplier = 1;
|
||||
|
||||
double get minValue => widget.entity.attributes["min"] ?? 0.0;
|
||||
double get maxValue => widget.entity.attributes["max"] ?? 100.0;
|
||||
double get valueStep => widget.entity.attributes["step"] ?? 1.0;
|
||||
double get doubleState => double.tryParse(widget.entity.state) ?? 0.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (valueStep < 1) {
|
||||
_multiplier = 10;
|
||||
} else if (valueStep < 0.1) {
|
||||
_multiplier = 100;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_value", widget.entity.entityId,
|
||||
{"value": "${newValue.toString()}"}));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildSecondRowWidget() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
super._buildSecondRowWidget(),
|
||||
_buildExtendedSlider()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExtendedSlider() {
|
||||
return Slider(
|
||||
min: this.minValue * _multiplier,
|
||||
max: this.maxValue * _multiplier,
|
||||
value: (doubleState <= this.maxValue) &&
|
||||
(doubleState >= this.minValue)
|
||||
? doubleState * _multiplier
|
||||
: this.minValue * _multiplier,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
widget.entity.state =
|
||||
(value.roundToDouble() / _multiplier).toString();
|
||||
});
|
||||
/*eventBus.fire(new StateChangedEvent(widget.entity.entityId,
|
||||
(value.roundToDouble() / _multiplier).toString(), true));*/
|
||||
},
|
||||
onChangeEnd: (value) {
|
||||
setNewState(value.roundToDouble() / _multiplier);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
Widget stateWidget = Padding(
|
||||
padding: EdgeInsets.only(right: rightWidgetPadding),
|
||||
child: Text("${widget.entity.state}${widget.entity.unitOfMeasurement}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: stateFontSize,
|
||||
)),
|
||||
);
|
||||
if (widget.widgetType == EntityWidgetType.regular) {
|
||||
return Expanded(
|
||||
//width: 200.0,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Slider(
|
||||
min: this.minValue * _multiplier,
|
||||
max: this.maxValue * _multiplier,
|
||||
value: (doubleState <= this.maxValue) &&
|
||||
(doubleState >= this.minValue)
|
||||
? doubleState * _multiplier
|
||||
: this.minValue * _multiplier,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
widget.entity.state =
|
||||
(value.roundToDouble() / _multiplier).toString();
|
||||
});
|
||||
/*eventBus.fire(new StateChangedEvent(widget.entity.entityId,
|
||||
(value.roundToDouble() / _multiplier).toString(), true));*/
|
||||
},
|
||||
onChangeEnd: (value) {
|
||||
setNewState(value.roundToDouble() / _multiplier);
|
||||
},
|
||||
),
|
||||
),
|
||||
stateWidget
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return stateWidget;
|
||||
}
|
||||
}
|
||||
}
|
440
lib/entity_class/stateful_widgets.dart
Normal file
440
lib/entity_class/stateful_widgets.dart
Normal file
@ -0,0 +1,440 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class SwitchControlWidget extends StatefulWidget {
|
||||
@override
|
||||
_SwitchControlWidgetState createState() => _SwitchControlWidgetState();
|
||||
}
|
||||
|
||||
class _SwitchControlWidgetState extends State<SwitchControlWidget> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _setNewState(newValue, Entity entity) {
|
||||
setState(() {
|
||||
entity.assumedState = newValue ? 'on' : 'off';
|
||||
});
|
||||
Timer(Duration(seconds: 2), (){
|
||||
setState(() {
|
||||
entity.assumedState = entity.state;
|
||||
});
|
||||
});
|
||||
eventBus.fire(new ServiceCallEvent(
|
||||
entity.domain, (newValue as bool) ? "turn_on" : "turn_off", entity.entityId, null));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return Switch(
|
||||
value: entityModel.entity.assumedState == 'on',
|
||||
onChanged: ((switchState) {
|
||||
_setNewState(switchState, entityModel.entity);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ButtonControlWidget extends StatefulWidget {
|
||||
@override
|
||||
_ButtonControlWidgetState createState() => _ButtonControlWidgetState();
|
||||
}
|
||||
|
||||
class _ButtonControlWidgetState extends State<ButtonControlWidget> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
void _setNewState(Entity entity) {
|
||||
eventBus.fire(new ServiceCallEvent(entity.domain, "turn_on", entity.entityId, null));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return FlatButton(
|
||||
onPressed: (() {
|
||||
_setNewState(entityModel.entity);
|
||||
}),
|
||||
child: Text(
|
||||
"EXECUTE",
|
||||
textAlign: TextAlign.right,
|
||||
style:
|
||||
new TextStyle(fontSize: entityModel.entity.stateFontSize, color: Colors.blue),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TextControlWidget extends StatefulWidget {
|
||||
|
||||
TextControlWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_TextControlWidgetState createState() => _TextControlWidgetState();
|
||||
}
|
||||
|
||||
class _TextControlWidgetState extends State<TextControlWidget> {
|
||||
String _tmpValue;
|
||||
String _entityState;
|
||||
String _entityDomain;
|
||||
String _entityId;
|
||||
int _minLength;
|
||||
int _maxLength;
|
||||
FocusNode _focusNode = FocusNode();
|
||||
bool validValue = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_focusNode.addListener(_focusListener);
|
||||
}
|
||||
|
||||
void setNewState(newValue, domain, entityId) {
|
||||
if (validate(newValue, _minLength, _maxLength)) {
|
||||
eventBus.fire(new ServiceCallEvent(domain, "set_value", entityId,
|
||||
{"value": "$newValue"}));
|
||||
} else {
|
||||
setState(() {
|
||||
_tmpValue = _entityState;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool validate(newValue, minLength, maxLength) {
|
||||
if (newValue is String) {
|
||||
validValue = (newValue.length >= minLength) &&
|
||||
(maxLength == -1 ||
|
||||
(newValue.length <= maxLength));
|
||||
} else {
|
||||
validValue = true;
|
||||
}
|
||||
return validValue;
|
||||
}
|
||||
|
||||
void _focusListener() {
|
||||
if (!_focusNode.hasFocus && (_tmpValue != _entityState)) {
|
||||
setNewState(_tmpValue, _entityDomain, _entityId);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final TextEntity entity = entityModel.entity;
|
||||
_entityState = entity.state;
|
||||
_entityDomain = entity.domain;
|
||||
_entityId = entity.entityId;
|
||||
_minLength = entity.valueMinLength;
|
||||
_maxLength = entity.valueMaxLength;
|
||||
|
||||
if (!_focusNode.hasFocus && (_tmpValue != entity.state)) {
|
||||
_tmpValue = entity.state;
|
||||
}
|
||||
if (entity.isTextField || entity.isPasswordField) {
|
||||
return Expanded(
|
||||
//width: Entity.INPUT_WIDTH,
|
||||
child: TextField(
|
||||
focusNode: _focusNode,
|
||||
obscureText: entity.isPasswordField,
|
||||
controller: new TextEditingController.fromValue(
|
||||
new TextEditingValue(
|
||||
text: _tmpValue,
|
||||
selection:
|
||||
new TextSelection.collapsed(offset: _tmpValue.length)
|
||||
)
|
||||
),
|
||||
onChanged: (value) {
|
||||
_tmpValue = value;
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
TheLogger.log("Warning", "Unsupported input mode for ${entity.entityId}");
|
||||
return SimpleEntityState();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.removeListener(_focusListener);
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SliderControlWidget extends StatefulWidget {
|
||||
|
||||
final bool expanded;
|
||||
|
||||
SliderControlWidget({Key key, @required this.expanded}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SliderControlWidgetState createState() => _SliderControlWidgetState();
|
||||
}
|
||||
|
||||
class _SliderControlWidgetState extends State<SliderControlWidget> {
|
||||
int _multiplier = 1;
|
||||
|
||||
void setNewState(newValue, domain, entityId) {
|
||||
eventBus.fire(new ServiceCallEvent(domain, "set_value", entityId,
|
||||
{"value": "${newValue.toString()}"}));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final SliderEntity entity = entityModel.entity;
|
||||
if (entity.valueStep < 1) {
|
||||
_multiplier = 10;
|
||||
} else if (entity.valueStep < 0.1) {
|
||||
_multiplier = 100;
|
||||
}
|
||||
Widget slider = Slider(
|
||||
min: entity.minValue * _multiplier,
|
||||
max: entity.maxValue * _multiplier,
|
||||
value: (entity.doubleState <= entity.maxValue) &&
|
||||
(entity.doubleState >= entity.minValue)
|
||||
? entity.doubleState * _multiplier
|
||||
: entity.minValue * _multiplier,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
entity.state =
|
||||
(value.roundToDouble() / _multiplier).toString();
|
||||
});
|
||||
eventBus.fire(new StateChangedEvent(entity.entityId,
|
||||
(value.roundToDouble() / _multiplier).toString(), true));
|
||||
|
||||
},
|
||||
onChangeEnd: (value) {
|
||||
setNewState(value.roundToDouble() / _multiplier, entity.domain, entity.entityId);
|
||||
},
|
||||
);
|
||||
if (widget.expanded) {
|
||||
return Expanded(
|
||||
child: slider,
|
||||
);
|
||||
} else {
|
||||
return slider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ClimateControlWidget extends StatefulWidget {
|
||||
|
||||
ClimateControlWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_ClimateControlWidgetState createState() => _ClimateControlWidgetState();
|
||||
}
|
||||
|
||||
class _ClimateControlWidgetState extends State<ClimateControlWidget> {
|
||||
|
||||
bool _showPending = false;
|
||||
bool _changedHere = false;
|
||||
Timer _resetTimer;
|
||||
double _tmpTemp = 0.0;
|
||||
String _tmpOperationMode = "";
|
||||
bool _tmpAwayMode = false;
|
||||
double _temperatureStep = 0.2;
|
||||
|
||||
void _resetVars(ClimateEntity entity) {
|
||||
_tmpTemp = entity.temperature;
|
||||
_tmpOperationMode = entity.operationMode;
|
||||
_tmpAwayMode = entity.awayMode;
|
||||
_showPending = false;
|
||||
_changedHere = false;
|
||||
}
|
||||
|
||||
void _temperatureUp(ClimateEntity entity) {
|
||||
_tmpTemp += _temperatureStep;
|
||||
_setTemperature(entity);
|
||||
}
|
||||
|
||||
void _temperatureDown(ClimateEntity entity) {
|
||||
_tmpTemp -= _temperatureStep;
|
||||
_setTemperature(entity);
|
||||
}
|
||||
|
||||
void _setTemperature(ClimateEntity entity) {
|
||||
setState(() {
|
||||
_tmpTemp = double.parse(_tmpTemp.toStringAsFixed(1));
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"temperature": "${_tmpTemp.toStringAsFixed(1)}"}));
|
||||
_resetStateTimer(entity);
|
||||
});
|
||||
}
|
||||
|
||||
void _setOperationMode(ClimateEntity entity, value) {
|
||||
setState(() {
|
||||
_tmpOperationMode = value;
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(entity.domain, "set_operation_mode", entity.entityId,{"operation_mode": "$_tmpOperationMode"}));
|
||||
_resetStateTimer(entity);
|
||||
});
|
||||
}
|
||||
|
||||
void _setAwayMode(ClimateEntity entity, value) {
|
||||
setState(() {
|
||||
_tmpAwayMode = value;
|
||||
_changedHere = true;
|
||||
eventBus.fire(new ServiceCallEvent(entity.domain, "set_away_mode", entity.entityId,{"away_mode": "${_tmpAwayMode ? 'on' : 'off'}"}));
|
||||
_resetStateTimer(entity);
|
||||
});
|
||||
}
|
||||
|
||||
void _resetStateTimer(ClimateEntity entity) {
|
||||
if (_resetTimer!=null) {
|
||||
_resetTimer.cancel();
|
||||
}
|
||||
_resetTimer = Timer(Duration(seconds: 3), () {
|
||||
setState(() {});
|
||||
_resetVars(entity);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final ClimateEntity entity = entityModel.entity;
|
||||
if (_changedHere) {
|
||||
_showPending = (_tmpTemp != entity.temperature);
|
||||
_changedHere = false;
|
||||
} else {
|
||||
_resetTimer?.cancel();
|
||||
_resetVars(entity);
|
||||
}
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(entity.leftWidgetPadding, entity.rowPadding, entity.rightWidgetPadding, 0.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text("Target temperature for ${_tmpOperationMode != 'off' ? _tmpOperationMode : ''}", style: TextStyle(
|
||||
fontSize: entity.stateFontSize
|
||||
)),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
"$_tmpTemp",
|
||||
style: TextStyle(
|
||||
fontSize: entity.largeFontSize,
|
||||
color: _showPending ? Colors.red : Colors.black
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
children: <Widget>[
|
||||
IconButton(
|
||||
icon: Icon(Icons.keyboard_arrow_up),
|
||||
iconSize: 30.0,
|
||||
onPressed: () => _temperatureUp(entity),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.keyboard_arrow_down),
|
||||
iconSize: 30.0,
|
||||
onPressed: () => _temperatureDown(entity),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Text("Operation", style: TextStyle(
|
||||
fontSize: entity.stateFontSize
|
||||
)),
|
||||
DropdownButton<String>(
|
||||
value: "$_tmpOperationMode",
|
||||
iconSize: 30.0,
|
||||
style: TextStyle(
|
||||
fontSize: entity.largeFontSize,
|
||||
color: Colors.black,
|
||||
),
|
||||
items: entity.operationList.map((String value) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: new Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (mode) => _setOperationMode(entity, mode),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: entity.rowPadding),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
"Away mode",
|
||||
style: TextStyle(
|
||||
fontSize: entity.stateFontSize
|
||||
),
|
||||
),
|
||||
),
|
||||
Switch(
|
||||
onChanged: (value) => _setAwayMode(entity, value),
|
||||
value: _tmpAwayMode,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_resetTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SelectControlWidget extends StatefulWidget {
|
||||
|
||||
SelectControlWidget({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_SelectControlWidgetState createState() => _SelectControlWidgetState();
|
||||
}
|
||||
|
||||
class _SelectControlWidgetState extends State<SelectControlWidget> {
|
||||
|
||||
void setNewState(domain, entityId, newValue) {
|
||||
eventBus.fire(new ServiceCallEvent(domain, "select_option", entityId,
|
||||
{"option": "$newValue"}));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final SelectEntity entity = entityModel.entity;
|
||||
Widget ctrl;
|
||||
if (entity.listOptions.isNotEmpty) {
|
||||
ctrl = DropdownButton<String>(
|
||||
value: entity.state,
|
||||
items: entity.listOptions.map((String value) {
|
||||
return new DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: new Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (_) {
|
||||
setNewState(entity.domain, entity.entityId,_);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
ctrl = Text('---');
|
||||
}
|
||||
return Expanded(
|
||||
//width: Entity.INPUT_WIDTH,
|
||||
child: ctrl,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
437
lib/entity_class/stateless_widgets.dart
Normal file
437
lib/entity_class/stateless_widgets.dart
Normal file
@ -0,0 +1,437 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class EntityWidgetsSizes {
|
||||
|
||||
}
|
||||
|
||||
class EntityModel extends InheritedWidget {
|
||||
|
||||
const EntityModel({
|
||||
Key key,
|
||||
@required this.entity,
|
||||
@required this.handleTap,
|
||||
@required Widget child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
final Entity entity;
|
||||
final bool handleTap;
|
||||
|
||||
static EntityModel of(BuildContext context) {
|
||||
return context.inheritFromWidgetOfExactType(EntityModel);
|
||||
}
|
||||
|
||||
@override
|
||||
bool updateShouldNotify(InheritedWidget oldWidget) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DefaultEntityContainer extends StatelessWidget {
|
||||
|
||||
DefaultEntityContainer({
|
||||
Key key,
|
||||
@required this.state,
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return SizedBox(
|
||||
height: entityModel.entity.widgetHeight,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
EntityIcon(),
|
||||
Expanded(
|
||||
child: EntityName(),
|
||||
),
|
||||
state
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EntityPageContainer extends StatelessWidget {
|
||||
|
||||
EntityPageContainer({Key key, @required this.children}) : super(key: key);
|
||||
|
||||
final List<Widget> children;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView(
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SimpleEntityState extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Text(
|
||||
"${entityModel.entity.state}${entityModel.entity.unitOfMeasurement}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: entityModel.entity.stateFontSize,
|
||||
)),
|
||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EntityName extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return GestureDetector(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(right: 10.0),
|
||||
child: Text(
|
||||
"${entityModel.entity.displayName}",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
style: TextStyle(fontSize: entityModel.entity.nameFontSize),
|
||||
),
|
||||
),
|
||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EntityIcon extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return GestureDetector(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(entityModel.entity.leftWidgetPadding, 0.0, 12.0, 0.0),
|
||||
//TODO: move createIconWidgetFromEntityData into this widget
|
||||
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||
entityModel.entity,
|
||||
entityModel.entity.iconSize,
|
||||
Entity.STATE_ICONS_COLORS[entityModel.entity.state] ?? Entity.STATE_ICONS_COLORS["default"]),
|
||||
),
|
||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entityModel.entity)) : null,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class LastUpdatedWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
return Padding(
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
entityModel.entity.leftWidgetPadding, 0.0, 0.0, 0.0),
|
||||
child: Text(
|
||||
'${entityModel.entity.lastUpdated}',
|
||||
textAlign: TextAlign.left,
|
||||
style:
|
||||
TextStyle(fontSize: entityModel.entity.smallFontSize, color: Colors.black26),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EntityAttributesList extends StatelessWidget {
|
||||
|
||||
EntityAttributesList({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
List<Widget> attrs = [];
|
||||
if ((entityModel.entity.attributesToShow == null) || (entityModel.entity.attributesToShow.contains("all"))) {
|
||||
entityModel.entity.attributes.forEach((name, value){
|
||||
attrs.add(
|
||||
_buildSingleAttribute(entityModel.entity, "$name", "$value")
|
||||
);
|
||||
});
|
||||
} else {
|
||||
entityModel.entity.attributesToShow.forEach((String attr) {
|
||||
String attrValue = entityModel.entity.getAttribute("$attr");
|
||||
if (attrValue != null) {
|
||||
attrs.add(
|
||||
_buildSingleAttribute(entityModel.entity, "$attr", "$attrValue")
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
return Column(
|
||||
children: attrs,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSingleAttribute(Entity entity, String name, String value) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(entity.leftWidgetPadding, entity.rowPadding, 0.0, 0.0),
|
||||
child: Text(
|
||||
"$name",
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(0.0, entity.rowPadding, entity.rightWidgetPadding, 0.0),
|
||||
child: Text(
|
||||
"$value",
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Badge extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
double iconSize = 26.0;
|
||||
Widget badgeIcon;
|
||||
String onBadgeTextValue;
|
||||
Color iconColor = Entity.badgeColors[entityModel.entity.domain] ?? Entity.badgeColors["default"];
|
||||
switch (entityModel.entity.domain) {
|
||||
case "sun": {
|
||||
badgeIcon = entityModel.entity.state == "below_horizon" ?
|
||||
Icon(
|
||||
MaterialDesignIcons.createIconDataFromIconCode(0xf0dc),
|
||||
size: iconSize,
|
||||
) :
|
||||
Icon(
|
||||
MaterialDesignIcons.createIconDataFromIconCode(0xf5a8),
|
||||
size: iconSize,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "sensor": {
|
||||
onBadgeTextValue = entityModel.entity.unitOfMeasurement;
|
||||
badgeIcon = Center(
|
||||
child: Text(
|
||||
"${entityModel.entity.state}",
|
||||
overflow: TextOverflow.fade,
|
||||
softWrap: false,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 17.0),
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
case "device_tracker": {
|
||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(entityModel.entity, iconSize,Colors.black);
|
||||
onBadgeTextValue = entityModel.entity.state;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
badgeIcon = MaterialDesignIcons.createIconWidgetFromEntityData(entityModel.entity, iconSize,Colors.black);
|
||||
}
|
||||
}
|
||||
Widget onBadgeText;
|
||||
if (onBadgeTextValue == null || onBadgeTextValue.length == 0) {
|
||||
onBadgeText = Container(width: 0.0, height: 0.0);
|
||||
} else {
|
||||
onBadgeText = Container(
|
||||
padding: EdgeInsets.fromLTRB(6.0, 2.0, 6.0, 2.0),
|
||||
child: Text("$onBadgeTextValue",
|
||||
style: TextStyle(fontSize: 12.0, color: Colors.white),
|
||||
textAlign: TextAlign.center, softWrap: false, overflow: TextOverflow.fade),
|
||||
decoration: new BoxDecoration(
|
||||
// Circle shape
|
||||
//shape: BoxShape.circle,
|
||||
color: iconColor,
|
||||
borderRadius: BorderRadius.circular(9.0),
|
||||
)
|
||||
);
|
||||
}
|
||||
return GestureDetector(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
|
||||
width: 50.0,
|
||||
height: 50.0,
|
||||
decoration: new BoxDecoration(
|
||||
// Circle shape
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
// The border you want
|
||||
border: new Border.all(
|
||||
width: 2.0,
|
||||
color: iconColor,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
overflow: Overflow.visible,
|
||||
children: <Widget>[
|
||||
Positioned(
|
||||
width: 46.0,
|
||||
height: 46.0,
|
||||
top: 0.0,
|
||||
left: 0.0,
|
||||
child: badgeIcon,
|
||||
),
|
||||
Positioned(
|
||||
//width: 50.0,
|
||||
bottom: -9.0,
|
||||
left: -10.0,
|
||||
right: -10.0,
|
||||
child: Center(
|
||||
child: onBadgeText,
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 60.0,
|
||||
child: Text(
|
||||
"${entityModel.entity.displayName}",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 12.0),
|
||||
softWrap: true,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () => eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ClimateStateWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final ClimateEntity entity = entityModel.entity;
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, entityModel.entity.rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${entity.state}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: entityModel.entity.stateFontSize,
|
||||
)),
|
||||
Text(
|
||||
" ${entity.attributes["temperature"]}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: entityModel.entity.stateFontSize,
|
||||
))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
"Currently: ${entity.attributes["current_temperature"]}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: entityModel.entity.stateFontSize,
|
||||
color: Colors.black45
|
||||
))
|
||||
],
|
||||
),
|
||||
onTap: () => entityModel.handleTap ? eventBus.fire(new ShowEntityPageEvent(entity)) : null,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DateTimeStateWidget extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final entityModel = EntityModel.of(context);
|
||||
final DateTimeEntity entity = entityModel.entity;
|
||||
return Padding(
|
||||
padding:
|
||||
EdgeInsets.fromLTRB(0.0, 0.0, entity.rightWidgetPadding, 0.0),
|
||||
child: GestureDetector(
|
||||
child: Text(
|
||||
"${entity.formattedState}",
|
||||
textAlign: TextAlign.right,
|
||||
style: new TextStyle(
|
||||
fontSize: entity.stateFontSize,
|
||||
)),
|
||||
onTap: () => _handleStateTap(context, entity),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStateTap(BuildContext context, DateTimeEntity entity) {
|
||||
if (entity.hasDate) {
|
||||
_showDatePicker(context, entity).then((date) {
|
||||
if (date != null) {
|
||||
if (entity.hasTime) {
|
||||
_showTimePicker(context, entity).then((time){
|
||||
entity.setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}", "time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
||||
});
|
||||
} else {
|
||||
entity.setNewState({"date": "${formatDate(date, [yyyy, '-', mm, '-', dd])}"});
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (entity.hasTime) {
|
||||
_showTimePicker(context, entity).then((time){
|
||||
if (time != null) {
|
||||
entity.setNewState({"time": "${formatDate(DateTime(1970, 1, 1, time.hour, time.minute), [HH, ':', nn])}"});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
TheLogger.log("Warning", "${entity.entityId} has no date and no time");
|
||||
}
|
||||
}
|
||||
|
||||
Future _showDatePicker(BuildContext context, DateTimeEntity entity) {
|
||||
return showDatePicker(
|
||||
context: context,
|
||||
initialDate: entity.dateTimeState,
|
||||
firstDate: DateTime(1970),
|
||||
lastDate: DateTime(2037) //Unix timestamp will finish at Jan 19, 2038
|
||||
);
|
||||
}
|
||||
|
||||
Future _showTimePicker(BuildContext context, DateTimeEntity entity) {
|
||||
return showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.fromDateTime(entity.dateTimeState)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _SunEntityWidgetState extends _EntityWidgetState {
|
||||
|
||||
@override
|
||||
Map <String, String> _displayStates = {
|
||||
"below_horizon": "Below horizon",
|
||||
"above_horizon": "Above horizon"
|
||||
};
|
||||
|
||||
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _SwitchEntityWidgetState extends _EntityWidgetState {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
setState(() {
|
||||
widget.entity.assumedState = newValue ? 'on' : 'off';
|
||||
});
|
||||
Timer(Duration(seconds: 2), (){
|
||||
setState(() {
|
||||
widget.entity.assumedState = widget.entity.state;
|
||||
});
|
||||
});
|
||||
eventBus.fire(new ServiceCallEvent(
|
||||
widget.entity.domain, (newValue as bool) ? "turn_on" : "turn_off", widget.entity.entityId, null));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
return Switch(
|
||||
value: widget.entity.assumedState == 'on',
|
||||
onChanged: ((switchState) {
|
||||
setNewState(switchState);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
part of '../main.dart';
|
||||
|
||||
class _TextEntityWidgetState extends _EntityWidgetState {
|
||||
String _tmpValue;
|
||||
FocusNode _focusNode = FocusNode();
|
||||
bool validValue = false;
|
||||
|
||||
int get valueMinLength => widget.entity.attributes["min"] ?? -1;
|
||||
int get valueMaxLength => widget.entity.attributes["max"] ?? -1;
|
||||
String get valuePattern => widget.entity.attributes["pattern"] ?? null;
|
||||
bool get isTextField => widget.entity.attributes["mode"] == "text";
|
||||
bool get isPasswordField => widget.entity.attributes["mode"] == "password";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_focusNode.addListener(_focusListener);
|
||||
_tmpValue = widget.entity.state;
|
||||
}
|
||||
|
||||
@override
|
||||
void setNewState(newValue) {
|
||||
if (validate(newValue)) {
|
||||
eventBus.fire(new ServiceCallEvent(widget.entity.domain, "set_value", widget.entity.entityId,
|
||||
{"value": "$newValue"}));
|
||||
} else {
|
||||
setState(() {
|
||||
_tmpValue = widget.entity.state;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool validate(newValue) {
|
||||
if (newValue is String) {
|
||||
validValue = (newValue.length >= this.valueMinLength) &&
|
||||
(this.valueMaxLength == -1 ||
|
||||
(newValue.length <= this.valueMaxLength));
|
||||
} else {
|
||||
validValue = true;
|
||||
}
|
||||
return validValue;
|
||||
}
|
||||
|
||||
void _focusListener() {
|
||||
if (!_focusNode.hasFocus && (_tmpValue != widget.entity.state)) {
|
||||
setNewState(_tmpValue);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget _buildActionWidget(BuildContext context) {
|
||||
if (!_focusNode.hasFocus && (_tmpValue != widget.entity.state)) {
|
||||
_tmpValue = widget.entity.state;
|
||||
}
|
||||
if (this.isTextField || this.isPasswordField) {
|
||||
return Expanded(
|
||||
//width: Entity.INPUT_WIDTH,
|
||||
child: TextField(
|
||||
focusNode: _focusNode,
|
||||
obscureText: this.isPasswordField,
|
||||
controller: new TextEditingController.fromValue(
|
||||
new TextEditingValue(
|
||||
text: _tmpValue,
|
||||
selection:
|
||||
new TextSelection.collapsed(offset: _tmpValue.length)
|
||||
)
|
||||
),
|
||||
onChanged: (value) {
|
||||
_tmpValue = value;
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
TheLogger.log("Warning", "Unsupported input mode for ${widget.entity.entityId}");
|
||||
return super._buildActionWidget(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusNode.removeListener(_focusListener);
|
||||
_focusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
}
|
@ -29,7 +29,39 @@ class EntityCollection {
|
||||
}
|
||||
|
||||
Entity _createEntityInstance(rawEntityData) {
|
||||
return Entity(rawEntityData);
|
||||
switch (rawEntityData["entity_id"].split(".")[0]) {
|
||||
case 'sun': {
|
||||
return SunEntity(rawEntityData);
|
||||
}
|
||||
case "automation":
|
||||
case "input_boolean":
|
||||
case "switch":
|
||||
case "light": {
|
||||
return SwitchEntity(rawEntityData);
|
||||
}
|
||||
case "script":
|
||||
case "scene": {
|
||||
return ButtonEntity(rawEntityData);
|
||||
}
|
||||
case "input_datetime": {
|
||||
return DateTimeEntity(rawEntityData);
|
||||
}
|
||||
case "input_select": {
|
||||
return SelectEntity(rawEntityData);
|
||||
}
|
||||
case "input_number": {
|
||||
return SliderEntity(rawEntityData);
|
||||
}
|
||||
case "input_text": {
|
||||
return TextEntity(rawEntityData);
|
||||
}
|
||||
case "climate": {
|
||||
return ClimateEntity(rawEntityData);
|
||||
}
|
||||
default: {
|
||||
return Entity(rawEntityData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateState(Map rawStateData) {
|
||||
|
@ -14,14 +14,8 @@ import 'package:date_format/date_format.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
part 'entity_class/entity.class.dart';
|
||||
part 'entity_class/button_entity.class.dart';
|
||||
part 'entity_class/datetime_entity.class.dart';
|
||||
part 'entity_class/select_entity.class.dart';
|
||||
part 'entity_class/slider_entity.class.dart';
|
||||
part 'entity_class/switch_entity.class.dart';
|
||||
part 'entity_class/text_entity.class.dart';
|
||||
part 'entity_class/sun_entity.class.dart';
|
||||
part 'entity_class/climate_entity.class.dart';
|
||||
part 'entity_class/stateless_widgets.dart';
|
||||
part 'entity_class/stateful_widgets.dart';
|
||||
|
||||
part 'settings.page.dart';
|
||||
part 'home_assistant.class.dart';
|
||||
|
@ -124,7 +124,7 @@ class ViewWidgetState extends State<ViewWidget> {
|
||||
List<Widget> _buildBadges(BuildContext context, List<Entity> badges) {
|
||||
List<Widget> result = [];
|
||||
badges.forEach((Entity entity) {
|
||||
result.add(entity.buildWidget(context, EntityWidgetType.badge));
|
||||
result.add(entity.buildBadgeWidget(context));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
@ -488,5 +488,5 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.15"
|
||||
sdks:
|
||||
dart: ">=2.0.0 <=2.1.0-dev.3.1.flutter-760a9690c2"
|
||||
dart: ">=2.0.0 <=2.1.0-dev.5.0.flutter-a2eb050044"
|
||||
flutter: ">=0.1.4 <2.0.0"
|
||||
|
Reference in New Issue
Block a user