Code structure
This commit is contained in:
parent
df56f6ceda
commit
9edfec7dff
@ -1,562 +0,0 @@
|
|||||||
part of 'main.dart';
|
|
||||||
|
|
||||||
class Entity {
|
|
||||||
static const STATE_ICONS_COLORS = {
|
|
||||||
"on": Colors.amber,
|
|
||||||
"off": Color.fromRGBO(68, 115, 158, 1.0),
|
|
||||||
"default": Color.fromRGBO(68, 115, 158, 1.0),
|
|
||||||
"unavailable": Colors.black12,
|
|
||||||
"unknown": Colors.black12,
|
|
||||||
"playing": Colors.amber
|
|
||||||
};
|
|
||||||
static const badgeColors = {
|
|
||||||
"default": Color.fromRGBO(223, 76, 30, 1.0),
|
|
||||||
"binary_sensor": Color.fromRGBO(3, 155, 229, 1.0)
|
|
||||||
};
|
|
||||||
static List badgeDomains = [
|
|
||||||
"alarm_control_panel",
|
|
||||||
"binary_sensor",
|
|
||||||
"device_tracker",
|
|
||||||
"updater",
|
|
||||||
"sun",
|
|
||||||
"timer",
|
|
||||||
"sensor"
|
|
||||||
];
|
|
||||||
|
|
||||||
static const rightWidgetPadding = 14.0;
|
|
||||||
static const leftWidgetPadding = 8.0;
|
|
||||||
static const extendedWidgetHeight = 50.0;
|
|
||||||
static const iconSize = 28.0;
|
|
||||||
static const stateFontSize = 16.0;
|
|
||||||
static const nameFontSize = 16.0;
|
|
||||||
static const smallFontSize = 14.0;
|
|
||||||
static const largeFontSize = 24.0;
|
|
||||||
static const inputWidth = 160.0;
|
|
||||||
static const rowPadding = 10.0;
|
|
||||||
|
|
||||||
double widgetHeight = 34.0;
|
|
||||||
|
|
||||||
Map attributes;
|
|
||||||
String domain;
|
|
||||||
String entityId;
|
|
||||||
String state;
|
|
||||||
String assumedState;
|
|
||||||
DateTime _lastUpdated;
|
|
||||||
|
|
||||||
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") &&
|
|
||||||
(attributes != null ? attributes["view"] ?? false : false);
|
|
||||||
bool get isGroup => domain == "group";
|
|
||||||
bool get isBadge => Entity.badgeDomains.contains(domain);
|
|
||||||
String get icon => attributes["icon"] ?? "";
|
|
||||||
bool get isOn => state == "on";
|
|
||||||
String get entityPicture => attributes["entity_picture"];
|
|
||||||
String get unitOfMeasurement => attributes["unit_of_measurement"] ?? "";
|
|
||||||
List get childEntityIds => attributes["entity_id"] ?? [];
|
|
||||||
String get lastUpdated => _getLastUpdatedFormatted();
|
|
||||||
bool get isHidden => attributes["hidden"] ?? false;
|
|
||||||
|
|
||||||
Entity(Map rawData) {
|
|
||||||
update(rawData);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(Map rawData) {
|
|
||||||
attributes = rawData["attributes"] ?? {};
|
|
||||||
domain = rawData["entity_id"].split(".")[0];
|
|
||||||
entityId = rawData["entity_id"];
|
|
||||||
state = rawData["state"];
|
|
||||||
assumedState = state;
|
|
||||||
_lastUpdated = DateTime.tryParse(rawData["last_updated"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
double _getDoubleAttributeValue(String attributeName) {
|
|
||||||
var temp1 = attributes["$attributeName"];
|
|
||||||
if (temp1 is int) {
|
|
||||||
return temp1.toDouble();
|
|
||||||
} else if (temp1 is double) {
|
|
||||||
return temp1;
|
|
||||||
} else {
|
|
||||||
return double.tryParse("$temp1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int _getIntAttributeValue(String attributeName) {
|
|
||||||
var temp1 = attributes["$attributeName"];
|
|
||||||
if (temp1 is int) {
|
|
||||||
return temp1;
|
|
||||||
} else if (temp1 is double) {
|
|
||||||
return temp1.round();
|
|
||||||
} else {
|
|
||||||
return int.tryParse("$temp1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildDefaultWidget(BuildContext context) {
|
|
||||||
return EntityModel(
|
|
||||||
entity: this,
|
|
||||||
child: DefaultEntityContainer(
|
|
||||||
state: _buildStatePart(context),
|
|
||||||
height: widgetHeight,
|
|
||||||
),
|
|
||||||
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), height: widgetHeight),
|
|
||||||
LastUpdatedWidget(),
|
|
||||||
Divider(),
|
|
||||||
_buildAdditionalControlsForPage(context),
|
|
||||||
Divider(),
|
|
||||||
EntityAttributesList()
|
|
||||||
]),
|
|
||||||
handleTap: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildBadgeWidget(BuildContext context) {
|
|
||||||
return EntityModel(
|
|
||||||
entity: this,
|
|
||||||
child: Badge(),
|
|
||||||
handleTap: true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
String getAttribute(String attributeName) {
|
|
||||||
if (attributes != null) {
|
|
||||||
return attributes["$attributeName"];
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String _getLastUpdatedFormatted() {
|
|
||||||
if (_lastUpdated == null) {
|
|
||||||
return "-";
|
|
||||||
} else {
|
|
||||||
DateTime now = DateTime.now();
|
|
||||||
Duration d = now.difference(_lastUpdated);
|
|
||||||
String text;
|
|
||||||
int v;
|
|
||||||
if (d.inDays == 0) {
|
|
||||||
if (d.inHours == 0) {
|
|
||||||
if (d.inMinutes == 0) {
|
|
||||||
text = "seconds ago";
|
|
||||||
v = d.inSeconds;
|
|
||||||
} else {
|
|
||||||
text = "minutes ago";
|
|
||||||
v = d.inMinutes;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
text = "hours ago";
|
|
||||||
v = d.inHours;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
text = "days ago";
|
|
||||||
v = d.inDays;
|
|
||||||
}
|
|
||||||
return "$v $text";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SwitchEntity extends Entity {
|
|
||||||
SwitchEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return SwitchStateWidget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ButtonEntity extends Entity {
|
|
||||||
ButtonEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return ButtonStateWidget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 TextInputStateWidget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SunEntity extends Entity {
|
|
||||||
SunEntity(Map rawData) : super(rawData);
|
|
||||||
}
|
|
||||||
|
|
||||||
class SliderEntity extends Entity {
|
|
||||||
SliderEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
double get minValue => attributes["min"] ?? 0.0;
|
|
||||||
double get maxValue => attributes["max"] ?? 100.0;
|
|
||||||
double get valueStep => attributes["step"] ?? 1.0;
|
|
||||||
double get doubleState => double.tryParse(state) ?? 0.0;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return Expanded(
|
|
||||||
//width: 200.0,
|
|
||||||
child: Row(
|
|
||||||
children: <Widget>[
|
|
||||||
SliderStateWidget(
|
|
||||||
expanded: true,
|
|
||||||
),
|
|
||||||
SimpleEntityState(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePartForPage(BuildContext context) {
|
|
||||||
return SimpleEntityState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
|
||||||
return SliderStateWidget(
|
|
||||||
expanded: false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClimateEntity extends Entity {
|
|
||||||
@override
|
|
||||||
double widgetHeight = 38.0;
|
|
||||||
|
|
||||||
static const SUPPORT_TARGET_TEMPERATURE = 1;
|
|
||||||
static const SUPPORT_TARGET_TEMPERATURE_HIGH = 2;
|
|
||||||
static const SUPPORT_TARGET_TEMPERATURE_LOW = 4;
|
|
||||||
static const SUPPORT_TARGET_HUMIDITY = 8;
|
|
||||||
static const SUPPORT_TARGET_HUMIDITY_HIGH = 16;
|
|
||||||
static const SUPPORT_TARGET_HUMIDITY_LOW = 32;
|
|
||||||
static const SUPPORT_FAN_MODE = 64;
|
|
||||||
static const SUPPORT_OPERATION_MODE = 128;
|
|
||||||
static const SUPPORT_HOLD_MODE = 256;
|
|
||||||
static const SUPPORT_SWING_MODE = 512;
|
|
||||||
static const SUPPORT_AWAY_MODE = 1024;
|
|
||||||
static const SUPPORT_AUX_HEAT = 2048;
|
|
||||||
static const SUPPORT_ON_OFF = 4096;
|
|
||||||
|
|
||||||
bool get supportTargetTemperature => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE);
|
|
||||||
bool get supportTargetTemperatureHigh => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH);
|
|
||||||
bool get supportTargetTemperatureLow => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW);
|
|
||||||
bool get supportTargetHumidity => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY);
|
|
||||||
bool get supportTargetHumidityHigh => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH);
|
|
||||||
bool get supportTargetHumidityLow => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW) ==
|
|
||||||
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW);
|
|
||||||
bool get supportFanMode =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_FAN_MODE) ==
|
|
||||||
ClimateEntity.SUPPORT_FAN_MODE);
|
|
||||||
bool get supportOperationMode => ((attributes["supported_features"] &
|
|
||||||
ClimateEntity.SUPPORT_OPERATION_MODE) ==
|
|
||||||
ClimateEntity.SUPPORT_OPERATION_MODE);
|
|
||||||
bool get supportHoldMode =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_HOLD_MODE) ==
|
|
||||||
ClimateEntity.SUPPORT_HOLD_MODE);
|
|
||||||
bool get supportSwingMode =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_SWING_MODE) ==
|
|
||||||
ClimateEntity.SUPPORT_SWING_MODE);
|
|
||||||
bool get supportAwayMode =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_AWAY_MODE) ==
|
|
||||||
ClimateEntity.SUPPORT_AWAY_MODE);
|
|
||||||
bool get supportAuxHeat =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_AUX_HEAT) ==
|
|
||||||
ClimateEntity.SUPPORT_AUX_HEAT);
|
|
||||||
bool get supportOnOff =>
|
|
||||||
((attributes["supported_features"] & ClimateEntity.SUPPORT_ON_OFF) ==
|
|
||||||
ClimateEntity.SUPPORT_ON_OFF);
|
|
||||||
|
|
||||||
List<String> get operationList => attributes["operation_list"] != null
|
|
||||||
? (attributes["operation_list"] as List).cast<String>()
|
|
||||||
: null;
|
|
||||||
List<String> get fanList => attributes["fan_list"] != null
|
|
||||||
? (attributes["fan_list"] as List).cast<String>()
|
|
||||||
: null;
|
|
||||||
List<String> get swingList => attributes["swing_list"] != null
|
|
||||||
? (attributes["swing_list"] as List).cast<String>()
|
|
||||||
: null;
|
|
||||||
double get temperature => _getDoubleAttributeValue('temperature');
|
|
||||||
double get targetHigh => _getDoubleAttributeValue('target_temp_high');
|
|
||||||
double get targetLow => _getDoubleAttributeValue('target_temp_low');
|
|
||||||
double get maxTemp => _getDoubleAttributeValue('max_temp') ?? 100.0;
|
|
||||||
double get minTemp => _getDoubleAttributeValue('min_temp') ?? -100.0;
|
|
||||||
double get targetHumidity => _getDoubleAttributeValue('humidity');
|
|
||||||
double get maxHumidity => _getDoubleAttributeValue('max_humidity');
|
|
||||||
double get minHumidity => _getDoubleAttributeValue('min_humidity');
|
|
||||||
String get operationMode => attributes['operation_mode'];
|
|
||||||
String get fanMode => attributes['fan_mode'];
|
|
||||||
String get swingMode => attributes['swing_mode'];
|
|
||||||
bool get awayMode => attributes['away_mode'] == "on";
|
|
||||||
bool get isOff => state == "off";
|
|
||||||
bool get auxHeat => attributes['aux_heat'] == "on";
|
|
||||||
|
|
||||||
ClimateEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return ClimateStateWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
|
||||||
return ClimateControlWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
double _getDoubleAttributeValue(String attributeName) {
|
|
||||||
var temp1 = attributes["$attributeName"];
|
|
||||||
if (temp1 is int) {
|
|
||||||
return temp1.toDouble();
|
|
||||||
} else if (temp1 is double) {
|
|
||||||
return temp1;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoverEntity extends Entity {
|
|
||||||
@override
|
|
||||||
double widgetHeight = 38.0;
|
|
||||||
|
|
||||||
static const SUPPORT_OPEN = 1;
|
|
||||||
static const SUPPORT_CLOSE = 2;
|
|
||||||
static const SUPPORT_SET_POSITION = 4;
|
|
||||||
static const SUPPORT_STOP = 8;
|
|
||||||
static const SUPPORT_OPEN_TILT = 16;
|
|
||||||
static const SUPPORT_CLOSE_TILT = 32;
|
|
||||||
static const SUPPORT_STOP_TILT = 64;
|
|
||||||
static const SUPPORT_SET_TILT_POSITION = 128;
|
|
||||||
|
|
||||||
bool get supportOpen => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_OPEN) ==
|
|
||||||
CoverEntity.SUPPORT_OPEN);
|
|
||||||
bool get supportClose => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_CLOSE) ==
|
|
||||||
CoverEntity.SUPPORT_CLOSE);
|
|
||||||
bool get supportSetPosition => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_SET_POSITION) ==
|
|
||||||
CoverEntity.SUPPORT_SET_POSITION);
|
|
||||||
bool get supportStop => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_STOP) ==
|
|
||||||
CoverEntity.SUPPORT_STOP);
|
|
||||||
|
|
||||||
bool get supportOpenTilt => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_OPEN_TILT) ==
|
|
||||||
CoverEntity.SUPPORT_OPEN_TILT);
|
|
||||||
bool get supportCloseTilt => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_CLOSE_TILT) ==
|
|
||||||
CoverEntity.SUPPORT_CLOSE_TILT);
|
|
||||||
bool get supportStopTilt => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_STOP_TILT) ==
|
|
||||||
CoverEntity.SUPPORT_STOP_TILT);
|
|
||||||
bool get supportSetTiltPosition => ((attributes["supported_features"] &
|
|
||||||
CoverEntity.SUPPORT_SET_TILT_POSITION) ==
|
|
||||||
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
|
||||||
|
|
||||||
|
|
||||||
double get currentPosition => _getDoubleAttributeValue('current_position');
|
|
||||||
double get currentTiltPosition => _getDoubleAttributeValue('current_tilt_position');
|
|
||||||
bool get canBeOpened => ((state != "opening") && (state != "open"));
|
|
||||||
bool get canBeClosed => ((state != "closing") && (state != "closed"));
|
|
||||||
bool get canTiltBeOpened => currentPosition < 100;
|
|
||||||
bool get canTiltBeClosed => currentPosition > 0;
|
|
||||||
|
|
||||||
CoverEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return CoverEntityControlState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
|
||||||
return CoverControlWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class LightEntity extends Entity {
|
|
||||||
|
|
||||||
static const SUPPORT_BRIGHTNESS = 1;
|
|
||||||
static const SUPPORT_COLOR_TEMP = 2;
|
|
||||||
static const SUPPORT_EFFECT = 4;
|
|
||||||
static const SUPPORT_FLASH = 8;
|
|
||||||
static const SUPPORT_COLOR = 16;
|
|
||||||
static const SUPPORT_TRANSITION = 32;
|
|
||||||
static const SUPPORT_WHITE_VALUE = 128;
|
|
||||||
|
|
||||||
bool get supportBrightness => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_BRIGHTNESS) ==
|
|
||||||
LightEntity.SUPPORT_BRIGHTNESS);
|
|
||||||
bool get supportColorTemp => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_COLOR_TEMP) ==
|
|
||||||
LightEntity.SUPPORT_COLOR_TEMP);
|
|
||||||
bool get supportEffect => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_EFFECT) ==
|
|
||||||
LightEntity.SUPPORT_EFFECT);
|
|
||||||
bool get supportFlash => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_FLASH) ==
|
|
||||||
LightEntity.SUPPORT_FLASH);
|
|
||||||
bool get supportColor => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_COLOR) ==
|
|
||||||
LightEntity.SUPPORT_COLOR);
|
|
||||||
bool get supportTransition => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_TRANSITION) ==
|
|
||||||
LightEntity.SUPPORT_TRANSITION);
|
|
||||||
bool get supportWhiteValue => ((attributes["supported_features"] &
|
|
||||||
LightEntity.SUPPORT_WHITE_VALUE) ==
|
|
||||||
LightEntity.SUPPORT_WHITE_VALUE);
|
|
||||||
|
|
||||||
int get brightness => _getIntAttributeValue("brightness");
|
|
||||||
int get colorTemp => _getIntAttributeValue("color_temp");
|
|
||||||
double get maxMireds => _getDoubleAttributeValue("max_mireds");
|
|
||||||
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
|
||||||
Color get color => _getColor();
|
|
||||||
bool get isAdditionalControls => ((attributes["supported_features"] != null) && (attributes["supported_features"] != 0));
|
|
||||||
List<String> get effectList => _getEffectList();
|
|
||||||
|
|
||||||
LightEntity(Map rawData) : super(rawData);
|
|
||||||
|
|
||||||
Color _getColor() {
|
|
||||||
List rgb = attributes["rgb_color"];
|
|
||||||
try {
|
|
||||||
if ((rgb != null) && (rgb.length > 0)) {
|
|
||||||
return Color.fromARGB(255, rgb[0], rgb[1], rgb[2]);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> _getEffectList() {
|
|
||||||
if (attributes["effect_list"] != null) {
|
|
||||||
List<String> result = (attributes["effect_list"] as List).cast<String>();
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildStatePart(BuildContext context) {
|
|
||||||
return SwitchStateWidget();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
|
||||||
if (!isAdditionalControls) {
|
|
||||||
return Container(height: 0.0, width: 0.0);
|
|
||||||
} else {
|
|
||||||
return LightControlsWidget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
10
lib/entity_class/button_entity.class.dart
Normal file
10
lib/entity_class/button_entity.class.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class ButtonEntity extends Entity {
|
||||||
|
ButtonEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return ButtonStateWidget();
|
||||||
|
}
|
||||||
|
}
|
109
lib/entity_class/climate_entity.class.dart
Normal file
109
lib/entity_class/climate_entity.class.dart
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class ClimateEntity extends Entity {
|
||||||
|
@override
|
||||||
|
double widgetHeight = 38.0;
|
||||||
|
|
||||||
|
static const SUPPORT_TARGET_TEMPERATURE = 1;
|
||||||
|
static const SUPPORT_TARGET_TEMPERATURE_HIGH = 2;
|
||||||
|
static const SUPPORT_TARGET_TEMPERATURE_LOW = 4;
|
||||||
|
static const SUPPORT_TARGET_HUMIDITY = 8;
|
||||||
|
static const SUPPORT_TARGET_HUMIDITY_HIGH = 16;
|
||||||
|
static const SUPPORT_TARGET_HUMIDITY_LOW = 32;
|
||||||
|
static const SUPPORT_FAN_MODE = 64;
|
||||||
|
static const SUPPORT_OPERATION_MODE = 128;
|
||||||
|
static const SUPPORT_HOLD_MODE = 256;
|
||||||
|
static const SUPPORT_SWING_MODE = 512;
|
||||||
|
static const SUPPORT_AWAY_MODE = 1024;
|
||||||
|
static const SUPPORT_AUX_HEAT = 2048;
|
||||||
|
static const SUPPORT_ON_OFF = 4096;
|
||||||
|
|
||||||
|
bool get supportTargetTemperature => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE);
|
||||||
|
bool get supportTargetTemperatureHigh => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_HIGH);
|
||||||
|
bool get supportTargetTemperatureLow => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_TEMPERATURE_LOW);
|
||||||
|
bool get supportTargetHumidity => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY);
|
||||||
|
bool get supportTargetHumidityHigh => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_HIGH);
|
||||||
|
bool get supportTargetHumidityLow => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW) ==
|
||||||
|
ClimateEntity.SUPPORT_TARGET_HUMIDITY_LOW);
|
||||||
|
bool get supportFanMode =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_FAN_MODE) ==
|
||||||
|
ClimateEntity.SUPPORT_FAN_MODE);
|
||||||
|
bool get supportOperationMode => ((attributes["supported_features"] &
|
||||||
|
ClimateEntity.SUPPORT_OPERATION_MODE) ==
|
||||||
|
ClimateEntity.SUPPORT_OPERATION_MODE);
|
||||||
|
bool get supportHoldMode =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_HOLD_MODE) ==
|
||||||
|
ClimateEntity.SUPPORT_HOLD_MODE);
|
||||||
|
bool get supportSwingMode =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_SWING_MODE) ==
|
||||||
|
ClimateEntity.SUPPORT_SWING_MODE);
|
||||||
|
bool get supportAwayMode =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_AWAY_MODE) ==
|
||||||
|
ClimateEntity.SUPPORT_AWAY_MODE);
|
||||||
|
bool get supportAuxHeat =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_AUX_HEAT) ==
|
||||||
|
ClimateEntity.SUPPORT_AUX_HEAT);
|
||||||
|
bool get supportOnOff =>
|
||||||
|
((attributes["supported_features"] & ClimateEntity.SUPPORT_ON_OFF) ==
|
||||||
|
ClimateEntity.SUPPORT_ON_OFF);
|
||||||
|
|
||||||
|
List<String> get operationList => attributes["operation_list"] != null
|
||||||
|
? (attributes["operation_list"] as List).cast<String>()
|
||||||
|
: null;
|
||||||
|
List<String> get fanList => attributes["fan_list"] != null
|
||||||
|
? (attributes["fan_list"] as List).cast<String>()
|
||||||
|
: null;
|
||||||
|
List<String> get swingList => attributes["swing_list"] != null
|
||||||
|
? (attributes["swing_list"] as List).cast<String>()
|
||||||
|
: null;
|
||||||
|
double get temperature => _getDoubleAttributeValue('temperature');
|
||||||
|
double get targetHigh => _getDoubleAttributeValue('target_temp_high');
|
||||||
|
double get targetLow => _getDoubleAttributeValue('target_temp_low');
|
||||||
|
double get maxTemp => _getDoubleAttributeValue('max_temp') ?? 100.0;
|
||||||
|
double get minTemp => _getDoubleAttributeValue('min_temp') ?? -100.0;
|
||||||
|
double get targetHumidity => _getDoubleAttributeValue('humidity');
|
||||||
|
double get maxHumidity => _getDoubleAttributeValue('max_humidity');
|
||||||
|
double get minHumidity => _getDoubleAttributeValue('min_humidity');
|
||||||
|
String get operationMode => attributes['operation_mode'];
|
||||||
|
String get fanMode => attributes['fan_mode'];
|
||||||
|
String get swingMode => attributes['swing_mode'];
|
||||||
|
bool get awayMode => attributes['away_mode'] == "on";
|
||||||
|
bool get isOff => state == "off";
|
||||||
|
bool get auxHeat => attributes['aux_heat'] == "on";
|
||||||
|
|
||||||
|
ClimateEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return ClimateStateWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return ClimateControlWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double _getDoubleAttributeValue(String attributeName) {
|
||||||
|
var temp1 = attributes["$attributeName"];
|
||||||
|
if (temp1 is int) {
|
||||||
|
return temp1.toDouble();
|
||||||
|
} else if (temp1 is double) {
|
||||||
|
return temp1;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
lib/entity_class/cover_entity.class.dart
Normal file
62
lib/entity_class/cover_entity.class.dart
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class CoverEntity extends Entity {
|
||||||
|
@override
|
||||||
|
double widgetHeight = 38.0;
|
||||||
|
|
||||||
|
static const SUPPORT_OPEN = 1;
|
||||||
|
static const SUPPORT_CLOSE = 2;
|
||||||
|
static const SUPPORT_SET_POSITION = 4;
|
||||||
|
static const SUPPORT_STOP = 8;
|
||||||
|
static const SUPPORT_OPEN_TILT = 16;
|
||||||
|
static const SUPPORT_CLOSE_TILT = 32;
|
||||||
|
static const SUPPORT_STOP_TILT = 64;
|
||||||
|
static const SUPPORT_SET_TILT_POSITION = 128;
|
||||||
|
|
||||||
|
bool get supportOpen => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_OPEN) ==
|
||||||
|
CoverEntity.SUPPORT_OPEN);
|
||||||
|
bool get supportClose => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_CLOSE) ==
|
||||||
|
CoverEntity.SUPPORT_CLOSE);
|
||||||
|
bool get supportSetPosition => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_SET_POSITION) ==
|
||||||
|
CoverEntity.SUPPORT_SET_POSITION);
|
||||||
|
bool get supportStop => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_STOP) ==
|
||||||
|
CoverEntity.SUPPORT_STOP);
|
||||||
|
|
||||||
|
bool get supportOpenTilt => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_OPEN_TILT) ==
|
||||||
|
CoverEntity.SUPPORT_OPEN_TILT);
|
||||||
|
bool get supportCloseTilt => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_CLOSE_TILT) ==
|
||||||
|
CoverEntity.SUPPORT_CLOSE_TILT);
|
||||||
|
bool get supportStopTilt => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_STOP_TILT) ==
|
||||||
|
CoverEntity.SUPPORT_STOP_TILT);
|
||||||
|
bool get supportSetTiltPosition => ((attributes["supported_features"] &
|
||||||
|
CoverEntity.SUPPORT_SET_TILT_POSITION) ==
|
||||||
|
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
||||||
|
|
||||||
|
|
||||||
|
double get currentPosition => _getDoubleAttributeValue('current_position');
|
||||||
|
double get currentTiltPosition => _getDoubleAttributeValue('current_tilt_position');
|
||||||
|
bool get canBeOpened => ((state != "opening") && (state != "open"));
|
||||||
|
bool get canBeClosed => ((state != "closing") && (state != "closed"));
|
||||||
|
bool get canTiltBeOpened => currentPosition < 100;
|
||||||
|
bool get canTiltBeClosed => currentPosition > 0;
|
||||||
|
|
||||||
|
CoverEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return CoverStateWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return CoverControlWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
lib/entity_class/date_time_entity.class.dart
Normal file
42
lib/entity_class/date_time_entity.class.dart
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
186
lib/entity_class/entity.class.dart
Normal file
186
lib/entity_class/entity.class.dart
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class Entity {
|
||||||
|
static const STATE_ICONS_COLORS = {
|
||||||
|
"on": Colors.amber,
|
||||||
|
"off": Color.fromRGBO(68, 115, 158, 1.0),
|
||||||
|
"default": Color.fromRGBO(68, 115, 158, 1.0),
|
||||||
|
"unavailable": Colors.black12,
|
||||||
|
"unknown": Colors.black12,
|
||||||
|
"playing": Colors.amber
|
||||||
|
};
|
||||||
|
static const badgeColors = {
|
||||||
|
"default": Color.fromRGBO(223, 76, 30, 1.0),
|
||||||
|
"binary_sensor": Color.fromRGBO(3, 155, 229, 1.0)
|
||||||
|
};
|
||||||
|
static List badgeDomains = [
|
||||||
|
"alarm_control_panel",
|
||||||
|
"binary_sensor",
|
||||||
|
"device_tracker",
|
||||||
|
"updater",
|
||||||
|
"sun",
|
||||||
|
"timer",
|
||||||
|
"sensor"
|
||||||
|
];
|
||||||
|
|
||||||
|
static const rightWidgetPadding = 14.0;
|
||||||
|
static const leftWidgetPadding = 8.0;
|
||||||
|
static const extendedWidgetHeight = 50.0;
|
||||||
|
static const iconSize = 28.0;
|
||||||
|
static const stateFontSize = 16.0;
|
||||||
|
static const nameFontSize = 16.0;
|
||||||
|
static const smallFontSize = 14.0;
|
||||||
|
static const largeFontSize = 24.0;
|
||||||
|
static const inputWidth = 160.0;
|
||||||
|
static const rowPadding = 10.0;
|
||||||
|
|
||||||
|
double widgetHeight = 34.0;
|
||||||
|
|
||||||
|
Map attributes;
|
||||||
|
String domain;
|
||||||
|
String entityId;
|
||||||
|
String state;
|
||||||
|
String assumedState;
|
||||||
|
DateTime _lastUpdated;
|
||||||
|
|
||||||
|
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") &&
|
||||||
|
(attributes != null ? attributes["view"] ?? false : false);
|
||||||
|
bool get isGroup => domain == "group";
|
||||||
|
bool get isBadge => Entity.badgeDomains.contains(domain);
|
||||||
|
String get icon => attributes["icon"] ?? "";
|
||||||
|
bool get isOn => state == "on";
|
||||||
|
String get entityPicture => attributes["entity_picture"];
|
||||||
|
String get unitOfMeasurement => attributes["unit_of_measurement"] ?? "";
|
||||||
|
List get childEntityIds => attributes["entity_id"] ?? [];
|
||||||
|
String get lastUpdated => _getLastUpdatedFormatted();
|
||||||
|
bool get isHidden => attributes["hidden"] ?? false;
|
||||||
|
|
||||||
|
Entity(Map rawData) {
|
||||||
|
update(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(Map rawData) {
|
||||||
|
attributes = rawData["attributes"] ?? {};
|
||||||
|
domain = rawData["entity_id"].split(".")[0];
|
||||||
|
entityId = rawData["entity_id"];
|
||||||
|
state = rawData["state"];
|
||||||
|
assumedState = state;
|
||||||
|
_lastUpdated = DateTime.tryParse(rawData["last_updated"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getDoubleAttributeValue(String attributeName) {
|
||||||
|
var temp1 = attributes["$attributeName"];
|
||||||
|
if (temp1 is int) {
|
||||||
|
return temp1.toDouble();
|
||||||
|
} else if (temp1 is double) {
|
||||||
|
return temp1;
|
||||||
|
} else {
|
||||||
|
return double.tryParse("$temp1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getIntAttributeValue(String attributeName) {
|
||||||
|
var temp1 = attributes["$attributeName"];
|
||||||
|
if (temp1 is int) {
|
||||||
|
return temp1;
|
||||||
|
} else if (temp1 is double) {
|
||||||
|
return temp1.round();
|
||||||
|
} else {
|
||||||
|
return int.tryParse("$temp1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildDefaultWidget(BuildContext context) {
|
||||||
|
return EntityModel(
|
||||||
|
entity: this,
|
||||||
|
child: DefaultEntityContainer(
|
||||||
|
state: _buildStatePart(context),
|
||||||
|
height: widgetHeight,
|
||||||
|
),
|
||||||
|
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), height: widgetHeight),
|
||||||
|
LastUpdatedWidget(),
|
||||||
|
Divider(),
|
||||||
|
_buildAdditionalControlsForPage(context),
|
||||||
|
Divider(),
|
||||||
|
EntityAttributesList()
|
||||||
|
]),
|
||||||
|
handleTap: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget buildBadgeWidget(BuildContext context) {
|
||||||
|
return EntityModel(
|
||||||
|
entity: this,
|
||||||
|
child: BadgeWidget(),
|
||||||
|
handleTap: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAttribute(String attributeName) {
|
||||||
|
if (attributes != null) {
|
||||||
|
return attributes["$attributeName"];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getLastUpdatedFormatted() {
|
||||||
|
if (_lastUpdated == null) {
|
||||||
|
return "-";
|
||||||
|
} else {
|
||||||
|
DateTime now = DateTime.now();
|
||||||
|
Duration d = now.difference(_lastUpdated);
|
||||||
|
String text;
|
||||||
|
int v;
|
||||||
|
if (d.inDays == 0) {
|
||||||
|
if (d.inHours == 0) {
|
||||||
|
if (d.inMinutes == 0) {
|
||||||
|
text = "seconds ago";
|
||||||
|
v = d.inSeconds;
|
||||||
|
} else {
|
||||||
|
text = "minutes ago";
|
||||||
|
v = d.inMinutes;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = "hours ago";
|
||||||
|
v = d.inHours;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = "days ago";
|
||||||
|
v = d.inDays;
|
||||||
|
}
|
||||||
|
return "$v $text";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
lib/entity_class/light_entity.class.dart
Normal file
81
lib/entity_class/light_entity.class.dart
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class LightEntity extends Entity {
|
||||||
|
|
||||||
|
static const SUPPORT_BRIGHTNESS = 1;
|
||||||
|
static const SUPPORT_COLOR_TEMP = 2;
|
||||||
|
static const SUPPORT_EFFECT = 4;
|
||||||
|
static const SUPPORT_FLASH = 8;
|
||||||
|
static const SUPPORT_COLOR = 16;
|
||||||
|
static const SUPPORT_TRANSITION = 32;
|
||||||
|
static const SUPPORT_WHITE_VALUE = 128;
|
||||||
|
|
||||||
|
bool get supportBrightness => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_BRIGHTNESS) ==
|
||||||
|
LightEntity.SUPPORT_BRIGHTNESS);
|
||||||
|
bool get supportColorTemp => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_COLOR_TEMP) ==
|
||||||
|
LightEntity.SUPPORT_COLOR_TEMP);
|
||||||
|
bool get supportEffect => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_EFFECT) ==
|
||||||
|
LightEntity.SUPPORT_EFFECT);
|
||||||
|
bool get supportFlash => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_FLASH) ==
|
||||||
|
LightEntity.SUPPORT_FLASH);
|
||||||
|
bool get supportColor => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_COLOR) ==
|
||||||
|
LightEntity.SUPPORT_COLOR);
|
||||||
|
bool get supportTransition => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_TRANSITION) ==
|
||||||
|
LightEntity.SUPPORT_TRANSITION);
|
||||||
|
bool get supportWhiteValue => ((attributes["supported_features"] &
|
||||||
|
LightEntity.SUPPORT_WHITE_VALUE) ==
|
||||||
|
LightEntity.SUPPORT_WHITE_VALUE);
|
||||||
|
|
||||||
|
int get brightness => _getIntAttributeValue("brightness");
|
||||||
|
int get colorTemp => _getIntAttributeValue("color_temp");
|
||||||
|
double get maxMireds => _getDoubleAttributeValue("max_mireds");
|
||||||
|
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
||||||
|
Color get color => _getColor();
|
||||||
|
bool get isAdditionalControls => ((attributes["supported_features"] != null) && (attributes["supported_features"] != 0));
|
||||||
|
List<String> get effectList => _getEffectList();
|
||||||
|
|
||||||
|
LightEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
Color _getColor() {
|
||||||
|
List rgb = attributes["rgb_color"];
|
||||||
|
try {
|
||||||
|
if ((rgb != null) && (rgb.length > 0)) {
|
||||||
|
return Color.fromARGB(255, rgb[0], rgb[1], rgb[2]);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> _getEffectList() {
|
||||||
|
if (attributes["effect_list"] != null) {
|
||||||
|
List<String> result = (attributes["effect_list"] as List).cast<String>();
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return SwitchStateWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
if (!isAdditionalControls) {
|
||||||
|
return Container(height: 0.0, width: 0.0);
|
||||||
|
} else {
|
||||||
|
return LightControlsWidget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
14
lib/entity_class/select_entity.class.dart
Normal file
14
lib/entity_class/select_entity.class.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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 SelectStateWidget();
|
||||||
|
}
|
||||||
|
}
|
41
lib/entity_class/sun_entity.class.dart
Normal file
41
lib/entity_class/sun_entity.class.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class SunEntity extends Entity {
|
||||||
|
SunEntity(Map rawData) : super(rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SliderEntity extends Entity {
|
||||||
|
SliderEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
double get minValue => attributes["min"] ?? 0.0;
|
||||||
|
double get maxValue => attributes["max"] ?? 100.0;
|
||||||
|
double get valueStep => attributes["step"] ?? 1.0;
|
||||||
|
double get doubleState => double.tryParse(state) ?? 0.0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
//width: 200.0,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
SliderStateWidget(
|
||||||
|
expanded: true,
|
||||||
|
),
|
||||||
|
SimpleEntityState(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePartForPage(BuildContext context) {
|
||||||
|
return SimpleEntityState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return SliderStateWidget(
|
||||||
|
expanded: false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
10
lib/entity_class/switch_entity.class.dart
Normal file
10
lib/entity_class/switch_entity.class.dart
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class SwitchEntity extends Entity {
|
||||||
|
SwitchEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return SwitchStateWidget();
|
||||||
|
}
|
||||||
|
}
|
16
lib/entity_class/text_entity.class.dart
Normal file
16
lib/entity_class/text_entity.class.dart
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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 TextInputStateWidget();
|
||||||
|
}
|
||||||
|
}
|
125
lib/entity_widgets/badge.dart
Normal file
125
lib/entity_widgets/badge.dart
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class BadgeWidget 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)));
|
||||||
|
}
|
||||||
|
}
|
467
lib/entity_widgets/controls/climate_controls.dart
Normal file
467
lib/entity_widgets/controls/climate_controls.dart
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
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 _tmpTemperature = 0.0;
|
||||||
|
double _tmpTargetLow = 0.0;
|
||||||
|
double _tmpTargetHigh = 0.0;
|
||||||
|
double _tmpTargetHumidity = 0.0;
|
||||||
|
String _tmpOperationMode;
|
||||||
|
String _tmpFanMode;
|
||||||
|
String _tmpSwingMode;
|
||||||
|
bool _tmpAwayMode = false;
|
||||||
|
bool _tmpIsOff = false;
|
||||||
|
bool _tmpAuxHeat = false;
|
||||||
|
|
||||||
|
void _resetVars(ClimateEntity entity) {
|
||||||
|
_tmpTemperature = entity.temperature;
|
||||||
|
_tmpTargetHigh = entity.targetHigh;
|
||||||
|
_tmpTargetLow = entity.targetLow;
|
||||||
|
_tmpOperationMode = entity.operationMode;
|
||||||
|
_tmpFanMode = entity.fanMode;
|
||||||
|
_tmpSwingMode = entity.swingMode;
|
||||||
|
_tmpAwayMode = entity.awayMode;
|
||||||
|
_tmpIsOff = entity.isOff;
|
||||||
|
_tmpAuxHeat = entity.auxHeat;
|
||||||
|
_tmpTargetHumidity = entity.targetHumidity;
|
||||||
|
|
||||||
|
_showPending = false;
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _temperatureUp(ClimateEntity entity, double step) {
|
||||||
|
_tmpTemperature = ((_tmpTemperature + step) <= entity.maxTemp) ? _tmpTemperature + step : entity.maxTemp;
|
||||||
|
_setTemperature(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _temperatureDown(ClimateEntity entity, double step) {
|
||||||
|
_tmpTemperature = ((_tmpTemperature - step) >= entity.minTemp) ? _tmpTemperature - step : entity.minTemp;
|
||||||
|
_setTemperature(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _targetLowUp(ClimateEntity entity, double step) {
|
||||||
|
_tmpTargetLow = ((_tmpTargetLow + step) <= entity.maxTemp) ? _tmpTargetLow + step : entity.maxTemp;
|
||||||
|
_setTargetTemp(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _targetLowDown(ClimateEntity entity, double step) {
|
||||||
|
_tmpTargetLow = ((_tmpTargetLow - step) >= entity.minTemp) ? _tmpTargetLow - step : entity.minTemp;
|
||||||
|
_setTargetTemp(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _targetHighUp(ClimateEntity entity, double step) {
|
||||||
|
_tmpTargetHigh = ((_tmpTargetHigh + step) <= entity.maxTemp) ? _tmpTargetHigh + step : entity.maxTemp;
|
||||||
|
_setTargetTemp(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _targetHighDown(ClimateEntity entity, double step) {
|
||||||
|
_tmpTargetHigh = ((_tmpTargetHigh - step) >= entity.minTemp) ? _tmpTargetHigh - step : entity.minTemp;
|
||||||
|
_setTargetTemp(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setTemperature(ClimateEntity entity) {
|
||||||
|
setState(() {
|
||||||
|
_tmpTemperature = double.parse(_tmpTemperature.toStringAsFixed(1));
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"temperature": "${_tmpTemperature.toStringAsFixed(1)}"}));
|
||||||
|
_resetStateTimer(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setTargetTemp(ClimateEntity entity) {
|
||||||
|
setState(() {
|
||||||
|
_tmpTargetLow = double.parse(_tmpTargetLow.toStringAsFixed(1));
|
||||||
|
_tmpTargetHigh = double.parse(_tmpTargetHigh.toStringAsFixed(1));
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"}));
|
||||||
|
_resetStateTimer(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setTargetHumidity(ClimateEntity entity, double value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpTargetHumidity = value.roundToDouble();
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_humidity", entity.entityId,{"humidity": "$_tmpTargetHumidity"}));
|
||||||
|
_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 _setSwingMode(ClimateEntity entity, value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpSwingMode = value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_swing_mode", entity.entityId,{"swing_mode": "$_tmpSwingMode"}));
|
||||||
|
_resetStateTimer(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setFanMode(ClimateEntity entity, value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpFanMode = value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_fan_mode", entity.entityId,{"fan_mode": "$_tmpFanMode"}));
|
||||||
|
_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 _setOnOf(ClimateEntity entity, value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpIsOff = !value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "${_tmpIsOff ? 'turn_off' : 'turn_on'}", entity.entityId, null));
|
||||||
|
_resetStateTimer(entity);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setAuxHeat(ClimateEntity entity, value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpAuxHeat = value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_aux_heat", entity.entityId, {"aux_heat": "$_tmpAuxHeat"}));
|
||||||
|
_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 = (_tmpTemperature != 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>[
|
||||||
|
_buildOnOffControl(entity),
|
||||||
|
_buildTemperatureControls(entity),
|
||||||
|
_buildTargetTemperatureControls(entity),
|
||||||
|
_buildHumidityControls(entity),
|
||||||
|
_buildOperationControl(entity),
|
||||||
|
_buildFanControl(entity),
|
||||||
|
_buildSwingControl(entity),
|
||||||
|
_buildAwayModeControl(entity),
|
||||||
|
_buildAuxHeatControl(entity)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAwayModeControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportAwayMode) {
|
||||||
|
return ModeSwitchWidget(
|
||||||
|
caption: "Away mode",
|
||||||
|
onChange: (value) => _setAwayMode(entity, value),
|
||||||
|
value: _tmpAwayMode,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOnOffControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportOnOff) {
|
||||||
|
return ModeSwitchWidget(
|
||||||
|
onChange: (value) => _setOnOf(entity, value),
|
||||||
|
caption: "On / Off",
|
||||||
|
value: !_tmpIsOff
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildAuxHeatControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportAuxHeat ) {
|
||||||
|
return ModeSwitchWidget(
|
||||||
|
caption: "Aux heat",
|
||||||
|
onChange: (value) => _setAuxHeat(entity, value),
|
||||||
|
value: _tmpAuxHeat
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOperationControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportOperationMode) {
|
||||||
|
return ModeSelectorWidget(
|
||||||
|
onChange: (mode) => _setOperationMode(entity, mode),
|
||||||
|
options: entity.operationList,
|
||||||
|
caption: "Operation",
|
||||||
|
value: _tmpOperationMode,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildFanControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportFanMode) {
|
||||||
|
return ModeSelectorWidget(
|
||||||
|
options: entity.fanList,
|
||||||
|
onChange: (mode) => _setFanMode(entity, mode),
|
||||||
|
caption: "Fan mode",
|
||||||
|
value: _tmpFanMode,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSwingControl(ClimateEntity entity) {
|
||||||
|
if (entity.supportSwingMode) {
|
||||||
|
return ModeSelectorWidget(
|
||||||
|
onChange: (mode) => _setSwingMode(entity, mode),
|
||||||
|
options: entity.swingList,
|
||||||
|
value: _tmpSwingMode,
|
||||||
|
caption: "Swing mode"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(height: 0.0, width: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTemperatureControls(ClimateEntity entity) {
|
||||||
|
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Target temperature", style: TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
TemperatureControlWidget(
|
||||||
|
value: _tmpTemperature,
|
||||||
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
|
onLargeDec: () => _temperatureDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _temperatureUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _temperatureDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _temperatureUp(entity, 0.1),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTargetTemperatureControls(ClimateEntity entity) {
|
||||||
|
List<Widget> controls = [];
|
||||||
|
if ((entity.supportTargetTemperatureLow) && (entity.targetLow != null)) {
|
||||||
|
controls.addAll(<Widget>[
|
||||||
|
TemperatureControlWidget(
|
||||||
|
value: _tmpTargetLow,
|
||||||
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
|
onLargeDec: () => _targetLowDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _targetLowUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _targetLowDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _targetLowUp(entity, 0.1),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Container(height: 10.0),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if ((entity.supportTargetTemperatureHigh) && (entity.targetHigh != null)) {
|
||||||
|
controls.add(
|
||||||
|
TemperatureControlWidget(
|
||||||
|
value: _tmpTargetHigh,
|
||||||
|
fontColor: _showPending ? Colors.red : Colors.black,
|
||||||
|
onLargeDec: () => _targetHighDown(entity, 0.5),
|
||||||
|
onLargeInc: () => _targetHighUp(entity, 0.5),
|
||||||
|
onSmallDec: () => _targetHighDown(entity, 0.1),
|
||||||
|
onSmallInc: () => _targetHighUp(entity, 0.1),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (controls.isNotEmpty) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Target temperature range", style: TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
Row(
|
||||||
|
children: controls,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHumidityControls(ClimateEntity entity) {
|
||||||
|
List<Widget> result = [];
|
||||||
|
if (entity.supportTargetHumidity) {
|
||||||
|
result.addAll(<Widget>[
|
||||||
|
Text(
|
||||||
|
"$_tmpTargetHumidity%",
|
||||||
|
style: TextStyle(fontSize: Entity.largeFontSize),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Slider(
|
||||||
|
value: _tmpTargetHumidity,
|
||||||
|
max: entity.maxHumidity,
|
||||||
|
min: entity.minHumidity,
|
||||||
|
onChanged: ((double val) {
|
||||||
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
|
_tmpTargetHumidity = val.roundToDouble();
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
onChangeEnd: (double v) => _setTargetHumidity(entity, v),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (result.isNotEmpty) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, Entity.rowPadding, 0.0, Entity.rowPadding),
|
||||||
|
child: Text("Target humidity", style: TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: result,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
height: Entity.rowPadding,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(
|
||||||
|
width: 0.0,
|
||||||
|
height: 0.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_resetTimer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TemperatureControlWidget extends StatelessWidget {
|
||||||
|
final double value;
|
||||||
|
final double fontSize;
|
||||||
|
final Color fontColor;
|
||||||
|
final onSmallInc;
|
||||||
|
final onLargeInc;
|
||||||
|
final onSmallDec;
|
||||||
|
final onLargeDec;
|
||||||
|
|
||||||
|
TemperatureControlWidget(
|
||||||
|
{Key key,
|
||||||
|
@required this.value,
|
||||||
|
@required this.onSmallInc,
|
||||||
|
@required this.onSmallDec,
|
||||||
|
@required this.onLargeInc,
|
||||||
|
@required this.onLargeDec,
|
||||||
|
this.fontSize,
|
||||||
|
this.fontColor})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
"$value",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: fontSize ?? 24.0,
|
||||||
|
color: fontColor ?? Colors.black
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-up')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onSmallInc(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-down')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onSmallDec(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-double-up')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onLargeInc(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
'mdi:chevron-double-down')),
|
||||||
|
iconSize: 30.0,
|
||||||
|
onPressed: () => onLargeDec(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
201
lib/entity_widgets/controls/cover_controls.dart
Normal file
201
lib/entity_widgets/controls/cover_controls.dart
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class CoverControlWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
CoverControlWidget({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CoverControlWidgetState createState() => _CoverControlWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CoverControlWidgetState extends State<CoverControlWidget> {
|
||||||
|
|
||||||
|
double _tmpPosition = 0.0;
|
||||||
|
double _tmpTiltPosition = 0.0;
|
||||||
|
bool _changedHere = false;
|
||||||
|
|
||||||
|
void _setNewPosition(CoverEntity entity, double position) {
|
||||||
|
setState(() {
|
||||||
|
_tmpPosition = position.roundToDouble();
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_cover_position", entity.entityId,{"position": _tmpPosition.round()}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setNewTiltPosition(CoverEntity entity, double position) {
|
||||||
|
setState(() {
|
||||||
|
_tmpTiltPosition = position.roundToDouble();
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(entity.domain, "set_cover_tilt_position", entity.entityId,{"tilt_position": _tmpTiltPosition.round()}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _resetVars(CoverEntity entity) {
|
||||||
|
_tmpPosition = entity.currentPosition;
|
||||||
|
_tmpTiltPosition = entity.currentTiltPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final CoverEntity entity = entityModel.entity;
|
||||||
|
TheLogger.debug("${entity.state}");
|
||||||
|
if (_changedHere) {
|
||||||
|
_changedHere = false;
|
||||||
|
} else {
|
||||||
|
_resetVars(entity);
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(Entity.leftWidgetPadding, Entity.rowPadding, Entity.rightWidgetPadding, 0.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
_buildPositionControls(entity),
|
||||||
|
_buildTiltControls(entity)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildPositionControls(CoverEntity entity) {
|
||||||
|
if (entity.supportSetPosition) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, Entity.rowPadding, 0.0, Entity.rowPadding),
|
||||||
|
child: Text("Position", style: TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
Slider(
|
||||||
|
value: _tmpPosition,
|
||||||
|
min: 0.0,
|
||||||
|
max: 100.0,
|
||||||
|
divisions: 10,
|
||||||
|
onChanged: (double value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpPosition = value.roundToDouble();
|
||||||
|
_changedHere = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChangeEnd: (double value) => _setNewPosition(entity, value),
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildTiltControls(CoverEntity entity) {
|
||||||
|
List<Widget> controls = [];
|
||||||
|
if (entity.supportCloseTilt || entity.supportOpenTilt || entity.supportStopTilt) {
|
||||||
|
controls.add(
|
||||||
|
CoverTiltControlsWidget()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (entity.supportSetTiltPosition) {
|
||||||
|
controls.addAll(<Widget>[
|
||||||
|
Slider(
|
||||||
|
value: _tmpTiltPosition,
|
||||||
|
min: 0.0,
|
||||||
|
max: 100.0,
|
||||||
|
divisions: 10,
|
||||||
|
onChanged: (double value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpTiltPosition = value.roundToDouble();
|
||||||
|
_changedHere = true;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChangeEnd: (double value) => _setNewTiltPosition(entity, value),
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if (controls.isNotEmpty) {
|
||||||
|
controls.insert(0, Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, Entity.rowPadding, 0.0, Entity.rowPadding),
|
||||||
|
child: Text("Tilt position", style: TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
));
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: controls,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class CoverTiltControlsWidget extends StatelessWidget {
|
||||||
|
void _open(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "open_cover_tilt", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _close(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "close_cover_tilt", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stop(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "stop_cover_tilt", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final CoverEntity entity = entityModel.entity;
|
||||||
|
List<Widget> buttons = [];
|
||||||
|
if (entity.supportOpenTilt) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:arrow-top-right"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: entity.canTiltBeOpened ? () => _open(entity) : null));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (entity.supportStopTilt) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: () => _stop(entity)));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (entity.supportCloseTilt) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName(
|
||||||
|
"mdi:arrow-bottom-left"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: entity.canTiltBeClosed ? () => _close(entity) : null));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: buttons,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
240
lib/entity_widgets/controls/light_controls.dart
Normal file
240
lib/entity_widgets/controls/light_controls.dart
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class LightControlsWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LightControlsWidgetState createState() => _LightControlsWidgetState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LightControlsWidgetState extends State<LightControlsWidget> {
|
||||||
|
|
||||||
|
int _tmpBrightness;
|
||||||
|
int _tmpColorTemp;
|
||||||
|
Color _tmpColor;
|
||||||
|
bool _changedHere = false;
|
||||||
|
String _tmpEffect;
|
||||||
|
|
||||||
|
void _resetState(LightEntity entity) {
|
||||||
|
_tmpBrightness = entity.brightness ?? 0;
|
||||||
|
_tmpColorTemp = entity.colorTemp;
|
||||||
|
_tmpColor = entity.color;
|
||||||
|
_tmpEffect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setBrightness(LightEntity entity, double value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpBrightness = value.round();
|
||||||
|
_changedHere = true;
|
||||||
|
if (_tmpBrightness > 0) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_on", entity.entityId,
|
||||||
|
{"brightness": _tmpBrightness}));
|
||||||
|
} else {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_off", entity.entityId,
|
||||||
|
null));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setColorTemp(LightEntity entity, double value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpColorTemp = value.round();
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_on", entity.entityId,
|
||||||
|
{"color_temp": _tmpColorTemp}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setColor(LightEntity entity, Color color) {
|
||||||
|
setState(() {
|
||||||
|
_tmpColor = color;
|
||||||
|
_changedHere = true;
|
||||||
|
TheLogger.debug( "Color: [${color.red}, ${color.green}, ${color.blue}]");
|
||||||
|
if ((color == Colors.black) || ((color.red == color.green) && (color.green == color.blue))) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_off", entity.entityId,
|
||||||
|
null));
|
||||||
|
} else {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_on", entity.entityId,
|
||||||
|
{"rgb_color": [color.red, color.green, color.blue]}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setEffect(LightEntity entity, String value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpEffect = value;
|
||||||
|
_changedHere = true;
|
||||||
|
if (_tmpEffect != null) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "turn_on", entity.entityId,
|
||||||
|
{"effect": "$value"}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final LightEntity entity = entityModel.entity;
|
||||||
|
if (!_changedHere) {
|
||||||
|
_resetState(entity);
|
||||||
|
} else {
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
_buildBrightnessControl(entity),
|
||||||
|
_buildColorTempControl(entity),
|
||||||
|
_buildColorControl(entity),
|
||||||
|
_buildEffectControl(entity)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBrightnessControl(LightEntity entity) {
|
||||||
|
if ((entity.supportBrightness) && (_tmpBrightness != null)) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(height: Entity.rowPadding,),
|
||||||
|
Text(
|
||||||
|
"Brightness",
|
||||||
|
style: TextStyle(fontSize: Entity.stateFontSize),
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.brightness_5),
|
||||||
|
Expanded(
|
||||||
|
child: Slider(
|
||||||
|
value: _tmpBrightness.toDouble(),
|
||||||
|
min: 0.0,
|
||||||
|
max: 255.0,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
|
_tmpBrightness = value.round();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) => _setBrightness(entity, value),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildColorTempControl(LightEntity entity) {
|
||||||
|
if ((entity.supportColorTemp) && (_tmpColorTemp != null)) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(height: Entity.rowPadding,),
|
||||||
|
Text(
|
||||||
|
"Color temperature",
|
||||||
|
style: TextStyle(fontSize: Entity.stateFontSize),
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Cold", style: TextStyle(color: Colors.lightBlue),),
|
||||||
|
Expanded(
|
||||||
|
child: Slider(
|
||||||
|
value: _tmpColorTemp.toDouble(),
|
||||||
|
min: entity.minMireds,
|
||||||
|
max: entity.maxMireds,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
|
_tmpColorTemp = value.round();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) => _setColorTemp(entity, value),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text("Warm", style: TextStyle(color: Colors.amberAccent),),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: Entity.rowPadding,)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildColorControl(LightEntity entity) {
|
||||||
|
if ((entity.supportColor) && (entity.color != null)) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(height: Entity.rowPadding,),
|
||||||
|
RaisedButton(
|
||||||
|
onPressed: () => _showColorPicker(entity),
|
||||||
|
color: _tmpColor ?? Colors.black45,
|
||||||
|
child: Text(
|
||||||
|
"COLOR",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 50.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.black12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Container(height: 2*Entity.rowPadding,),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showColorPicker(LightEntity entity) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertDialog(
|
||||||
|
titlePadding: EdgeInsets.all(0.0),
|
||||||
|
contentPadding: EdgeInsets.all(0.0),
|
||||||
|
content: SingleChildScrollView(
|
||||||
|
child: MaterialPicker(
|
||||||
|
pickerColor: _tmpColor,
|
||||||
|
onColorChanged: (color) {
|
||||||
|
_setColor(entity, color);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
enableLabel: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildEffectControl(LightEntity entity) {
|
||||||
|
if ((entity.supportEffect) && (entity.effectList != null)) {
|
||||||
|
return ModeSelectorWidget(
|
||||||
|
onChange: (effect) => _setEffect(entity, effect),
|
||||||
|
caption: "Effect",
|
||||||
|
options: entity.effectList,
|
||||||
|
value: _tmpEffect
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
28
lib/entity_widgets/default_entity_container.dart
Normal file
28
lib/entity_widgets/default_entity_container.dart
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class DefaultEntityContainer extends StatelessWidget {
|
||||||
|
DefaultEntityContainer({
|
||||||
|
Key key,
|
||||||
|
@required this.state,
|
||||||
|
@required this.height
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
final Widget state;
|
||||||
|
final double height;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SizedBox(
|
||||||
|
height: height,
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
EntityIcon(),
|
||||||
|
Expanded(
|
||||||
|
child: EntityName(),
|
||||||
|
),
|
||||||
|
state
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
57
lib/entity_widgets/entity_attributes_list.dart
Normal file
57
lib/entity_widgets/entity_attributes_list.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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("$name", "$value"));
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
entityModel.entity.attributesToShow.forEach((String attr) {
|
||||||
|
String attrValue = entityModel.entity.getAttribute("$attr");
|
||||||
|
if (attrValue != null) {
|
||||||
|
attrs.add(
|
||||||
|
_buildSingleAttribute("$attr", "$attrValue"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
children: attrs,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSingleAttribute(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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
22
lib/entity_widgets/entity_icon.dart
Normal file
22
lib/entity_widgets/entity_icon.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class EntityIcon extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
return GestureDetector(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
Entity.leftWidgetPadding, 0.0, 12.0, 0.0),
|
||||||
|
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
|
entityModel.entity,
|
||||||
|
Entity.iconSize,
|
||||||
|
Entity.STATE_ICONS_COLORS[entityModel.entity.state] ??
|
||||||
|
Entity.STATE_ICONS_COLORS["default"]),
|
||||||
|
),
|
||||||
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
22
lib/entity_widgets/entity_model.dart
Normal file
22
lib/entity_widgets/entity_model.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
23
lib/entity_widgets/entity_name.dart
Normal file
23
lib/entity_widgets/entity_name.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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: Entity.nameFontSize),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () =>
|
||||||
|
entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
|
: null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
14
lib/entity_widgets/entity_page_container.dart
Normal file
14
lib/entity_widgets/entity_page_container.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
18
lib/entity_widgets/last_updated.dart
Normal file
18
lib/entity_widgets/last_updated.dart
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class LastUpdatedWidget extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
Entity.leftWidgetPadding, 0.0, 0.0, 0.0),
|
||||||
|
child: Text(
|
||||||
|
'${entityModel.entity.lastUpdated}',
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: Entity.smallFontSize, color: Colors.black26),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
62
lib/entity_widgets/mode_selector.dart
Normal file
62
lib/entity_widgets/mode_selector.dart
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class ModeSelectorWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
final String caption;
|
||||||
|
final List<String> options;
|
||||||
|
final String value;
|
||||||
|
final double captionFontSize;
|
||||||
|
final double valueFontSize;
|
||||||
|
final double bottomPadding;
|
||||||
|
final onChange;
|
||||||
|
|
||||||
|
ModeSelectorWidget({
|
||||||
|
Key key,
|
||||||
|
this.caption,
|
||||||
|
@required this.options,
|
||||||
|
this.value,
|
||||||
|
@required this.onChange,
|
||||||
|
this.captionFontSize,
|
||||||
|
this.valueFontSize,
|
||||||
|
this.bottomPadding
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("$caption", style: TextStyle(
|
||||||
|
fontSize: captionFontSize ?? Entity.stateFontSize
|
||||||
|
)),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: ButtonTheme(
|
||||||
|
alignedDropdown: true,
|
||||||
|
child: DropdownButton<String>(
|
||||||
|
value: value,
|
||||||
|
iconSize: 30.0,
|
||||||
|
isExpanded: true,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: valueFontSize ?? Entity.largeFontSize,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
hint: Text("Select ${caption.toLowerCase()}"),
|
||||||
|
items: options.map((String value) {
|
||||||
|
return new DropdownMenuItem<String>(
|
||||||
|
value: value,
|
||||||
|
child: Text(value),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (mode) => onChange(mode),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: bottomPadding ?? Entity.rowPadding,)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
38
lib/entity_widgets/mode_swicth.dart
Normal file
38
lib/entity_widgets/mode_swicth.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class ModeSwitchWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
final String caption;
|
||||||
|
final onChange;
|
||||||
|
final double captionFontSize;
|
||||||
|
final bool value;
|
||||||
|
|
||||||
|
ModeSwitchWidget({
|
||||||
|
Key key,
|
||||||
|
@required this.caption,
|
||||||
|
@required this.onChange,
|
||||||
|
this.captionFontSize,
|
||||||
|
this.value
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
"$caption",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: captionFontSize ?? Entity.stateFontSize
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Switch(
|
||||||
|
onChanged: (value) => onChange(value),
|
||||||
|
value: value ?? false,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
lib/entity_widgets/state/button_state.dart
Normal file
24
lib/entity_widgets/state/button_state.dart
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class ButtonStateWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
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: Entity.stateFontSize, color: Colors.blue),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
57
lib/entity_widgets/state/climate_state.dart
Normal file
57
lib/entity_widgets/state/climate_state.dart
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class ClimateStateWidget extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final ClimateEntity entity = entityModel.entity;
|
||||||
|
String targetTemp = "-";
|
||||||
|
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
||||||
|
targetTemp = "${entity.temperature}";
|
||||||
|
} else if ((entity.supportTargetTemperatureLow) &&
|
||||||
|
(entity.targetLow != null)) {
|
||||||
|
targetTemp = "${entity.targetLow}";
|
||||||
|
if ((entity.supportTargetTemperatureHigh) &&
|
||||||
|
(entity.targetHigh != null)) {
|
||||||
|
targetTemp += " - ${entity.targetHigh}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, 0.0, Entity.rightWidgetPadding, 0.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Text("${entity.state}",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontSize: Entity.stateFontSize,
|
||||||
|
)),
|
||||||
|
Text(" $targetTemp",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize,
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
entity.attributes["current_temperature"] != null ?
|
||||||
|
Text("Currently: ${entity.attributes["current_temperature"]}",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize,
|
||||||
|
color: Colors.black45)
|
||||||
|
) :
|
||||||
|
Container(height: 0.0,)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entity))
|
||||||
|
: null,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
65
lib/entity_widgets/state/cover_state.dart
Normal file
65
lib/entity_widgets/state/cover_state.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class CoverStateWidget extends StatelessWidget {
|
||||||
|
void _open(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "open_cover", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _close(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "close_cover", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stop(CoverEntity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
entity.domain, "stop_cover", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final CoverEntity entity = entityModel.entity;
|
||||||
|
List<Widget> buttons = [];
|
||||||
|
if (entity.supportOpen) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-up"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: entity.canBeOpened ? () => _open(entity) : null));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (entity.supportStop) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: () => _stop(entity)));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if (entity.supportClose) {
|
||||||
|
buttons.add(IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-down"),
|
||||||
|
size: Entity.iconSize,
|
||||||
|
),
|
||||||
|
onPressed: entity.canBeClosed ? () => _close(entity) : null));
|
||||||
|
} else {
|
||||||
|
buttons.add(Container(
|
||||||
|
width: Entity.iconSize + 20.0,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
children: buttons,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
75
lib/entity_widgets/state/date_time_state.dart
Normal file
75
lib/entity_widgets/state/date_time_state.dart
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
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.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));
|
||||||
|
}
|
||||||
|
}
|
46
lib/entity_widgets/state/select_state.dart
Normal file
46
lib/entity_widgets/state/select_state.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class SelectStateWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
SelectStateWidget({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SelectStateWidgetState createState() => _SelectStateWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectStateWidgetState extends State<SelectStateWidget> {
|
||||||
|
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
22
lib/entity_widgets/state/simple_state.dart
Normal file
22
lib/entity_widgets/state/simple_state.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class SimpleEntityState extends StatelessWidget {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
0.0, 0.0, Entity.rightWidgetPadding, 0.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
child: Text(
|
||||||
|
"${entityModel.entity.state}${entityModel.entity.unitOfMeasurement}",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: Entity.stateFontSize,
|
||||||
|
)),
|
||||||
|
onTap: () => entityModel.handleTap
|
||||||
|
? eventBus.fire(new ShowEntityPageEvent(entityModel.entity))
|
||||||
|
: null,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
58
lib/entity_widgets/state/slider_state.dart
Normal file
58
lib/entity_widgets/state/slider_state.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class SliderStateWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
final bool expanded;
|
||||||
|
|
||||||
|
SliderStateWidget({Key key, @required this.expanded}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_SliderStateWidgetState createState() => _SliderStateWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SliderStateWidgetState extends State<SliderStateWidget> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
lib/entity_widgets/state/switch_state.dart
Normal file
60
lib/entity_widgets/state/switch_state.dart
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class SwitchStateWidget extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_SwitchStateWidgetState createState() => _SwitchStateWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SwitchStateWidgetState extends State<SwitchStateWidget> {
|
||||||
|
|
||||||
|
@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);
|
||||||
|
final entity = entityModel.entity;
|
||||||
|
if ((entity.attributes["assumed_state"] == null) || (entity.attributes["assumed_state"] == false)) {
|
||||||
|
return Switch(
|
||||||
|
value: entity.assumedState == 'on',
|
||||||
|
onChanged: ((switchState) {
|
||||||
|
_setNewState(switchState, entity);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => _setNewState(false, entity),
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName("mdi:flash-off")),
|
||||||
|
color: entity.assumedState == 'on' ? Colors.black : Colors.blue,
|
||||||
|
iconSize: Entity.iconSize,
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => _setNewState(true, entity),
|
||||||
|
icon: Icon(MaterialDesignIcons.createIconDataFromIconName("mdi:flash")),
|
||||||
|
color: entity.assumedState == 'on' ? Colors.blue : Colors.black,
|
||||||
|
iconSize: Entity.iconSize
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
98
lib/entity_widgets/state/text_input_state.dart
Normal file
98
lib/entity_widgets/state/text_input_state.dart
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class TextInputStateWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
TextInputStateWidget({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_TextInputStateWidgetState createState() => _TextInputStateWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TextInputStateWidgetState extends State<TextInputStateWidget> {
|
||||||
|
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.warning( "Unsupported input mode for ${entity.entityId}");
|
||||||
|
return SimpleEntityState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_focusNode.removeListener(_focusListener);
|
||||||
|
_focusNode.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -14,9 +14,38 @@ import 'package:date_format/date_format.dart';
|
|||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:flutter_colorpicker/material_picker.dart';
|
import 'package:flutter_colorpicker/material_picker.dart';
|
||||||
|
|
||||||
part 'entity.class.dart';
|
part 'entity_class/entity.class.dart';
|
||||||
part 'widgets/stateless_widgets.dart';
|
part 'entity_class/switch_entity.class.dart';
|
||||||
part 'widgets/stateful_widgets.dart';
|
part 'entity_class/button_entity.class.dart';
|
||||||
|
part 'entity_class/text_entity.class.dart';
|
||||||
|
part 'entity_class/climate_entity.class.dart';
|
||||||
|
part 'entity_class/cover_entity.class.dart';
|
||||||
|
part 'entity_class/date_time_entity.class.dart';
|
||||||
|
part 'entity_class/light_entity.class.dart';
|
||||||
|
part 'entity_class/select_entity.class.dart';
|
||||||
|
part 'entity_class/sun_entity.class.dart';
|
||||||
|
part 'entity_widgets/badge.dart';
|
||||||
|
part 'entity_widgets/entity_model.dart';
|
||||||
|
part 'entity_widgets/default_entity_container.dart';
|
||||||
|
part 'entity_widgets/entity_attributes_list.dart';
|
||||||
|
part 'entity_widgets/entity_icon.dart';
|
||||||
|
part 'entity_widgets/entity_name.dart';
|
||||||
|
part 'entity_widgets/last_updated.dart';
|
||||||
|
part 'entity_widgets/mode_swicth.dart';
|
||||||
|
part 'entity_widgets/mode_selector.dart';
|
||||||
|
part 'entity_widgets/entity_page_container.dart';
|
||||||
|
part 'entity_widgets/state/switch_state.dart';
|
||||||
|
part 'entity_widgets/state/slider_state.dart';
|
||||||
|
part 'entity_widgets/state/text_input_state.dart';
|
||||||
|
part 'entity_widgets/state/select_state.dart';
|
||||||
|
part 'entity_widgets/state/simple_state.dart';
|
||||||
|
part 'entity_widgets/state/climate_state.dart';
|
||||||
|
part 'entity_widgets/state/cover_state.dart';
|
||||||
|
part 'entity_widgets/state/date_time_state.dart';
|
||||||
|
part 'entity_widgets/state/button_state.dart';
|
||||||
|
part 'entity_widgets/controls/climate_controls.dart';
|
||||||
|
part 'entity_widgets/controls/cover_controls.dart';
|
||||||
|
part 'entity_widgets/controls/light_controls.dart';
|
||||||
part 'settings.page.dart';
|
part 'settings.page.dart';
|
||||||
part 'home_assistant.class.dart';
|
part 'home_assistant.class.dart';
|
||||||
part 'log.page.dart';
|
part 'log.page.dart';
|
||||||
@ -24,7 +53,9 @@ part 'entity.page.dart';
|
|||||||
part 'utils.class.dart';
|
part 'utils.class.dart';
|
||||||
part 'mdi.class.dart';
|
part 'mdi.class.dart';
|
||||||
part 'entity_collection.class.dart';
|
part 'entity_collection.class.dart';
|
||||||
part 'ui.dart';
|
part 'ui/ui.dart';
|
||||||
|
part 'ui/view.class.dart';
|
||||||
|
part 'ui/card.class.dart';
|
||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
|
74
lib/ui/card.class.dart
Normal file
74
lib/ui/card.class.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class HACard {
|
||||||
|
List<Entity> entities = [];
|
||||||
|
Entity linkedEntity;
|
||||||
|
String name;
|
||||||
|
String id;
|
||||||
|
|
||||||
|
HACard({
|
||||||
|
this.name,
|
||||||
|
this.id,
|
||||||
|
this.linkedEntity
|
||||||
|
});
|
||||||
|
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return HACardWidget(
|
||||||
|
card: this,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class HACardWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
final HACard card;
|
||||||
|
|
||||||
|
const HACardWidget({
|
||||||
|
Key key,
|
||||||
|
this.card
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
if ((card.linkedEntity!= null) && (card.linkedEntity.isHidden)) {
|
||||||
|
return Container(width: 0.0, height: 0.0,);
|
||||||
|
}
|
||||||
|
List<Widget> body = [];
|
||||||
|
body.add(_buildCardHeader());
|
||||||
|
body.addAll(_buildCardBody(context));
|
||||||
|
return Card(
|
||||||
|
child: new Column(mainAxisSize: MainAxisSize.min, children: body)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildCardHeader() {
|
||||||
|
var result;
|
||||||
|
if ((card.name != null) && (card.name.trim().length > 0)) {
|
||||||
|
result = new ListTile(
|
||||||
|
title: Text("${card.name}",
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: 25.0)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = new Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildCardBody(BuildContext context) {
|
||||||
|
List<Widget> result = [];
|
||||||
|
card.entities.forEach((Entity entity) {
|
||||||
|
if (!entity.isHidden) {
|
||||||
|
result.add(
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
|
||||||
|
child: entity.buildDefaultWidget(context),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
lib/ui/ui.dart
Normal file
26
lib/ui/ui.dart
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class HomeAssistantUI {
|
||||||
|
List<HAView> views;
|
||||||
|
|
||||||
|
HomeAssistantUI() {
|
||||||
|
views = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TabBarView(
|
||||||
|
children: _buildViews(context)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildViews(BuildContext context) {
|
||||||
|
List<Widget> result = [];
|
||||||
|
views.forEach((view) {
|
||||||
|
result.add(
|
||||||
|
view.build(context)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,29 +1,4 @@
|
|||||||
part of 'main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class HomeAssistantUI {
|
|
||||||
List<HAView> views;
|
|
||||||
|
|
||||||
HomeAssistantUI() {
|
|
||||||
views = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return TabBarView(
|
|
||||||
children: _buildViews(context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildViews(BuildContext context) {
|
|
||||||
List<Widget> result = [];
|
|
||||||
views.forEach((view) {
|
|
||||||
result.add(
|
|
||||||
view.build(context)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class HAView {
|
class HAView {
|
||||||
List<HACard> cards = [];
|
List<HACard> cards = [];
|
||||||
@ -80,28 +55,28 @@ class HAView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return NewViewWidget(
|
return HAViewWidget(
|
||||||
view: this,
|
view: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewViewWidget extends StatefulWidget {
|
class HAViewWidget extends StatefulWidget {
|
||||||
final HAView view;
|
final HAView view;
|
||||||
|
|
||||||
const NewViewWidget({
|
const HAViewWidget({
|
||||||
Key key,
|
Key key,
|
||||||
this.view
|
this.view
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() {
|
State<StatefulWidget> createState() {
|
||||||
return NewViewWidgetState();
|
return HAViewWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewViewWidgetState extends State<NewViewWidget> {
|
class HAViewWidgetState extends State<HAViewWidget> {
|
||||||
|
|
||||||
StreamSubscription _refreshDataSubscription;
|
StreamSubscription _refreshDataSubscription;
|
||||||
Completer _refreshCompleter;
|
Completer _refreshCompleter;
|
||||||
@ -179,76 +154,3 @@ class NewViewWidgetState extends State<NewViewWidget> {
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class HACard {
|
|
||||||
List<Entity> entities = [];
|
|
||||||
Entity linkedEntity;
|
|
||||||
String name;
|
|
||||||
String id;
|
|
||||||
|
|
||||||
HACard({
|
|
||||||
this.name,
|
|
||||||
this.id,
|
|
||||||
this.linkedEntity
|
|
||||||
});
|
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return NewCardWidget(
|
|
||||||
card: this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class NewCardWidget extends StatelessWidget {
|
|
||||||
|
|
||||||
final HACard card;
|
|
||||||
|
|
||||||
const NewCardWidget({
|
|
||||||
Key key,
|
|
||||||
this.card
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if ((card.linkedEntity!= null) && (card.linkedEntity.isHidden)) {
|
|
||||||
return Container(width: 0.0, height: 0.0,);
|
|
||||||
}
|
|
||||||
List<Widget> body = [];
|
|
||||||
body.add(_buildCardHeader());
|
|
||||||
body.addAll(_buildCardBody(context));
|
|
||||||
return Card(
|
|
||||||
child: new Column(mainAxisSize: MainAxisSize.min, children: body)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildCardHeader() {
|
|
||||||
var result;
|
|
||||||
if ((card.name != null) && (card.name.trim().length > 0)) {
|
|
||||||
result = new ListTile(
|
|
||||||
title: Text("${card.name}",
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: 25.0)),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
result = new Container(width: 0.0, height: 0.0);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildCardBody(BuildContext context) {
|
|
||||||
List<Widget> result = [];
|
|
||||||
card.entities.forEach((Entity entity) {
|
|
||||||
if (!entity.isHidden) {
|
|
||||||
result.add(
|
|
||||||
Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
|
|
||||||
child: entity.buildDefaultWidget(context),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,774 +0,0 @@
|
|||||||
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,
|
|
||||||
@required this.height
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
final Widget state;
|
|
||||||
final double height;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return SizedBox(
|
|
||||||
height: height,
|
|
||||||
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, Entity.rightWidgetPadding, 0.0),
|
|
||||||
child: GestureDetector(
|
|
||||||
child: Text(
|
|
||||||
"${entityModel.entity.state}${entityModel.entity.unitOfMeasurement}",
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: 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: 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(
|
|
||||||
Entity.leftWidgetPadding, 0.0, 12.0, 0.0),
|
|
||||||
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
|
||||||
entityModel.entity,
|
|
||||||
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(
|
|
||||||
Entity.leftWidgetPadding, 0.0, 0.0, 0.0),
|
|
||||||
child: Text(
|
|
||||||
'${entityModel.entity.lastUpdated}',
|
|
||||||
textAlign: TextAlign.left,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 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("$name", "$value"));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
entityModel.entity.attributesToShow.forEach((String attr) {
|
|
||||||
String attrValue = entityModel.entity.getAttribute("$attr");
|
|
||||||
if (attrValue != null) {
|
|
||||||
attrs.add(
|
|
||||||
_buildSingleAttribute("$attr", "$attrValue"));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return Column(
|
|
||||||
children: attrs,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildSingleAttribute(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;
|
|
||||||
String targetTemp = "-";
|
|
||||||
if ((entity.supportTargetTemperature) && (entity.temperature != null)) {
|
|
||||||
targetTemp = "${entity.temperature}";
|
|
||||||
} else if ((entity.supportTargetTemperatureLow) &&
|
|
||||||
(entity.targetLow != null)) {
|
|
||||||
targetTemp = "${entity.targetLow}";
|
|
||||||
if ((entity.supportTargetTemperatureHigh) &&
|
|
||||||
(entity.targetHigh != null)) {
|
|
||||||
targetTemp += " - ${entity.targetHigh}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(
|
|
||||||
0.0, 0.0, Entity.rightWidgetPadding, 0.0),
|
|
||||||
child: GestureDetector(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Text("${entity.state}",
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: Entity.stateFontSize,
|
|
||||||
)),
|
|
||||||
Text(" $targetTemp",
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: Entity.stateFontSize,
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
entity.attributes["current_temperature"] != null ?
|
|
||||||
Text("Currently: ${entity.attributes["current_temperature"]}",
|
|
||||||
textAlign: TextAlign.right,
|
|
||||||
style: new TextStyle(
|
|
||||||
fontSize: Entity.stateFontSize,
|
|
||||||
color: Colors.black45)
|
|
||||||
) :
|
|
||||||
Container(height: 0.0,)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
onTap: () => entityModel.handleTap
|
|
||||||
? eventBus.fire(new ShowEntityPageEvent(entity))
|
|
||||||
: null,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TemperatureControlWidget extends StatelessWidget {
|
|
||||||
final double value;
|
|
||||||
final double fontSize;
|
|
||||||
final Color fontColor;
|
|
||||||
final onSmallInc;
|
|
||||||
final onLargeInc;
|
|
||||||
final onSmallDec;
|
|
||||||
final onLargeDec;
|
|
||||||
|
|
||||||
TemperatureControlWidget(
|
|
||||||
{Key key,
|
|
||||||
@required this.value,
|
|
||||||
@required this.onSmallInc,
|
|
||||||
@required this.onSmallDec,
|
|
||||||
@required this.onLargeInc,
|
|
||||||
@required this.onLargeDec,
|
|
||||||
this.fontSize,
|
|
||||||
this.fontColor})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
"$value",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: fontSize ?? 24.0,
|
|
||||||
color: fontColor ?? Colors.black
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onSmallInc(),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onSmallDec(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Column(
|
|
||||||
children: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-double-up')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onLargeInc(),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
'mdi:chevron-double-down')),
|
|
||||||
iconSize: 30.0,
|
|
||||||
onPressed: () => onLargeDec(),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoverEntityControlState extends StatelessWidget {
|
|
||||||
void _open(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "open_cover", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _close(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "close_cover", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stop(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "stop_cover", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final entityModel = EntityModel.of(context);
|
|
||||||
final CoverEntity entity = entityModel.entity;
|
|
||||||
List<Widget> buttons = [];
|
|
||||||
if (entity.supportOpen) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-up"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: entity.canBeOpened ? () => _open(entity) : null));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (entity.supportStop) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: () => _stop(entity)));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (entity.supportClose) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:arrow-down"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: entity.canBeClosed ? () => _close(entity) : null));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: buttons,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CoverEntityTiltControlButtons extends StatelessWidget {
|
|
||||||
void _open(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "open_cover_tilt", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _close(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "close_cover_tilt", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
void _stop(CoverEntity entity) {
|
|
||||||
eventBus.fire(new ServiceCallEvent(
|
|
||||||
entity.domain, "stop_cover_tilt", entity.entityId, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final entityModel = EntityModel.of(context);
|
|
||||||
final CoverEntity entity = entityModel.entity;
|
|
||||||
List<Widget> buttons = [];
|
|
||||||
if (entity.supportOpenTilt) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
"mdi:arrow-top-right"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: entity.canTiltBeOpened ? () => _open(entity) : null));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (entity.supportStopTilt) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:stop"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: () => _stop(entity)));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if (entity.supportCloseTilt) {
|
|
||||||
buttons.add(IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
MaterialDesignIcons.createIconDataFromIconName(
|
|
||||||
"mdi:arrow-bottom-left"),
|
|
||||||
size: Entity.iconSize,
|
|
||||||
),
|
|
||||||
onPressed: entity.canTiltBeClosed ? () => _close(entity) : null));
|
|
||||||
} else {
|
|
||||||
buttons.add(Container(
|
|
||||||
width: Entity.iconSize + 20.0,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Row(
|
|
||||||
children: buttons,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ButtonStateWidget extends StatelessWidget {
|
|
||||||
|
|
||||||
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: Entity.stateFontSize, color: Colors.blue),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModeSelectorWidget extends StatelessWidget {
|
|
||||||
|
|
||||||
final String caption;
|
|
||||||
final List<String> options;
|
|
||||||
final String value;
|
|
||||||
final double captionFontSize;
|
|
||||||
final double valueFontSize;
|
|
||||||
final double bottomPadding;
|
|
||||||
final onChange;
|
|
||||||
|
|
||||||
ModeSelectorWidget({
|
|
||||||
Key key,
|
|
||||||
this.caption,
|
|
||||||
@required this.options,
|
|
||||||
this.value,
|
|
||||||
@required this.onChange,
|
|
||||||
this.captionFontSize,
|
|
||||||
this.valueFontSize,
|
|
||||||
this.bottomPadding
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: <Widget>[
|
|
||||||
Text("$caption", style: TextStyle(
|
|
||||||
fontSize: captionFontSize ?? Entity.stateFontSize
|
|
||||||
)),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: ButtonTheme(
|
|
||||||
alignedDropdown: true,
|
|
||||||
child: DropdownButton<String>(
|
|
||||||
value: value,
|
|
||||||
iconSize: 30.0,
|
|
||||||
isExpanded: true,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: valueFontSize ?? Entity.largeFontSize,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
hint: Text("Select ${caption.toLowerCase()}"),
|
|
||||||
items: options.map((String value) {
|
|
||||||
return new DropdownMenuItem<String>(
|
|
||||||
value: value,
|
|
||||||
child: Text(value),
|
|
||||||
);
|
|
||||||
}).toList(),
|
|
||||||
onChanged: (mode) => onChange(mode),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(height: bottomPadding ?? Entity.rowPadding,)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ModeSwitchWidget extends StatelessWidget {
|
|
||||||
|
|
||||||
final String caption;
|
|
||||||
final onChange;
|
|
||||||
final double captionFontSize;
|
|
||||||
final bool value;
|
|
||||||
|
|
||||||
ModeSwitchWidget({
|
|
||||||
Key key,
|
|
||||||
@required this.caption,
|
|
||||||
@required this.onChange,
|
|
||||||
this.captionFontSize,
|
|
||||||
this.value
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: Text(
|
|
||||||
"$caption",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: captionFontSize ?? Entity.stateFontSize
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Switch(
|
|
||||||
onChanged: (value) => onChange(value),
|
|
||||||
value: value ?? false,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user