From 43f47091b0e90b9b387d65e534cef5eb1101139c Mon Sep 17 00:00:00 2001 From: Jules Dommartin Date: Wed, 21 Sep 2022 11:35:19 +0200 Subject: [PATCH 1/5] Added generic battery get message and status parsing --- .../opcodes/ApplicationMessageOpCodes.java | 10 + .../DefaultNoOperationMessageState.java | 4 + .../mesh/transport/GenericBatteryGet.java | 33 +++ .../mesh/transport/GenericBatteryStatus.java | 243 ++++++++++++++++++ 4 files changed, 290 insertions(+) create mode 100644 mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryGet.java create mode 100644 mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/opcodes/ApplicationMessageOpCodes.java b/mesh/src/main/java/no/nordicsemi/android/mesh/opcodes/ApplicationMessageOpCodes.java index 7067074c..03d4315d 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/opcodes/ApplicationMessageOpCodes.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/opcodes/ApplicationMessageOpCodes.java @@ -84,6 +84,16 @@ public class ApplicationMessageOpCodes { */ public static final int GENERIC_LEVEL_STATUS = 0x8208; + /** + * Opcode for the "Generic Battery Get" message. + */ + public static final int GENERIC_BATTERY_GET = 0x8223; + + /** + * Opcode for the "Generic Battery Status" message. + */ + public static final int GENERIC_BATTERY_STATUS = 0x8224; + /** * Opcode for the "Light Lightness Get" message */ diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/DefaultNoOperationMessageState.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/DefaultNoOperationMessageState.java index 753febaf..5ef79d04 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/DefaultNoOperationMessageState.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/DefaultNoOperationMessageState.java @@ -430,6 +430,10 @@ private void parseAccessMessage(final AccessMessage message) { final GenericLevelStatus genericLevelStatus = new GenericLevelStatus(message); mInternalTransportCallbacks.updateMeshNetwork(genericLevelStatus); mMeshStatusCallbacks.onMeshMessageReceived(message.getSrc(), genericLevelStatus); + } else if (message.getOpCode() == ApplicationMessageOpCodes.GENERIC_BATTERY_STATUS) { + final GenericBatteryStatus status = new GenericBatteryStatus(message); + mInternalTransportCallbacks.updateMeshNetwork(status); + mMeshStatusCallbacks.onMeshMessageReceived(message.getSrc(), status); } else if (message.getOpCode() == ApplicationMessageOpCodes.LIGHT_LIGHTNESS_STATUS) { final LightLightnessStatus lightLightnessStatus = new LightLightnessStatus(message); mInternalTransportCallbacks.updateMeshNetwork(lightLightnessStatus); diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryGet.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryGet.java new file mode 100644 index 00000000..cb84a968 --- /dev/null +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryGet.java @@ -0,0 +1,33 @@ +package no.nordicsemi.android.mesh.transport; + + +import androidx.annotation.NonNull; +import no.nordicsemi.android.mesh.ApplicationKey; +import no.nordicsemi.android.mesh.opcodes.ApplicationMessageOpCodes; +import no.nordicsemi.android.mesh.utils.SecureUtils; + +public class GenericBatteryGet extends ApplicationMessage { + + private static final String TAG = GenericBatteryGet.class.getSimpleName(); + private static final int OP_CODE = ApplicationMessageOpCodes.GENERIC_BATTERY_GET; + + /** + * Constructs a Generic Battery Get message + * + * @param appKey application key + */ + public GenericBatteryGet(@NonNull ApplicationKey appKey) { + super(appKey); + assembleMessageParameters(); + } + + @Override + void assembleMessageParameters() { + mAid = SecureUtils.calculateK4(mAppKey.getKey()); + } + + @Override + public int getOpCode() { + return OP_CODE; + } +} diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java new file mode 100644 index 00000000..23771b92 --- /dev/null +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java @@ -0,0 +1,243 @@ +package no.nordicsemi.android.mesh.transport; + +import androidx.annotation.NonNull; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import no.nordicsemi.android.mesh.logger.MeshLogger; +import no.nordicsemi.android.mesh.opcodes.ApplicationMessageOpCodes; +import no.nordicsemi.android.mesh.utils.MeshAddress; + +/** + * To be used as a wrapper class to create generic level status message. + */ +@SuppressWarnings({"WeakerAccess"}) +public class GenericBatteryStatus extends ApplicationStatusMessage { + + private static final String TAG = GenericBatteryStatus.class.getSimpleName(); + private static final int GENERIC_BATTERY_STATUS_MANDATORY_LENGTH = 10; + private static final int OP_CODE = ApplicationMessageOpCodes.GENERIC_BATTERY_STATUS; + private byte mBatteryLevel; + private int mTimeToDischarge; + private int mTimeToCharge; + private byte mFlags; + + /** + * Constructs GenericBatteryStatus message + * @param message access message + */ + public GenericBatteryStatus(@NonNull AccessMessage message) { + super(message); + this.mMessage = message; + this.mParameters = message.getParameters(); + parseStatusParameters(); + } + + @Override + void parseStatusParameters() { + MeshLogger.verbose(TAG, "Received generic battery status from: " + MeshAddress.formatAddress(mMessage.getSrc(), true)); + final ByteBuffer buffer = ByteBuffer.wrap(mParameters).order(ByteOrder.LITTLE_ENDIAN); + mBatteryLevel = buffer.get(); + MeshLogger.verbose(TAG, "Battery level: " + mBatteryLevel); + if (buffer.limit() > GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { + mTimeToDischarge = (buffer.get() << 24) + (buffer.get() << 16) + (buffer.get() << 8) + buffer.get(); + mTimeToCharge = (buffer.get() << 24) + (buffer.get() << 16) + (buffer.get() << 8) + buffer.get(); + mFlags = buffer.get(); + MeshLogger.verbose(TAG, "Time to discharge: " + mTimeToDischarge); + MeshLogger.verbose(TAG, "Time to charge: " + mTimeToCharge); + MeshLogger.verbose(TAG, "Flags: " + mFlags); + } + } + + @Override + public int getOpCode() { + return OP_CODE; + } + + /** + * Returns the battery level of the node + * + * @return battery level + */ + public byte getBatteryLevel() { + return mBatteryLevel; + } + + /** + * Returns the time to discharge of the battery + * + * @return time to discharge + */ + public int getTimeToDischarge() { + return mTimeToDischarge; + } + + /** + * Returns the time to charge of the battery + * + * @return time to charge + */ + public int getTimeToCharge() { + return mTimeToCharge; + } + + + /** + * Returns the battery flags + * + * @return battery flags + */ + public byte getFlags() { + return mFlags; + } + + /** + * Returns the battery presence + * + * @return BatteryPresence + */ + public BatteryPresence getBatteryPresence() { + return BatteryPresence.getBatteryPresence(mFlags & 0x03); + } + + /** + * Returns the battery charge level + * + * @return BatteryIndicator + */ + public BatteryIndicator getBatteryIndicator() { + return BatteryIndicator.getBatteryIndicator((mFlags >> 2) & 0x03); + } + + /** + * Returns the battery charging state + * + * @return BatteryChargingState + */ + public BatteryChargingState getBatteryChargingState() { + return BatteryChargingState.getBatteryChargingState((mFlags >> 4) & 0x03); + } + + /** + * Returns the battery serviceability + * + * @return BatteryServiceability + */ + public BatteryServiceability batteryServiceability() { + return BatteryServiceability.getBatteryServiceability((mFlags >> 6) & 0x03); + } + + /** + * Battery presence values enumeration + */ + public enum BatteryPresence { + NOT_PRESENT(0b00), + REMOVABLE(0b01), + NOT_REMOVABLE(0b10), + UNKNOWN(0b11); + + private int flag; + //Constructor to initialize the instance variable + BatteryPresence(int flag) { + this.flag = flag; + } + + public int getFlag() { + return this.flag; + } + + public static BatteryPresence getBatteryPresence(int flag) { + for (BatteryPresence bp : BatteryPresence.values()) { + if (bp.flag == flag) return bp; + } + throw new IllegalArgumentException("BatteryPresence flag not found"); + } + } + + + /** + * Battery indicator values enumeration + */ + public enum BatteryIndicator { + CRITICALLY_LOW(0b00), + LOW(0b01), + GOOD(0b10), + UNKNOWN(0b11); + + private int flag; + //Constructor to initialize the instance variable + BatteryIndicator(int flag) { + this.flag = flag; + } + + public int getFlag() { + return this.flag; + } + + public static BatteryIndicator getBatteryIndicator(int flag) { + for (BatteryIndicator bi : BatteryIndicator.values()) { + if (bi.flag == flag) return bi; + } + throw new IllegalArgumentException("BatteryIndicator flag not found"); + } + } + + + + /** + * Battery charging state values enumeration + */ + public enum BatteryChargingState { + NOT_CHARGEABLE(0b00), + NOT_CHARGING(0b01), + CHARGING(0b10), + UNKNOWN(0b11); + + private int flag; + //Constructor to initialize the instance variable + BatteryChargingState(int flag) { + this.flag = flag; + } + + public int getFlag() { + return this.flag; + } + + public static BatteryChargingState getBatteryChargingState(int flag) { + for (BatteryChargingState bcs : BatteryChargingState.values()) { + if (bcs.flag == flag) return bcs; + } + throw new IllegalArgumentException("BatteryChargingState flag not found"); + } + } + + + /** + * Battery serviceability values enumeration + */ + public enum BatteryServiceability { + RESERVED(0b00), + SERVICE_NOT_REQUIRED(0b01), + SERVICE_REQUIRED(0b10), + UNKNOWN(0b11); + + private int flag; + //Constructor to initialize the instance variable + BatteryServiceability(int flag) { + this.flag = flag; + } + + public int getFlag() { + return this.flag; + } + + public static BatteryServiceability getBatteryServiceability(int flag) { + for (BatteryServiceability bs : BatteryServiceability.values()) { + if (bs.flag == flag) return bs; + } + throw new IllegalArgumentException("BatteryServiceability flag not found"); + } + } + +} From 65f73a34bc581ee6d509706f60d3756f586f7461 Mon Sep 17 00:00:00 2001 From: Jules Dommartin Date: Wed, 21 Sep 2022 14:56:15 +0200 Subject: [PATCH 2/5] Fixed size of buffer which was not 10 but 8 --- .../android/mesh/transport/GenericBatteryStatus.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java index 23771b92..e5d92492 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java @@ -16,7 +16,7 @@ public class GenericBatteryStatus extends ApplicationStatusMessage { private static final String TAG = GenericBatteryStatus.class.getSimpleName(); - private static final int GENERIC_BATTERY_STATUS_MANDATORY_LENGTH = 10; + private static final int GENERIC_BATTERY_STATUS_MANDATORY_LENGTH = 8; private static final int OP_CODE = ApplicationMessageOpCodes.GENERIC_BATTERY_STATUS; private byte mBatteryLevel; private int mTimeToDischarge; @@ -41,8 +41,8 @@ void parseStatusParameters() { mBatteryLevel = buffer.get(); MeshLogger.verbose(TAG, "Battery level: " + mBatteryLevel); if (buffer.limit() > GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { - mTimeToDischarge = (buffer.get() << 24) + (buffer.get() << 16) + (buffer.get() << 8) + buffer.get(); - mTimeToCharge = (buffer.get() << 24) + (buffer.get() << 16) + (buffer.get() << 8) + buffer.get(); + mTimeToDischarge = buffer.get() | (buffer.get() << 8) | (buffer.get() << 16); + mTimeToCharge = buffer.get() | (buffer.get() << 8) | (buffer.get() << 16); mFlags = buffer.get(); MeshLogger.verbose(TAG, "Time to discharge: " + mTimeToDischarge); MeshLogger.verbose(TAG, "Time to charge: " + mTimeToCharge); From cf0e0ece86074c06752efcab4969499a85c9d2a2 Mon Sep 17 00:00:00 2001 From: Jules Dommartin Date: Wed, 21 Sep 2022 16:37:19 +0200 Subject: [PATCH 3/5] Fixed GenericBatteryStatus parsing --- .../android/mesh/transport/GenericBatteryStatus.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java index e5d92492..7afe287c 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java @@ -40,9 +40,9 @@ void parseStatusParameters() { final ByteBuffer buffer = ByteBuffer.wrap(mParameters).order(ByteOrder.LITTLE_ENDIAN); mBatteryLevel = buffer.get(); MeshLogger.verbose(TAG, "Battery level: " + mBatteryLevel); - if (buffer.limit() > GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { - mTimeToDischarge = buffer.get() | (buffer.get() << 8) | (buffer.get() << 16); - mTimeToCharge = buffer.get() | (buffer.get() << 8) | (buffer.get() << 16); + if (buffer.limit() >= GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { + mTimeToDischarge = (buffer.get() & 0xFF) | ((buffer.get() & 0xFF) << 8) | ((buffer.get() & 0xFF) << 16); + mTimeToCharge = (buffer.get() & 0xFF) | ((buffer.get() & 0xFF)<< 8) | ((buffer.get() & 0xFF) << 16); mFlags = buffer.get(); MeshLogger.verbose(TAG, "Time to discharge: " + mTimeToDischarge); MeshLogger.verbose(TAG, "Time to charge: " + mTimeToCharge); From fb0d2aba0b21fad6486dc3c298b721074d95f031 Mon Sep 17 00:00:00 2001 From: Jules Dommartin Date: Thu, 22 Sep 2022 19:01:48 +0200 Subject: [PATCH 4/5] Simplified code and followed Nordic advices --- .../android/mesh/transport/GenericBatteryStatus.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java index 7afe287c..ec3f26bf 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java @@ -37,13 +37,12 @@ public GenericBatteryStatus(@NonNull AccessMessage message) { @Override void parseStatusParameters() { MeshLogger.verbose(TAG, "Received generic battery status from: " + MeshAddress.formatAddress(mMessage.getSrc(), true)); - final ByteBuffer buffer = ByteBuffer.wrap(mParameters).order(ByteOrder.LITTLE_ENDIAN); - mBatteryLevel = buffer.get(); + mBatteryLevel = mParameters[0]; MeshLogger.verbose(TAG, "Battery level: " + mBatteryLevel); - if (buffer.limit() >= GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { - mTimeToDischarge = (buffer.get() & 0xFF) | ((buffer.get() & 0xFF) << 8) | ((buffer.get() & 0xFF) << 16); - mTimeToCharge = (buffer.get() & 0xFF) | ((buffer.get() & 0xFF)<< 8) | ((buffer.get() & 0xFF) << 16); - mFlags = buffer.get(); + if (mParameters.length >= GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { + mTimeToDischarge = (mParameters[1] & 0xFF) | ((mParameters[2] & 0xFF) << 8) | ((mParameters[3] & 0xFF) << 16); + mTimeToCharge = (mParameters[4] & 0xFF) | ((mParameters[5] & 0xFF) << 8) | ((mParameters[6] & 0xFF) << 16); + mFlags = mParameters[7]; MeshLogger.verbose(TAG, "Time to discharge: " + mTimeToDischarge); MeshLogger.verbose(TAG, "Time to charge: " + mTimeToCharge); MeshLogger.verbose(TAG, "Flags: " + mFlags); From 3d3533c8d771db82fa2b1bc96708dd07d755981f Mon Sep 17 00:00:00 2001 From: Jules Dommartin Date: Fri, 23 Sep 2022 23:31:38 +0200 Subject: [PATCH 5/5] Added 0xFF mask to get the unsigned byte value --- .../android/mesh/transport/GenericBatteryStatus.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java index ec3f26bf..fa032c59 100644 --- a/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java +++ b/mesh/src/main/java/no/nordicsemi/android/mesh/transport/GenericBatteryStatus.java @@ -18,10 +18,10 @@ public class GenericBatteryStatus extends ApplicationStatusMessage { private static final String TAG = GenericBatteryStatus.class.getSimpleName(); private static final int GENERIC_BATTERY_STATUS_MANDATORY_LENGTH = 8; private static final int OP_CODE = ApplicationMessageOpCodes.GENERIC_BATTERY_STATUS; - private byte mBatteryLevel; + private int mBatteryLevel; private int mTimeToDischarge; private int mTimeToCharge; - private byte mFlags; + private int mFlags; /** * Constructs GenericBatteryStatus message @@ -37,12 +37,12 @@ public GenericBatteryStatus(@NonNull AccessMessage message) { @Override void parseStatusParameters() { MeshLogger.verbose(TAG, "Received generic battery status from: " + MeshAddress.formatAddress(mMessage.getSrc(), true)); - mBatteryLevel = mParameters[0]; + mBatteryLevel = mParameters[0] & 0xFF; MeshLogger.verbose(TAG, "Battery level: " + mBatteryLevel); if (mParameters.length >= GENERIC_BATTERY_STATUS_MANDATORY_LENGTH) { mTimeToDischarge = (mParameters[1] & 0xFF) | ((mParameters[2] & 0xFF) << 8) | ((mParameters[3] & 0xFF) << 16); mTimeToCharge = (mParameters[4] & 0xFF) | ((mParameters[5] & 0xFF) << 8) | ((mParameters[6] & 0xFF) << 16); - mFlags = mParameters[7]; + mFlags = mParameters[7] & 0xFF; MeshLogger.verbose(TAG, "Time to discharge: " + mTimeToDischarge); MeshLogger.verbose(TAG, "Time to charge: " + mTimeToCharge); MeshLogger.verbose(TAG, "Flags: " + mFlags);