mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-12-25 18:15:49 +01:00
Initial support for AsteroidOS watches
The watch does not auto-detect through pairing and therefore requires discovering unsupported devices.
This commit is contained in:
parent
f3fa01dfba
commit
f7395ec6b2
@ -0,0 +1,42 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.asteroidos;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class AsteroidOSConstants {
|
||||
// AsteroidOS Service Watch Filter UUID
|
||||
public static final UUID SERVICE_UUID = UUID.fromString("00000000-0000-0000-0000-00A57E401D05");
|
||||
|
||||
// Battery level
|
||||
public static final UUID BATTERY_SERVICE_UUID = UUID.fromString("0000180F-0000-1000-8000-00805F9B34FB");
|
||||
public static final UUID BATTERY_UUID = UUID.fromString("00002A19-0000-1000-8000-00805F9B34FB");
|
||||
|
||||
// Time
|
||||
public static final UUID TIME_SERVICE_UUID = UUID.fromString("00005071-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID TIME_SET_CHAR = UUID.fromString("00005001-0000-0000-0000-00A57E401D05");
|
||||
|
||||
// ScreenshotService
|
||||
public static final UUID SCREENSHOT_SERVICE_UUID = UUID.fromString("00006071-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID SCREENSHOT_REQUEST = UUID.fromString("00006001-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID SCREENSHOT_CONTENT = UUID.fromString("00006002-0000-0000-0000-00A57E401D05");
|
||||
|
||||
// MediaService
|
||||
public static final UUID MEDIA_SERVICE_UUID = UUID.fromString("00007071-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_TITLE_CHAR = UUID.fromString("00007001-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_ALBUM_CHAR = UUID.fromString("00007002-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_ARTIST_CHAR = UUID.fromString("00007003-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_PLAYING_CHAR = UUID.fromString("00007004-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_COMMANDS_CHAR = UUID.fromString("00007005-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID MEDIA_VOLUME_CHAR = UUID.fromString("00007006-0000-0000-0000-00A57E401D05");
|
||||
|
||||
// WeatherService
|
||||
public static final UUID WEATHER_SERVICE_UUID = UUID.fromString("00008071-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID WEATHER_CITY_CHAR = UUID.fromString("00008001-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID WEATHER_IDS_CHAR = UUID.fromString("00008002-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID WEATHER_MIN_TEMPS_CHAR = UUID.fromString("00008003-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID WEATHER_MAX_TEMPS_CHAR = UUID.fromString("00008004-0000-0000-0000-00A57E401D05");
|
||||
|
||||
// Notification Service
|
||||
public static final UUID NOTIFICATION_SERVICE_UUID = UUID.fromString("00009071-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID NOTIFICATION_UPDATE_CHAR = UUID.fromString("00009001-0000-0000-0000-00A57E401D05");
|
||||
public static final UUID NOTIFICATION_FEEDBACK_CHAR = UUID.fromString("00009002-0000-0000-0000-00A57E401D05");
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.asteroidos;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.bluetooth.le.ScanFilter;
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelUuid;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.asteroidos.AsteroidOSDeviceSupport;
|
||||
|
||||
public class AsteroidOSDeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AsteroidOSDeviceCoordinator.class);
|
||||
@Override
|
||||
public DeviceType getDeviceType() {
|
||||
return DeviceType.ASTEROIDOS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getManufacturer() {
|
||||
return "AsteroidOS";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<? extends ScanFilter> createBLEScanFilters() {
|
||||
ParcelUuid asteroidUUID = ParcelUuid.fromString(AsteroidOSConstants.SERVICE_UUID.toString());
|
||||
ScanFilter serviceFilter = new ScanFilter.Builder().setServiceUuid(asteroidUUID).build();
|
||||
|
||||
List<ScanFilter> filters = new ArrayList();
|
||||
filters.add(serviceFilter);
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeviceType getSupportedType(GBDeviceCandidate candidate) {
|
||||
if (candidate.supportsService(AsteroidOSConstants.SERVICE_UUID)) {
|
||||
return DeviceType.ASTEROIDOS;
|
||||
}
|
||||
{
|
||||
LOG.debug("Name: " + candidate.getName());
|
||||
LOG.debug("Num services: " + candidate.getServiceUuids().length);
|
||||
for (ParcelUuid uuid : candidate.getServiceUuids()) {
|
||||
LOG.debug("Service UUID: " + uuid.toString());
|
||||
}
|
||||
}
|
||||
return DeviceType.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle() {
|
||||
return BONDING_STYLE_ASK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCalendarEvents() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsRealtimeData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Class<? extends Activity> getPairingActivity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityDataFetching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsActivityTracking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SampleProvider<? extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallHandler findInstallHandler(Uri uri, Context context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWeather() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsScreenshots() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSmartWakeup(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsHeartRateMeasurement(GBDevice device) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsMusicInfo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.asteroidos;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
|
||||
public class AsteroidOSMediaCommand {
|
||||
public static final byte COMMAND_PREVIOUS = 0x0;
|
||||
public static final byte COMMAND_NEXT = 0x1;
|
||||
public static final byte COMMAND_PLAY = 0x2;
|
||||
public static final byte COMMAND_PAUSE = 0x3;
|
||||
public static final byte COMMAND_VOLUME = 0x4;
|
||||
|
||||
public byte command = 0x0;
|
||||
public AsteroidOSMediaCommand(byte value) {
|
||||
command = value;
|
||||
}
|
||||
|
||||
public GBDeviceEventMusicControl toMusicControlEvent() {
|
||||
GBDeviceEventMusicControl event = new GBDeviceEventMusicControl();
|
||||
switch (command) {
|
||||
case COMMAND_PREVIOUS:
|
||||
event.event = GBDeviceEventMusicControl.Event.PREVIOUS;
|
||||
break;
|
||||
case COMMAND_NEXT:
|
||||
event.event = GBDeviceEventMusicControl.Event.NEXT;
|
||||
break;
|
||||
case COMMAND_PLAY:
|
||||
event.event = GBDeviceEventMusicControl.Event.PLAY;
|
||||
break;
|
||||
case COMMAND_PAUSE:
|
||||
event.event = GBDeviceEventMusicControl.Event.PAUSE;
|
||||
break;
|
||||
case COMMAND_VOLUME:
|
||||
default:
|
||||
event.event = GBDeviceEventMusicControl.Event.UNKNOWN;
|
||||
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.asteroidos;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
|
||||
public class AsteroidOSNotification {
|
||||
private String packageName = null;
|
||||
private Integer id = null;
|
||||
private String applicationName = null;
|
||||
private String body = null;
|
||||
private String summary = null;
|
||||
private String icon = null;
|
||||
private Boolean remove = false;
|
||||
|
||||
public enum VibrationStrength {
|
||||
STRONG,
|
||||
NORMAL,
|
||||
RINGTONE,
|
||||
NONE;
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (this) {
|
||||
case STRONG:
|
||||
return "strong";
|
||||
case NORMAL:
|
||||
return "normal";
|
||||
case RINGTONE:
|
||||
return "ringtone";
|
||||
case NONE:
|
||||
return "none";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
private VibrationStrength vibrationStrength = VibrationStrength.NORMAL;
|
||||
public AsteroidOSNotification(NotificationSpec spec) {
|
||||
this.body = spec.body;
|
||||
this.applicationName = spec.sourceName;
|
||||
this.summary = spec.subject;
|
||||
this.id = spec.getId();
|
||||
this.packageName = spec.sourceAppId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a call notification
|
||||
* @param callSpec The callSpec given by the device support
|
||||
*/
|
||||
public AsteroidOSNotification(CallSpec callSpec) {
|
||||
switch (callSpec.command) {
|
||||
case CallSpec.CALL_INCOMING:
|
||||
this.applicationName = GBApplication.getContext().getString(R.string.pref_screen_notification_profile_incoming_call);
|
||||
this.summary = callSpec.name;
|
||||
this.body = callSpec.number;
|
||||
this.vibrationStrength = VibrationStrength.RINGTONE;
|
||||
this.id = (callSpec.name + callSpec.number).hashCode();
|
||||
break;
|
||||
default:
|
||||
this.id = (callSpec.name + callSpec.number).hashCode();
|
||||
this.remove = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "remove" notification
|
||||
* @param id Notification ID to remove
|
||||
*/
|
||||
public AsteroidOSNotification(int id) {
|
||||
this.id = id;
|
||||
this.remove = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (remove) {
|
||||
return "<remove><id>" + this.id + "</id></remove>";
|
||||
}
|
||||
String retString = new String();
|
||||
retString += "<insert>";
|
||||
if (id != null)
|
||||
retString += "<id>" + id + "</id>";
|
||||
retString += "<vb>" + vibrationStrength.toString() + "</vb>";
|
||||
if (packageName != null)
|
||||
retString += "<pn>" + packageName + "</pn>";
|
||||
if (applicationName != null)
|
||||
retString += "<an>" + applicationName + "</an>";
|
||||
if (icon != null)
|
||||
retString += "<ai>" + icon + "</ai>";
|
||||
if (summary != null)
|
||||
retString += "<su>" + summary + "</su>";
|
||||
if (body != null)
|
||||
retString += "<bo>" + body + "</bo>";
|
||||
retString += "</insert>";
|
||||
return retString;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.asteroidos;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
|
||||
public class AsteroidOSWeather {
|
||||
public class Day {
|
||||
public int minTemp;
|
||||
public int maxTemp;
|
||||
public int condition;
|
||||
public Day(WeatherSpec.Forecast forecast) {
|
||||
minTemp = forecast.minTemp;
|
||||
maxTemp = forecast.maxTemp;
|
||||
condition = forecast.conditionCode;
|
||||
}
|
||||
}
|
||||
public Day[] days = new Day[5];
|
||||
public String cityName = "";
|
||||
|
||||
|
||||
public AsteroidOSWeather(WeatherSpec spec) {
|
||||
cityName = spec.location;
|
||||
for (int i = 0; i < 5 && i < spec.forecasts.size(); i++) {
|
||||
days[i] = new Day(spec.forecasts.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getCityName() {
|
||||
return cityName.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
public byte[] getWeatherConditions() {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
for (Day day : days) {
|
||||
stream.write((byte) (day.condition >> 8));
|
||||
stream.write((byte) (day.condition));
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] getMinTemps() {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
for (Day day : days) {
|
||||
stream.write((byte) (day.minTemp >> 8));
|
||||
stream.write((byte) (day.minTemp));
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
|
||||
public byte[] getMaxTemps() {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
for (Day day : days) {
|
||||
stream.write((byte) (day.maxTemp >> 8));
|
||||
stream.write((byte) (day.maxTemp));
|
||||
}
|
||||
return stream.toByteArray();
|
||||
}
|
||||
}
|
@ -127,6 +127,7 @@ public enum DeviceType {
|
||||
BINARY_SENSOR(510, R.drawable.ic_device_unknown, R.drawable.ic_device_unknown_disabled, R.string.devicetype_binary_sensor),
|
||||
FLIPPER_ZERO(520, R.drawable.ic_device_flipper, R.drawable.ic_device_flipper_disabled, R.string.devicetype_flipper_zero),
|
||||
SUPER_CARS(530, R.drawable.ic_device_supercars, R.drawable.ic_device_supercars_disabled, R.string.devicetype_super_cars),
|
||||
ASTEROIDOS(540, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_asteroidos),
|
||||
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
|
||||
|
||||
private final int key;
|
||||
|
@ -33,6 +33,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.asteroidos.AsteroidOSDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.binary_sensor.BinarySensorSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.fitpro.FitProDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.banglejs.BangleJSDeviceSupport;
|
||||
@ -355,6 +356,8 @@ public class DeviceSupportFactory {
|
||||
return new ServiceDeviceSupport(new FlipperZeroSupport());
|
||||
case SUPER_CARS:
|
||||
return new ServiceDeviceSupport(new SuperCarsSupport());
|
||||
case ASTEROIDOS:
|
||||
return new ServiceDeviceSupport(new AsteroidOSDeviceSupport());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -0,0 +1,373 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.asteroidos;
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.asteroidos.AsteroidOSConstants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.asteroidos.AsteroidOSMediaCommand;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.asteroidos.AsteroidOSNotification;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.asteroidos.AsteroidOSWeather;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.actions.SetDeviceStateAction;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile;
|
||||
|
||||
public class AsteroidOSDeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AsteroidOSDeviceSupport.class);
|
||||
private final BatteryInfoProfile<AsteroidOSDeviceSupport> batteryInfoProfile;
|
||||
|
||||
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
||||
public AsteroidOSDeviceSupport() {
|
||||
super(LOG);
|
||||
addSupportedService(AsteroidOSConstants.SERVICE_UUID);
|
||||
addSupportedService(AsteroidOSConstants.TIME_SERVICE_UUID);
|
||||
addSupportedService(GattService.UUID_SERVICE_BATTERY_SERVICE);
|
||||
addSupportedService(AsteroidOSConstants.WEATHER_SERVICE_UUID);
|
||||
addSupportedService(AsteroidOSConstants.NOTIFICATION_SERVICE_UUID);
|
||||
addSupportedService(AsteroidOSConstants.MEDIA_SERVICE_UUID);
|
||||
|
||||
IntentListener mListener = new IntentListener() {
|
||||
@Override
|
||||
public void notify(Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (BatteryInfoProfile.ACTION_BATTERY_INFO.equals(action)) {
|
||||
handleBatteryInfo((nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo) intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
batteryInfoProfile = new BatteryInfoProfile<>(this);
|
||||
batteryInfoProfile.addListener(mListener);
|
||||
addSupportedProfile(batteryInfoProfile);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void handleBatteryInfo(nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo info) {
|
||||
batteryCmd.level = info.getPercentCharged();
|
||||
handleGBDeviceEvent(batteryCmd);
|
||||
}
|
||||
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic) {
|
||||
super.onCharacteristicChanged(gatt, characteristic);
|
||||
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
byte[] value = characteristic.getValue();
|
||||
|
||||
if (characteristicUUID.equals(AsteroidOSConstants.MEDIA_COMMANDS_CHAR)) {
|
||||
handleMediaCommand(gatt, characteristic);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG.info("Characteristic changed UUID: " + characteristicUUID);
|
||||
LOG.info("Characteristic changed value: " + characteristic.getValue());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZING, getContext()));
|
||||
getDevice().setFirmwareVersion("N/A");
|
||||
getDevice().setFirmwareVersion2("N/A");
|
||||
|
||||
builder.notify(getCharacteristic(AsteroidOSConstants.MEDIA_COMMANDS_CHAR), true);
|
||||
builder.add(new SetDeviceStateAction(getDevice(), GBDevice.State.INITIALIZED, getContext()));
|
||||
|
||||
batteryInfoProfile.requestBatteryInfo(builder);
|
||||
batteryInfoProfile.enableNotify(builder, true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
builder.requestMtu(256);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNotification(NotificationSpec notificationSpec) {
|
||||
AsteroidOSNotification notif = new AsteroidOSNotification(notificationSpec);
|
||||
TransactionBuilder builder = new TransactionBuilder("send notification");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.NOTIFICATION_UPDATE_CHAR, notif.toString().getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteNotification(int id) {
|
||||
AsteroidOSNotification notif = new AsteroidOSNotification(id);
|
||||
TransactionBuilder builder = new TransactionBuilder("delete notification");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.NOTIFICATION_UPDATE_CHAR, notif.toString().getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetTime() {
|
||||
GregorianCalendar now = BLETypeConversions.createCalendar();
|
||||
Date nowTime = now.getTime();
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
baos.write((byte) nowTime.getYear());
|
||||
baos.write((byte) nowTime.getMonth());
|
||||
baos.write((byte) nowTime.getDay());
|
||||
baos.write((byte) nowTime.getHours());
|
||||
baos.write((byte) nowTime.getMinutes());
|
||||
baos.write((byte) nowTime.getSeconds());
|
||||
TransactionBuilder builder = new TransactionBuilder("set time");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.TIME_SET_CHAR, baos.toByteArray());
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onSetCallState(CallSpec callSpec) {
|
||||
AsteroidOSNotification call = new AsteroidOSNotification(callSpec);
|
||||
TransactionBuilder builder = new TransactionBuilder("send call");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.NOTIFICATION_UPDATE_CHAR, call.toString().getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||
TransactionBuilder builder = new TransactionBuilder("set music state");
|
||||
if (stateSpec.state == MusicStateSpec.STATE_PLAYING) {
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.MEDIA_PLAYING_CHAR, new byte[]{1});
|
||||
} else {
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.MEDIA_PLAYING_CHAR, new byte[]{0});
|
||||
}
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||
// Send title
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("set music title");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.MEDIA_TITLE_CHAR, musicSpec.track.getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
// Send album
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("set music album");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.MEDIA_ALBUM_CHAR, musicSpec.album.getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
// Send artist
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("set music artist");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.MEDIA_ARTIST_CHAR, musicSpec.artist.getBytes(StandardCharsets.UTF_8));
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeSteps(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstallApp(Uri uri) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppInfoReq() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppStart(UUID uuid, boolean start) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppDelete(UUID uuid) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppConfiguration(UUID appUuid, String config, Integer id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppReorder(UUID[] uuids) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchRecordedData(int dataTypes) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset(int flags) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeartRateTest() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFindDevice(boolean start) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetConstantVibration(int integer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScreenshotReq() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnableHeartRateSleepSupport(boolean enable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetHeartRateMeasurementInterval(int seconds) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteCalendarEvent(byte type, long id) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(String config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadConfiguration(String config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTestNewFunction() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendWeather(WeatherSpec weatherSpec) {
|
||||
AsteroidOSWeather asteroidOSWeather = new AsteroidOSWeather(weatherSpec);
|
||||
// Send weather city
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("send weather city");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.WEATHER_CITY_CHAR, asteroidOSWeather.getCityName());
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
// Send weather conditions
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("send weather conditions");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.WEATHER_IDS_CHAR, asteroidOSWeather.getWeatherConditions());
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
// Send min temps
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("send weather min temps");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.WEATHER_MIN_TEMPS_CHAR, asteroidOSWeather.getMinTemps());
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
// Send max temps
|
||||
{
|
||||
TransactionBuilder builder = new TransactionBuilder("send weather max temps");
|
||||
safeWriteToCharacteristic(builder, AsteroidOSConstants.WEATHER_MAX_TEMPS_CHAR, asteroidOSWeather.getMaxTemps());
|
||||
builder.queue(getQueue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAutoConnect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will check if the characteristic exists and can be written
|
||||
* <p>
|
||||
* Keeps backwards compatibility with firmware that can't take all the information
|
||||
*/
|
||||
private void safeWriteToCharacteristic(TransactionBuilder builder, UUID uuid, byte[] data) {
|
||||
BluetoothGattCharacteristic characteristic = getCharacteristic(uuid);
|
||||
if (characteristic != null &&
|
||||
(characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
|
||||
builder.write(characteristic, data);
|
||||
} else {
|
||||
LOG.warn("Tried to write to a characteristic that did not exist or was not writable!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicRead(BluetoothGatt gatt,
|
||||
BluetoothGattCharacteristic characteristic, int status) {
|
||||
if (super.onCharacteristicRead(gatt, characteristic, status)) {
|
||||
return true;
|
||||
}
|
||||
UUID characteristicUUID = characteristic.getUuid();
|
||||
|
||||
LOG.info("Unhandled characteristic read: " + characteristicUUID);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void handleMediaCommand (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
LOG.info("handle media command");
|
||||
AsteroidOSMediaCommand command = new AsteroidOSMediaCommand(characteristic.getValue()[0]);
|
||||
GBDeviceEventMusicControl event = command.toMusicControlEvent();
|
||||
evaluateGBDeviceEvent(event);
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.asteroidos.AsteroidOSDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts4.AmazfitGTS4Coordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitgts4mini.AmazfitGTS4MiniCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.sony.headphones.coordinators.SonyLinkBudsSCoordinator;
|
||||
@ -359,6 +360,7 @@ public class DeviceHelper {
|
||||
result.add(new BinarySensorCoordinator());
|
||||
result.add(new FlipperZeroCoordinator());
|
||||
result.add(new SuperCarsCoordinator());
|
||||
result.add(new AsteroidOSDeviceCoordinator());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -2001,4 +2001,5 @@
|
||||
<string name="supercars_turbo_speed_label">Turbo Speed</string>
|
||||
<string name="supercars_lights_label">Lights</string>
|
||||
<string name="supercars_lights_blinking_label">Blinking</string>
|
||||
<string name="devicetype_asteroidos">AsteroidOS</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user