Pebble: Unify Pebble hardware definitions into PebbleHardware class

Create PebbleHardware.java as the single source of truth for all
Pebble hardware definitions and for determining what model and features
a watch has. The goal of this commit is to better unify the pebble watch
model definitions into a single source of truth to make adding new
models easier, as well as to bring all the relevant model specific logic
about what a watch is capable of, or which watch model to use into a
single file versus being spread out among multiple files making it
easier to fail to fix logic bugs.

The PebbleHardware class now has all the definitions for the following
information in one place:
- Platform enum with capabilities (BLE-only, hasHealth, hasHRM)
- Hardware revision registry mapping hardware IDs to codenames
- BLE-only device detection methods
- Platform name lookup for watchface installation

This cleans up PebbleUtils to only contain non-hardware utilities.
(maybe should move into the pebble directory away from the utils folder
in another commit?)
This commit is contained in:
Benjamin Temple
2026-04-07 00:22:44 +02:00
committed by José Rebelo
parent fc6dcc793b
commit c595e69ef2
9 changed files with 621 additions and 98 deletions
@@ -92,6 +92,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.GridAutoFitLayoutManager;
import nodomain.freeyourgadget.gadgetbridge.util.InternetHelperSingleton;
import nodomain.freeyourgadget.gadgetbridge.util.InternetUtils;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.Version;
@@ -415,16 +416,19 @@ public abstract class AbstractAppManagerFragment extends Fragment {
}
*/
if (mGBDevice != null) {
if (PebbleUtils.hasHealth(mGBDevice.getModel())) {
if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) {
cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
continue;
PebbleHardware.HardwareRevision hw = PebbleHardware.getByModelString(mGBDevice.getModel());
if (hw != null) {
if (hw.hasHealth()) {
if (baseName.equals(PebbleProtocol.UUID_PEBBLE_HEALTH.toString())) {
cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
continue;
}
}
}
if (PebbleUtils.hasHRM(mGBDevice.getModel())) {
if (baseName.equals(PebbleProtocol.UUID_WORKOUT.toString())) {
cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
continue;
if (hw.hasHRM()) {
if (baseName.equals(PebbleProtocol.UUID_WORKOUT.toString())) {
cachedAppList.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
continue;
}
}
}
if (PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 4) {
@@ -25,6 +25,7 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleProtocol;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment {
@@ -42,12 +43,15 @@ public class AppManagerFragmentInstalledApps extends AbstractAppManagerFragment
systemApps.add(new GBDeviceApp(UUID.fromString("18e443ce-38fd-47c8-84d5-6d0c775fbe55"), "Watchfaces (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
if (mGBDevice != null) {
if (PebbleUtils.hasHealth(mGBDevice.getModel())) {
systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
}
if (PebbleUtils.hasHRM(mGBDevice.getModel())) {
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
PebbleHardware.HardwareRevision hw = PebbleHardware.getByModelString(mGBDevice.getModel());
if (hw != null) {
if (hw.hasHealth()) {
systemApps.add(new GBDeviceApp(UUID.fromString("0863fc6a-66c5-4f62-ab8a-82ed00a98b5d"), "Send Text (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_PEBBLE_HEALTH, "Health (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
}
if (hw.hasHRM()) {
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WORKOUT, "Workout (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
}
}
if (PebbleUtils.getFwMajor(mGBDevice.getFirmwareVersion()) >= 4) {
systemApps.add(new GBDeviceApp(PebbleProtocol.UUID_WEATHER, "Weather (System)", "Pebble Inc.", "", GBDeviceApp.Type.APP_SYSTEM));
@@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
public class PBWInstallHandler implements InstallHandler {
@@ -73,7 +74,7 @@ public class PBWInstallHandler implements InstallHandler {
return;
}
String platformName = PebbleUtils.getPlatformName(device.getModel());
String platformName = PebbleHardware.getPlatformName(device.getModel());
try {
mPBWReader = new PBWReader(mUri, mContext, platformName);
@@ -56,6 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.entities.PebbleMorpheuzSampleDao;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
import nodomain.freeyourgadget.gadgetbridge.util.preferences.DevicePrefs;
@@ -125,7 +126,7 @@ public class PebbleCoordinator extends AbstractBLClassicDeviceCoordinator {
@Override
public boolean supportsHeartRateMeasurement(GBDevice device) {
return PebbleUtils.hasHRM(device.getModel());
return PebbleHardware.hasHRM(device.getModel());
}
@Override
@@ -0,0 +1,578 @@
/* Copyright (C) 2015-2025 Andreas Shimokawa, Carsten Pfeiffer, Daniele Gobbetti
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.pebble;
import android.bluetooth.BluetoothDevice;
import android.util.SparseArray;
import androidx.annotation.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
/**
* Unified source of truth for Pebble hardware definitions.
* Contains platform types, hardware revisions, codenames, and BLE-only status.
*/
public class PebbleHardware {
private static final Logger LOG = LoggerFactory.getLogger(PebbleHardware.class);
// ======================= Vendor IDs =======================
public static final int PEBBLE_VENDOR_ID = 0x0154; // 340 - Standard Pebble devices
public static final int CORE_VENDOR_ID = 0x0EEA; // 3818 - Core devices (Pebble 2 Duo)
// ======================= Platform Enum =======================
/**
* Pebble platform types, corresponding to the SDK platform names.
* Each platform defines its capabilities: BLE-only, health tracking, heart rate monitor.
* Codename prefixes are used for model string matching (e.g., "snowy_dvt" matches "snowy" → BASALT).
*/
public enum Platform {
// platformName, displayName, bleOnly, hasHealth, hasHRM, codenamePrefixes
APLITE("aplite", "Pebble Classic", false, false, false),
BASALT("basalt", "Pebble Time", false, true, false, "snowy"),
CHALK("chalk", "Pebble Time Round", false, true, false, "spalding"),
DIORITE("diorite", "Pebble 2", true, true, true, "silk"),
EMERY("emery", "Pebble Time 2", true, true, true, "robert", "obelix"),
FLINT("flint", "Pebble 2 Duo", true, true, false, "asterix"),
GABBRO("gabbro", "Pebble Time Round 2", true, true, true, "getafix");
private final String platformName;
private final String displayName;
private final boolean bleOnly;
private final boolean hasHealth;
private final boolean hasHRM;
private final String[] codenamePrefixes;
Platform(String platformName, String displayName, boolean bleOnly, boolean hasHealth, boolean hasHRM, String... codenamePrefixes) {
this.platformName = platformName;
this.displayName = displayName;
this.bleOnly = bleOnly;
this.hasHealth = hasHealth;
this.hasHRM = hasHRM;
this.codenamePrefixes = codenamePrefixes;
}
public String getPlatformName() {
return platformName;
}
public String getDisplayName() {
return displayName;
}
public boolean isBleOnly() {
return bleOnly;
}
public boolean hasHealth() {
return hasHealth;
}
public boolean hasHRM() {
return hasHRM;
}
public String[] getCodenamePrefixes() {
return codenamePrefixes;
}
/**
* Find platform by codename prefix.
* @param model Model string to match
* @return Platform or null if no prefix matches
*/
public static Platform findByCodenamePrefix(String model) {
if (model == null || model.isEmpty()) {
return null;
}
for (Platform platform : values()) {
for (String prefix : platform.codenamePrefixes) {
if (model.startsWith(prefix)) {
return platform;
}
}
}
return null;
}
}
// ======================= Hardware Revision =======================
/**
* Represents a specific Pebble hardware revision.
*/
public static class HardwareRevision {
private final int hardwareId;
private final String codename;
private final Platform platform;
public HardwareRevision(int hardwareId, String codename, Platform platform) {
this.hardwareId = hardwareId;
this.codename = codename;
this.platform = platform;
}
public int getHardwareId() {
return hardwareId;
}
public String getCodename() {
return codename;
}
public Platform getPlatform() {
return platform;
}
public boolean isBleOnly() {
return platform.isBleOnly();
}
public boolean hasHealth() {
return platform.hasHealth();
}
public boolean hasHRM() {
return platform.hasHRM();
}
public String getPlatformName() {
return platform.getPlatformName();
}
public String getDisplayName() {
return platform.getDisplayName();
}
}
// ======================= Hardware Registry =======================
// Maps hardware ID (from advertisement data) to HardwareRevision
private static final SparseArray<HardwareRevision> BY_HARDWARE_ID = new SparseArray<>();
// Maps codename to HardwareRevision
private static final Map<String, HardwareRevision> BY_CODENAME = new HashMap<>();
// Array indexed by hardware ID for PebbleProtocol compatibility
private static final String[] HW_REVISIONS_BY_ID;
static {
// Register all hardware revisions
// Format: register(hardwareId, codename, platform)
// Unknown / Default
register(0, "unknown", Platform.BASALT);
// Pebble Classic (APLITE)
register(1, "ev1", Platform.APLITE);
register(2, "ev2", Platform.APLITE);
register(3, "ev2_3", Platform.APLITE);
register(4, "ev2_4", Platform.APLITE);
register(5, "v1_5", Platform.APLITE);
register(6, "v2_0", Platform.APLITE);
register(254, "bb2", Platform.APLITE);
register(255, "bigboard", Platform.APLITE);
// Pebble Time (BASALT)
register(7, "snowy_evt2", Platform.BASALT);
register(8, "snowy_dvt", Platform.BASALT);
register(10, "snowy_s3", Platform.BASALT);
register(252, "snowy_bb2", Platform.BASALT);
register(253, "snowy_bb", Platform.BASALT);
// Pebble Time Round (CHALK)
register(9, "spalding_evt", Platform.CHALK);
register(11, "spalding", Platform.CHALK);
register(251, "spalding_bb2", Platform.CHALK);
// Pebble 2 (DIORITE) - BLE only
register(12, "silk_evt", Platform.DIORITE);
register(14, "silk", Platform.DIORITE);
register(248, "silk_bb2", Platform.DIORITE);
register(250, "silk_bb", Platform.DIORITE);
// Pebble Time 2 (EMERY) - BLE only
register(13, "robert_evt", Platform.EMERY);
register(16, "obelix_evt", Platform.EMERY);
register(17, "obelix_dvt", Platform.EMERY);
register(18, "obelix_pvt", Platform.EMERY);
register(243, "obelix_bb2", Platform.EMERY);
register(244, "obelix_bb", Platform.EMERY);
register(247, "robert_bb2", Platform.EMERY);
register(249, "robert_bb", Platform.EMERY);
// Pebble 2 Duo / Core (FLINT) - BLE only
register(15, "asterix", Platform.FLINT);
// Pebble Time Round 2 (GABBRO) - unreleased, BLE only
register(19, "getafix_evt", Platform.GABBRO);
register(20, "getafix_dvt", Platform.GABBRO);
// Build the array for PebbleProtocol compatibility
int maxId = 0;
for (int i = 0; i < BY_HARDWARE_ID.size(); i++) {
int id = BY_HARDWARE_ID.keyAt(i);
if (id > maxId) maxId = id;
}
HW_REVISIONS_BY_ID = new String[maxId + 1];
for (int i = 0; i < BY_HARDWARE_ID.size(); i++) {
int id = BY_HARDWARE_ID.keyAt(i);
HW_REVISIONS_BY_ID[id] = BY_HARDWARE_ID.valueAt(i).getCodename();
}
}
private static void register(int hardwareId, String codename, Platform platform) {
HardwareRevision hw = new HardwareRevision(hardwareId, codename, platform);
BY_HARDWARE_ID.put(hardwareId, hw);
BY_CODENAME.put(codename, hw);
}
// ======================= Lookup Methods =======================
/**
* Get hardware revision by hardware ID (from advertisement data).
*
* @param hardwareId The hardware ID from BLE advertisement
* @return HardwareRevision or null if unknown
*/
@Nullable
public static HardwareRevision getByHardwareId(int hardwareId) {
return BY_HARDWARE_ID.get(hardwareId);
}
/**
* Get hardware revision by exact codename.
*
* @param codename The exact codename (e.g., "silk", "snowy_dvt")
* @return HardwareRevision or null if unknown
*/
@Nullable
public static HardwareRevision getByCodename(String codename) {
return BY_CODENAME.get(codename);
}
/**
* Get hardware revision by codename prefix (for model string matching).
* The model string may have additional suffixes (e.g., "silk_evt" matches "silk").
*
* @param model The model/hardware revision string from device
* @return HardwareRevision or null if unknown
*/
@Nullable
public static HardwareRevision getByModelString(String model) {
if (model == null || model.isEmpty()) {
return null;
}
// First try exact match in registry
HardwareRevision exact = BY_CODENAME.get(model);
if (exact != null) {
return exact;
}
// Fall back to platform codename prefix matching
Platform platform = Platform.findByCodenamePrefix(model);
if (platform != null) {
// Return a synthetic HardwareRevision for the platform
return new HardwareRevision(-1, model, platform);
}
return null;
}
/**
* Get codename string by hardware ID for PebbleProtocol compatibility.
*
* @param hardwareId The hardware ID
* @return Codename string or null if out of range
*/
@Nullable
public static String getCodenameByHardwareId(int hardwareId) {
if (hardwareId >= 0 && hardwareId < HW_REVISIONS_BY_ID.length) {
return HW_REVISIONS_BY_ID[hardwareId];
}
return null;
}
// ======================= BLE-Only Detection =======================
/**
* Check if hardware ID indicates a BLE-only device.
*
* @param hardwareId Hardware ID from advertisement data
* @return true if this is a BLE-only device
*/
public static boolean isBleOnlyByHardwareId(int hardwareId) {
HardwareRevision hw = BY_HARDWARE_ID.get(hardwareId);
return hw != null && hw.isBleOnly();
}
/**
* Check if model/codename string indicates a BLE-only device.
*
* @param model Model string from GBDevice.getModel()
* @return true if this is a BLE-only device
*/
public static boolean isBleOnlyByModel(@Nullable String model) {
if (model == null || model.isEmpty()) {
return false;
}
HardwareRevision hw = getByModelString(model);
return hw != null && hw.isBleOnly();
}
/**
* Check if device is a BLE-only Pebble using GBDevice model info.
* Falls back to name pattern matching if model is not available.
*
* @param gbDevice The GBDevice with model info
* @param btDevice The BluetoothDevice (used for type and name check)
* @return true if this is a BLE-only Pebble device
*/
public static boolean isBleOnly(@Nullable GBDevice gbDevice, @Nullable BluetoothDevice btDevice) {
if (btDevice == null) {
return false;
}
int type = btDevice.getType();
if (type != BluetoothDevice.DEVICE_TYPE_LE && type != BluetoothDevice.DEVICE_TYPE_DUAL) {
return false;
}
// First check hardware revision (model) if available
if (gbDevice != null) {
String model = gbDevice.getModel();
if (model != null && !model.isEmpty()) {
if (isBleOnlyByModel(model)) {
LOG.debug("isBleOnly: {} identified via hardware revision '{}'",
gbDevice.getName(), model);
return true;
}
}
}
// Fall back to name pattern matching
return isBleOnlyByName(btDevice.getName());
}
/**
* Check if device is a BLE-only Pebble using BluetoothDevice name.
*
* @param btDevice The BluetoothDevice to check
* @return true if this is a BLE-only Pebble device
*/
public static boolean isBleOnly(BluetoothDevice btDevice) {
if (btDevice == null) {
return false;
}
int type = btDevice.getType();
if (type != BluetoothDevice.DEVICE_TYPE_LE && type != BluetoothDevice.DEVICE_TYPE_DUAL) {
return false;
}
return isBleOnlyByName(btDevice.getName());
}
/**
* Check if device is a BLE-only Pebble using manufacturer data.
*
* @param manufacturerData SparseArray from BLE scan
* @return true if this is a BLE-only Pebble device
*/
public static boolean isBleOnly(SparseArray<byte[]> manufacturerData) {
if (manufacturerData == null) {
return false;
}
byte[] data = manufacturerData.get(PEBBLE_VENDOR_ID);
if (data == null) {
data = manufacturerData.get(CORE_VENDOR_ID);
}
if (data == null || data.length < 14) {
return false;
}
int hardwareId = data[13] & 0xFF;
return isBleOnlyByHardwareId(hardwareId);
}
/**
* Check if device name matches BLE-only Pebble pattern.
* Pebble 2, Time 2, and 2 Duo advertise as "Pebble XXXX" (4 hex digits).
*
* @param name Device name to check
* @return true if name matches BLE-only Pebble pattern
*/
public static boolean isBleOnlyByName(@Nullable String name) {
if (name == null) {
return false;
}
// Must start with "Pebble "
if (!name.startsWith("Pebble ")) {
return false;
}
// Exclude old-style LE companions
if (name.startsWith("Pebble-LE ") || name.startsWith("Pebble Time LE ")) {
return false;
}
// Check for exact "Pebble XXXX" pattern (11 chars, 4 hex digits)
if (name.length() == 11) {
String suffix = name.substring(7);
if (suffix.matches("[0-9A-Fa-f]{4}")) {
LOG.debug("isBleOnlyByName: {} matches Pebble 2/Time 2/2 Duo pattern", name);
return true;
}
}
return false;
}
/**
* Check if device is an old-style LE Pebble companion (not BLE-only).
*
* @param btDevice The BluetoothDevice to check
* @return true if this is an LE companion device
*/
public static boolean isLePebbleCompanion(BluetoothDevice btDevice) {
if (btDevice == null) {
return false;
}
int type = btDevice.getType();
if (type != BluetoothDevice.DEVICE_TYPE_LE && type != BluetoothDevice.DEVICE_TYPE_DUAL) {
return false;
}
String name = btDevice.getName();
return name != null && (name.startsWith("Pebble-LE ") || name.startsWith("Pebble Time LE "));
}
// ======================= Platform Utilities =======================
/**
* Get platform name for a model/codename string.
*
* @param model The model string
* @return Platform name (aplite, basalt, etc.) or "aplite" as default
*/
public static String getPlatformName(@Nullable String model) {
if (model == null || model.isEmpty()) {
return Platform.APLITE.getPlatformName();
}
HardwareRevision hw = getByModelString(model);
return hw != null ? hw.getPlatformName() : Platform.APLITE.getPlatformName();
}
/**
* Get display model name for a hardware revision string.
*
* @param hwRev Hardware revision string
* @return Display model name (e.g., "pebble_time_black")
*/
public static String getModelDisplayName(@Nullable String hwRev) {
if (hwRev == null || hwRev.isEmpty()) {
return "pebble_black";
}
HardwareRevision hw = getByModelString(hwRev);
if (hw == null) {
return "pebble_black";
}
return switch (hw.getPlatform()) {
case BASALT -> "pebble_time_black";
case CHALK -> "pebble_time_round_black_20mm";
case DIORITE -> "pebble2_black";
case EMERY -> hwRev.startsWith("obelix") ? "coredevices_pt2_black_grey" : "pebble_time2_black";
case FLINT -> "coredevices_p2d_black";
case GABBRO -> "coredevices_ptr2_black";
default -> "pebble_black";
};
}
/**
* Check if hardware revision supports heart rate monitor.
*
* @param hwRev Hardware revision string
* @return true if device has HRM
*/
public static boolean hasHRM(@Nullable String hwRev) {
HardwareRevision hw = getByModelString(hwRev);
if (hw == null) {
return false;
}
return hw.getPlatform().hasHRM();
}
/**
* Check if hardware revision supports health tracking.
*
* @param hwRev Hardware revision string
* @return true if device supports health
*/
public static boolean hasHealth(@Nullable String hwRev) {
HardwareRevision hw = getByModelString(hwRev);
if (hw == null) {
return true; // Assume yes for unknown
}
return hw.getPlatform().hasHealth();
}
/**
* Check if manufacturer data indicates any Pebble device.
*
* @param manufacturerData SparseArray from BLE scan
* @return true if vendor ID matches Pebble or Core
*/
public static boolean isPebbleFromManufacturerData(SparseArray<byte[]> manufacturerData) {
if (manufacturerData == null) {
return false;
}
return manufacturerData.get(PEBBLE_VENDOR_ID) != null ||
manufacturerData.get(CORE_VENDOR_ID) != null;
}
/**
* Parse hardware platform from manufacturer data.
*
* @param manufacturerData SparseArray from BLE scan
* @return HardwareRevision or null if not a Pebble or data is insufficient
*/
@Nullable
public static HardwareRevision parseManufacturerData(SparseArray<byte[]> manufacturerData) {
if (manufacturerData == null) {
return null;
}
byte[] data = manufacturerData.get(PEBBLE_VENDOR_ID);
if (data == null) {
data = manufacturerData.get(CORE_VENDOR_ID);
}
if (data == null || data.length < 14) {
return null;
}
int hardwareId = data[13] & 0xFF;
return getByHardwareId(hardwareId);
}
}
@@ -63,6 +63,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.ble.PebbleLES
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.PebbleJsService;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
@@ -602,7 +603,7 @@ class PebbleIoThread extends GBDeviceIoThread {
return;
}
String platformName = PebbleUtils.getPlatformName(gbDevice.getModel());
String platformName = PebbleHardware.getPlatformName(gbDevice.getModel());
try {
mPBWReader = new PBWReader(uri, getContext(), platformName);
@@ -41,6 +41,7 @@ import java.util.SimpleTimeZone;
import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppManagement;
@@ -282,24 +283,7 @@ public class PebbleProtocol extends GBDeviceProtocol {
private static final long GB_UUID_MASK = 0x4767744272646700L;
// base is -8
private static final String[] hwRevisions = {
// Emulator
"silk_bb2", "robert_bb", "silk_bb",
"spalding_bb2", "snowy_bb2", "snowy_bb",
"bb2", "bb",
"unknown",
// Pebble Classic Series
"ev1", "ev2", "ev2_3", "ev2_4", "v1_5", "v2_0",
// Pebble Time Series
"snowy_evt2", "snowy_dvt", "spalding_dvt", "snowy_s3", "spalding",
// Pebble 2 Series
"silk_evt", "robert_evt", "silk",
// Pebble 2 Duo
"asterix",
// Pebble Time 2
"obelix",
};
// Hardware revisions are now defined in PebbleHardware class
private static final Random mRandom = new Random();
@@ -2411,9 +2395,10 @@ public class PebbleProtocol extends GBDeviceProtocol {
String gitHash = getFixedString(buf, 8);
int fwFlags = buf.get();
LOG.info("git hash: {}, flags: {}", gitHash, fwFlags);
int hwRev = buf.get() + 8;
if (hwRev >= 0 && hwRev < hwRevisions.length) {
versionCmd.hwVersion = hwRevisions[hwRev];
int hwRev = buf.get() & 0xFF; // Convert to unsigned
String codename = PebbleHardware.getCodenameByHardwareId(hwRev);
if (codename != null) {
versionCmd.hwVersion = codename;
} else {
LOG.warn("unknown hw revision {}", hwRev);
}
@@ -45,6 +45,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.CheckSums;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware;
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
import nodomain.freeyourgadget.gadgetbridge.webview.CurrentPosition;
@@ -132,8 +133,8 @@ public class JSInterface {
JSONObject wi = new JSONObject();
try {
wi.put("firmware", device.getFirmwareVersion());
wi.put("platform", PebbleUtils.getPlatformName(device.getModel()));
wi.put("model", PebbleUtils.getModel(device.getModel()));
wi.put("platform", PebbleHardware.getPlatformName(device.getModel()));
wi.put("model", PebbleHardware.getModelDisplayName(device.getModel()));
//TODO: use real info
wi.put("language", "en");
} catch (JSONException e) {
@@ -39,69 +39,18 @@ import java.util.UUID;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.webview.PebbleJsService;
/**
* Utility methods for Pebble devices.
* For hardware definitions and BLE detection, see
* {@link nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleHardware}.
*/
public class PebbleUtils {
private static final Logger LOG = LoggerFactory.getLogger(PebbleUtils.class);
public static String getPlatformName(String hwRev) {
final String DEFAULT_PLATFORM = "aplite";
if (hwRev == null || hwRev.isEmpty()) {
return DEFAULT_PLATFORM;
}
if (hwRev.startsWith("snowy")) {
return "basalt";
} else if (hwRev.startsWith("spalding")) {
return "chalk";
} else if (hwRev.startsWith("silk")) {
return "diorite";
} else if (hwRev.startsWith("robert") || hwRev.startsWith("obelix")) {
return "emery";
} else if (hwRev.startsWith("asterix")) {
return "flint";
} else if (hwRev.startsWith("getafix")) {
return "gabbro";
}
return DEFAULT_PLATFORM;
}
public static String getModel(String hwRev) {
//TODO: get real data?
final String DEFAULT_MODEL = "pebble_black";
if (hwRev == null || hwRev.isEmpty()) {
return DEFAULT_MODEL;
}
if (hwRev.startsWith("snowy")) {
return "pebble_time_black";
} else if (hwRev.startsWith("spalding")) {
return "pebble_time_round_black_20mm";
} else if (hwRev.startsWith("silk")) {
return "pebble2_black";
} else if (hwRev.startsWith("robert")) {
return "pebble_time2_black";
} else if (hwRev.startsWith("asterix")) {
return "coredevices_p2d_black";
} else if (hwRev.startsWith("obelix")) {
return "coredevices_pt2_black_grey";
} else if (hwRev.startsWith("getafix")) {
return "coredevices_ptr2_black";
}
return DEFAULT_MODEL;
}
public static int getFwMajor(String fwString) {
return fwString.charAt(1) - 48;
}
public static boolean hasHRM(String hwRev) {
String platformName = getPlatformName(hwRev);
return "diorite".equals(platformName) || "emery".equals(platformName);
}
public static boolean hasHealth(String hwRev) {
String platformName = getPlatformName(hwRev);
return !"aplite".equals(platformName);
}
/**
* Get the closest Pebble-compatible color from the associated Android Color Integer.
* @param color An Android Color Integer to convert
@@ -127,7 +76,6 @@ public class PebbleUtils {
return getPebbleColor(Color.parseColor(colorHex));
}
/**
* Returns the directory containing the .pbw cache.
* @throws IOException when the external files directory cannot be accessed