Compare commits

...

29 Commits

Author SHA1 Message Date
4b9ec5ca6e Foreground location updates 2019-11-10 21:53:28 +00:00
5792652619 Experimental location tracking for every 10 or 5 minutes 2019-11-10 14:41:29 +00:00
2c900333a5 WIP #344 Add network security config to allow user certificates 2019-11-10 13:53:25 +00:00
1f782d7cd3 Resolves #498 Handle bool state 2019-11-10 13:48:05 +00:00
89cc1833de Resolves #493 Send media_player calls even if it if unavailable 2019-11-08 20:50:31 +00:00
1262d8c9aa Resolves #484 Fix entity-filter cards 2019-11-08 20:41:51 +00:00
85b0c4f814 Resolves #419 Fallback to states if no lovelace config found 2019-11-08 20:14:34 +00:00
551a8dfa31 Fixx service calls 2019-11-08 19:37:41 +00:00
139533d2ca 0.7.2 2019-11-01 14:00:13 +00:00
889682f771 Resolves #491 Lovelace badges parse issue 2019-11-01 13:54:35 +00:00
f16c98057f Location fixes 2019-11-01 13:44:51 +00:00
26ec807c25 Resolves #490 Prevent fused coarse location 2019-10-30 16:54:25 +00:00
45af6cbe3c Fix play_media call 2019-10-30 15:04:23 +00:00
5dd9cde12d Entity page fixes 2019-10-30 14:25:30 +00:00
472fb1d367 Merge pull request #489 from estevez-dev/hotfix/0.7.1
Hotfix/0.7.1
2019-10-29 19:47:33 +02:00
8b372fbc0b Merge branch 'master' into hotfix/0.7.1 2019-10-29 19:47:24 +02:00
40d72eb6e1 0.7.1 2019-10-29 17:46:16 +00:00
ced008a7c1 Resolves #486 Fix for very small screens 2019-10-29 17:44:18 +00:00
d1f652282a Merge pull request #488 from estevez-dev/pre-release/0.7.1
Pre release/0.7.1
2019-10-29 19:35:10 +02:00
f656528d5b 711 2019-10-28 20:42:05 +00:00
bcdb2a648c Fix entity page linking after app resumed 2019-10-28 20:40:30 +00:00
8a78745aa7 State change event log 2019-10-28 20:34:43 +00:00
2a3eaabbe4 Remove some logs 2019-10-28 20:32:23 +00:00
bcd175fbfb Fix service calls 2019-10-28 20:31:45 +00:00
f9f013636d 710 2019-10-28 18:02:03 +00:00
b34cc97080 Show entity page on main page 2019-10-28 17:59:47 +00:00
327f623ef7 Remove HA url from main manu 2019-10-28 16:39:47 +00:00
4d0877e5ae Remove Profile link 2019-10-28 16:36:20 +00:00
0eac217399 Set minimum location update intervhals to 15 minute 2019-10-28 16:28:01 +00:00
34 changed files with 427 additions and 241 deletions

View File

@ -3,18 +3,13 @@
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
<!-- The INTERNET permission is required for development. Specifically,
flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
@ -24,7 +19,8 @@
android:name=".Application"
android:label="HA Client"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true">
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config">
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<certificates src="system"/>
<certificates src="user"/>
</trust-anchors>
</base-config>
</network-security-config>

View File

@ -25,9 +25,12 @@ class _AlarmControlPanelControlsWidgetWidgetState extends State<AlarmControlPane
void _callService(AlarmControlPanelEntity entity, String service) {
eventBus.fire(new ServiceCallEvent(
entity.domain, service, entity.entityId,
{"code": "$code"}));
ConnectionManager().callService(
domain: entity.domain,
service: service,
entityId: entity.entityId,
data: {"code": "$code"}
);
setState(() {
code = "";
});
@ -58,7 +61,11 @@ class _AlarmControlPanelControlsWidgetWidgetState extends State<AlarmControlPane
FlatButton(
child: new Text("Yes"),
onPressed: () {
eventBus.fire(new ServiceCallEvent(entity.domain, "alarm_trigger", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain,
service: "alarm_trigger",
entityId: entity.entityId
);
Navigator.of(context).pop();
},
),

View File

@ -83,7 +83,12 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
_tempThrottleTimer = Timer(Duration(seconds: 2), () {
setState(() {
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"temperature": "${_tmpTemperature.toStringAsFixed(1)}"}));
ConnectionManager().callService(
domain: entity.domain,
service: "set_temperature",
entityId: entity.entityId,
data: {"temperature": "${_tmpTemperature.toStringAsFixed(1)}"}
);
_resetStateTimer(entity);
});
});
@ -101,7 +106,12 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
_targetTempThrottleTimer = Timer(Duration(seconds: 2), () {
setState(() {
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_temperature", entity.entityId,{"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"}));
ConnectionManager().callService(
domain: entity.domain,
service: "set_temperature",
entityId: entity.entityId,
data: {"target_temp_high": "${_tmpTargetHigh.toStringAsFixed(1)}", "target_temp_low": "${_tmpTargetLow.toStringAsFixed(1)}"}
);
_resetStateTimer(entity);
});
});
@ -111,7 +121,12 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpTargetHumidity = value.roundToDouble();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_humidity", entity.entityId,{"humidity": "$_tmpTargetHumidity"}));
ConnectionManager().callService(
domain: entity.domain,
service: "set_humidity",
entityId: entity.entityId,
data: {"humidity": "$_tmpTargetHumidity"}
);
_resetStateTimer(entity);
});
}
@ -120,7 +135,12 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpHVACMode = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_hvac_mode", entity.entityId,{"hvac_mode": "$_tmpHVACMode"}));
ConnectionManager().callService(
domain: entity.domain,
service: "set_hvac_mode",
entityId: entity.entityId,
data: {"hvac_mode": "$_tmpHVACMode"}
);
_resetStateTimer(entity);
});
}
@ -129,7 +149,12 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpSwingMode = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_swing_mode", entity.entityId,{"swing_mode": "$_tmpSwingMode"}));
ConnectionManager().callService(
domain: entity.domain,
service: "set_swing_mode",
entityId: entity.entityId,
data: {"swing_mode": "$_tmpSwingMode"}
);
_resetStateTimer(entity);
});
}
@ -138,7 +163,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpFanMode = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_fan_mode", entity.entityId,{"fan_mode": "$_tmpFanMode"}));
ConnectionManager().callService(domain: entity.domain, service: "set_fan_mode", entityId: entity.entityId, data: {"fan_mode": "$_tmpFanMode"});
_resetStateTimer(entity);
});
}
@ -147,7 +172,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpPresetMode = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_preset_mode", entity.entityId,{"preset_mode": "$_tmpPresetMode"}));
ConnectionManager().callService(domain: entity.domain, service: "set_preset_mode", entityId: entity.entityId, data: {"preset_mode": "$_tmpPresetMode"});
_resetStateTimer(entity);
});
}
@ -165,7 +190,7 @@ class _ClimateControlWidgetState extends State<ClimateControlWidget> {
setState(() {
_tmpAuxHeat = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_aux_heat", entity.entityId, {"aux_heat": "$_tmpAuxHeat"}));
ConnectionManager().callService(domain: entity.domain, service: "set_aux_heat", entityId: entity.entityId, data: {"aux_heat": "$_tmpAuxHeat"});
_resetStateTimer(entity);
});
}

