Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
4492fb9f0c | |||
36410752e4 | |||
0219f7bfbb | |||
5f3c77f4b9 | |||
a36c7a9ca3 | |||
56ce6dfeeb | |||
67c214454f | |||
73398378c4 | |||
215871ce9e | |||
fd8ea6befd | |||
809a1a1c8c | |||
fc8f2f200f | |||
f41c9f9197 | |||
cdf55ce68b | |||
12088d9516 | |||
a0235ee385 | |||
67fbdb13c6 | |||
c5960de0be | |||
da15e880ec | |||
efbe33f4e3 | |||
af84c99a2d | |||
438449cad8 | |||
d9ca55c3b7 | |||
f248268984 | |||
8ee096595c |
@ -1,9 +1,9 @@
|
|||||||
part of 'main.dart';
|
part of 'main.dart';
|
||||||
|
|
||||||
class EntityViewPage extends StatefulWidget {
|
class EntityViewPage extends StatefulWidget {
|
||||||
EntityViewPage({Key key, @required this.entity, @required this.homeAssistant }) : super(key: key);
|
EntityViewPage({Key key, @required this.entityId, @required this.homeAssistant }) : super(key: key);
|
||||||
|
|
||||||
final Entity entity;
|
final String entityId;
|
||||||
final HomeAssistant homeAssistant;
|
final HomeAssistant homeAssistant;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -12,30 +12,26 @@ class EntityViewPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _EntityViewPageState extends State<EntityViewPage> {
|
class _EntityViewPageState extends State<EntityViewPage> {
|
||||||
String _title;
|
String _title;
|
||||||
|
StreamSubscription _refreshDataSubscription;
|
||||||
StreamSubscription _stateSubscription;
|
StreamSubscription _stateSubscription;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
|
||||||
if (event.entityId == widget.entity.entityId) {
|
TheLogger.debug("State change event handled by entity page: ${event.entityId}");
|
||||||
|
if (event.entityId == widget.entityId) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
_refreshDataSubscription = eventBus.on<RefreshDataFinishedEvent>().listen((event) {
|
||||||
|
setState(() {});
|
||||||
|
});
|
||||||
_prepareData();
|
_prepareData();
|
||||||
_getHistory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _prepareData() async {
|
void _prepareData() async {
|
||||||
_title = widget.entity.displayName;
|
_title = widget.homeAssistant.entities.get(widget.entityId).displayName;
|
||||||
}
|
|
||||||
|
|
||||||
void _getHistory() {
|
|
||||||
/* widget.homeAssistant.getHistory(widget.entity.entityId).then((List history) {
|
|
||||||
if (history != null) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -54,7 +50,7 @@ class _EntityViewPageState extends State<EntityViewPage> {
|
|||||||
padding: EdgeInsets.all(10.0),
|
padding: EdgeInsets.all(10.0),
|
||||||
child: HomeAssistantModel(
|
child: HomeAssistantModel(
|
||||||
homeAssistant: widget.homeAssistant,
|
homeAssistant: widget.homeAssistant,
|
||||||
child: widget.entity.buildEntityPageWidget(context)
|
child: widget.homeAssistant.entities.get(widget.entityId).buildEntityPageWidget(context)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -63,6 +59,7 @@ class _EntityViewPageState extends State<EntityViewPage> {
|
|||||||
@override
|
@override
|
||||||
void dispose(){
|
void dispose(){
|
||||||
if (_stateSubscription != null) _stateSubscription.cancel();
|
if (_stateSubscription != null) _stateSubscription.cancel();
|
||||||
|
if (_refreshDataSubscription != null) _refreshDataSubscription.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,4 +34,24 @@ class EntityTapAction {
|
|||||||
static const moreInfo = 'more-info';
|
static const moreInfo = 'more-info';
|
||||||
static const toggle = 'toggle';
|
static const toggle = 'toggle';
|
||||||
static const callService = 'call-service';
|
static const callService = 'call-service';
|
||||||
|
static const none = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
class CardType {
|
||||||
|
static const entities = "entities";
|
||||||
|
static const glance = "glance";
|
||||||
|
static const mediaControl = "media-control";
|
||||||
|
static const weatherForecast = "weather-forecast";
|
||||||
|
static const thermostat = "thermostat";
|
||||||
|
static const sensor = "sensor";
|
||||||
|
static const plantStatus = "plant-status";
|
||||||
|
static const pictureEntity = "picture-entity";
|
||||||
|
static const pictureElements = "picture-elements";
|
||||||
|
static const picture = "picture";
|
||||||
|
static const map = "map";
|
||||||
|
static const iframe = "iframe";
|
||||||
|
static const gauge = "gauge";
|
||||||
|
static const entityButton = "entity-button";
|
||||||
|
static const conditional = "conditional";
|
||||||
|
static const alarmPanel = "alarm-panel";
|
||||||
}
|
}
|
@ -76,6 +76,15 @@ class Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getStringListAttributeValue(String attribute) {
|
||||||
|
if (attributes["$attribute"] != null) {
|
||||||
|
List<String> result = (attributes["$attribute"] as List).cast<String>();
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget buildDefaultWidget(BuildContext context) {
|
Widget buildDefaultWidget(BuildContext context) {
|
||||||
return DefaultEntityContainer(
|
return DefaultEntityContainer(
|
||||||
state: _buildStatePart(context)
|
state: _buildStatePart(context)
|
||||||
|
@ -6,8 +6,10 @@ class EntityWrapper {
|
|||||||
String icon;
|
String icon;
|
||||||
String tapAction;
|
String tapAction;
|
||||||
String holdAction;
|
String holdAction;
|
||||||
String actionService;
|
String tapActionService;
|
||||||
Map<String, dynamic> actionServiceData;
|
Map<String, dynamic> tapActionServiceData;
|
||||||
|
String holdActionService;
|
||||||
|
Map<String, dynamic> holdActionServiceData;
|
||||||
Entity entity;
|
Entity entity;
|
||||||
|
|
||||||
|
|
||||||
@ -16,12 +18,66 @@ class EntityWrapper {
|
|||||||
String icon,
|
String icon,
|
||||||
String displayName,
|
String displayName,
|
||||||
this.tapAction: EntityTapAction.moreInfo,
|
this.tapAction: EntityTapAction.moreInfo,
|
||||||
this.holdAction,
|
this.holdAction: EntityTapAction.none,
|
||||||
this.actionService,
|
this.tapActionService,
|
||||||
this.actionServiceData
|
this.tapActionServiceData,
|
||||||
|
this.holdActionService,
|
||||||
|
this.holdActionServiceData
|
||||||
}) {
|
}) {
|
||||||
this.icon = icon ?? entity.icon;
|
this.icon = icon ?? entity.icon;
|
||||||
this.displayName = displayName ?? entity.displayName;
|
this.displayName = displayName ?? entity.displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleTap() {
|
||||||
|
switch (tapAction) {
|
||||||
|
case EntityTapAction.toggle: {
|
||||||
|
eventBus.fire(
|
||||||
|
ServiceCallEvent("homeassistant", "toggle", entity.entityId, null));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EntityTapAction.callService: {
|
||||||
|
eventBus.fire(
|
||||||
|
ServiceCallEvent(tapActionService.split(".")[0], tapActionService.split(".")[1], null, tapActionServiceData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EntityTapAction.none: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
eventBus.fire(
|
||||||
|
new ShowEntityPageEvent(entity));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleHold() {
|
||||||
|
switch (holdAction) {
|
||||||
|
case EntityTapAction.toggle: {
|
||||||
|
eventBus.fire(
|
||||||
|
ServiceCallEvent("homeassistant", "toggle", entity.entityId, null));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EntityTapAction.callService: {
|
||||||
|
eventBus.fire(
|
||||||
|
ServiceCallEvent(tapActionService.split(".")[0], tapActionService.split(".")[1], null, tapActionServiceData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EntityTapAction.moreInfo: {
|
||||||
|
eventBus.fire(
|
||||||
|
new ShowEntityPageEvent(entity));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
32
lib/entity_class/fan_entity.class.dart
Normal file
32
lib/entity_class/fan_entity.class.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class FanEntity extends Entity {
|
||||||
|
|
||||||
|
static const SUPPORT_SET_SPEED = 1;
|
||||||
|
static const SUPPORT_OSCILLATE = 2;
|
||||||
|
static const SUPPORT_DIRECTION = 4;
|
||||||
|
|
||||||
|
FanEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
bool get supportSetSpeed => ((attributes["supported_features"] &
|
||||||
|
FanEntity.SUPPORT_SET_SPEED) ==
|
||||||
|
FanEntity.SUPPORT_SET_SPEED);
|
||||||
|
bool get supportOscillate => ((attributes["supported_features"] &
|
||||||
|
FanEntity.SUPPORT_OSCILLATE) ==
|
||||||
|
FanEntity.SUPPORT_OSCILLATE);
|
||||||
|
bool get supportDirection => ((attributes["supported_features"] &
|
||||||
|
FanEntity.SUPPORT_DIRECTION) ==
|
||||||
|
FanEntity.SUPPORT_DIRECTION);
|
||||||
|
|
||||||
|
List<String> get speedList => getStringListAttributeValue("speed_list");
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return SwitchStateWidget();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
|
return FanControlsWidget();
|
||||||
|
}
|
||||||
|
}
|
43
lib/entity_class/group_entity.class.dart
Normal file
43
lib/entity_class/group_entity.class.dart
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class GroupEntity extends Entity {
|
||||||
|
GroupEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
final List<String> _domainsForSwitchableGroup = ["switch", "light", "automation", "input_boolean"];
|
||||||
|
String mutualDomain;
|
||||||
|
bool switchable = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
if (switchable) {
|
||||||
|
return SwitchStateWidget(
|
||||||
|
domainForService: "homeassistant",
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return super._buildStatePart(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void update(Map rawData) {
|
||||||
|
super.update(rawData);
|
||||||
|
if (_isOneDomain()) {
|
||||||
|
mutualDomain = attributes['entity_id'][0].split(".")[0];
|
||||||
|
switchable = _domainsForSwitchableGroup.contains(mutualDomain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isOneDomain() {
|
||||||
|
bool result = false;
|
||||||
|
if (attributes['entity_id'] != null && attributes['entity_id'] is List && attributes['entity_id'].isNotEmpty) {
|
||||||
|
String firstChildDomain = attributes['entity_id'][0].split(".")[0];
|
||||||
|
result = true;
|
||||||
|
attributes['entity_id'].forEach((childEntityId){
|
||||||
|
if (childEntityId.split(".")[0] != firstChildDomain) {
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -38,7 +38,7 @@ class LightEntity extends Entity {
|
|||||||
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
double get minMireds => _getDoubleAttributeValue("min_mireds");
|
||||||
Color get color => _getColor();
|
Color get color => _getColor();
|
||||||
bool get isAdditionalControls => ((attributes["supported_features"] != null) && (attributes["supported_features"] != 0));
|
bool get isAdditionalControls => ((attributes["supported_features"] != null) && (attributes["supported_features"] != 0));
|
||||||
List<String> get effectList => _getEffectList();
|
List<String> get effectList => getStringListAttributeValue("effect_list");
|
||||||
|
|
||||||
LightEntity(Map rawData) : super(rawData);
|
LightEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
@ -55,15 +55,6 @@ class LightEntity extends Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> _getEffectList() {
|
|
||||||
if (attributes["effect_list"] != null) {
|
|
||||||
List<String> result = (attributes["effect_list"] as List).cast<String>();
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget _buildStatePart(BuildContext context) {
|
Widget _buildStatePart(BuildContext context) {
|
||||||
return SwitchStateWidget();
|
return SwitchStateWidget();
|
||||||
|
12
lib/entity_class/lock_entity.class.dart
Normal file
12
lib/entity_class/lock_entity.class.dart
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
part of '../main.dart';
|
||||||
|
|
||||||
|
class LockEntity extends Entity {
|
||||||
|
LockEntity(Map rawData) : super(rawData);
|
||||||
|
|
||||||
|
bool get isLocked => state == "locked";
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget _buildStatePart(BuildContext context) {
|
||||||
|
return LockStateWidget();
|
||||||
|
}
|
||||||
|
}
|
@ -72,6 +72,9 @@ class MediaPlayerEntity extends Entity {
|
|||||||
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE) ==
|
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE) ==
|
||||||
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE);
|
MediaPlayerEntity.SUPPORT_SELECT_SOUND_MODE);
|
||||||
|
|
||||||
|
List<String> get soundModeList => getStringListAttributeValue("sound_mode_list");
|
||||||
|
List<String> get sourceList => getStringListAttributeValue("source_list");
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
Widget _buildAdditionalControlsForPage(BuildContext context) {
|
||||||
return MediaPlayerControls();
|
return MediaPlayerControls();
|
||||||
|
@ -44,6 +44,9 @@ class EntityCollection {
|
|||||||
case 'sensor': {
|
case 'sensor': {
|
||||||
return SensorEntity(rawEntityData);
|
return SensorEntity(rawEntityData);
|
||||||
}
|
}
|
||||||
|
case 'lock': {
|
||||||
|
return LockEntity(rawEntityData);
|
||||||
|
}
|
||||||
case "automation":
|
case "automation":
|
||||||
case "input_boolean":
|
case "input_boolean":
|
||||||
case "switch": {
|
case "switch": {
|
||||||
@ -52,6 +55,9 @@ class EntityCollection {
|
|||||||
case "light": {
|
case "light": {
|
||||||
return LightEntity(rawEntityData);
|
return LightEntity(rawEntityData);
|
||||||
}
|
}
|
||||||
|
case "group": {
|
||||||
|
return GroupEntity(rawEntityData);
|
||||||
|
}
|
||||||
case "script":
|
case "script":
|
||||||
case "scene": {
|
case "scene": {
|
||||||
return ButtonEntity(rawEntityData);
|
return ButtonEntity(rawEntityData);
|
||||||
@ -74,6 +80,9 @@ class EntityCollection {
|
|||||||
case "cover": {
|
case "cover": {
|
||||||
return CoverEntity(rawEntityData);
|
return CoverEntity(rawEntityData);
|
||||||
}
|
}
|
||||||
|
case "fan": {
|
||||||
|
return FanEntity(rawEntityData);
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
return Entity(rawEntityData);
|
return Entity(rawEntityData);
|
||||||
}
|
}
|
||||||
@ -126,7 +135,7 @@ class EntityCollection {
|
|||||||
List<Entity> groups = [];
|
List<Entity> groups = [];
|
||||||
List<Entity> nonGroupEntities = [];
|
List<Entity> nonGroupEntities = [];
|
||||||
_allEntities.forEach((id, entity){
|
_allEntities.forEach((id, entity){
|
||||||
if ((id.indexOf("group.") == 0) && (id.indexOf(".all_") == -1) && (!entity.isView)) {
|
if (entity.isGroup && (entity.attributes['auto'] == null || (entity.attributes['auto'] && !entity.isHidden)) && (!entity.isView)) {
|
||||||
groups.add(entity);
|
groups.add(entity);
|
||||||
}
|
}
|
||||||
if (!entity.isGroup) {
|
if (!entity.isGroup) {
|
||||||
|
@ -12,7 +12,7 @@ class ModeSelectorWidget extends StatelessWidget {
|
|||||||
|
|
||||||
ModeSelectorWidget({
|
ModeSelectorWidget({
|
||||||
Key key,
|
Key key,
|
||||||
this.caption,
|
@required this.caption,
|
||||||
@required this.options,
|
@required this.options,
|
||||||
this.value,
|
this.value,
|
||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
|
@ -6,27 +6,22 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
final onChange;
|
final onChange;
|
||||||
final double captionFontSize;
|
final double captionFontSize;
|
||||||
final bool value;
|
final bool value;
|
||||||
|
final bool expanded;
|
||||||
|
|
||||||
ModeSwitchWidget({
|
ModeSwitchWidget({
|
||||||
Key key,
|
Key key,
|
||||||
@required this.caption,
|
@required this.caption,
|
||||||
@required this.onChange,
|
@required this.onChange,
|
||||||
this.captionFontSize,
|
this.captionFontSize,
|
||||||
this.value
|
this.value,
|
||||||
|
this.expanded: true
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
return Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
_buildCaption(),
|
||||||
child: Text(
|
|
||||||
"$caption",
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: captionFontSize ?? Sizes.stateFontSize
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Switch(
|
Switch(
|
||||||
onChanged: (value) => onChange(value),
|
onChanged: (value) => onChange(value),
|
||||||
value: value ?? false,
|
value: value ?? false,
|
||||||
@ -35,4 +30,19 @@ class ModeSwitchWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildCaption() {
|
||||||
|
Widget captionWidget = Text(
|
||||||
|
"$caption",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: captionFontSize ?? Sizes.stateFontSize
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (expanded) {
|
||||||
|
return Expanded(
|
||||||
|
child: captionWidget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return captionWidget;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
123
lib/entity_widgets/controls/fan_controls.dart
Normal file
123
lib/entity_widgets/controls/fan_controls.dart
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class FanControlsWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
@override
|
||||||
|
_FanControlsWidgetState createState() => _FanControlsWidgetState();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FanControlsWidgetState extends State<FanControlsWidget> {
|
||||||
|
|
||||||
|
bool _tmpOscillate;
|
||||||
|
bool _tmpDirectionForward;
|
||||||
|
bool _changedHere = false;
|
||||||
|
String _tmpSpeed;
|
||||||
|
|
||||||
|
void _resetState(FanEntity entity) {
|
||||||
|
_tmpOscillate = entity.attributes["oscillating"] ?? false;
|
||||||
|
_tmpDirectionForward = entity.attributes["direction"] == "forward";
|
||||||
|
_tmpSpeed = entity.attributes["speed"];
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setOscillate(FanEntity entity, bool oscillate) {
|
||||||
|
setState(() {
|
||||||
|
_tmpOscillate = oscillate;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
"fan", "oscillate", entity.entityId,
|
||||||
|
{"oscillating": oscillate}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setDirection(FanEntity entity, bool forward) {
|
||||||
|
setState(() {
|
||||||
|
_tmpDirectionForward = forward;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
"fan", "set_direction", entity.entityId,
|
||||||
|
{"direction": forward ? "forward" : "reverse"}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setSpeed(FanEntity entity, String value) {
|
||||||
|
setState(() {
|
||||||
|
_tmpSpeed = value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(new ServiceCallEvent(
|
||||||
|
"fan", "set_speed", entity.entityId,
|
||||||
|
{"speed": value}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final FanEntity entity = entityModel.entityWrapper.entity;
|
||||||
|
if (!_changedHere) {
|
||||||
|
_resetState(entity);
|
||||||
|
} else {
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
_buildSpeedControl(entity),
|
||||||
|
_buildOscillateControl(entity),
|
||||||
|
_buildDirectionControl(entity)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSpeedControl(FanEntity entity) {
|
||||||
|
if (entity.supportSetSpeed && entity.speedList != null && entity.speedList.isNotEmpty) {
|
||||||
|
return ModeSelectorWidget(
|
||||||
|
onChange: (effect) => _setSpeed(entity, effect),
|
||||||
|
caption: "Speed",
|
||||||
|
options: entity.speedList,
|
||||||
|
value: _tmpSpeed
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOscillateControl(FanEntity entity) {
|
||||||
|
if (entity.supportOscillate) {
|
||||||
|
return ModeSwitchWidget(
|
||||||
|
onChange: (value) => _setOscillate(entity, value),
|
||||||
|
caption: "Oscillate",
|
||||||
|
value: _tmpOscillate,
|
||||||
|
expanded: false,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDirectionControl(FanEntity entity) {
|
||||||
|
if (entity.supportDirection) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
onPressed: _tmpDirectionForward ?
|
||||||
|
() => _setDirection(entity, false) :
|
||||||
|
null,
|
||||||
|
icon: Icon(Icons.rotate_left),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: !_tmpDirectionForward ?
|
||||||
|
() => _setDirection(entity, true) :
|
||||||
|
null,
|
||||||
|
icon: Icon(Icons.rotate_right),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0.0, height: 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -79,11 +79,13 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Image(
|
Flexible(
|
||||||
|
child: Image(
|
||||||
image: CachedNetworkImageProvider("$homeAssistantWebHost${entity.entityPicture}"),
|
image: CachedNetworkImageProvider("$homeAssistantWebHost${entity.entityPicture}"),
|
||||||
height: 240.0,
|
height: 240.0,
|
||||||
width: 320.0,
|
//width: 320.0,
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -255,6 +257,8 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
|
|||||||
|
|
||||||
double _newVolumeLevel;
|
double _newVolumeLevel;
|
||||||
bool _changedHere = false;
|
bool _changedHere = false;
|
||||||
|
String _newSoundMode;
|
||||||
|
String _newSource;
|
||||||
|
|
||||||
void _setVolume(double value, String entityId) {
|
void _setVolume(double value, String entityId) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -276,6 +280,22 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
|
|||||||
eventBus.fire(ServiceCallEvent("media_player", "volume_down", entityId, null));
|
eventBus.fire(ServiceCallEvent("media_player", "volume_down", entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _setSoundMode(String value, String entityId) {
|
||||||
|
setState(() {
|
||||||
|
_newSoundMode = value;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(ServiceCallEvent("media_player", "select_sound_mode", entityId, {"sound_mode": "$value"}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setSource(String source, String entityId) {
|
||||||
|
setState(() {
|
||||||
|
_newSource = source;
|
||||||
|
_changedHere = true;
|
||||||
|
eventBus.fire(ServiceCallEvent("media_player", "select_source", entityId, {"source": "$source"}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final MediaPlayerEntity entity = EntityModel.of(context).entityWrapper.entity;
|
final MediaPlayerEntity entity = EntityModel.of(context).entityWrapper.entity;
|
||||||
@ -347,6 +367,38 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entity.supportSelectSoundMode && entity.soundModeList != null) {
|
||||||
|
if (!_changedHere) {
|
||||||
|
_newSoundMode = entity.attributes["sound_mode"];
|
||||||
|
} else {
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
ModeSelectorWidget(
|
||||||
|
options: entity.soundModeList,
|
||||||
|
caption: "Sound mode",
|
||||||
|
value: _newSoundMode,
|
||||||
|
onChange: (value) => _setSoundMode(value, entity.entityId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity.supportSelectSource && entity.sourceList != null) {
|
||||||
|
if (!_changedHere) {
|
||||||
|
_newSource = entity.attributes["source"];
|
||||||
|
} else {
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
ModeSelectorWidget(
|
||||||
|
options: entity.sourceList,
|
||||||
|
caption: "Source",
|
||||||
|
value: _newSource,
|
||||||
|
onChange: (value) => _setSource(value, entity.entityId)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return Column(
|
return Column(
|
||||||
children: children,
|
children: children,
|
||||||
|
@ -10,15 +10,31 @@ class DefaultEntityContainer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Row(
|
final EntityModel entityModel = EntityModel.of(context);
|
||||||
|
return InkWell(
|
||||||
|
onLongPress: () {
|
||||||
|
if (entityModel.handleTap) {
|
||||||
|
entityModel.entityWrapper.handleHold();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onTap: () {
|
||||||
|
if (entityModel.handleTap) {
|
||||||
|
entityModel.entityWrapper.handleTap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
EntityIcon(),
|
EntityIcon(),
|
||||||
Expanded(
|
|
||||||
|
Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
flex: 3,
|
||||||
child: EntityName(),
|
child: EntityName(),
|
||||||
),
|
),
|
||||||
state
|
state
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,59 +10,14 @@ class EntityIcon extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
||||||
return GestureDetector(
|
return Padding(
|
||||||
child: Padding(
|
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
child: MaterialDesignIcons.createIconWidgetFromEntityData(
|
||||||
entityModel.entityWrapper,
|
entityWrapper,
|
||||||
iconSize,
|
iconSize,
|
||||||
EntityColor.stateColor(entityModel.entityWrapper.entity.state)
|
EntityColor.stateColor(entityWrapper.entity.state)
|
||||||
),
|
),
|
||||||
),
|
|
||||||
onLongPress: () {
|
|
||||||
if (entityModel.handleTap) {
|
|
||||||
switch (entityModel.entityWrapper.holdAction) {
|
|
||||||
case EntityTapAction.toggle: {
|
|
||||||
eventBus.fire(
|
|
||||||
ServiceCallEvent("homeassistant", "toggle", entityModel.entityWrapper.entity.entityId, null));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
eventBus.fire(
|
|
||||||
new ShowEntityPageEvent(entityModel.entityWrapper.entity));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onTap: () {
|
|
||||||
if (entityModel.handleTap) {
|
|
||||||
switch (entityModel.entityWrapper.tapAction) {
|
|
||||||
case EntityTapAction.toggle: {
|
|
||||||
eventBus.fire(
|
|
||||||
ServiceCallEvent("homeassistant", "toggle", entityModel.entityWrapper.entity.entityId, null));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case EntityTapAction.callService: {
|
|
||||||
eventBus.fire(
|
|
||||||
ServiceCallEvent(entityModel.entityWrapper.actionService.split(".")[0], entityModel.entityWrapper.actionService.split(".")[1], null, entityModel.entityWrapper.actionServiceData));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
eventBus.fire(
|
|
||||||
new ShowEntityPageEvent(entityModel.entityWrapper.entity));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,22 +12,16 @@ class EntityName extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final entityModel = EntityModel.of(context);
|
final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
||||||
return GestureDetector(
|
return Padding(
|
||||||
child: Padding(
|
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: Text(
|
child: Text(
|
||||||
"${entityModel.entityWrapper.displayName}",
|
"${entityWrapper.displayName}",
|
||||||
overflow: textOverflow,
|
overflow: textOverflow,
|
||||||
softWrap: wordsWrap,
|
softWrap: wordsWrap,
|
||||||
style: TextStyle(fontSize: fontSize),
|
style: TextStyle(fontSize: fontSize),
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
onTap: () =>
|
|
||||||
entityModel.handleTap
|
|
||||||
? eventBus.fire(new ShowEntityPageEvent(entityModel.entityWrapper.entity))
|
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,6 +11,7 @@ class GlanceEntityContainer extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final EntityWrapper entityWrapper = EntityModel.of(context).entityWrapper;
|
||||||
List<Widget> result = [];
|
List<Widget> result = [];
|
||||||
if (showName) {
|
if (showName) {
|
||||||
result.add(EntityName(
|
result.add(EntityName(
|
||||||
@ -21,10 +22,12 @@ class GlanceEntityContainer extends StatelessWidget {
|
|||||||
fontSize: Sizes.smallFontSize,
|
fontSize: Sizes.smallFontSize,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
result.add(EntityIcon(
|
result.add(
|
||||||
|
EntityIcon(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
iconSize: Sizes.largeIconSize,
|
iconSize: Sizes.iconSize,
|
||||||
));
|
)
|
||||||
|
);
|
||||||
if (showState) {
|
if (showState) {
|
||||||
result.add(SimpleEntityState(
|
result.add(SimpleEntityState(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -32,11 +35,20 @@ class GlanceEntityContainer extends StatelessWidget {
|
|||||||
padding: EdgeInsets.only(top: Sizes.rowPadding),
|
padding: EdgeInsets.only(top: Sizes.rowPadding),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
return Column(
|
return Center(
|
||||||
|
child: InkResponse(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(minWidth: Sizes.iconSize*2),
|
||||||
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
//mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
//crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: result,
|
children: result,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => entityWrapper.handleTap(),
|
||||||
|
onLongPress: () => entityWrapper.handleHold(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -19,7 +19,6 @@ class ClimateStateWidget extends StatelessWidget {
|
|||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(
|
padding: EdgeInsets.fromLTRB(
|
||||||
0.0, 0.0, Sizes.rightWidgetPadding, 0.0),
|
0.0, 0.0, Sizes.rightWidgetPadding, 0.0),
|
||||||
child: GestureDetector(
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.end,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -48,10 +47,6 @@ class ClimateStateWidget extends StatelessWidget {
|
|||||||
) :
|
) :
|
||||||
Container(height: 0.0,)
|
Container(height: 0.0,)
|
||||||
],
|
],
|
||||||
),
|
|
||||||
onTap: () => entityModel.handleTap
|
|
||||||
? eventBus.fire(new ShowEntityPageEvent(entity))
|
|
||||||
: null,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
32
lib/entity_widgets/state/lock_state.dart
Normal file
32
lib/entity_widgets/state/lock_state.dart
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
part of '../../main.dart';
|
||||||
|
|
||||||
|
class LockStateWidget extends StatelessWidget {
|
||||||
|
|
||||||
|
void _lock(Entity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent("lock", "lock", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _unlock(Entity entity) {
|
||||||
|
eventBus.fire(new ServiceCallEvent("lock", "unlock", entity.entityId, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final entityModel = EntityModel.of(context);
|
||||||
|
final LockEntity entity = entityModel.entityWrapper.entity;
|
||||||
|
return SizedBox(
|
||||||
|
height: 34.0,
|
||||||
|
child: FlatButton(
|
||||||
|
onPressed: (() {
|
||||||
|
entity.isLocked ? _unlock(entity) : _lock(entity);
|
||||||
|
}),
|
||||||
|
child: Text(
|
||||||
|
entity.isLocked ? "UNLOCK" : "LOCK",
|
||||||
|
textAlign: TextAlign.right,
|
||||||
|
style:
|
||||||
|
new TextStyle(fontSize: Sizes.stateFontSize, color: Colors.blue),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,7 @@ class _SelectStateWidgetState extends State<SelectStateWidget> {
|
|||||||
if (entity.listOptions.isNotEmpty) {
|
if (entity.listOptions.isNotEmpty) {
|
||||||
ctrl = DropdownButton<String>(
|
ctrl = DropdownButton<String>(
|
||||||
value: entity.state,
|
value: entity.state,
|
||||||
|
isExpanded: true,
|
||||||
items: entity.listOptions.map((String value) {
|
items: entity.listOptions.map((String value) {
|
||||||
return new DropdownMenuItem<String>(
|
return new DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
@ -36,7 +37,9 @@ class _SelectStateWidgetState extends State<SelectStateWidget> {
|
|||||||
} else {
|
} else {
|
||||||
ctrl = Text('---');
|
ctrl = Text('---');
|
||||||
}
|
}
|
||||||
return Expanded(
|
return Flexible(
|
||||||
|
flex: 2,
|
||||||
|
fit: FlexFit.tight,
|
||||||
//width: Entity.INPUT_WIDTH,
|
//width: Entity.INPUT_WIDTH,
|
||||||
child: ctrl,
|
child: ctrl,
|
||||||
);
|
);
|
||||||
|
@ -13,24 +13,21 @@ class SimpleEntityState extends StatelessWidget {
|
|||||||
final entityModel = EntityModel.of(context);
|
final entityModel = EntityModel.of(context);
|
||||||
Widget result = Padding(
|
Widget result = Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: GestureDetector(
|
|
||||||
child: Text(
|
child: Text(
|
||||||
"${entityModel.entityWrapper.entity.state}${entityModel.entityWrapper.entity.unitOfMeasurement}",
|
"${entityModel.entityWrapper.entity.state} ${entityModel.entityWrapper.entity.unitOfMeasurement}",
|
||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
maxLines: 4,
|
maxLines: 10,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
softWrap: true,
|
softWrap: true,
|
||||||
style: new TextStyle(
|
style: new TextStyle(
|
||||||
fontSize: Sizes.stateFontSize,
|
fontSize: Sizes.stateFontSize,
|
||||||
)),
|
)
|
||||||
onTap: () => entityModel.handleTap
|
|
||||||
? eventBus.fire(new ShowEntityPageEvent(entityModel.entityWrapper.entity))
|
|
||||||
: null,
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
return SizedBox(
|
return Flexible(
|
||||||
width: MediaQuery.of(context).size.width * 0.3,
|
fit: FlexFit.tight,
|
||||||
|
flex: 2,
|
||||||
child: result,
|
child: result,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
part of '../../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class SwitchStateWidget extends StatefulWidget {
|
class SwitchStateWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
final String domainForService;
|
||||||
|
|
||||||
|
const SwitchStateWidget({Key key, this.domainForService}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_SwitchStateWidgetState createState() => _SwitchStateWidgetState();
|
_SwitchStateWidgetState createState() => _SwitchStateWidgetState();
|
||||||
}
|
}
|
||||||
@ -24,11 +29,17 @@ class _SwitchStateWidgetState extends State<SwitchStateWidget> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
newState = entity.state;
|
newState = entity.state;
|
||||||
updatedHere = true;
|
updatedHere = true;
|
||||||
TheLogger.debug("Timer@!!");
|
//TheLogger.debug("Timer@!!");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
String domain;
|
||||||
|
if (widget.domainForService != null) {
|
||||||
|
domain = widget.domainForService;
|
||||||
|
} else {
|
||||||
|
domain = entity.domain;
|
||||||
|
}
|
||||||
eventBus.fire(new ServiceCallEvent(
|
eventBus.fire(new ServiceCallEvent(
|
||||||
entity.domain, (newValue as bool) ? "turn_on" : "turn_off", entity.entityId, null));
|
domain, (newValue as bool) ? "turn_on" : "turn_off", entity.entityId, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -66,7 +66,9 @@ class _TextInputStateWidgetState extends State<TextInputStateWidget> {
|
|||||||
_tmpValue = entity.state;
|
_tmpValue = entity.state;
|
||||||
}
|
}
|
||||||
if (entity.isTextField || entity.isPasswordField) {
|
if (entity.isTextField || entity.isPasswordField) {
|
||||||
return Expanded(
|
return Flexible(
|
||||||
|
fit: FlexFit.tight,
|
||||||
|
flex: 2,
|
||||||
//width: Entity.INPUT_WIDTH,
|
//width: Entity.INPUT_WIDTH,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
focusNode: _focusNode,
|
focusNode: _focusNode,
|
||||||
|
@ -447,15 +447,21 @@ class HomeAssistant {
|
|||||||
} else {
|
} else {
|
||||||
if (entities.isExist(rawEntity["entity"])) {
|
if (entities.isExist(rawEntity["entity"])) {
|
||||||
Entity e = entities.get(rawEntity["entity"]);
|
Entity e = entities.get(rawEntity["entity"]);
|
||||||
|
String tapAction = EntityTapAction.moreInfo;
|
||||||
|
String holdAction = EntityTapAction.none;
|
||||||
|
if (card.type == CardType.glance) {
|
||||||
|
tapAction = rawEntity["tap_action"] ?? EntityTapAction.moreInfo;
|
||||||
|
holdAction = rawEntity["hold_action"] ?? EntityTapAction.none;
|
||||||
|
}
|
||||||
card.entities.add(
|
card.entities.add(
|
||||||
EntityWrapper(
|
EntityWrapper(
|
||||||
entity: e,
|
entity: e,
|
||||||
displayName: rawEntity["name"],
|
displayName: rawEntity["name"],
|
||||||
icon: rawEntity["icon"],
|
icon: rawEntity["icon"],
|
||||||
tapAction: rawEntity["tap_action"] ?? EntityTapAction.moreInfo,
|
tapAction: tapAction,
|
||||||
holdAction: rawEntity["hold_action"] ?? EntityTapAction.moreInfo,
|
holdAction: holdAction,
|
||||||
actionService: rawEntity["service"],
|
tapActionService: rawEntity["service"],
|
||||||
actionServiceData: rawEntity["service_data"] ?? {"entity_id": e.entityId}
|
tapActionServiceData: rawEntity["service_data"] ?? {"entity_id": e.entityId}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
207
lib/main.dart
207
lib/main.dart
@ -4,7 +4,6 @@ import 'package:flutter/rendering.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:web_socket_channel/io.dart';
|
import 'package:web_socket_channel/io.dart';
|
||||||
import 'package:progress_indicators/progress_indicators.dart';
|
|
||||||
import 'package:event_bus/event_bus.dart';
|
import 'package:event_bus/event_bus.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
@ -14,6 +13,7 @@ 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';
|
||||||
import 'package:charts_flutter/flutter.dart' as charts;
|
import 'package:charts_flutter/flutter.dart' as charts;
|
||||||
|
import 'package:progress_indicators/progress_indicators.dart';
|
||||||
|
|
||||||
part 'entity_class/const.dart';
|
part 'entity_class/const.dart';
|
||||||
part 'entity_class/entity.class.dart';
|
part 'entity_class/entity.class.dart';
|
||||||
@ -29,6 +29,9 @@ part 'entity_class/select_entity.class.dart';
|
|||||||
part 'entity_class/other_entity.class.dart';
|
part 'entity_class/other_entity.class.dart';
|
||||||
part 'entity_class/slider_entity.dart';
|
part 'entity_class/slider_entity.dart';
|
||||||
part 'entity_class/media_player_entity.class.dart';
|
part 'entity_class/media_player_entity.class.dart';
|
||||||
|
part 'entity_class/lock_entity.class.dart';
|
||||||
|
part 'entity_class/group_entity.class.dart';
|
||||||
|
part 'entity_class/fan_entity.class.dart';
|
||||||
part 'entity_widgets/common/badge.dart';
|
part 'entity_widgets/common/badge.dart';
|
||||||
part 'entity_widgets/model_widgets.dart';
|
part 'entity_widgets/model_widgets.dart';
|
||||||
part 'entity_widgets/default_entity_container.dart';
|
part 'entity_widgets/default_entity_container.dart';
|
||||||
@ -57,10 +60,12 @@ part 'entity_widgets/state/climate_state.dart';
|
|||||||
part 'entity_widgets/state/cover_state.dart';
|
part 'entity_widgets/state/cover_state.dart';
|
||||||
part 'entity_widgets/state/date_time_state.dart';
|
part 'entity_widgets/state/date_time_state.dart';
|
||||||
part 'entity_widgets/state/button_state.dart';
|
part 'entity_widgets/state/button_state.dart';
|
||||||
|
part 'entity_widgets/state/lock_state.dart';
|
||||||
part 'entity_widgets/controls/climate_controls.dart';
|
part 'entity_widgets/controls/climate_controls.dart';
|
||||||
part 'entity_widgets/controls/cover_controls.dart';
|
part 'entity_widgets/controls/cover_controls.dart';
|
||||||
part 'entity_widgets/controls/light_controls.dart';
|
part 'entity_widgets/controls/light_controls.dart';
|
||||||
part 'entity_widgets/controls/media_player_widgets.dart';
|
part 'entity_widgets/controls/media_player_widgets.dart';
|
||||||
|
part 'entity_widgets/controls/fan_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';
|
||||||
@ -82,7 +87,7 @@ part 'ui_widgets/card_header_widget.dart';
|
|||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
const String appName = "HA Client";
|
const String appName = "HA Client";
|
||||||
const appVersion = "0.3.9";
|
const appVersion = "0.3.10";
|
||||||
|
|
||||||
String homeAssistantWebHost;
|
String homeAssistantWebHost;
|
||||||
|
|
||||||
@ -147,7 +152,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
StreamSubscription _showEntityPageSubscription;
|
StreamSubscription _showEntityPageSubscription;
|
||||||
StreamSubscription _refreshDataSubscription;
|
StreamSubscription _refreshDataSubscription;
|
||||||
StreamSubscription _showErrorSubscription;
|
StreamSubscription _showErrorSubscription;
|
||||||
int _isLoading = 1;
|
|
||||||
bool _settingsLoaded = false;
|
bool _settingsLoaded = false;
|
||||||
bool _accountMenuExpanded = false;
|
bool _accountMenuExpanded = false;
|
||||||
bool _useLovelaceUI;
|
bool _useLovelaceUI;
|
||||||
@ -158,6 +162,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
_settingsLoaded = false;
|
_settingsLoaded = false;
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
|
TheLogger.debug("<!!!> Creating new HomeAssistant instance");
|
||||||
_homeAssistant = HomeAssistant();
|
_homeAssistant = HomeAssistant();
|
||||||
|
|
||||||
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
|
_settingsSubscription = eventBus.on<SettingsChangedEvent>().listen((event) {
|
||||||
@ -176,10 +181,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
_subscribe();
|
_subscribe();
|
||||||
_refreshData();
|
_refreshData();
|
||||||
}, onError: (_) {
|
}, onError: (_) {
|
||||||
setState(() {
|
_showErrorBottomBar(message: _, errorCode: 5);
|
||||||
_isLoading = 2;
|
|
||||||
});
|
|
||||||
_showErrorSnackBar(message: _, errorCode: 5);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,7 +228,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
if (_showEntityPageSubscription == null) {
|
if (_showEntityPageSubscription == null) {
|
||||||
_showEntityPageSubscription =
|
_showEntityPageSubscription =
|
||||||
eventBus.on<ShowEntityPageEvent>().listen((event) {
|
eventBus.on<ShowEntityPageEvent>().listen((event) {
|
||||||
_showEntityPage(event.entity);
|
_showEntityPage(event.entity.entityId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,21 +240,17 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
if (_showErrorSubscription == null) {
|
if (_showErrorSubscription == null) {
|
||||||
_showErrorSubscription = eventBus.on<ShowErrorEvent>().listen((event){
|
_showErrorSubscription = eventBus.on<ShowErrorEvent>().listen((event){
|
||||||
_showErrorSnackBar(message: event.text, errorCode: event.errorCode);
|
_showErrorBottomBar(message: event.text, errorCode: event.errorCode);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_refreshData() async {
|
_refreshData() async {
|
||||||
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _authType, _useLovelaceUI);
|
_homeAssistant.updateSettings(_webSocketApiEndpoint, _password, _authType, _useLovelaceUI);
|
||||||
setState(() {
|
_hideBottomBar();
|
||||||
_hideErrorSnackBar();
|
_showInfoBottomBar(progress: true,);
|
||||||
_isLoading = 1;
|
|
||||||
});
|
|
||||||
await _homeAssistant.fetch().then((result) {
|
await _homeAssistant.fetch().then((result) {
|
||||||
setState(() {
|
_hideBottomBar();
|
||||||
_isLoading = 0;
|
|
||||||
});
|
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
_setErrorState(e);
|
_setErrorState(e);
|
||||||
});
|
});
|
||||||
@ -260,18 +258,15 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_setErrorState(e) {
|
_setErrorState(e) {
|
||||||
setState(() {
|
|
||||||
_isLoading = 2;
|
|
||||||
});
|
|
||||||
if (e is Error) {
|
if (e is Error) {
|
||||||
TheLogger.error(e.toString());
|
TheLogger.error(e.toString());
|
||||||
TheLogger.error("${e.stackTrace}");
|
TheLogger.error("${e.stackTrace}");
|
||||||
_showErrorSnackBar(
|
_showErrorBottomBar(
|
||||||
message: "There was some error",
|
message: "There was some error",
|
||||||
errorCode: 13
|
errorCode: 13
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_showErrorSnackBar(
|
_showErrorBottomBar(
|
||||||
message: e != null ? e["errorMessage"] ?? "$e" : "Unknown error",
|
message: e != null ? e["errorMessage"] ?? "$e" : "Unknown error",
|
||||||
errorCode: e["errorCode"] != null ? e["errorCode"] : 99
|
errorCode: e["errorCode"] != null ? e["errorCode"] : 99
|
||||||
);
|
);
|
||||||
@ -279,14 +274,18 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _callService(String domain, String service, String entityId, Map<String, dynamic> additionalParams) {
|
void _callService(String domain, String service, String entityId, Map<String, dynamic> additionalParams) {
|
||||||
|
_showInfoBottomBar(
|
||||||
|
message: "Calling $domain.$service",
|
||||||
|
duration: Duration(seconds: 3)
|
||||||
|
);
|
||||||
_homeAssistant.callService(domain, service, entityId, additionalParams).catchError((e) => _setErrorState(e));
|
_homeAssistant.callService(domain, service, entityId, additionalParams).catchError((e) => _setErrorState(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showEntityPage(Entity entity) {
|
void _showEntityPage(String entityId) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => EntityViewPage(entity: entity, homeAssistant: _homeAssistant),
|
builder: (context) => EntityViewPage(entityId: entityId, homeAssistant: _homeAssistant),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -303,31 +302,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAppTitle() {
|
|
||||||
Row titleRow = Row(
|
|
||||||
children: [Text(_homeAssistant != null ? _homeAssistant.locationName : "")],
|
|
||||||
);
|
|
||||||
if (_isLoading == 1) {
|
|
||||||
titleRow.children.add(Padding(
|
|
||||||
child: JumpingDotsProgressIndicator(
|
|
||||||
fontSize: 26.0,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(5.0, 0.0, 0.0, 30.0),
|
|
||||||
));
|
|
||||||
} else if (_isLoading == 2) {
|
|
||||||
titleRow.children.add(Padding(
|
|
||||||
child: Icon(
|
|
||||||
Icons.error_outline,
|
|
||||||
size: 20.0,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.fromLTRB(5.0, 0.0, 0.0, 0.0),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
return titleRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
Drawer _buildAppDrawer() {
|
Drawer _buildAppDrawer() {
|
||||||
List<Widget> menuItems = [];
|
List<Widget> menuItems = [];
|
||||||
menuItems.add(
|
menuItems.add(
|
||||||
@ -409,21 +383,51 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _hideErrorSnackBar() {
|
void _hideBottomBar() {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
|
setState(() {
|
||||||
|
_showBottomBar = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showErrorSnackBar({Key key, @required String message, @required int errorCode}) {
|
Widget _bottomBarAction;
|
||||||
SnackBarAction action;
|
bool _showBottomBar = false;
|
||||||
|
String _bottomBarText;
|
||||||
|
bool _bottomBarProgress;
|
||||||
|
Color _bottomBarColor;
|
||||||
|
Timer _bottomBarTimer;
|
||||||
|
|
||||||
|
void _showInfoBottomBar({String message, bool progress: false, Duration duration}) {
|
||||||
|
_bottomBarTimer?.cancel();
|
||||||
|
_bottomBarAction = Container(height: 0.0, width: 0.0,);
|
||||||
|
_bottomBarColor = Colors.grey.shade50;
|
||||||
|
setState(() {
|
||||||
|
_bottomBarText = message;
|
||||||
|
_bottomBarProgress = progress;
|
||||||
|
_showBottomBar = true;
|
||||||
|
});
|
||||||
|
if (duration != null) {
|
||||||
|
_bottomBarTimer = Timer(duration, () {
|
||||||
|
_hideBottomBar();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showErrorBottomBar({Key key, @required String message, @required int errorCode}) {
|
||||||
|
TextStyle textStyle = TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
fontSize: Sizes.nameFontSize
|
||||||
|
);
|
||||||
|
_bottomBarColor = Colors.red.shade100;
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
case 9:
|
case 9:
|
||||||
case 11:
|
case 11:
|
||||||
case 7:
|
case 7:
|
||||||
case 1: {
|
case 1: {
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Retry",
|
child: Text("Retry", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
_refreshData();
|
_refreshData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -432,10 +436,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
case 5: {
|
case 5: {
|
||||||
message = "Check connection settings";
|
message = "Check connection settings";
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Open",
|
child: Text("Open", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
Navigator.pushNamed(context, '/connection-settings');
|
Navigator.pushNamed(context, '/connection-settings');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -443,10 +447,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 6: {
|
case 6: {
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Settings",
|
child: Text("Settings", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
Navigator.pushNamed(context, '/connection-settings');
|
Navigator.pushNamed(context, '/connection-settings');
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -454,10 +458,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 10: {
|
case 10: {
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Refresh",
|
child: Text("Refresh", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
_refreshData();
|
_refreshData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -465,10 +469,10 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 8: {
|
case 8: {
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Reconnect",
|
child: Text("Reconnect", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
_refreshData();
|
_refreshData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -476,24 +480,29 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
action = SnackBarAction(
|
_bottomBarAction = FlatButton(
|
||||||
label: "Reload",
|
child: Text("Reload", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
//_scaffoldKey?.currentState?.hideCurrentSnackBar();
|
||||||
_refreshData();
|
_refreshData();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_scaffoldKey.currentState.hideCurrentSnackBar();
|
setState(() {
|
||||||
|
_bottomBarProgress = false;
|
||||||
|
_bottomBarText = "$message (code: $errorCode)";
|
||||||
|
_showBottomBar = true;
|
||||||
|
});
|
||||||
|
/*_scaffoldKey.currentState.hideCurrentSnackBar();
|
||||||
_scaffoldKey.currentState.showSnackBar(
|
_scaffoldKey.currentState.showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text("$message (code: $errorCode)"),
|
content: Text("$message (code: $errorCode)"),
|
||||||
action: action,
|
action: action,
|
||||||
duration: Duration(hours: 1),
|
duration: Duration(hours: 1),
|
||||||
)
|
)
|
||||||
);
|
);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||||
@ -506,7 +515,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
floating: true,
|
floating: true,
|
||||||
pinned: true,
|
pinned: true,
|
||||||
primary: true,
|
primary: true,
|
||||||
title: _buildAppTitle(),
|
title: Text(_homeAssistant != null ? _homeAssistant.locationName : ""),
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.menu),
|
icon: Icon(Icons.menu),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -532,7 +541,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
Icon(
|
Icon(
|
||||||
MaterialDesignIcons.createIconDataFromIconName("mdi:home-assistant"),
|
MaterialDesignIcons.createIconDataFromIconName("mdi:home-assistant"),
|
||||||
size: 100.0,
|
size: 100.0,
|
||||||
color: _isLoading == 2 ? Colors.redAccent : Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
@ -544,12 +553,61 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Widget bottomBar;
|
||||||
|
if (_showBottomBar) {
|
||||||
|
List<Widget> bottomBarChildren = [];
|
||||||
|
if (_bottomBarText != null) {
|
||||||
|
bottomBarChildren.add(
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(
|
||||||
|
Sizes.leftWidgetPadding, Sizes.rowPadding, 0.0,
|
||||||
|
Sizes.rowPadding),
|
||||||
|
child: Text(
|
||||||
|
"$_bottomBarText",
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (_bottomBarProgress) {
|
||||||
|
bottomBarChildren.add(
|
||||||
|
CollectionScaleTransition(
|
||||||
|
children: <Widget>[
|
||||||
|
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.on),),
|
||||||
|
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.unavailable),),
|
||||||
|
Icon(Icons.stop, size: 10.0, color: EntityColor.stateColor(EntityState.off),),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (bottomBarChildren.isNotEmpty) {
|
||||||
|
bottomBar = Container(
|
||||||
|
color: _bottomBarColor,
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: _bottomBarProgress ? CrossAxisAlignment.center : CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: bottomBarChildren,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_bottomBarAction
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
// This method is rerun every time setState is called.
|
// This method is rerun every time setState is called.
|
||||||
if (_homeAssistant.ui == null || _homeAssistant.ui.views == null) {
|
if (_homeAssistant.ui == null || _homeAssistant.ui.views == null) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
primary: false,
|
primary: false,
|
||||||
drawer: _buildAppDrawer(),
|
drawer: _buildAppDrawer(),
|
||||||
|
bottomNavigationBar: bottomBar,
|
||||||
body: _buildScaffoldBody(true)
|
body: _buildScaffoldBody(true)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -557,6 +615,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver {
|
|||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
drawer: _buildAppDrawer(),
|
drawer: _buildAppDrawer(),
|
||||||
primary: false,
|
primary: false,
|
||||||
|
bottomNavigationBar: bottomBar,
|
||||||
body: DefaultTabController(
|
body: DefaultTabController(
|
||||||
length: _homeAssistant.ui?.views?.length ?? 0,
|
length: _homeAssistant.ui?.views?.length ?? 0,
|
||||||
child: _buildScaffoldBody(false),
|
child: _buildScaffoldBody(false),
|
||||||
|
@ -22,6 +22,9 @@ class MaterialDesignIcons {
|
|||||||
"cover.closed": "mdi:window-closed",
|
"cover.closed": "mdi:window-closed",
|
||||||
"cover.closing": "mdi:window-open",
|
"cover.closing": "mdi:window-open",
|
||||||
"cover.opening": "mdi:window-open",
|
"cover.opening": "mdi:window-open",
|
||||||
|
"lock.locked": "mdi:lock",
|
||||||
|
"lock.unlocked": "mdi:lock-open",
|
||||||
|
"fan": "mdi:fan"
|
||||||
};
|
};
|
||||||
|
|
||||||
static Map _defaultIconsByDeviceClass = {
|
static Map _defaultIconsByDeviceClass = {
|
||||||
|
@ -97,6 +97,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: ListView(
|
||||||
|
scrollDirection: Axis.vertical,
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
@ -150,8 +151,15 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
new Row(
|
new Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text("Login with access token (HA >= 0.78.0)"),
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
"Login with access token (HA >= 0.78.0)",
|
||||||
|
softWrap: true,
|
||||||
|
maxLines: 2,
|
||||||
|
),
|
||||||
|
),
|
||||||
Switch(
|
Switch(
|
||||||
value: (_newAuthType == "access_token"),
|
value: (_newAuthType == "access_token"),
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
|
@ -23,37 +23,37 @@ class HACard {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
||||||
case "entities": {
|
case CardType.entities: {
|
||||||
return EntitiesCardWidget(
|
return EntitiesCardWidget(
|
||||||
card: this,
|
card: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
case "glance": {
|
case CardType.glance: {
|
||||||
return GlanceCardWidget(
|
return GlanceCardWidget(
|
||||||
card: this,
|
card: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
case "media-control": {
|
case CardType.mediaControl: {
|
||||||
return MediaControlCardWidget(
|
return MediaControlCardWidget(
|
||||||
card: this,
|
card: this,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
case "weather-forecast":
|
case CardType.weatherForecast:
|
||||||
case "thermostat":
|
case CardType.thermostat:
|
||||||
case "sensor":
|
case CardType.sensor:
|
||||||
case "plant-status":
|
case CardType.plantStatus:
|
||||||
case "picture-entity":
|
case CardType.pictureEntity:
|
||||||
case "picture-elements":
|
case CardType.pictureElements:
|
||||||
case "picture":
|
case CardType.picture:
|
||||||
case "map":
|
case CardType.map:
|
||||||
case "iframe":
|
case CardType.iframe:
|
||||||
case "gauge":
|
case CardType.gauge:
|
||||||
case "entity-button":
|
case CardType.entityButton:
|
||||||
case "conditional":
|
case CardType.conditional:
|
||||||
case "alarm-panel": {
|
case CardType.alarmPanel: {
|
||||||
return UnsupportedCardWidget(
|
return UnsupportedCardWidget(
|
||||||
card: this,
|
card: this,
|
||||||
);
|
);
|
||||||
|
@ -6,8 +6,8 @@ class Sizes {
|
|||||||
static const extendedWidgetHeight = 50.0;
|
static const extendedWidgetHeight = 50.0;
|
||||||
static const iconSize = 28.0;
|
static const iconSize = 28.0;
|
||||||
static const largeIconSize = 34.0;
|
static const largeIconSize = 34.0;
|
||||||
static const stateFontSize = 16.0;
|
static const stateFontSize = 15.0;
|
||||||
static const nameFontSize = 16.0;
|
static const nameFontSize = 15.0;
|
||||||
static const smallFontSize = 14.0;
|
static const smallFontSize = 14.0;
|
||||||
static const largeFontSize = 24.0;
|
static const largeFontSize = 24.0;
|
||||||
static const inputWidth = 160.0;
|
static const inputWidth = 160.0;
|
||||||
|
@ -14,7 +14,7 @@ class CardHeaderWidget extends StatelessWidget {
|
|||||||
title: Text("$name",
|
title: Text("$name",
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: 25.0)),
|
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
result = new Container(width: 0.0, height: 0.0);
|
result = new Container(width: 0.0, height: 0.0);
|
||||||
|
@ -24,14 +24,13 @@ class GlanceCardWidget extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildRows(BuildContext context) {
|
Widget _buildRows(BuildContext context) {
|
||||||
List<Widget> result = [];
|
List<Widget> result = [];
|
||||||
double width = MediaQuery.of(context).size.width - Sizes.leftWidgetPadding - (2*Sizes.rightWidgetPadding);
|
|
||||||
List<EntityWrapper> toShow = card.entities.where((entity) {return !entity.entity.isHidden;}).toList();
|
List<EntityWrapper> toShow = card.entities.where((entity) {return !entity.entity.isHidden;}).toList();
|
||||||
int columnsCount = toShow.length >= card.columnsCount ? card.columnsCount : toShow.length;
|
int columnsCount = toShow.length >= card.columnsCount ? card.columnsCount : toShow.length;
|
||||||
card.entities.forEach((EntityWrapper entity) {
|
|
||||||
if (!entity.entity.isHidden) {
|
toShow.forEach((EntityWrapper entity) {
|
||||||
result.add(
|
result.add(
|
||||||
SizedBox(
|
FractionallySizedBox(
|
||||||
width: width / columnsCount,
|
widthFactor: 1/columnsCount,
|
||||||
child: EntityModel(
|
child: EntityModel(
|
||||||
entityWrapper: entity,
|
entityWrapper: entity,
|
||||||
child: entity.entity.buildGlanceWidget(context, card.showName, card.showState),
|
child: entity.entity.buildGlanceWidget(context, card.showName, card.showState),
|
||||||
@ -39,12 +38,11 @@ class GlanceCardWidget extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: EdgeInsets.fromLTRB(0.0, Sizes.rowPadding, 0.0, 2*Sizes.rowPadding),
|
padding: EdgeInsets.fromLTRB(0.0, Sizes.rowPadding, 0.0, 2*Sizes.rowPadding),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
alignment: WrapAlignment.spaceAround,
|
//alignment: WrapAlignment.spaceAround,
|
||||||
runSpacing: Sizes.rowPadding*2,
|
runSpacing: Sizes.rowPadding*2,
|
||||||
children: result,
|
children: result,
|
||||||
),
|
),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 0.3.9+67
|
version: 0.3.10+74
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
sdk: ">=2.0.0-dev.68.0 <3.0.0"
|
||||||
|
Reference in New Issue
Block a user