Full ui structure refactoring. InheritedWidget as entity model

This commit is contained in:
Yegor Vialov 2018-10-15 00:15:09 +03:00
parent b5f9ecf601
commit ffc053fbe6
17 changed files with 1118 additions and 960 deletions

View File

@ -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;

View File

@ -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)
),
);
}

View File

@ -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),
),
);
}
}

View File

@ -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();
}
}

View File

@ -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)
);
}
}

View File

@ -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));
}
}

View File

@ -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,
);
}
}

View File

@ -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;
}
}
}

View 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,
);
}
}

View 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)
);
}
}

View File

@ -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"
};
}

View File

@ -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);
}),
);
}
}

View File

@ -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();
}
}

View File

@ -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) {

View File

@ -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';

View File

@ -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;
}

View File

@ -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"