View File

@ -18,7 +18,7 @@ class _CoverControlWidgetState extends State<CoverControlWidget> {
setState(() {
_tmpPosition = position.roundToDouble();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_cover_position", entity.entityId,{"position": _tmpPosition.round()}));
ConnectionManager().callService(domain: entity.domain, service: "set_cover_position", entityId: entity.entityId, data: {"position": _tmpPosition.round()});
});
}
@ -26,7 +26,7 @@ class _CoverControlWidgetState extends State<CoverControlWidget> {
setState(() {
_tmpTiltPosition = position.roundToDouble();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(entity.domain, "set_cover_tilt_position", entity.entityId,{"tilt_position": _tmpTiltPosition.round()}));
ConnectionManager().callService(domain: entity.domain, service: "set_cover_tilt_position", entityId: entity.entityId, data: {"tilt_position": _tmpTiltPosition.round()});
});
}
@ -135,18 +135,18 @@ class _CoverControlWidgetState extends State<CoverControlWidget> {
class CoverTiltControlsWidget extends StatelessWidget {
void _open(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "open_cover_tilt", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain, service: "open_cover_tilt", entityId: entity.entityId);
}
void _close(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "close_cover_tilt", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain, service: "close_cover_tilt", entityId: entity.entityId);
}
void _stop(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "stop_cover_tilt", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain, service: "stop_cover_tilt", entityId: entity.entityId);
}
@override

View File

@ -2,18 +2,27 @@ part of '../../../main.dart';
class CoverStateWidget extends StatelessWidget {
void _open(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "open_cover", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain,
service: "open_cover",
entityId: entity.entityId
);
}
void _close(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "close_cover", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain,
service: "close_cover",
entityId: entity.entityId
);
}
void _stop(CoverEntity entity) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "stop_cover", entity.entityId, null));
ConnectionManager().callService(
domain: entity.domain,
service: "stop_cover",
entityId: entity.entityId
);
}
@override

View File

@ -35,8 +35,7 @@ class DateTimeEntity extends Entity {
return formattedState;
}
void setNewState(newValue) {
eventBus
.fire(new ServiceCallEvent(domain, "set_datetime", entityId, newValue));
void setNewState(Map newValue) {
ConnectionManager().callService(domain: domain, service: "set_datetime", entityId: entityId, data: newValue);
}
}

View File

