/* Copyright (C) 2024 Martin.JM
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 . */
package nodomain.freeyourgadget.gadgetbridge.devices.huawei.packets;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiPacket;
import nodomain.freeyourgadget.gadgetbridge.devices.huawei.HuaweiTLV;
public class Weather {
public static final byte id = 0x0f;
public static class Settings {
// WeatherSupport
public boolean weatherSupported = false;
public boolean windSupported = false;
public boolean pm25Supported = false;
public boolean temperatureSupported = false;
public boolean locationNameSupported = false;
public boolean currentTemperatureSupported = false;
public boolean unitSupported = false;
public boolean airQualityIndexSupported = false;
// WeatherExtendedSupport
public boolean timeSupported = false;
public boolean sourceSupported = false;
public boolean weatherIconSupported = false;
// WeatherSunMoonSupport
public boolean sunRiseSetSupported = false;
public boolean moonPhaseSupported = false;
// ExpandCapabilityRequest
public boolean uvIndexSupported = false;
}
public enum WeatherIcon {
// Also used for the text on the watch
SUNNY,
CLOUDY,
OVERCAST,
SHOWERS,
THUNDERSTORMS,
THUNDER_AND_HAIL,
SLEET,
LIGHT_RAIN,
RAIN,
HEAVY_RAIN,
RAIN_STORM,
HEAVY_RAIN_STORMS,
SEVERE_RAIN_STORMS,
SNOW_FLURRIES,
LIGHT_SNOW,
SNOW,
HEAVY_SNOW,
SNOWSTORMS,
FOG,
FREEZING_RAIN,
DUST_STORM,
LIGHT_TO_MODERATE_RAIN,
MODERATE_TO_HEAVY_RAIN,
HEAVY_TO_SEVERE_RAIN,
HEAVY_TO_TORRENTIAL_RAIN,
SEVERE_TO_TORRENTIAL_RAIN,
LIGHT_TO_MODERATE_SNOW,
MODERATE_TO_HEAVY_SNOW,
HEAVY_SNOW_TO_BLIZZARD,
DUST,
SAND,
SANDSTORMS,
FREEZING, // misses small/non-moving icon
HOT, // misses small/non-moving icon
COLD, // misses small/non-moving icon
WINDY,
HAZY,
UNKNOWN // Good to have probably
}
private static byte iconToByte(WeatherIcon weatherIcon) {
switch (weatherIcon) {
case SUNNY:
return 0x00;
case CLOUDY:
return 0x01;
case OVERCAST:
return 0x02;
case SHOWERS:
return 0x03;
case THUNDERSTORMS:
return 0x04;
case THUNDER_AND_HAIL:
return 0x05;
case SLEET:
return 0x06;
case LIGHT_RAIN:
return 0x07;
case RAIN:
return 0x08;
case HEAVY_RAIN:
return 0x09;
case RAIN_STORM:
return 0x0a;
case HEAVY_RAIN_STORMS:
return 0x0b;
case SEVERE_RAIN_STORMS:
return 0x0c;
case SNOW_FLURRIES:
return 0x0d;
case LIGHT_SNOW:
return 0x0e;
case SNOW:
return 0x0f;
case HEAVY_SNOW:
return 0x10;
case SNOWSTORMS:
return 0x11;
case FOG:
return 0x12;
case FREEZING_RAIN:
return 0x13;
case DUST_STORM:
return 0x14;
case LIGHT_TO_MODERATE_RAIN:
return 0x15;
case MODERATE_TO_HEAVY_RAIN:
return 0x16;
case HEAVY_TO_SEVERE_RAIN:
return 0x17;
case HEAVY_TO_TORRENTIAL_RAIN:
return 0x18;
case SEVERE_TO_TORRENTIAL_RAIN:
return 0x19;
case LIGHT_TO_MODERATE_SNOW:
return 0x1a;
case MODERATE_TO_HEAVY_SNOW:
return 0x1b;
case HEAVY_SNOW_TO_BLIZZARD:
return 0x1c;
case DUST:
return 0x1d;
case SAND:
return 0x1e;
case SANDSTORMS:
return 0x1f;
case FREEZING:
return 0x20;
case HOT:
return 0x21;
case COLD:
return 0x22;
case WINDY:
return 0x23;
case HAZY:
return 0x35;
default:
return 0x63; // Any higher and the current weather breaks
}
}
private static WeatherIcon byteToIcon(byte weatherIcon) {
switch (weatherIcon) {
case 0x00:
return WeatherIcon.SUNNY;
case 0x01:
return WeatherIcon.CLOUDY;
case 0x02:
return WeatherIcon.OVERCAST;
case 0x03:
return WeatherIcon.SHOWERS;
case 0x04:
return WeatherIcon.THUNDERSTORMS;
case 0x05:
return WeatherIcon.THUNDER_AND_HAIL;
case 0x06:
return WeatherIcon.SLEET;
case 0x07:
return WeatherIcon.LIGHT_RAIN;
case 0x08:
return WeatherIcon.RAIN;
case 0x09:
return WeatherIcon.HEAVY_RAIN;
case 0x0a:
return WeatherIcon.RAIN_STORM;
case 0x0b:
return WeatherIcon.HEAVY_RAIN_STORMS;
case 0x0c:
return WeatherIcon.SEVERE_RAIN_STORMS;
case 0x0d:
return WeatherIcon.SNOW_FLURRIES;
case 0x0e:
return WeatherIcon.LIGHT_SNOW;
case 0x0f:
return WeatherIcon.SNOW;
case 0x10:
return WeatherIcon.HEAVY_SNOW;
case 0x11:
return WeatherIcon.SNOWSTORMS;
case 0x12:
return WeatherIcon.FOG;
case 0x13:
return WeatherIcon.FREEZING_RAIN;
case 0x14:
return WeatherIcon.DUST_STORM;
case 0x15:
return WeatherIcon.LIGHT_TO_MODERATE_RAIN;
case 0x16:
return WeatherIcon.MODERATE_TO_HEAVY_RAIN;
case 0x17:
return WeatherIcon.HEAVY_TO_SEVERE_RAIN;
case 0x18:
return WeatherIcon.HEAVY_TO_TORRENTIAL_RAIN;
case 0x19:
return WeatherIcon.SEVERE_TO_TORRENTIAL_RAIN;
case 0x1a:
return WeatherIcon.LIGHT_TO_MODERATE_SNOW;
case 0x1b:
return WeatherIcon.MODERATE_TO_HEAVY_SNOW;
case 0x1c:
return WeatherIcon.HEAVY_SNOW_TO_BLIZZARD;
case 0x1d:
return WeatherIcon.DUST;
case 0x1e:
return WeatherIcon.SAND;
case 0x1f:
return WeatherIcon.SANDSTORMS;
case 0x20:
return WeatherIcon.FREEZING;
case 0x21:
return WeatherIcon.HOT;
case 0x22:
return WeatherIcon.COLD;
case 0x23:
return WeatherIcon.WINDY;
case 0x35:
return WeatherIcon.HAZY;
default:
return WeatherIcon.UNKNOWN;
}
}
public enum HuaweiTemperatureFormat {
CELSIUS,
FAHRENHEIT
}
private static byte temperatureFormatToByte(HuaweiTemperatureFormat temperatureFormat) {
if (temperatureFormat == HuaweiTemperatureFormat.FAHRENHEIT)
return 1;
return 0;
}
public enum MoonPhase {
NEW_MOON,
WAXING_CRESCENT,
FIRST_QUARTER,
WAXING_GIBBOUS,
FULL_MOON,
WANING_GIBBOUS,
THIRD_QUARTER,
WANING_CRESCENT
}
public static MoonPhase degreesToMoonPhase(int degrees) {
final int leeway = 6; // Give some leeway for the new moon, first quarter, full moon, and third quarter
if (degrees < 0 || degrees > 360)
return null;
else if (degrees >= 360 - leeway || degrees <= leeway)
return MoonPhase.NEW_MOON;
else if (degrees < 90)
return MoonPhase.WAXING_CRESCENT;
else if (degrees <= 90 + leeway)
return MoonPhase.FIRST_QUARTER;
else if (degrees < 180 - leeway)
return MoonPhase.WAXING_GIBBOUS;
else if (degrees <= 180 + leeway)
return MoonPhase.FULL_MOON;
else if (degrees < 270 - leeway)
return MoonPhase.WANING_GIBBOUS;
else if (degrees <= 270 + leeway)
return MoonPhase.THIRD_QUARTER;
else
return MoonPhase.WANING_CRESCENT;
}
private static byte moonPhaseToByte (MoonPhase moonPhase) {
switch (moonPhase) {
case NEW_MOON:
return 1;
case WAXING_CRESCENT:
return 2;
case FIRST_QUARTER:
return 3;
case WAXING_GIBBOUS:
return 4;
case FULL_MOON:
return 5;
case WANING_GIBBOUS:
return 6;
case THIRD_QUARTER:
return 7;
case WANING_CRESCENT:
return 8;
default:
return -1;
}
}
private static MoonPhase byteToMoonPhase(byte moonPhase) {
switch (moonPhase) {
case 1:
return MoonPhase.NEW_MOON;
case 2:
return MoonPhase.WAXING_CRESCENT;
case 3:
return MoonPhase.FIRST_QUARTER;
case 4:
return MoonPhase.WAXING_GIBBOUS;
case 5:
return MoonPhase.FULL_MOON;
case 6:
return MoonPhase.WANING_GIBBOUS;
case 7:
return MoonPhase.THIRD_QUARTER;
case 8:
return MoonPhase.WANING_CRESCENT;
default:
return null;
}
}
public enum ErrorCode {
NETWORK_ERROR,
GPS_PERMISSION_ERROR,
WEATHER_DISABLED
}
private static byte errorCodeToByte(ErrorCode errorCode) {
switch (errorCode) {
case NETWORK_ERROR:
return 0;
case GPS_PERMISSION_ERROR:
return 1;
case WEATHER_DISABLED:
return 2;
}
throw new RuntimeException(); // Shouldn't happen
}
public static class CurrentWeatherRequest extends HuaweiPacket {
public static final byte id = 0x01;
public CurrentWeatherRequest(
ParamsProvider paramsProvider,
Settings settings,
WeatherIcon icon,
Byte windDirection,
Byte windSpeed,
Byte lowestTemperature,
Byte highestTemperature,
Short pm25, // TODO: might be float?
String locationName,
Byte currentTemperature,
HuaweiTemperatureFormat temperatureUnit,
Short airQualityIndex,
Integer observationTime,
Float uvIndex,
String sourceName
) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
HuaweiTLV tlv81 = new HuaweiTLV();
if (icon != null && settings.weatherIconSupported) {
tlv81.put(0x02, iconToByte(icon));
}
if (settings.windSupported) {
short wind = 0;
if (windSpeed != null)
wind = (short) windSpeed;
if (windDirection != null) {
if (windDirection > 0)
wind |= (short) (((short) (windDirection * 8 / 360)) << 8);
else
wind |= (short) (((short) (360 + windDirection) * 8 / 360) << 8);
}
tlv81.put(0x03, wind);
}
if (settings.weatherIconSupported || settings.windSupported)
this.tlv.put(0x81, tlv81);
if (lowestTemperature != null && highestTemperature != null && settings.temperatureSupported) {
this.tlv.put(0x85, new HuaweiTLV()
.put(0x06, lowestTemperature)
.put(0x07, highestTemperature)
);
}
if (windDirection != null && windSpeed != null && settings.windSupported)
this.tlv.put(0x03, (short) ((((short) windDirection) << 8) | ((short) windSpeed)));
if (pm25 != null && settings.pm25Supported)
this.tlv.put(0x04, pm25);
if (locationName != null && settings.locationNameSupported)
this.tlv.put(0x08, locationName);
if (currentTemperature != null && settings.currentTemperatureSupported)
this.tlv.put(0x09, currentTemperature);
if (temperatureUnit != null && settings.unitSupported)
this.tlv.put(0x0a, temperatureFormatToByte(temperatureUnit));
if (airQualityIndex != null && settings.airQualityIndexSupported)
this.tlv.put(0x0b, airQualityIndex);
if (observationTime != null && settings.timeSupported)
this.tlv.put(0x0c, observationTime);
if (sourceName != null && settings.sourceSupported)
this.tlv.put(0x0e, sourceName);
if (uvIndex != null && settings.uvIndexSupported)
this.tlv.put(0x0f, uvIndex.byteValue());
this.isEncrypted = true;
this.complete = true;
}
}
public static class WeatherSupport {
public static final byte id = 0x02;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01);
this.isEncrypted = true;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public byte supportedBitmap = 0;
public boolean weatherSupported = false;
public boolean windSupported = false;
public boolean pm25Supported = false;
public boolean temperatureSupported = false;
public boolean locationNameSupported = false;
public boolean currentTemperatureSupported = false;
public boolean unitSupported = false;
public boolean airQualityIndexSupported = false;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
this.supportedBitmap = this.tlv.getByte(0x01);
this.weatherSupported = (this.supportedBitmap & 0x01) != 0;
this.windSupported = (this.supportedBitmap & 0x02) != 0;
this.pm25Supported = (this.supportedBitmap & 0x04) != 0;
this.temperatureSupported = (this.supportedBitmap & 0x08) != 0;
this.locationNameSupported = (this.supportedBitmap & 0x10) != 0;
this.currentTemperatureSupported = (this.supportedBitmap & 0x20) != 0;
this.unitSupported = (this.supportedBitmap & 0x40) != 0;
this.airQualityIndexSupported = (this.supportedBitmap & 0x80) != 0;
}
}
}
public static class WeatherDeviceRequest extends HuaweiPacket {
public static final byte id = 0x04;
public WeatherDeviceRequest(ParamsProvider paramsProvider, int responseValue) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01, responseValue);
this.isEncrypted = false;
this.complete = true;
}
}
public static class WeatherUnitRequest extends HuaweiPacket {
public static final byte id = 0x05;
public WeatherUnitRequest(ParamsProvider paramsProvider, HuaweiTemperatureFormat temperatureFormat) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01, temperatureFormatToByte(temperatureFormat));
this.isEncrypted = true;
this.complete = true;
}
}
public static class WeatherExtendedSupport {
public static final byte id = 0x06;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01);
this.isEncrypted = true;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public short supportedBitmap = 0;
public boolean timeSupported = false;
public boolean sourceSupported = false;
public boolean weatherIconSupported = false;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
this.supportedBitmap = this.tlv.getShort(0x01);
this.timeSupported = (this.supportedBitmap & 0x01) != 0;
this.sourceSupported = (this.supportedBitmap & 0x02) != 0;
this.weatherIconSupported = (this.supportedBitmap & 0x04) != 0;
}
}
}
public static class WeatherErrorSimple {
public static final byte id = 0x07;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, ErrorCode errorCode) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01, errorCodeToByte(errorCode));
this.isEncrypted = true;
this.complete = true;
}
}
}
public static class WeatherForecastData {
public static final byte id = 0x08;
public static class TimeData {
public int timestamp;
public WeatherIcon icon;
public Byte temperature;
@NonNull
@Override
public String toString() {
String timestampStr = new Date(timestamp * 1000L).toString();
return "TimeData{" +
"timestamp=" + timestamp +
", timestamp=" + timestampStr +
", icon=" + icon +
", temperature=" + temperature +
'}';
}
}
public static class DayData {
public int timestamp;
public WeatherIcon icon;
public Byte highTemperature;
public Byte lowTemperature;
public Integer sunriseTime;
public Integer sunsetTime;
public Integer moonRiseTime;
public Integer moonSetTime;
public MoonPhase moonPhase;
@NonNull
@Override
public String toString() {
String timestampStr = new Date(timestamp * 1000L).toString();
return "DayData{" +
"timestamp=" + timestamp +
", timestamp=" + timestampStr +
", icon=" + icon +
", highTemperature=" + highTemperature +
", lowTemperature=" + lowTemperature +
", sunriseTime=" + sunriseTime +
", sunsetTime=" + sunsetTime +
", moonRiseTime=" + moonRiseTime +
", moonSetTime=" + moonSetTime +
", moonPhase=" + moonPhase +
'}';
}
}
public static class Request extends HuaweiPacket {
public Request(
ParamsProvider paramsProvider,
Settings settings,
List timeDataList,
List dayDataList
) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV();
if (timeDataList != null && !timeDataList.isEmpty()) {
HuaweiTLV timeDataTlv = new HuaweiTLV();
for (TimeData timeData : timeDataList) {
HuaweiTLV timeTlv = new HuaweiTLV();
timeTlv.put(0x03, timeData.timestamp);
if (timeData.icon != null && settings.weatherIconSupported)
timeTlv.put(0x04, iconToByte(timeData.icon));
if (timeData.temperature != null && (settings.temperatureSupported || settings.currentTemperatureSupported))
timeTlv.put(0x05, timeData.temperature);
timeDataTlv.put(0x82, timeTlv);
}
this.tlv.put(0x81, timeDataTlv);
}
if (dayDataList != null && !dayDataList.isEmpty()) {
HuaweiTLV dayDataTlv = new HuaweiTLV();
for (DayData dayData : dayDataList) {
HuaweiTLV dayTlv = new HuaweiTLV();
dayTlv.put(0x12, dayData.timestamp);
if (dayData.icon != null && settings.weatherIconSupported)
dayTlv.put(0x13, iconToByte(dayData.icon));
if (settings.temperatureSupported) {
if (dayData.highTemperature != null)
dayTlv.put(0x14, dayData.highTemperature);
if (dayData.lowTemperature != null)
dayTlv.put(0x15, dayData.lowTemperature);
}
if (settings.sunRiseSetSupported) {
if (dayData.sunriseTime != null && dayData.sunriseTime != 0)
dayTlv.put(0x16, dayData.sunriseTime);
if (dayData.sunsetTime != null && dayData.sunsetTime != 0)
dayTlv.put(0x17, dayData.sunsetTime);
if (dayData.moonRiseTime != null && dayData.moonRiseTime != 0)
dayTlv.put(0x1a, dayData.moonRiseTime);
if (dayData.moonSetTime != null && dayData.moonSetTime != 0)
dayTlv.put(0x1b, dayData.moonSetTime);
}
if (dayData.moonPhase != null && settings.moonPhaseSupported)
dayTlv.put(0x1e, moonPhaseToByte(dayData.moonPhase));
dayDataTlv.put(0x91, dayTlv);
}
this.tlv.put(0x90, dayDataTlv);
}
this.isEncrypted = true;
this.isSliced = true;
this.complete = true;
}
}
public static class OutgoingRequest extends HuaweiPacket {
List timeDataList;
List dayDataList;
public OutgoingRequest(ParamsProvider paramsProvider) {
super(paramsProvider);
this.complete = false;
}
@Override
public void parseTlv() throws ParseException {
timeDataList = new ArrayList<>(this.tlv.getObject(0x81).getObjects(0x82).size());
for (HuaweiTLV timeTlv : this.tlv.getObject(0x81).getObjects(0x82)) {
TimeData timeData = new TimeData();
timeData.timestamp = timeTlv.getInteger(0x03);
timeData.icon = byteToIcon(timeTlv.getByte(0x04));
timeData.temperature = timeTlv.getByte(0x05);
timeDataList.add(timeData);
}
dayDataList = new ArrayList<>(this.tlv.getObject(0x90).getObjects(0x91).size());
for (HuaweiTLV dayTlv : this.tlv.getObject(0x90).getObjects(0x91)) {
DayData dayData = new DayData();
dayData.timestamp = dayTlv.getInteger(0x12);
dayData.icon = byteToIcon(dayTlv.getByte(0x13));
dayData.highTemperature = dayTlv.getByte(0x14);
dayData.lowTemperature = dayTlv.getByte(0x15);
dayData.sunriseTime = dayTlv.getInteger(0x16);
dayData.sunsetTime = dayTlv.getInteger(0x17);
dayData.moonRiseTime = dayTlv.getInteger(0x1a);
dayData.moonSetTime = dayTlv.getInteger(0x1b);
dayData.moonPhase = byteToMoonPhase(dayTlv.getByte(0x1e));
dayDataList.add(dayData);
}
}
}
}
public static class WeatherStart {
public static final byte id = 0x09;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01, (byte) 0x03); // TODO: find out what this means
this.isEncrypted = true;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public int successCode = -1;
public boolean success = false;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
this.successCode = this.tlv.getInteger(0x7f);
this.success = this.successCode == 0x000186A0 || this.successCode == 0x000186A3;
}
}
}
public static class WeatherSunMoonSupport {
public static final byte id = 0x0a;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
this.tlv = new HuaweiTLV().put(0x01);
this.isEncrypted = true;
this.complete = true;
}
}
public static class Response extends HuaweiPacket {
public int supportedBitmap = 0;
public boolean sunRiseSetSupported = false;
public boolean moonPhaseSupported = false;
public Response(ParamsProvider paramsProvider) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
}
@Override
public void parseTlv() throws ParseException {
if (!this.tlv.contains(0x01))
throw new MissingTagException(0x01);
this.supportedBitmap = this.tlv.getInteger(0x01);
this.sunRiseSetSupported = (this.supportedBitmap & 0x01) != 0;
this.moonPhaseSupported = (this.supportedBitmap & 0x02) != 0;
}
}
}
public static class WeatherErrorExtended {
public static final byte id = 0x0c;
public static class Request extends HuaweiPacket {
public Request(ParamsProvider paramsProvider, ErrorCode errorCode, boolean serverError) {
super(paramsProvider);
this.serviceId = Weather.id;
this.commandId = id;
HuaweiTLV innerTlv = new HuaweiTLV();
innerTlv.put(0x02, errorCodeToByte(errorCode));
if (errorCode == ErrorCode.NETWORK_ERROR && serverError)
innerTlv.put(0x03, (byte) 0x01);
this.tlv = new HuaweiTLV().put(0x81, innerTlv);
this.isEncrypted = true;
this.complete = true;
}
}
}
}