2021-01-20 16:03:58 +01:00
/ * Copyright ( C ) 2020 Andreas Shimokawa
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 < http : //www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.service.devices.huami.amazfitneo ;
2021-03-24 23:49:52 +01:00
import android.content.Context ;
import android.net.Uri ;
2022-07-30 22:17:06 +02:00
import org.apache.commons.lang3.ArrayUtils ;
2021-01-20 16:03:58 +01:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
2021-03-24 23:49:52 +01:00
import java.io.IOException ;
2022-07-30 22:17:06 +02:00
import nodomain.freeyourgadget.gadgetbridge.GBApplication ;
2021-01-20 16:03:58 +01:00
import nodomain.freeyourgadget.gadgetbridge.R ;
2022-07-30 22:17:06 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiCoordinator ;
2021-03-24 23:49:52 +01:00
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiFWHelper ;
2022-07-30 22:17:06 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService ;
2021-03-24 23:49:52 +01:00
import nodomain.freeyourgadget.gadgetbridge.devices.huami.amazfitneo.AmazfitNeoFWHelper ;
2022-08-15 14:07:03 +02:00
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator ;
2022-07-30 22:17:06 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser ;
2022-09-19 17:53:20 +02:00
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec ;
2021-03-24 09:51:51 +01:00
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec ;
2022-07-30 22:17:06 +02:00
import nodomain.freeyourgadget.gadgetbridge.service.btle.BLETypeConversions ;
2021-01-20 16:03:58 +01:00
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder ;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.miband5.MiBand5Support ;
2021-03-24 23:49:52 +01:00
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation ;
import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.operations.UpdateFirmwareOperation2020 ;
2021-01-20 16:03:58 +01:00
public class AmazfitNeoSupport extends MiBand5Support {
private static final Logger LOG = LoggerFactory . getLogger ( AmazfitNeoSupport . class ) ;
2022-09-04 21:43:39 +02:00
private boolean heartRateRealtimeStarted = false ;
private boolean heartRateTestStarted = false ;
private byte heartRateRealtimeCount = 0 ;
2021-03-24 09:51:51 +01:00
@Override
2022-06-14 20:15:51 +02:00
protected boolean notificationHasExtraHeader ( ) {
return false ;
2021-03-24 09:51:51 +01:00
}
2021-01-20 16:03:58 +01:00
@Override
protected AmazfitNeoSupport setDisplayItems ( TransactionBuilder builder ) {
2021-03-24 00:42:25 +01:00
setDisplayItemsNew ( builder , false , false , R . array . pref_neo_display_items_default ) ;
2021-01-20 16:03:58 +01:00
return this ;
}
2022-07-30 21:37:21 +02:00
@Override
protected AmazfitNeoSupport setFitnessGoal ( TransactionBuilder builder ) {
LOG . info ( " Attempting to set Fitness Goal... " ) ;
setNeoFitnessGoal ( builder ) ;
return this ;
}
@Override
protected AmazfitNeoSupport setGoalNotification ( TransactionBuilder builder ) {
LOG . info ( " Attempting to set goal notification... " ) ;
setNeoFitnessGoal ( builder ) ;
return this ;
}
private void setNeoFitnessGoal ( TransactionBuilder builder ) {
int fitnessGoal = GBApplication . getPrefs ( ) . getInt ( ActivityUser . PREF_USER_STEPS_GOAL , ActivityUser . defaultUserStepsGoal ) ;
boolean fitnessGoalNotification = HuamiCoordinator . getGoalNotification ( gbDevice . getAddress ( ) ) ;
LOG . info ( " Setting Amazfit Neo fitness goal to: " + fitnessGoal + " , notification: " + fitnessGoalNotification ) ;
byte [ ] bytes = ArrayUtils . addAll (
new byte [ ] { 0x3a , 1 , 0 , 0 , 0 , ( byte ) ( fitnessGoalNotification ? 1 : 0 ) } ,
BLETypeConversions . fromUint16 ( fitnessGoal ) ) ;
bytes = ArrayUtils . addAll ( bytes ,
HuamiService . COMMAND_SET_FITNESS_GOAL_END ) ;
writeToChunked ( builder , 2 , bytes ) ;
}
2022-08-12 15:57:24 +02:00
@Override
protected AmazfitNeoSupport requestAlarms ( TransactionBuilder builder ) {
return this ; //Neo always returns response array with '03' in it which marks alarms unused on connect
}
2022-08-09 14:30:21 +02:00
@Override
public boolean supportsHourlyChime ( ) { return true ; }
2022-08-15 14:07:03 +02:00
@Override
protected AmazfitNeoSupport setHeartrateSleepSupport ( TransactionBuilder builder ) {
final boolean enableHrSleepSupport = MiBandCoordinator . getHeartrateSleepSupport ( gbDevice . getAddress ( ) ) ;
LOG . info ( " Setting Amazfit Neo heartrate sleep support to " + enableHrSleepSupport ) ;
writeToConfiguration ( builder , new byte [ ] { 0x06 , 0x3c , 0x00 , ( byte ) ( enableHrSleepSupport ? 1 : 0 ) } ) ;
return this ;
}
2022-09-04 21:43:39 +02:00
@Override
public void onHeartRateTest ( ) {
if ( characteristicHRControlPoint = = null ) {
return ;
}
try {
TransactionBuilder builder = performInitialized ( " HeartRateTest " ) ;
enableNotifyHeartRateMeasurements ( true , builder ) ;
builder . write ( characteristicHRControlPoint , new byte [ ] { 0x15 , 0x01 , 0x01 } ) ;
builder . queue ( getQueue ( ) ) ;
heartRateTestStarted = true ;
} catch ( IOException ex ) {
LOG . error ( " Unable to read heart rate from Huami device " , ex ) ;
}
}
@Override
public void onEnableRealtimeHeartRateMeasurement ( boolean enable ) {
heartRateTestStarted = false ;
if ( characteristicHRControlPoint = = null ) {
return ;
}
try {
TransactionBuilder builder = performInitialized ( " Enable realtime heart rate measurement " ) ;
enableNotifyHeartRateMeasurements ( enable , builder ) ;
if ( enable ) {
if ( heartRateRealtimeStarted ) {
if ( heartRateRealtimeCount > = 10 ) {
builder . write ( characteristicHRControlPoint , new byte [ ] { 0x16 } ) ; //send continue every 10 seconds
heartRateRealtimeCount = 0 ;
}
heartRateRealtimeCount + + ;
}
else {
builder . write ( characteristicHRControlPoint , new byte [ ] { 0x15 , 0x01 , 0x01 } ) ;
heartRateRealtimeCount = 10 ; //sometimes first measurement times out, send first keep alive immediately
}
heartRateRealtimeStarted = true ;
} else {
builder . write ( characteristicHRControlPoint , new byte [ ] { 0x15 , 0x01 , 0x00 } ) ;
heartRateRealtimeStarted = false ;
}
builder . queue ( getQueue ( ) ) ;
enableRealtimeSamplesTimer ( enable ) ;
} catch ( IOException ex ) {
LOG . error ( " Unable to enable realtime heart rate measurement " , ex ) ;
}
}
@Override
protected void handleHeartrate ( byte [ ] value ) {
super . handleHeartrate ( value ) ;
if ( heartRateTestStarted )
onEnableRealtimeHeartRateMeasurement ( false ) ; //stop test after single measurement, disable HR notify
}
2022-09-19 17:53:20 +02:00
@Override
public void onSetCallState ( CallSpec callSpec ) {
if ( callSpec . name ! = null )
if ( callSpec . name . length ( ) > 7 ) //if char 0 is number, +, ( or ) and char 1 is a number it renders in bold scrolling font with letters stripped, else it renders in thin font and displays 'incoming call' if the lengh is > 7
if ( ! ( ( Character . isDigit ( callSpec . name . charAt ( 0 ) ) | | callSpec . name . charAt ( 0 ) = = '+' | | callSpec . name . charAt ( 0 ) = = '(' | | callSpec . name . charAt ( 0 ) = = ')' ) & & Character . isDigit ( callSpec . name . charAt ( 1 ) ) ) )
callSpec . name = callSpec . name . substring ( 0 , 7 ) ;
super . onSetCallState ( callSpec ) ;
}
2021-03-24 23:49:52 +01:00
@Override
public HuamiFWHelper createFWHelper ( Uri uri , Context context ) throws IOException {
return new AmazfitNeoFWHelper ( uri , context ) ;
}
@Override
public UpdateFirmwareOperation createUpdateFirmwareOperation ( Uri uri ) {
return new UpdateFirmwareOperation2020 ( uri , this ) ;
}
2021-01-20 16:03:58 +01:00
}