@ -153,7 +153,7 @@ class Entity {
domain = rawData["entity_id"].split(".")[0];
entityId = rawData["entity_id"];
deviceClass = attributes["device_class"];
state = rawData["state"];
state = rawData["state"] is bool ? (rawData["state"] ? EntityState.on : EntityState.off) : rawData["state"];
displayState = Entity.StateByDeviceClass["$deviceClass.$state"] ?? (state.toLowerCase() == 'unknown' ? '-' : state);
_lastUpdated = DateTime.tryParse(rawData["last_updated"]);
entityPicture = _getEntityPictureUrl(webHost);

View File

@ -17,7 +17,7 @@ class EntityPageLayout extends StatelessWidget {
showClose ?
Container(
color: Colors.blue[300],
height: 36,
height: 40,
child: Row(
children: <Widget>[
Expanded(
@ -37,7 +37,7 @@ class EntityPageLayout extends StatelessWidget {
padding: EdgeInsets.all(0),
icon: Icon(Icons.close),
color: Colors.white,
iconSize: 30.0,
iconSize: 36.0,
onPressed: () {
eventBus.fire(ShowEntityPageEvent());
},

View File

@ -32,17 +32,17 @@ class EntityWrapper {
void handleTap() {
switch (uiAction.tapAction) {
case EntityUIAction.toggle: {
eventBus.fire(
ServiceCallEvent("homeassistant", "toggle", entity.entityId, null));
ConnectionManager().callService(domain: "homeassistant", service: "toggle", entityId: entity.entityId);
break;
}
case EntityUIAction.callService: {
if (uiAction.tapService != null) {
eventBus.fire(
ServiceCallEvent(uiAction.tapService.split(".")[0],
uiAction.tapService.split(".")[1], null,
uiAction.tapServiceData));
ConnectionManager().callService(
domain: uiAction.tapService.split(".")[0],
service: uiAction.tapService.split(".")[1],
data: uiAction.tapServiceData
);
}
break;
}
@ -76,17 +76,17 @@ class EntityWrapper {
void handleHold() {
switch (uiAction.holdAction) {
case EntityUIAction.toggle: {
eventBus.fire(
ServiceCallEvent("homeassistant", "toggle", entity.entityId, null));
ConnectionManager().callService(domain: "homeassistant", service: "toggle", entityId: entity.entityId);
break;
}
case EntityUIAction.callService: {
if (uiAction.holdService != null) {
eventBus.fire(
ServiceCallEvent(uiAction.holdService.split(".")[0],
uiAction.holdService.split(".")[1], null,
uiAction.holdServiceData));
ConnectionManager().callService(
domain: uiAction.holdService.split(".")[0],
service: uiAction.holdService.split(".")[1],
data: uiAction.holdServiceData
);
}
break;
}

View File

@ -24,9 +24,12 @@ class _FanControlsWidgetState extends State<FanControlsWidget> {
setState(() {
_tmpOscillate = oscillate;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
"fan", "oscillate", entity.entityId,
{"oscillating": oscillate}));
ConnectionManager().callService(
domain: "fan",
service: "oscillate",
entityId: entity.entityId,
data: {"oscillating": oscillate}
);
});
}
@ -34,9 +37,12 @@ class _FanControlsWidgetState extends State<FanControlsWidget> {
setState(() {
_tmpDirectionForward = forward;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
"fan", "set_direction", entity.entityId,
{"direction": forward ? "forward" : "reverse"}));
ConnectionManager().callService(
domain: "fan",
service: "set_direction",
entityId: entity.entityId,
data: {"direction": forward ? "forward" : "reverse"}
);
});
}
@ -44,9 +50,12 @@ class _FanControlsWidgetState extends State<FanControlsWidget> {
setState(() {
_tmpSpeed = value;
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
"fan", "set_speed", entity.entityId,
{"speed": value}));
ConnectionManager().callService(
domain: "fan",
service: "set_speed",
entityId: entity.entityId,
data: {"speed": value}
);
});
}

View File

@ -18,7 +18,7 @@ class FlatServiceButton extends StatelessWidget {
}) : super(key: key);
void _setNewState() {
eventBus.fire(new ServiceCallEvent(serviceDomain, serviceName, entityId, null));
ConnectionManager().callService(domain: serviceDomain, service: serviceName, entityId: entityId);
}
@override

View File

@ -28,9 +28,12 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
setState(() {
_tmpBrightness = value.round();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
{"brightness": _tmpBrightness}));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"brightness": _tmpBrightness}
);
});
}
@ -38,9 +41,12 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
setState(() {
_tmpWhiteValue = value.round();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
{"white_value": _tmpWhiteValue}));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"white_value": _tmpWhiteValue}
);
});
}
@ -49,9 +55,12 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
setState(() {
_tmpColorTemp = value.round();
_changedHere = true;
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
{"color_temp": _tmpColorTemp}));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"color_temp": _tmpColorTemp}
);
});
}
@ -59,10 +68,12 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
setState(() {
_tmpColor = color;
_changedHere = true;
Logger.d( "HS Color: [${color.hue}, ${color.saturation}]");
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
{"hs_color": [color.hue, color.saturation*100]}));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"hs_color": [color.hue, color.saturation*100]}
);
});
}
@ -71,9 +82,12 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
_tmpEffect = value;
_changedHere = true;
if (_tmpEffect != null) {
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
{"effect": "$value"}));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId,
data: {"effect": "$value"}
);
}
});
}
@ -227,8 +241,6 @@ class _LightControlsWidgetState extends State<LightControlsWidget> {
Widget _buildEffectControl(LightEntity entity) {
if ((entity.supportEffect) && (entity.effectList != null)) {
Logger.d("[LIGHT] entity effects: ${entity.effectList}");
Logger.d("[LIGHT] current effect: $_tmpEffect");
List<String> list = List.from(entity.effectList);
if (_tmpEffect!= null && !list.contains(_tmpEffect)) {
list.insert(0, _tmpEffect);

View File

@ -7,11 +7,11 @@ class LockStateWidget extends StatelessWidget {
const LockStateWidget({Key key, this.assumedState: false}) : super(key: key);
void _lock(Entity entity) {
eventBus.fire(new ServiceCallEvent("lock", "lock", entity.entityId, null));
ConnectionManager().callService(domain: "lock", service: "lock", entityId: entity.entityId);
}
void _unlock(Entity entity) {
eventBus.fire(new ServiceCallEvent("lock", "unlock", entity.entityId, null));
ConnectionManager().callService(domain: "lock", service: "unlock", entityId: entity.entityId);
}
@override

View File

@ -56,12 +56,12 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
color: Colors.orange,
focusColor: Colors.white,
onPressed: () {
eventBus.fire(ServiceCallEvent(
"media_player",
"media_seek",
"${entity.entityId}",
{"seek_position": _savedPosition}
));
ConnectionManager().callService(
domain: "media_player",
service: "media_seek",
entityId: entity.entityId,
data: {"seek_position": _savedPosition}
);
setState(() {
_savedPosition = 0;
});
@ -103,12 +103,12 @@ class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
_seekStarted = false;
Timer(Duration(milliseconds: 500), () {
if (!_seekStarted) {
eventBus.fire(ServiceCallEvent(
"media_player",
"media_seek",
"${entity.entityId}",
{"seek_position": val}
));
ConnectionManager().callService(
domain: "media_player",
service: "media_seek",
entityId: entity.entityId,
data: {"seek_position": val}
);
setState(() {
_changedHere = true;
_currentPosition = val;

View File

@ -118,26 +118,28 @@ class MediaPlayerPlaybackControls extends StatelessWidget {
void _setPower(MediaPlayerEntity entity) {
if (entity.state != EntityState.unavailable && entity.state != EntityState.unknown) {
if (entity.state == EntityState.off) {
Logger.d("${entity.entityId} turn_on");
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_on", entity.entityId,
null));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_on",
entityId: entity.entityId
);
} else {
Logger.d("${entity.entityId} turn_off");
eventBus.fire(new ServiceCallEvent(
entity.domain, "turn_off", entity.entityId,
null));
ConnectionManager().callService(
domain: entity.domain,
service: "turn_off",
entityId: entity.entityId
);
}
}
}
void _callAction(MediaPlayerEntity entity, String action) {
Logger.d("${entity.entityId} $action");
eventBus.fire(new ServiceCallEvent(
entity.domain, "$action", entity.entityId,
null));
ConnectionManager().callService(
domain: entity.domain,
service: "$action",
entityId: entity.entityId
);
}
@override
@ -264,27 +266,50 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
setState(() {
_changedHere = true;
_newVolumeLevel = value;
eventBus.fire(ServiceCallEvent("media_player", "volume_set", entityId, {"volume_level": value}));
ConnectionManager().callService(
domain: "media_player",
service: "volume_set",
entityId: entityId,
data: {"volume_level": value}
);
});
}
void _setVolumeMute(bool isMuted, String entityId) {
eventBus.fire(ServiceCallEvent("media_player", "volume_mute", entityId, {"is_volume_muted": isMuted}));
ConnectionManager().callService(
domain: "media_player",
service: "volume_mute",
entityId: entityId,
data: {"is_volume_muted": isMuted}
);
}
void _setVolumeUp(String entityId) {
eventBus.fire(ServiceCallEvent("media_player", "volume_up", entityId, null));
ConnectionManager().callService(
domain: "media_player",
service: "volume_up",
entityId: entityId
);
}
void _setVolumeDown(String entityId) {
eventBus.fire(ServiceCallEvent("media_player", "volume_down", entityId, null));
ConnectionManager().callService(
domain: "media_player",
service: "volume_down",
entityId: entityId
);
}
void _setSoundMode(String value, String entityId) {
setState(() {
_newSoundMode = value;
_changedHere = true;
eventBus.fire(ServiceCallEvent("media_player", "select_sound_mode", entityId, {"sound_mode": "$value"}));
ConnectionManager().callService(
domain: "media_player",
service: "select_sound_mode",
entityId: entityId,
data: {"sound_mode": "$value"}
);
});
}
@ -292,7 +317,12 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
setState(() {
_newSource = source;
_changedHere = true;
eventBus.fire(ServiceCallEvent("media_player", "select_source", entityId, {"source": "$source"}));
ConnectionManager().callService(
domain: "media_player",
service: "select_source",
entityId: entityId,
data: {"source": "$source"}
);
});
}
@ -431,14 +461,10 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
void _duplicateTo(entity) {
HomeAssistant().savedPlayerPosition = entity.getActualPosition().toInt();
if (MediaQuery.of(context).size.width < Sizes.tabletMinWidth) {
Navigator.of(context).popAndPushNamed("/play-media", arguments: {"url": entity.attributes["media_content_id"], "type": entity.attributes["media_content_type"]});
} else {
Navigator.of(context).pushNamed("/play-media", arguments: {
Navigator.of(context).pushNamed("/play-media", arguments: {
"url": entity.attributes["media_content_id"],
"type": entity.attributes["media_content_type"]
});
}
}
void _switchTo(entity) {

View File

@ -11,8 +11,12 @@ class SelectStateWidget extends StatefulWidget {
class _SelectStateWidgetState extends State<SelectStateWidget> {
void setNewState(domain, entityId, newValue) {
eventBus.fire(new ServiceCallEvent(domain, "select_option", entityId,
{"option": "$newValue"}));
ConnectionManager().callService(
domain: domain,
service: "select_option",
entityId: entityId,
data: {"option": "$newValue"}
);
}
@override

View File

@ -18,8 +18,12 @@ class _SliderControlsWidgetState extends State<SliderControlsWidget> {
_newValue = newValue;
_changedHere = true;
});
eventBus.fire(new ServiceCallEvent(domain, "set_value", entityId,
{"value": "${newValue.toString()}"}));
ConnectionManager().callService(
domain: domain,
service: "set_value",
entityId: entityId,
data: {"value": "${newValue.toString()}"}
);
}
@override

View File

@ -38,8 +38,11 @@ class _SwitchStateWidgetState extends State<SwitchStateWidget> {
} else {
domain = entity.domain;
}
eventBus.fire(new ServiceCallEvent(
domain, (newValue as bool) ? "turn_on" : "turn_off", entity.entityId, null));
ConnectionManager().callService(
domain: domain,
service: (newValue as bool) ? "turn_on" : "turn_off",
entityId: entity.entityId
);
}
@override

View File

@ -26,8 +26,12 @@ class _TextInputStateWidgetState extends State<TextInputStateWidget> {
void setNewState(newValue, domain, entityId) {
if (validate(newValue, _minLength, _maxLength)) {
eventBus.fire(new ServiceCallEvent(domain, "set_value", entityId,
{"value": "$newValue"}));
ConnectionManager().callService(
domain: domain,
service: "set_value",
entityId: entityId,
data: {"value": "$newValue"}
);
} else {
setState(() {
_tmpValue = _entityState;

View File

@ -197,7 +197,7 @@ class VacuumControls extends StatelessWidget {
domain: "vacuum",
entityId: entity.entityId,
service: "set_fan_speed",
additionalServiceData: {"fan_speed": val}
data: {"fan_speed": val}
)
),
);

View File

@ -106,10 +106,20 @@ class HomeAssistant {
});
}
Future _getLovelace() async {
await ConnectionManager().sendSocketMessage(type: "lovelace/config").then((data) => _rawLovelaceData = data).catchError((e) {
throw HAError("Error getting lovelace config: $e");
Future _getLovelace() {
Completer completer = Completer();
ConnectionManager().sendSocketMessage(type: "lovelace/config").then((data) {
_rawLovelaceData = data;
completer.complete();
}).catchError((e) {
if ("$e" == "config_not_found") {
ConnectionManager().useLovelace = false;
completer.complete();
} else {
completer.completeError(HAError("Error getting lovelace config: $e"));
}
});
return completer.future;
}
Future _getUserInfo() async {
@ -125,7 +135,6 @@ class HomeAssistant {
Future _getServices() async {
await ConnectionManager().sendSocketMessage(type: "get_services").then((data) {
Logger.d("Got ${data.length} services");
Logger.d("Media extractor: ${data["media_extractor"]}");
services = data;
}).catchError((e) {
Logger.w("Can't get services: $e");
@ -180,9 +189,17 @@ class HomeAssistant {
if (rawView['badges'] != null && rawView['badges'] is List) {
rawView['badges'].forEach((entity) {
if (entities.isExist(entity)) {
Entity e = entities.get(entity);
view.badges.add(e);
if (entity is String) {
if (entities.isExist(entity)) {
Entity e = entities.get(entity);
view.badges.add(e);
}
} else {
String eId = '${entity['entity']}';
if (entities.isExist(eId)) {
Entity e = entities.get(eId);
view.badges.add(e);
}
}
});
}
@ -201,6 +218,9 @@ class HomeAssistant {
try {
//bool isThereCardOptionsInside = rawCard["card"] != null;
var rawCardInfo = rawCard["card"] ?? rawCard;
if (rawCardInfo['state_filter'] != null) {
Logger.d("Hey!!!!!! We found a card with state filter: ${rawCardInfo['state_filter']}");
}
HACard card = HACard(
id: "card",
name: rawCardInfo["title"] ?? rawCardInfo["name"],
@ -209,7 +229,7 @@ class HomeAssistant {
showName: rawCardInfo['show_name'] ?? true,
showState: rawCardInfo['show_state'] ?? true,
showEmpty: rawCardInfo['show_empty'] ?? true,
stateFilter: rawCardInfo['state_filter'] ?? [],
stateFilter: (rawCard['state_filter'] ?? rawCardInfo['state_filter']) ?? [],
states: rawCardInfo['states'],
conditions: rawCard['conditions'] ?? [],
content: rawCardInfo['content'],

View File

@ -141,7 +141,7 @@ EventBus eventBus = new EventBus();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
const String appName = "HA Client";
const appVersionNumber = "0.7.0";
const appVersionNumber = "0.7.2";
const appVersionAdd = "";
const appVersion = "$appVersionNumber-$appVersionAdd";

View File

@ -189,13 +189,13 @@ class ConnectionManager {
//Logger.d("[Received] <== Request id ${data['id']} was successful");
_messageResolver["${data["id"]}"]?.complete(data["result"]);
} else if (data["id"] != null) {
//Logger.e("[Received] <== Error received on request id ${data['id']}: ${data['error']}");
_messageResolver["${data["id"]}"]?.completeError("${data['error']["message"]}");
Logger.e("[Received] <== Error received on request id ${data['id']}: ${data['error']}");
_messageResolver["${data["id"]}"]?.completeError("${data["error"]["code"]}");
}
_messageResolver.remove("${data["id"]}");
} else if (data["type"] == "event") {
if ((data["event"] != null) && (data["event"]["event_type"] == "state_changed")) {
//Logger.d("[Received] <== ${data['type']}.${data["event"]["event_type"]}: ${data["event"]["data"]["entity_id"]}");
Logger.d("[Received] <== ${data['type']}.${data["event"]["event_type"]}: ${data["event"]["data"]["entity_id"]}");
onStateChangeCallback(data["event"]["data"]);
} else if (data["event"] != null) {
Logger.w("Unhandled event type: ${data["event"]["event_type"]}");
@ -348,14 +348,16 @@ class ConnectionManager {
_currentMessageId += 1;
}
Future callService({String domain, String service, String entityId, Map additionalServiceData}) {
Future callService({@required String domain, @required String service, String entityId, Map data}) {
eventBus.fire(NotifyServiceCallEvent(domain, service, entityId));
Logger.d("Service call: $domain.$service, $entityId, $data");
Completer completer = Completer();
Map serviceData = {};
if (entityId != null) {
serviceData["entity_id"] = entityId;
}
if (additionalServiceData != null && additionalServiceData.isNotEmpty) {
serviceData.addAll(additionalServiceData);
if (data != null && data.isNotEmpty) {
serviceData.addAll(data);
}
if (serviceData.isNotEmpty)
sendHTTPPost(
@ -366,7 +368,7 @@ class ConnectionManager {
else
sendHTTPPost(
endPoint: "/api/services/$domain/$service"
).then((data) => completer.complete(data)).catchError((e) => completer.completeError(HAError("${e["message"]}")));;
).then((data) => completer.complete(data)).catchError((e) => completer.completeError(HAError("${e["message"]}")));
//return sendSocketMessage(type: "call_service", additionalData: {"domain": domain, "service": service});
return completer.future;
}
@ -407,10 +409,11 @@ class ConnectionManager {
headers: headers,
body: data
).then((response) {
Logger.d("[Received] <== HTTP ${response.statusCode}");
if (response.statusCode >= 200 && response.statusCode < 300 ) {
Logger.d("[Received] <== HTTP ${response.statusCode}");
completer.complete(response.body);
} else {
Logger.d("[Received] <== HTTP ${response.statusCode}: ${response.body}");
completer.completeError({"code": response.statusCode, "message": "${response.body}"});
}
}).catchError((e) {

View File

@ -14,7 +14,7 @@ class LocationManager {
}
final int defaultUpdateIntervalMinutes = 20;
final String backgroundTaskId = "haclocationtask4352";
final String backgroundTaskId = "haclocationtask0";
final String backgroundTaskTag = "haclocation";
Duration _updateInterval;
bool _isRunning;
@ -27,6 +27,7 @@ class LocationManager {
_isRunning = prefs.getBool("location-enabled") ?? false;
if (_isRunning) {
await _startLocationService();
updateDeviceLocation(false);
}
}
@ -57,27 +58,48 @@ class LocationManager {
}
_startLocationService() async {
Logger.d("Scheduling location update for every ${_updateInterval
.inMinutes} minutes...");
String webhookId = ConnectionManager().webhookId;
String httpWebHost = ConnectionManager().httpWebHost;
if (webhookId != null && webhookId.isNotEmpty) {
await workManager.Workmanager.registerPeriodicTask(
backgroundTaskId,
"haClientLocationTracking",
tag: backgroundTaskTag,
inputData: {
"webhookId": webhookId,
"httpWebHost": httpWebHost
},
frequency: _updateInterval,
existingWorkPolicy: workManager.ExistingWorkPolicy.keep,
backoffPolicy: workManager.BackoffPolicy.linear,
backoffPolicyDelay: _updateInterval,
constraints: workManager.Constraints(
networkType: workManager.NetworkType.connected
)
);
Duration interval;
int delayFactor;
int taskCount;
Logger.d("Starting location update for every ${_updateInterval
.inMinutes} minutes...");
if (_updateInterval.inMinutes == 10) {
interval = Duration(minutes: 20);
taskCount = 2;
delayFactor = 10;
} else if (_updateInterval.inMinutes == 5) {
interval = Duration(minutes: 15);
taskCount = 3;
delayFactor = 5;
} else {
interval = _updateInterval;
taskCount = 1;
delayFactor = 0;
}
for (int i = 1; i <= taskCount; i++) {
int delay = i*delayFactor;
Logger.d("Scheduling location update task #$i for every ${interval.inMinutes} minutes in $delay minutes...");
await workManager.Workmanager.registerPeriodicTask(
"$backgroundTaskId$n",
"haClientLocationTracking",
tag: backgroundTaskTag,
inputData: {
"webhookId": webhookId,
"httpWebHost": httpWebHost
},
frequency: interval,
initialDelay: Duration(minutes: delay),
existingWorkPolicy: workManager.ExistingWorkPolicy.keep,
backoffPolicy: workManager.BackoffPolicy.linear,
backoffPolicyDelay: interval,
constraints: workManager.Constraints(
networkType: workManager.NetworkType.connected
)
);
}
}
}
@ -86,33 +108,46 @@ class LocationManager {
await workManager.Workmanager.cancelByTag(backgroundTaskTag);
}
updateDeviceLocation() async {
if (ConnectionManager().webhookId != null &&
ConnectionManager().webhookId.isNotEmpty) {
String url = "${ConnectionManager()
.httpWebHost}/api/webhook/${ConnectionManager().webhookId}";
Map<String, String> headers = {};
Logger.d("[Location] Getting device location...");
Position location = await Geolocator().getCurrentPosition(
desiredAccuracy: LocationAccuracy.medium);
Logger.d("[Location] Got location: ${location.latitude} ${location
.longitude}. Sending home...");
int battery = await Battery().batteryLevel;
var data = {
"type": "update_location",
"data": {
"gps": [location.latitude, location.longitude],
"gps_accuracy": location.accuracy,
"battery": battery
}
};
headers["Content-Type"] = "application/json";
await http.post(
updateDeviceLocation(bool force) async {
if (!force && !_isRunning) {
Logger.d("[Foreground location] Not enabled. Aborting.");
return;
}
Logger.d("[Foreground location] Started");
//Logger.d("[Foreground location] Forcing Android location manager...");
Geolocator geolocator = Geolocator()..forceAndroidLocationManager = true;
var battery = Battery();
String webhookId = ConnectionManager().webhookId;
String httpWebHost = ConnectionManager().httpWebHost;
if (webhookId != null && webhookId.isNotEmpty) {
Logger.d("[Foreground location] Getting battery level...");
int batteryLevel = await battery.batteryLevel;
Logger.d("[Foreground location] Getting device location...");
Position position = await geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high,
locationPermissionLevel: GeolocationPermission.locationAlways
);
if (position != null) {
Logger.d("[Foreground location] Location: ${position.latitude} ${position.longitude}. Accuracy: ${position.accuracy}. (${position.timestamp})");
String url = "$httpWebHost/api/webhook/$webhookId";
Map data = {
"type": "update_location",
"data": {
"gps": [position.latitude, position.longitude],
"gps_accuracy": position.accuracy,
"battery": batteryLevel ?? 100
}
};
Logger.d("[Foreground location] Sending data home...");
var response = await http.post(
url,
headers: headers,
headers: {"Content-Type": "application/json"},
body: json.encode(data)
);
Logger.d("[Location] ...done.");
);
Logger.d("[Foreground location] Got HTTP ${response.statusCode}");
} else {
Logger.d("[Foreground location] No location. Aborting.");
}
}
}
@ -121,6 +156,7 @@ class LocationManager {
void updateDeviceLocationIsolate() {
workManager.Workmanager.executeTask((backgroundTask, data) {
//print("[Background $backgroundTask] Started");
Geolocator geolocator = Geolocator()..forceAndroidLocationManager = true;
var battery = Battery();
int batteryLevel = 100;
String webhookId = data["webhookId"];
@ -141,7 +177,7 @@ void updateDeviceLocationIsolate() {
//print("[Background $backgroundTask] Getting battery level...");
battery.batteryLevel.then((val) => data["data"]["battery"] = val).whenComplete((){
//print("[Background $backgroundTask] Getting device location...");
Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.medium).then((location) {
geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high, locationPermissionLevel: GeolocationPermission.locationAlways).then((location) {
//print("[Background $backgroundTask] Got location: ${location.latitude} ${location.longitude}");
if (location != null) {
data["data"]["gps"] = [location.latitude, location.longitude];
@ -155,7 +191,7 @@ void updateDeviceLocationIsolate() {
}
}).catchError((e) {
//print("[Background $backgroundTask] Error getting current location: ${e.toString()}. Trying last known...");
Geolocator().getLastKnownPosition(desiredAccuracy: LocationAccuracy.medium).then((location){
geolocator.getLastKnownPosition(desiredAccuracy: LocationAccuracy.medium).then((location){
//print("[Background $backgroundTask] Got last known location: ${location.latitude} ${location.longitude}");
if (location != null) {
data["data"]["gps"] = [location.latitude, location.longitude];

View File

@ -45,7 +45,7 @@ class MobileAppIntegrationManager {
positiveText: "Restart now",
negativeText: "Later",
onPositive: () {
ConnectionManager().callService(domain: "homeassistant", service: "restart", entityId: null);
ConnectionManager().callService(domain: "homeassistant", service: "restart");
},
));
});

View File

@ -50,8 +50,6 @@ class _EntityViewPageState extends State<EntityViewPage> {
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
Navigator.pop(context);
}),
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: new Text("${entity.displayName}"),
),
body: body,

View File

@ -29,6 +29,9 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
setState(() {
_locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
_locationInterval = prefs.getInt("location-interval") ?? LocationManager().defaultUpdateIntervalMinutes;
if (_locationInterval % 5 != 0) {
_locationInterval = 5 * (_locationInterval ~/ 5);
}
});
});
}
@ -36,15 +39,15 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
void incLocationInterval() {
if (_locationInterval < 720) {
setState(() {
_locationInterval = _locationInterval + 1;
_locationInterval = _locationInterval + 5;
});
}
}
void decLocationInterval() {
if (_locationInterval > 1) {
if (_locationInterval > 5) {
setState(() {
_locationInterval = _locationInterval - 1;
_locationInterval = _locationInterval - 5;
});
}
}
@ -56,7 +59,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
positiveText: "Sure. Make it so",
negativeText: "What?? No!",
onPositive: () {
ConnectionManager().callService(domain: "homeassistant", service: "restart", entityId: null);
ConnectionManager().callService(domain: "homeassistant", service: "restart");
},
));
}
@ -68,7 +71,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
positiveText: "Sure. Make it so",
negativeText: "What?? No!",
onPositive: () {
ConnectionManager().callService(domain: "homeassistant", service: "stop", entityId: null);
ConnectionManager().callService(domain: "homeassistant", service: "stop");
},
));
}
@ -91,7 +94,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
_switchLocationTrackingState(bool state) async {
if (state) {
await LocationManager().updateDeviceLocation();
await LocationManager().updateDeviceLocation(true);
}
await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
setState(() {

View File

@ -26,7 +26,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
bool _showLoginButton = false;
bool _preventAppRefresh = false;
String _savedSharedText;
String _entityToShow;
Entity _entityToShow;
@override
void initState() {
@ -122,6 +122,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
_showInfoBottomBar(progress: true,);
ConnectionManager().init(loadSettings: false, forceReconnect: false).then((_){
_fetchData();
LocationManager().updateDeviceLocation(false);
//StartupUserMessagesManager().checkMessagesToShow();
}, onError: (e) {
_setErrorState(e);
@ -142,6 +143,9 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
_viewsTabController = TabController(vsync: this, length: currentViewCount);
_previousViewCount = currentViewCount;
}
if (_entityToShow != null) {
_entityToShow = HomeAssistant().entities.get(_entityToShow.entityId);
}
}).catchError((e) {
if (e is HAError) {
_setErrorState(e);
@ -218,9 +222,8 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
}
if (_serviceCallSubscription == null) {
_serviceCallSubscription =
eventBus.on<ServiceCallEvent>().listen((event) {
_callService(event.domain, event.service, event.entityId,
event.additionalParams);
eventBus.on<NotifyServiceCallEvent>().listen((event) {
_notifyServiceCalled(event.domain, event.service, event.entityId);
});
}
@ -318,27 +321,28 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
);
}
//TODO remove this shit.... maybe
void _callService(String domain, String service, String entityId, Map additionalParams) {
void _notifyServiceCalled(String domain, String service, String entityId) {
_showInfoBottomBar(
message: "Calling $domain.$service",
duration: Duration(seconds: 3)
duration: Duration(seconds: 4)
);
ConnectionManager().callService(domain: domain, service: service, entityId: entityId, additionalServiceData: additionalParams).catchError((e) => _setErrorState(e));
}
void _showEntityPage(String entityId) {
setState(() {
_entityToShow = entityId;
_entityToShow = HomeAssistant().entities.get(entityId);
if (_entityToShow != null) {
_mainScrollController?.jumpTo(0);
}
});
if (_entityToShow!= null && MediaQuery.of(context).size.width < Sizes.tabletMinWidth) {
/*if (_entityToShow!= null && MediaQuery.of(context).size.width < Sizes.tabletMinWidth) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EntityViewPage(entityId: entityId),
)
);
}
}*/
}
void _showPage(String path, bool goBackFirst) {
@ -368,12 +372,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
menuItems.add(
UserAccountsDrawerHeader(
accountName: Text(HomeAssistant().userName),
accountEmail: Text(ConnectionManager().displayHostname ?? "Not configured"),
onDetailsPressed: () {
Launcher.launchURLInCustomTab(
url: "${ConnectionManager().httpWebHost}/profile?external_auth=1"
);
},
accountEmail: Text(HomeAssistant().locationName ?? ""),
currentAccountPicture: CircleAvatar(
child: Text(
HomeAssistant().userAvatarText,
@ -636,6 +635,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
}
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
final ScrollController _mainScrollController = ScrollController();
Widget _buildScaffoldBody(bool empty) {
List<PopupMenuItem<String>> serviceMenuItems = [];
@ -730,7 +730,6 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
}
} else {
if (_entityToShow != null && MediaQuery.of(context).size.width >= Sizes.tabletMinWidth) {
Entity entity = HomeAssistant().entities.get(_entityToShow);
mainScrollBody = Flex(
direction: Axis.horizontal,
children: <Widget>[
@ -743,12 +742,13 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
),
ConstrainedBox(
constraints: BoxConstraints.tightFor(width: Sizes.entityPageMaxWidth),
child: EntityPageLayout(entity: entity, showClose: true,),
child: EntityPageLayout(entity: _entityToShow, showClose: true,),
)
],
);
} else if (_entityToShow != null) {
mainScrollBody = EntityPageLayout(entity: _entityToShow, showClose: true,);
} else {
_entityToShow = null;
mainScrollBody = HomeAssistant().buildViews(context, _viewsTabController);
}
}
@ -804,7 +804,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
_scaffoldKey.currentState.openDrawer();
},
),
bottom: empty ? null : TabBar(
bottom: (empty || _entityToShow != null) ? null : TabBar(
controller: _viewsTabController,
tabs: buildUIViewTabs(),
isScrollable: true,
@ -813,7 +813,8 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
];
},
body: mainScrollBody
body: mainScrollBody,
controller: _mainScrollController,
);
}
@ -879,12 +880,22 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
body: _buildScaffoldBody(true)
);
} else {
return Scaffold(
key: _scaffoldKey,
drawer: _buildAppDrawer(),
primary: false,
bottomNavigationBar: bottomBar,
body: _buildScaffoldBody(false)
return WillPopScope(
child: Scaffold(
key: _scaffoldKey,
drawer: _buildAppDrawer(),
primary: false,
bottomNavigationBar: bottomBar,
body: _buildScaffoldBody(false)
),
onWillPop: () {
if (_entityToShow != null) {
eventBus.fire(ShowEntityPageEvent());
return Future.value(false);
} else {
return Future.value(true);
}
},
);
}
}

View File

@ -90,16 +90,20 @@ class _PlayMediaPageState extends State<PlayMediaPage> {
Navigator.pop(context);
ConnectionManager().callService(
domain: serviceDomain,
entityId: entity.entityId,
service: "play_media",
additionalServiceData: {
"media_content_id": _mediaUrl,
"media_content_type": _contentType
}
entityId: entity.entityId,
data: {
"media_content_id": _mediaUrl,
"media_content_type": _contentType
}
);
HomeAssistant().sendToPlayerId = entity.entityId;
if (HomeAssistant().sendFromPlayerId != null && HomeAssistant().sendFromPlayerId != HomeAssistant().sendToPlayerId) {
eventBus.fire(ServiceCallEvent(HomeAssistant().sendFromPlayerId.split(".")[0], "turn_off", HomeAssistant().sendFromPlayerId, null));
ConnectionManager().callService(
domain: HomeAssistant().sendFromPlayerId.split(".")[0],
service: "turn_off",
entityId: HomeAssistant().sendFromPlayerId
);
HomeAssistant().sendFromPlayerId = null;
}
eventBus.fire(ShowEntityPageEvent(entity: entity));

View File

@ -24,7 +24,7 @@ class _WhatsNewPageState extends State<WhatsNewPage> {
error = "";
});
http.Response response;
response = await http.get("http://ha-client.homemade.systems/service/whats_new_$appVersionNumber.md");
response = await http.get("http://ha-client.homemade.systems/service/whats_new_0.7.0.md");
if (response.statusCode == 200) {
setState(() {
data = response.body;

View File

@ -86,7 +86,11 @@ class RenderCustomLayoutBox extends RenderBox
int columnsCount;
List<double> columnXPositions = [];
List<double> columnYPositions = [];
columnsCount = (constraints.maxWidth ~/ this.minColumnWidth);
if (constraints.maxWidth < this.minColumnWidth) {
columnsCount = 1;
} else {
columnsCount = (constraints.maxWidth ~/ this.minColumnWidth);
}
if (childCount == 0 || columnsCount == 0) {
size = constraints.biggest;
assert(size.isFinite);

View File

@ -33,13 +33,12 @@ class StartAuthEvent {
StartAuthEvent(this.oauthUrl, this.showButton);
}
class ServiceCallEvent {
class NotifyServiceCallEvent {
String domain;
String service;
String entityId;
Map<String, dynamic> additionalParams;
ServiceCallEvent(this.domain, this.service, this.entityId, this.additionalParams);
NotifyServiceCallEvent(this.domain, this.service, this.entityId);
}
class ShowPopupDialogEvent {

View File

@ -1,7 +1,8 @@
name: hass_client
description: Home Assistant Android Client
version: 0.7.0+706
version: 0.7.2+720
environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
@ -25,7 +26,7 @@ dependencies:
flutter_secure_storage: ^3.3.1+1
device_info: ^0.4.0+3
flutter_local_notifications: ^0.8.4
geolocator: ^5.1.4+2
geolocator: ^5.1.5
workmanager: ^0.1.3
battery: ^0.3.1+1
share: