From a885e0eb930193f92481c189405030a54e778cb1 Mon Sep 17 00:00:00 2001 From: Johannes Krude Date: Sat, 16 Sep 2023 12:01:55 +0200 Subject: [PATCH] Casio GW-B5600: Alarms --- .../activities/ConfigureAlarms.java | 4 +- .../CasioGWB5600DeviceCoordinator.java | 3 +- .../devices/casio/Casio2C2DSupport.java | 147 ++++++++++++++++++ .../gbx100/CasioGBX100DeviceSupport.java | 4 +- .../gwb5600/CasioGWB5600DeviceSupport.java | 24 +-- .../gadgetbridge/util/AlarmUtils.java | 26 ++++ 6 files changed, 189 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java index 014a4806b..86ac98c4b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/ConfigureAlarms.java @@ -125,8 +125,6 @@ public class ConfigureAlarms extends AbstractGBActivity { if (supportedNumAlarms > alarms.size()) { try (DBHandler db = GBApplication.acquireDB()) { DaoSession daoSession = db.getDaoSession(); - Device device = DBHelper.getDevice(getGbDevice(), daoSession); - User user = DBHelper.getUser(daoSession); for (int position = 0; position < supportedNumAlarms; position++) { boolean found = false; for (Alarm alarm : alarms) { @@ -137,7 +135,7 @@ public class ConfigureAlarms extends AbstractGBActivity { } if (!found) { LOG.info("adding missing alarm at position " + position); - alarms.add(position, createDefaultAlarm(device, user, position)); + alarms.add(position, AlarmUtils.createDefaultAlarm(daoSession, getGbDevice(), position)); } } } catch (Exception e) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gwb5600/CasioGWB5600DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gwb5600/CasioGWB5600DeviceCoordinator.java index afc56746d..24764c875 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gwb5600/CasioGWB5600DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/casio/gwb5600/CasioGWB5600DeviceCoordinator.java @@ -65,7 +65,6 @@ public class CasioGWB5600DeviceCoordinator extends CasioDeviceCoordinator { R.xml.devicesettings_casio_connection_duration, R.xml.devicesettings_time_sync, - // alarms // timer // reminder // world time @@ -99,7 +98,7 @@ public class CasioGWB5600DeviceCoordinator extends CasioDeviceCoordinator { @Override public int getAlarmSlotCount(GBDevice device) { - return 0; + return 5; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/Casio2C2DSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/Casio2C2DSupport.java index c5a01e278..b87032104 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/Casio2C2DSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/Casio2C2DSupport.java @@ -50,6 +50,8 @@ import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; +import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils; +import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.DeviceService; import nodomain.freeyourgadget.gadgetbridge.database.DBHelper; import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder; @@ -387,8 +389,11 @@ public abstract class Casio2C2DSupport extends CasioSupport { }; ArrayList deviceSettings = new ArrayList(); + DeviceAlarms deviceAlarms = new DeviceAlarms(); HashMap devicePreferenceByName = new HashMap(); { + deviceSettings.add(deviceAlarms); + for (DevicePreference pref: supportedDevicePreferences()) { deviceSettings.add(pref); devicePreferenceByName.put(pref.getName(), pref); @@ -437,6 +442,148 @@ public abstract class Casio2C2DSupport extends CasioSupport { builder.queue(getQueue()); } + public class DeviceAlarms extends DeviceSetting { + + @Override + public FeatureRequest[] getFeatureRequests() { + final int maxAlarms = gbDevice.getDeviceCoordinator().getAlarmSlotCount(gbDevice); + + if (maxAlarms == 0) { + return new FeatureRequest[] {}; + } else if (maxAlarms == 1) { + return new FeatureRequest[] {new FeatureRequest(FEATURE_SETTING_FOR_ALM)}; + } else { + return new FeatureRequest[] {new FeatureRequest(FEATURE_SETTING_FOR_ALM), new FeatureRequest(FEATURE_SETTING_FOR_ALM2)}; + } + }; + + public void updateValues(byte[][] data, List alarms) { + for (Alarm alarm: alarms) { + updateValues(data, alarm); + } + } + + public void updateValues(byte[][] data, Alarm alarm) { + int pos = alarm.getPosition(); + int alm = pos == 0 ? 0 : 1; + int index = pos == 0 ? 1 : 1 + 4 * (pos-1); + if (data.length <= alm || index + 4 > data[alm].length) { + LOG.error("alarm data too small"); + } + updateValue(data[alm], index, alarm); + } + + public void updateValue(byte[] data, int index, Alarm alarm) { + if(alarm.getEnabled()) { + data[index] |= 0x40; + } else { + data[index] &= ~0x40; + } + //data[index+1] = 0x40; + data[index+2] = (byte) alarm.getHour(); + data[index+3] = (byte) alarm.getMinute(); + } + + @Override + public final boolean mergeValues(byte[][] data, byte[][] previous, SharedPreferences.Editor editor) { + boolean changed = false; + + LinkedHashSet foundAlarms = new LinkedHashSet(); + for (nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm: DBHelper.getAlarms(gbDevice)) { + foundAlarms.add(alarm.getPosition()); + if (mergeValues(data, previous, alarm)) { + changed = true; + } + } + + final int maxAlarms = gbDevice.getDeviceCoordinator().getAlarmSlotCount(gbDevice); + for (int i = 0; i < maxAlarms; i++) { + if (!foundAlarms.contains(i)) { + nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm = AlarmUtils.createDefaultAlarm(gbDevice, i); + if (alarm == null) { + continue; + } + readValues(data, alarm, true); + } + } + return changed; + } + + public boolean mergeValues(byte[][] data, byte[][] previous, nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm) { + LOG.info("merge " + alarm.getPosition() + " : " + Arrays.toString(data[1])); + boolean needsUpdating = false; + // check if GB state has changed + if (previous != null) { + byte[][] copies = new byte[][] {previous[0].clone(), previous[1].clone()}; + updateValues(copies, alarm); + if (!Arrays.equals(previous[0], copies[0]) || ! Arrays.equals(previous[1], copies[1])) { + LOG.info("GB changed"); + needsUpdating = true; + } + } + // update GB state and check if data needs change + if (!needsUpdating) { + needsUpdating = readValues(data, alarm, false); + if (needsUpdating) { + LOG.info("updated from watch"); + } + } + // maybe update data + if (needsUpdating) { + updateValues(data, alarm); + LOG.info("updated from GB"); + return true; + } + return false; + } + + public boolean readValues(byte[][] data, nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm, boolean forceStore) { + int pos = alarm.getPosition(); + int alm = pos == 0 ? 0 : 1; + int index = pos == 0 ? 1 : 1+ 4 * (pos-1); + if (data.length <= alm || index + 4 > data[alm].length) { + LOG.error("alarm data too small"); + } + return readValues(data[alm], index, alarm, forceStore); + } + + public boolean readValues(byte[] data, int index, nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm, boolean forceStore) { + boolean enabled = (data[index] & 0x40) == 0x40; + byte hour = data[index+2]; + byte minute = data[index+3]; + + if (forceStore || alarm.getEnabled() != enabled || alarm.getHour() != hour || alarm.getMinute() != minute) { + alarm.setEnabled(enabled); + alarm.setHour(hour); + alarm.setMinute(minute); + alarm.setRepetition(Alarm.ALARM_MON | Alarm.ALARM_TUE | Alarm.ALARM_WED | Alarm.ALARM_THU | Alarm.ALARM_FRI | Alarm.ALARM_SAT | Alarm.ALARM_SUN); + DBHelper.store(alarm); + Intent intent = new Intent(DeviceService.ACTION_SAVE_ALARMS); + LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent); + } + return false; + } + } + + @Override + public void onSetAlarms(ArrayList alarms) { + if (!isInitialized()) { + return; + } + byte[][] currentValues = featureCache.get(deviceAlarms.getFeatureRequests()); + if (currentValues == null) { + LOG.error("unknown current alarm watch values"); + return; + } + deviceAlarms.updateValues(currentValues, alarms); + + SharedPreferences.Editor editor = GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).edit(); + featureCache.save(editor); + editor.apply(); + + sendSettingsToDevice(currentValues); + } + public abstract class DevicePreference extends DeviceSetting { byte feature; public final FeatureRequest getFeatureRequest() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gbx100/CasioGBX100DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gbx100/CasioGBX100DeviceSupport.java index ab3ddb21b..31efb77b4 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gbx100/CasioGBX100DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gbx100/CasioGBX100DeviceSupport.java @@ -95,8 +95,8 @@ public class CasioGBX100DeviceSupport extends Casio2C2DSupport implements Shared } @Override - public DeviceSetting[] supportedDeviceSettings() { - return new DeviceSetting[] { + public DevicePreference[] supportedDevicePreferences() { + return new DevicePreference[] { }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gwb5600/CasioGWB5600DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gwb5600/CasioGWB5600DeviceSupport.java index c5b20fc3c..c260dc13b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gwb5600/CasioGWB5600DeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/casio/gwb5600/CasioGWB5600DeviceSupport.java @@ -50,18 +50,18 @@ public class CasioGWB5600DeviceSupport extends Casio2C2DSupport { } @Override - public DeviceSetting[] supportedDeviceSettings() { - return new DeviceSetting[] { - new LanguageSetting(), - new TimeFormatSetting(), - new DayMonthOrderSetting(), - new OperatingSoundSetting(), - new HourlyChimeSetting(), - new AutoLightSetting(), - new LongerLightDurationSetting(), - new PowerSavingSetting(), - new ConnectionDurationSetting(), - new TimeSyncSetting(), + public DevicePreference[] supportedDevicePreferences() { + return new DevicePreference[] { + new LanguagePreference(), + new TimeFormatPreference(), + new DayMonthOrderPreference(), + new OperatingSoundPreference(), + new HourlyChimePreference(), + new AutoLightPreference(), + new LongerLightDurationPreference(), + new PowerSavingPreference(), + new ConnectionDurationPreference(), + new TimeSyncPreference(), }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AlarmUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AlarmUtils.java index 14dc93911..5cccdcfd5 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AlarmUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AlarmUtils.java @@ -55,6 +55,32 @@ public class AlarmUtils { return new Alarm(-1, -1, index, true, smartWakeup, null, snooze, Alarm.ALARM_ONCE, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), false, GBApplication.getContext().getString(R.string.quick_alarm), GBApplication.getContext().getString(R.string.quick_alarm_description)); } + /** + * Creates a default Alarm + * @param device + * @param position + */ + public static Alarm createDefaultAlarm(GBDevice gbDevice, int position) { + try (DBHandler db = GBApplication.acquireDB()) { + DaoSession daoSession = db.getDaoSession(); + return createDefaultAlarm(daoSession, gbDevice, position); + } catch (Exception e) { + GB.log("Error accessing database", GB.ERROR, e); + return null; + } + } + + /** + * Creates a default Alarm + * @param daoSession + * @param position + */ + public static Alarm createDefaultAlarm(DaoSession daoSession, GBDevice gbDevice, int position) { + Device device = DBHelper.getDevice(gbDevice, daoSession); + User user = DBHelper.getUser(daoSession); + return new Alarm(device.getId(), user.getId(), position, false, false, null, false, 0, 6, 30, false, null, null); + } + /** * Creates a Calendar object representing the time of the given alarm (not taking the * day/date into account.