1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-09 19:49:30 +01:00

Pixoo: Decode alarms from device, support sending alarms

This is probably not quite right yet.

Also we need to properly chunk incoming protocol messages before decoding them
This commit is contained in:
Andreas Shimokawa 2023-12-19 10:58:49 +01:00
parent 275deb4d06
commit 198800e087
6 changed files with 134 additions and 1 deletions

View File

@ -61,6 +61,11 @@ public class PixooCoordinator extends AbstractBLEDeviceCoordinator {
return true; return true;
} }
@Override
public int getAlarmSlotCount(GBDevice device) {
return 10;
}
@Override @Override
public int getDefaultIconResource() { public int getDefaultIconResource() {
return R.drawable.ic_device_lovetoy; return R.drawable.ic_device_lovetoy;

View File

@ -31,19 +31,24 @@ import java.util.Arrays;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread; import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.devices.nothing.NothingProtocol;
public class PixooIOThread extends BtClassicIoThread { public class PixooIOThread extends BtClassicIoThread {
private static final Logger LOG = LoggerFactory.getLogger(PixooIOThread.class); private static final Logger LOG = LoggerFactory.getLogger(PixooIOThread.class);
private final PixooProtocol mPixooProtocol;
@Override @Override
protected void initialize() { protected void initialize() {
write(mPixooProtocol.encodeReqestAlarms());
setUpdateState(GBDevice.State.INITIALIZED); setUpdateState(GBDevice.State.INITIALIZED);
} }
public PixooIOThread(GBDevice device, Context context, PixooProtocol deviceProtocol, public PixooIOThread(GBDevice device, Context context, PixooProtocol deviceProtocol,
PixooSupport PixooSupport, BluetoothAdapter bluetoothAdapter) { PixooSupport PixooSupport, BluetoothAdapter bluetoothAdapter) {
super(device, context, deviceProtocol, PixooSupport, bluetoothAdapter); super(device, context, deviceProtocol, PixooSupport, bluetoothAdapter);
mPixooProtocol = deviceProtocol;
} }
@Override @Override

View File

@ -17,8 +17,12 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.divoom; package nodomain.freeyourgadget.gadgetbridge.service.devices.divoom;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -26,17 +30,22 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import lineageos.weather.util.WeatherUtils; import lineageos.weather.util.WeatherUtils;
import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
import nodomain.freeyourgadget.gadgetbridge.entities.Alarm;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions; import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
@ -64,10 +73,86 @@ public class PixooProtocol extends GBDeviceProtocol {
ByteBuffer incoming = ByteBuffer.wrap(responseData); ByteBuffer incoming = ByteBuffer.wrap(responseData);
incoming.order(ByteOrder.LITTLE_ENDIAN); incoming.order(ByteOrder.LITTLE_ENDIAN);
if (incoming.get() != 0x01) {
LOG.warn("first byte not 0x01");
return devEvts.toArray(new GBDeviceEvent[0]);
}
int length = incoming.getShort() & 0xffff;
byte status = incoming.get(); // unsure
if (status != 0x04) {
LOG.warn("status byte not 0x04");
return devEvts.toArray(new GBDeviceEvent[0]);
}
byte endpoint = incoming.get(); // unsure
LOG.info("endpoint " + endpoint);
if (endpoint == 0x42) {
decodeAlarms(incoming);
}
return devEvts.toArray(new GBDeviceEvent[0]); return devEvts.toArray(new GBDeviceEvent[0]);
} }
private void decodeAlarms(ByteBuffer incoming) {
byte unknown = incoming.get();
if (unknown != 0x55) { // expected
LOG.warn("unexpected byte when decoding Alarms " + unknown);
return;
}
// Map of alarm position to Alarm, as returned by the band
final Map<Integer, nodomain.freeyourgadget.gadgetbridge.model.Alarm> payloadAlarms = new HashMap<>();
while (incoming.remaining() > 10) {
int position = incoming.get();
boolean enabled = incoming.get() == 1;
int hour = incoming.get();
int minute = incoming.get();
int repeatMask = incoming.get();
int unknown2 = incoming.getInt();
byte unknown3 = incoming.get(); // normally 0x01, on fresh alarms 0x32
final Alarm alarm = new nodomain.freeyourgadget.gadgetbridge.entities.Alarm();
alarm.setEnabled(enabled);
alarm.setPosition(position);
alarm.setHour(hour);
alarm.setMinute(minute);
alarm.setRepetition(repeatMask);
alarm.setUnused(unknown3 == 0x32 && !enabled);
payloadAlarms.put(position, alarm);
}
final List<nodomain.freeyourgadget.gadgetbridge.entities.Alarm> dbAlarms = DBHelper.getAlarms(getDevice());
int numUpdatedAlarms = 0;
for (nodomain.freeyourgadget.gadgetbridge.entities.Alarm alarm : dbAlarms) {
final int pos = alarm.getPosition();
final nodomain.freeyourgadget.gadgetbridge.model.Alarm updatedAlarm = payloadAlarms.get(pos);
final boolean alarmNeedsUpdate = updatedAlarm == null ||
alarm.getUnused() != updatedAlarm.getUnused() ||
alarm.getEnabled() != updatedAlarm.getEnabled() ||
alarm.getSmartWakeup() != updatedAlarm.getSmartWakeup() ||
alarm.getHour() != updatedAlarm.getHour() ||
alarm.getMinute() != updatedAlarm.getMinute() ||
alarm.getRepetition() != updatedAlarm.getRepetition();
if (alarmNeedsUpdate) {
numUpdatedAlarms++;
LOG.info("Updating alarm index={}, unused={}", pos, updatedAlarm == null);
alarm.setUnused(updatedAlarm == null);
if (updatedAlarm != null) {
alarm.setEnabled(updatedAlarm.getEnabled());
alarm.setUnused(updatedAlarm.getUnused());
alarm.setSmartWakeup(updatedAlarm.getSmartWakeup());
alarm.setHour(updatedAlarm.getHour());
alarm.setMinute(updatedAlarm.getMinute());
alarm.setRepetition(updatedAlarm.getRepetition());
}
DBHelper.store(alarm);
}
}
if (numUpdatedAlarms > 0) {
final Intent intent = new Intent(DeviceService.ACTION_SAVE_ALARMS);
LocalBroadcastManager.getInstance(GBApplication.getContext()).sendBroadcast(intent);
}
}
@Override @Override
public byte[] encodeSendConfiguration(String config) { public byte[] encodeSendConfiguration(String config) {
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()); SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
@ -171,6 +256,26 @@ public class PixooProtocol extends GBDeviceProtocol {
} }
@Override
public byte[] encodeSetAlarms(ArrayList<? extends nodomain.freeyourgadget.gadgetbridge.model.Alarm> alarms) {
byte[] complete_command = new byte[]{};
for (nodomain.freeyourgadget.gadgetbridge.model.Alarm alarm : alarms) {
byte[] cmd = new byte[]{
0x43,
(byte) alarm.getPosition(),
(byte) (alarm.getEnabled() && !alarm.getUnused() ? 1 : 0),
(byte) alarm.getHour(),
(byte) alarm.getMinute(),
(byte) alarm.getRepetition(),
0, 0, 0, 0,
(byte) (alarm.getUnused() ? 0x32 : 0x00)};
complete_command = ArrayUtils.addAll(complete_command, encodeProtocol(cmd));
}
return complete_command;
}
@Override @Override
public byte[] encodeSendWeather(WeatherSpec weatherSpec) { public byte[] encodeSendWeather(WeatherSpec weatherSpec) {
byte pixooWeatherCode = 0; byte pixooWeatherCode = 0;
@ -238,6 +343,10 @@ public class PixooProtocol extends GBDeviceProtocol {
}); });
} }
public byte[] encodeReqestAlarms() {
return encodeProtocol(new byte[]{0x42});
}
@Override @Override
public byte[] encodeTestNewFunction() { public byte[] encodeTestNewFunction() {
//return encodeAudioModeCommand(1); // works //return encodeAudioModeCommand(1); // works
@ -262,3 +371,4 @@ public class PixooProtocol extends GBDeviceProtocol {
} }
} }

