Compare commits
18 Commits
1.1.0-beta
...
release/1.
Author | SHA1 | Date | |
---|---|---|---|
85693ac5ce | |||
f84728b948 | |||
26a62d341e | |||
772bddeb9a | |||
5b55940ccf | |||
7683d18e81 | |||
d09afc37b5 | |||
1c686402d0 | |||
5f4a3fbdfc | |||
312ed99e9f | |||
25e6d51c17 | |||
b501574bab | |||
53b31d8e90 | |||
6d80420a9b | |||
e977054139 | |||
6367d38524 | |||
f9b2d7d84c | |||
44c28ad106 |
@ -13,6 +13,6 @@ Discuss it on [Discord](https://discord.gg/u9vq7QE) or at [Home Assistant commun
|
|||||||
#### Last release build status
|
#### Last release build status
|
||||||
[](https://codemagic.io/apps/5da8bdab9f20ef798f7c2c65/5db1862025dc3f0b0288a57a/latest_build)
|
[](https://codemagic.io/apps/5da8bdab9f20ef798f7c2c65/5db1862025dc3f0b0288a57a/latest_build)
|
||||||
|
|
||||||
#### Special thanks to
|
#### Projects used
|
||||||
- [Crewski](https://github.com/Crewski) for his [HANotify](https://github.com/Crewski/HANotify)
|
- [HANotify](https://github.com/Crewski/HANotify) by [Crewski](https://github.com/Crewski)
|
||||||
- [Home Assistant](https://github.com/home-assistant) for some support and [Home Assistant](https://www.home-assistant.io/)
|
- [hassalarm](https://github.com/Johboh/hassalarm) by [Johboh](https://github.com/Johboh) distributed under [MIT License](https://github.com/Johboh/hassalarm/blob/master/LICENSE)
|
||||||
|
@ -62,7 +62,12 @@
|
|||||||
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
|
<action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
<receiver android:name=".NextAlarmBroadcastReceiver">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
<action android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
<service
|
<service
|
||||||
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
@ -74,7 +79,7 @@
|
|||||||
android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
|
android:name="io.flutter.plugins.androidalarmmanager.RebootBroadcastReceiver"
|
||||||
android:enabled="false">
|
android:enabled="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
|
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
</application>
|
</application>
|
||||||
|
@ -145,7 +145,7 @@ public class MessagingService extends FirebaseMessagingService {
|
|||||||
connection.connect();
|
connection.connect();
|
||||||
InputStream input = connection.getInputStream();
|
InputStream input = connection.getInputStream();
|
||||||
return BitmapFactory.decodeStream(input);
|
return BitmapFactory.decodeStream(input);
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.work.BackoffPolicy;
|
||||||
|
import androidx.work.Constraints;
|
||||||
|
import androidx.work.ExistingWorkPolicy;
|
||||||
|
import androidx.work.NetworkType;
|
||||||
|
import androidx.work.OneTimeWorkRequest;
|
||||||
|
import androidx.work.WorkManager;
|
||||||
|
import androidx.work.WorkRequest;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
|
public class NextAlarmBroadcastReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private static final String TAG = "NextAlarmReceiver";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isBootIntent = Intent.ACTION_BOOT_COMPLETED.equalsIgnoreCase(intent.getAction());
|
||||||
|
final boolean isNextAlarmIntent = AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equalsIgnoreCase(intent.getAction());
|
||||||
|
if (!isBootIntent && !isNextAlarmIntent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Constraints constraints = new Constraints.Builder()
|
||||||
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
OneTimeWorkRequest uploadWorkRequest =
|
||||||
|
new OneTimeWorkRequest.Builder(UpdateNextAlarmWorker.class)
|
||||||
|
.setBackoffCriteria(
|
||||||
|
BackoffPolicy.EXPONENTIAL,
|
||||||
|
10,
|
||||||
|
TimeUnit.SECONDS)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
WorkManager
|
||||||
|
.getInstance(context)
|
||||||
|
.enqueueUniqueWork("NextAlarmUpdate", ExistingWorkPolicy.REPLACE, uploadWorkRequest);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -15,10 +15,14 @@ import android.content.SharedPreferences;
|
|||||||
|
|
||||||
public class NotificationActionReceiver extends BroadcastReceiver {
|
public class NotificationActionReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final String TAG = "NotificationActionReceiver";
|
private static final String TAG = "NotificationAction";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
if (intent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String rawActionData = intent.getStringExtra("actionData");
|
String rawActionData = intent.getStringExtra("actionData");
|
||||||
if (intent.hasExtra("tag")) {
|
if (intent.hasExtra("tag")) {
|
||||||
String notificationTag = intent.getStringExtra("tag");
|
String notificationTag = intent.getStringExtra("tag");
|
||||||
|
@ -0,0 +1,119 @@
|
|||||||
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.URLUtil;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.work.Worker;
|
||||||
|
import androidx.work.WorkerParameters;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class UpdateNextAlarmWorker extends Worker {
|
||||||
|
|
||||||
|
private Context currentContext;
|
||||||
|
private static final String TAG = "NextAlarmWorker";
|
||||||
|
private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:00", Locale.ENGLISH);
|
||||||
|
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.ENGLISH);
|
||||||
|
private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("HH:mm:00", Locale.ENGLISH);
|
||||||
|
|
||||||
|
public UpdateNextAlarmWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||||
|
super(context, workerParams);
|
||||||
|
currentContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Result doWork() {
|
||||||
|
final AlarmManager alarmManager;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||||
|
alarmManager = currentContext.getSystemService(AlarmManager.class);
|
||||||
|
} else {
|
||||||
|
alarmManager = (AlarmManager)currentContext.getSystemService(Context.ALARM_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
final AlarmManager.AlarmClockInfo alarmClockInfo = alarmManager.getNextAlarmClock();
|
||||||
|
|
||||||
|
SharedPreferences prefs = currentContext.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE);
|
||||||
|
String webhookId = prefs.getString("flutter.app-webhook-id", null);
|
||||||
|
if (webhookId != null) {
|
||||||
|
try {
|
||||||
|
String requestUrl = prefs.getString("flutter.hassio-res-protocol", "") +
|
||||||
|
"://" +
|
||||||
|
prefs.getString("flutter.hassio-domain", "") +
|
||||||
|
":" +
|
||||||
|
prefs.getString("flutter.hassio-port", "") + "/api/webhook/" + webhookId;
|
||||||
|
JSONObject dataToSend = new JSONObject();
|
||||||
|
if (URLUtil.isValidUrl(requestUrl)) {
|
||||||
|
dataToSend.put("type", "update_sensor_states");
|
||||||
|
JSONArray dataArray = new JSONArray();
|
||||||
|
JSONObject sensorData = new JSONObject();
|
||||||
|
JSONObject sensorAttrs = new JSONObject();
|
||||||
|
sensorData.put("unique_id", "next_alarm");
|
||||||
|
sensorData.put("type", "sensor");
|
||||||
|
final long triggerTimestamp;
|
||||||
|
if (alarmClockInfo != null) {
|
||||||
|
triggerTimestamp = alarmClockInfo.getTriggerTime();
|
||||||
|
final Calendar calendar = Calendar.getInstance();
|
||||||
|
calendar.setTimeInMillis(triggerTimestamp);
|
||||||
|
sensorData.put("state", DATE_TIME_FORMAT.format(calendar.getTime()));
|
||||||
|
sensorAttrs.put("date", DATE_FORMAT.format(calendar.getTime()));
|
||||||
|
sensorAttrs.put("time", TIME_FORMAT.format(calendar.getTime()));
|
||||||
|
sensorAttrs.put("timestamp", triggerTimestamp);
|
||||||
|
} else {
|
||||||
|
sensorData.put("state", "");
|
||||||
|
sensorAttrs.put("date", "");
|
||||||
|
sensorAttrs.put("time", "");
|
||||||
|
sensorAttrs.put("timestamp", 0);
|
||||||
|
}
|
||||||
|
sensorData.put("icon", "mdi:alarm");
|
||||||
|
sensorData.put("attributes", sensorAttrs);
|
||||||
|
dataArray.put(0, sensorData);
|
||||||
|
dataToSend.put("data", dataArray);
|
||||||
|
|
||||||
|
String stringRequest = dataToSend.toString();
|
||||||
|
try {
|
||||||
|
URL url = new URL(requestUrl);
|
||||||
|
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
|
||||||
|
urlConnection.setRequestMethod("POST");
|
||||||
|
urlConnection.setRequestProperty("Content-Type", "application/json");
|
||||||
|
urlConnection.setDoOutput(true);
|
||||||
|
byte[] outputBytes = stringRequest.getBytes("UTF-8");
|
||||||
|
OutputStream os = urlConnection.getOutputStream();
|
||||||
|
os.write(outputBytes);
|
||||||
|
|
||||||
|
int responseCode = urlConnection.getResponseCode();
|
||||||
|
urlConnection.disconnect();
|
||||||
|
if (responseCode >= 300) {
|
||||||
|
return Result.retry();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error sending data", e);
|
||||||
|
return Result.retry();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Invalid HA url");
|
||||||
|
return Result.failure();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Error setting next alarm", e);
|
||||||
|
return Result.failure();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Webhook id not found");
|
||||||
|
return Result.failure();
|
||||||
|
}
|
||||||
|
return Result.success();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx2g
|
org.gradle.jvmargs=-Xmx512m
|
||||||
org.gradle.daemon=true
|
|
||||||
org.gradle.caching=true
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.enableR8=true
|
android.enableR8=true
|
||||||
|
@ -90,6 +90,12 @@ class CardData {
|
|||||||
return BadgesData(rawData);
|
return BadgesData(rawData);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
if (rawData.containsKey('entity')) {
|
||||||
|
rawData['entities'] = [rawData['entity']];
|
||||||
|
}
|
||||||
|
if (rawData.containsKey('entities') && rawData['entities'] is List) {
|
||||||
|
return EntitiesCardData(rawData);
|
||||||
|
}
|
||||||
return CardData(null);
|
return CardData(null);
|
||||||
}
|
}
|
||||||
} catch (error, stacktrace) {
|
} catch (error, stacktrace) {
|
||||||
@ -103,7 +109,11 @@ class CardData {
|
|||||||
type = rawData['type'];
|
type = rawData['type'];
|
||||||
conditions = rawData['conditions'] ?? [];
|
conditions = rawData['conditions'] ?? [];
|
||||||
showEmpty = rawData['show_empty'] ?? true;
|
showEmpty = rawData['show_empty'] ?? true;
|
||||||
stateFilter = rawData['state_filter'] ?? [];
|
if (rawData.containsKey('state_filter') && rawData['state_filter'] is List) {
|
||||||
|
stateFilter = rawData['state_filter'];
|
||||||
|
} else {
|
||||||
|
stateFilter = [];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
type = CardType.UNKNOWN;
|
type = CardType.UNKNOWN;
|
||||||
conditions = [];
|
conditions = [];
|
||||||
@ -374,7 +384,13 @@ class LightCardData extends CardData {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildCardWidget() {
|
Widget buildCardWidget() {
|
||||||
return LightCard(card: this);
|
if (this.entity != null && this.entity.entity is LightEntity) {
|
||||||
|
return LightCard(card: this);
|
||||||
|
}
|
||||||
|
return ErrorCard(
|
||||||
|
errorText: 'Specify an entity from within the light domain.',
|
||||||
|
showReportButton: false,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
LightCardData(rawData) : super(rawData) {
|
LightCardData(rawData) : super(rawData) {
|
||||||
|
@ -2,12 +2,21 @@ part of '../main.dart';
|
|||||||
|
|
||||||
class ErrorCard extends StatelessWidget {
|
class ErrorCard extends StatelessWidget {
|
||||||
final ErrorCardData card;
|
final ErrorCardData card;
|
||||||
|
final String errorText;
|
||||||
|
final bool showReportButton;
|
||||||
|
|
||||||
const ErrorCard({Key key, this.card}) : super(key: key);
|
const ErrorCard({Key key, this.card, this.errorText, this.showReportButton: true}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
String error;
|
||||||
|
if (errorText == null) {
|
||||||
|
error = 'There was an error showing ${card?.type}';
|
||||||
|
} else {
|
||||||
|
error = errorText;
|
||||||
|
}
|
||||||
return CardWrapper(
|
return CardWrapper(
|
||||||
|
color: Theme.of(context).errorColor,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, Sizes.rowPadding),
|
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, Sizes.rowPadding, Sizes.rightWidgetPadding, Sizes.rowPadding),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -15,21 +24,25 @@ class ErrorCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
'There was an error rendering card: ${card.type}. Please copy card config to clipboard and report this issue. Thanks!',
|
error,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
|
card != null ?
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(new ClipboardData(text: card.cardConfig));
|
Clipboard.setData(new ClipboardData(text: card.cardConfig));
|
||||||
},
|
},
|
||||||
child: Text('Copy card config'),
|
child: Text('Copy card config'),
|
||||||
),
|
) :
|
||||||
|
Container(width: 0, height: 0),
|
||||||
|
showReportButton ?
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Launcher.launchURLInBrowser("https://github.com/estevez-dev/ha_client/issues/new?assignees=&labels=&template=bug_report.md&title=");
|
Launcher.launchURLInBrowser("https://github.com/estevez-dev/ha_client/issues/new?assignees=&labels=&template=bug_report.md&title=");
|
||||||
},
|
},
|
||||||
child: Text('Report issue'),
|
child: Text('Report issue'),
|
||||||
)
|
) :
|
||||||
|
Container(width: 0, height: 0)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -7,6 +7,6 @@ class UnsupportedCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container();
|
return Container(height: 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,12 +4,14 @@ class CardWrapper extends StatelessWidget {
|
|||||||
|
|
||||||
final Widget child;
|
final Widget child;
|
||||||
final EdgeInsets padding;
|
final EdgeInsets padding;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
const CardWrapper({Key key, this.child, this.padding: const EdgeInsets.all(0)}) : super(key: key);
|
const CardWrapper({Key key, this.child, this.color, this.padding: const EdgeInsets.all(0)}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
|
color: color,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: child
|
child: child
|
||||||
|
@ -40,8 +40,8 @@ class CoverEntity extends Entity {
|
|||||||
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
CoverEntity.SUPPORT_SET_TILT_POSITION);
|
||||||
|
|
||||||
|
|
||||||
double get currentPosition => _getDoubleAttributeValue('current_position');
|
double get currentPosition => _getDoubleAttributeValue('current_position') ?? 0;
|
||||||
double get currentTiltPosition => _getDoubleAttributeValue('current_tilt_position');
|
double get currentTiltPosition => _getDoubleAttributeValue('current_tilt_position') ?? 0;
|
||||||
bool get canBeOpened => ((state != EntityState.opening) && (state != EntityState.open)) || (state == EntityState.open && currentPosition != null && currentPosition > 0.0 && currentPosition < 100.0);
|
bool get canBeOpened => ((state != EntityState.opening) && (state != EntityState.open)) || (state == EntityState.open && currentPosition != null && currentPosition > 0.0 && currentPosition < 100.0);
|
||||||
bool get canBeClosed => ((state != EntityState.closing) && (state != EntityState.closed));
|
bool get canBeClosed => ((state != EntityState.closing) && (state != EntityState.closed));
|
||||||
bool get canTiltBeOpened => currentTiltPosition < 100;
|
bool get canTiltBeOpened => currentTiltPosition < 100;
|
||||||
|
@ -8,8 +8,8 @@ class TimerEntity extends Entity {
|
|||||||
@override
|
@override
|
||||||
void update(Map rawData, String webHost) {
|
void update(Map rawData, String webHost) {
|
||||||
super.update(rawData, webHost);
|
super.update(rawData, webHost);
|
||||||
String durationSource = "${attributes["duration"]}";
|
if (attributes.containsKey('duration')) {
|
||||||
if (durationSource != null && durationSource.isNotEmpty) {
|
String durationSource = "${attributes["duration"]}";
|
||||||
try {
|
try {
|
||||||
List<String> durationList = durationSource.split(":");
|
List<String> durationList = durationSource.split(":");
|
||||||
if (durationList.length == 1) {
|
if (durationList.length == 1) {
|
||||||
|
@ -149,7 +149,7 @@ class EntityCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isExist(String entityId) {
|
bool isExist(String entityId) {
|
||||||
return _allEntities[entityId] != null;
|
return _allEntities.containsKey(entityId);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Entity> getByDomains({List<String> includeDomains: const [], List<String> excludeDomains: const [], List<String> stateFiler}) {
|
List<Entity> getByDomains({List<String> includeDomains: const [], List<String> excludeDomains: const [], List<String> stateFiler}) {
|
||||||
|
@ -221,7 +221,7 @@ class HomeAssistant {
|
|||||||
var data = json.decode(prefs.getString('cached_services'));
|
var data = json.decode(prefs.getString('cached_services'));
|
||||||
_parseServices(data ?? {});
|
_parseServices(data ?? {});
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
Logger.e(e, stacktrace: stacktrace);
|
Logger.e(e, stacktrace: stacktrace, skipCrashlytics: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await ConnectionManager().sendSocketMessage(type: "get_services").then((data) => _parseServices(data)).catchError((e) {
|
await ConnectionManager().sendSocketMessage(type: "get_services").then((data) => _parseServices(data)).catchError((e) {
|
||||||
@ -261,7 +261,7 @@ class HomeAssistant {
|
|||||||
var data = json.decode(sharedPrefs.getString('cached_panels'));
|
var data = json.decode(sharedPrefs.getString('cached_panels'));
|
||||||
_parsePanels(data ?? {});
|
_parsePanels(data ?? {});
|
||||||
} catch (e, stacktrace) {
|
} catch (e, stacktrace) {
|
||||||
Logger.e(e, stacktrace: stacktrace);
|
Logger.e(e, stacktrace: stacktrace, skipCrashlytics: true);
|
||||||
panels.clear();
|
panels.clear();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,11 +156,9 @@ part 'cards/badges.dart';
|
|||||||
part 'managers/app_settings.dart';
|
part 'managers/app_settings.dart';
|
||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
//final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
|
||||||
//FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
|
|
||||||
const String appName = 'HA Client';
|
const String appName = 'HA Client';
|
||||||
const String appVersion = String.fromEnvironment('versionName', defaultValue: '0.0.0');
|
const String appVersion = String.fromEnvironment('versionName', defaultValue: '0.0.0');
|
||||||
const whatsNewUrl = 'http://ha-client.app/service/whats_new_1.1.0-b2.md';
|
const whatsNewUrl = 'http://ha-client.app/service/whats_new_1.2.0.md';
|
||||||
|
|
||||||
Future<void> _reportError(dynamic error, dynamic stackTrace) async {
|
Future<void> _reportError(dynamic error, dynamic stackTrace) async {
|
||||||
// Print the exception to the console.
|
// Print the exception to the console.
|
||||||
@ -249,6 +247,7 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
positiveText: "Ok"
|
positiveText: "Ok"
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
InAppPurchaseConnection.instance.completePurchase(purchase[0]);
|
||||||
} else {
|
} else {
|
||||||
Logger.d("Purchase change handler: ${purchase[0].status}");
|
Logger.d("Purchase change handler: ${purchase[0].status}");
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class AppSettings {
|
|||||||
String webhookId;
|
String webhookId;
|
||||||
double haVersion;
|
double haVersion;
|
||||||
bool scrollBadges;
|
bool scrollBadges;
|
||||||
|
bool nextAlarmSensorCreated = false;
|
||||||
DisplayMode displayMode;
|
DisplayMode displayMode;
|
||||||
AppTheme appTheme;
|
AppTheme appTheme;
|
||||||
final int defaultLocationUpdateIntervalMinutes = 20;
|
final int defaultLocationUpdateIntervalMinutes = 20;
|
||||||
@ -61,6 +62,7 @@ class AppSettings {
|
|||||||
locationUpdateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
locationUpdateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
||||||
defaultLocationUpdateIntervalMinutes);
|
defaultLocationUpdateIntervalMinutes);
|
||||||
locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
||||||
|
nextAlarmSensorCreated = prefs.getBool("next-alarm-sensor-created") ?? false;
|
||||||
longLivedToken = Hive.box(DEFAULT_HIVE_BOX).get(AUTH_TOKEN_KEY);
|
longLivedToken = Hive.box(DEFAULT_HIVE_BOX).get(AUTH_TOKEN_KEY);
|
||||||
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent(
|
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent(
|
||||||
'https://ha-client.app')}&redirect_uri=${Uri
|
'https://ha-client.app')}&redirect_uri=${Uri
|
||||||
|
@ -5,9 +5,9 @@ class MobileAppIntegrationManager {
|
|||||||
static final _appRegistrationData = {
|
static final _appRegistrationData = {
|
||||||
"device_name": "",
|
"device_name": "",
|
||||||
"app_version": "$appVersion",
|
"app_version": "$appVersion",
|
||||||
"manufacturer": DeviceInfoManager().manufacturer,
|
"manufacturer": DeviceInfoManager().manufacturer ?? "unknown",
|
||||||
"model": DeviceInfoManager().model,
|
"model": DeviceInfoManager().model ?? "unknown",
|
||||||
"os_version": DeviceInfoManager().osVersion,
|
"os_version": DeviceInfoManager().osVersion ?? "0",
|
||||||
"app_data": {
|
"app_data": {
|
||||||
"push_token": "",
|
"push_token": "",
|
||||||
"push_url": "https://us-central1-ha-client-c73c4.cloudfunctions.net/pushNotifyV3"
|
"push_url": "https://us-central1-ha-client-c73c4.cloudfunctions.net/pushNotifyV3"
|
||||||
@ -62,12 +62,13 @@ class MobileAppIntegrationManager {
|
|||||||
includeAuthHeader: true,
|
includeAuthHeader: true,
|
||||||
data: json.encode(registrationData)
|
data: json.encode(registrationData)
|
||||||
).then((response) {
|
).then((response) {
|
||||||
Logger.d("Processing registration responce...");
|
Logger.d("Processing registration response...");
|
||||||
var responseObject = json.decode(response);
|
var responseObject = json.decode(response);
|
||||||
AppSettings().webhookId = responseObject["webhook_id"];
|
AppSettings().webhookId = responseObject["webhook_id"];
|
||||||
AppSettings().save({
|
AppSettings().save({
|
||||||
'app-webhook-id': responseObject["webhook_id"]
|
'app-webhook-id': responseObject["webhook_id"]
|
||||||
}).then((prefs) {
|
}).then((_) {
|
||||||
|
_createNextAlarmSensor(true);
|
||||||
completer.complete();
|
completer.complete();
|
||||||
eventBus.fire(ShowPopupEvent(
|
eventBus.fire(ShowPopupEvent(
|
||||||
popup: Popup(
|
popup: Popup(
|
||||||
@ -112,6 +113,7 @@ class MobileAppIntegrationManager {
|
|||||||
_askToRegisterApp();
|
_askToRegisterApp();
|
||||||
} else {
|
} else {
|
||||||
Logger.d('App registration works fine');
|
Logger.d('App registration works fine');
|
||||||
|
_createNextAlarmSensor(false);
|
||||||
}
|
}
|
||||||
completer.complete();
|
completer.complete();
|
||||||
}).catchError((e) {
|
}).catchError((e) {
|
||||||
@ -131,6 +133,42 @@ class MobileAppIntegrationManager {
|
|||||||
return completer.future;
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static _createNextAlarmSensor(bool force) {
|
||||||
|
if (AppSettings().nextAlarmSensorCreated && !force) {
|
||||||
|
Logger.d("Next alarm sensor was previously created");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Logger.d("Creating next alarm sensor...");
|
||||||
|
ConnectionManager().sendHTTPPost(
|
||||||
|
endPoint: "/api/webhook/${AppSettings().webhookId}",
|
||||||
|
includeAuthHeader: false,
|
||||||
|
data: json.encode(
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"device_class": "timestamp",
|
||||||
|
"icon": "mdi:alarm",
|
||||||
|
"name": "Next Alarm",
|
||||||
|
"state": "",
|
||||||
|
"type": "sensor",
|
||||||
|
"unique_id": "next_alarm"
|
||||||
|
},
|
||||||
|
"type": "register_sensor"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
).then((_){
|
||||||
|
AppSettings().nextAlarmSensorCreated = true;
|
||||||
|
AppSettings().save({
|
||||||
|
'next-alarm-sensor-created': true
|
||||||
|
});
|
||||||
|
}).catchError((e) {
|
||||||
|
if (e is http.Response) {
|
||||||
|
Logger.e("Error creating next alarm sensor: ${e.statusCode}: ${e.body}");
|
||||||
|
} else {
|
||||||
|
Logger.e("Error creating next alarm sensor: ${e?.toString()}");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
static void _showError() {
|
static void _showError() {
|
||||||
eventBus.fire(ShowPopupEvent(
|
eventBus.fire(ShowPopupEvent(
|
||||||
popup: Popup(
|
popup: Popup(
|
||||||
|
@ -63,7 +63,18 @@ class _PurchasePageState extends State<PurchasePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _buildProducts() {
|
List<Widget> _buildProducts() {
|
||||||
List<Widget> productWidgets = [];
|
List<Widget> productWidgets = [
|
||||||
|
Card(
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.all(15),
|
||||||
|
child: Text(
|
||||||
|
'This will not unlock any additional functionality. This is only a donation to the HA Client open source project.',
|
||||||
|
style: Theme.of(context).textTheme.headline5,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
];
|
||||||
for (ProductDetails product in _products) {
|
for (ProductDetails product in _products) {
|
||||||
productWidgets.add(
|
productWidgets.add(
|
||||||
ProductPurchase(
|
ProductPurchase(
|
||||||
|
@ -15,7 +15,7 @@ class ProductPurchase extends StatelessWidget {
|
|||||||
String buttonText = '';
|
String buttonText = '';
|
||||||
String buttonTextInactive = '';
|
String buttonTextInactive = '';
|
||||||
if (product.id.contains("year")) {
|
if (product.id.contains("year")) {
|
||||||
period += "/ year";
|
period += "once a year";
|
||||||
buttonText = "Subscribe";
|
buttonText = "Subscribe";
|
||||||
buttonTextInactive = "Already";
|
buttonTextInactive = "Already";
|
||||||
priceColor = Colors.amber;
|
priceColor = Colors.amber;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 1.1.0+1156
|
version: 1.2.0+1200
|
||||||
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -19,13 +19,13 @@ dependencies:
|
|||||||
date_format: ^1.0.8
|
date_format: ^1.0.8
|
||||||
charts_flutter: ^0.8.1
|
charts_flutter: ^0.8.1
|
||||||
flutter_markdown: ^0.3.3
|
flutter_markdown: ^0.3.3
|
||||||
in_app_purchase: ^0.3.0+3
|
in_app_purchase: ^0.3.4
|
||||||
flutter_custom_tabs: ^0.6.0
|
flutter_custom_tabs: ^0.6.0
|
||||||
flutter_webview_plugin: ^0.3.10+1
|
flutter_webview_plugin: ^0.3.10+1
|
||||||
webview_flutter: ^0.3.19+7
|
webview_flutter: ^0.3.19+7
|
||||||
hive: ^1.4.1+1
|
hive: ^1.4.1+1
|
||||||
hive_flutter: ^0.3.0+2
|
hive_flutter: ^0.3.0+2
|
||||||
device_info: ^0.4.1+4
|
device_info: ^0.4.2+4
|
||||||
geolocator: ^5.3.1
|
geolocator: ^5.3.1
|
||||||
workmanager: ^0.2.2
|
workmanager: ^0.2.2
|
||||||
battery: ^1.0.0
|
battery: ^1.0.0
|
||||||
|
Reference in New Issue
Block a user