View File

@ -20,6 +20,7 @@ package nodomain.freeyourgadget.gadgetbridge.service.devices.divoom;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport; import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;

View File

@ -25,6 +25,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventSendBytes;
import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler; import nodomain.freeyourgadget.gadgetbridge.devices.EventHandler;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
@ -286,6 +287,12 @@ public abstract class AbstractSerialDeviceSupport extends AbstractDeviceSupport
sendToDevice(bytes); sendToDevice(bytes);
} }
@Override
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
byte[] bytes = gbDeviceProtocol.encodeSetAlarms(alarms);
sendToDevice(bytes);
}
@Override @Override
public void onSetReminders(ArrayList<? extends Reminder> reminders) { public void onSetReminders(ArrayList<? extends Reminder> reminders) {
byte[] bytes = gbDeviceProtocol.encodeReminders(reminders); byte[] bytes = gbDeviceProtocol.encodeReminders(reminders);

View File

@ -24,6 +24,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec; import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@ -158,6 +159,10 @@ public abstract class GBDeviceProtocol {
return null; return null;
} }
public byte[] encodeSetAlarms(ArrayList<? extends Alarm> alarms) {
return null;
}
public byte[] encodeReminders(ArrayList<? extends Reminder> reminders) { public byte[] encodeReminders(ArrayList<? extends Reminder> reminders) {
return null; return null;
} }