1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-06-02 03:16:07 +02:00
Gadgetbridge/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/xiaomi/services/XiaomiWeatherService.java
opcode 372aa88e6e Xiaomi: Weather fixes and Implement wind and AQI
Many fields were incorrectly being sent as uint32 instead of sint32. This caused issues with forecast icons.
2024-01-09 17:28:50 +00:00

213 lines
11 KiB
Java

/* Copyright (C) 2023 José Rebelo
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.xiaomi.services;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiWeatherConditions;
import nodomain.freeyourgadget.gadgetbridge.devices.xiaomi.XiaomiCoordinator;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.proto.xiaomi.XiaomiProto;
import nodomain.freeyourgadget.gadgetbridge.service.devices.xiaomi.XiaomiSupport;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class XiaomiWeatherService extends AbstractXiaomiService {
public static final int COMMAND_TYPE = 10;
private static final Logger LOG = LoggerFactory.getLogger(XiaomiWeatherService.class);
private static final int CMD_TEMPERATURE_UNIT_GET = 9;
private static final int CMD_TEMPERATURE_UNIT_SET = 10;
private static final int CMD_SET_CURRENT_WEATHER = 0;
private static final int CMD_SET_DAILY_WEATHER = 1;
private static final int CMD_SET_CURRENT_LOCATION = 6;
public XiaomiWeatherService(final XiaomiSupport support) {
super(support);
}
@Override
public void handleCommand(final XiaomiProto.Command cmd) {
// TODO
}
@Override
public void initialize() {
// TODO setMeasurementSystem();, or request
}
@Override
public boolean onSendConfiguration(final String config, final Prefs prefs) {
switch (config) {
case SettingsActivity.PREF_MEASUREMENT_SYSTEM:
setMeasurementSystem();
return true;
}
return false;
}
public void onSendWeather(final WeatherSpec weatherSpec) {
String timestamp = unixTimetstampToISOWithColons(weatherSpec.timestamp);
final XiaomiCoordinator coordinator = getSupport().getCoordinator();
if (coordinator.supportsMultipleWeatherLocations()) {
// TODO actually support multiple locations (primary + 4 secondary)
getSupport().sendCommand(
"set current location",
XiaomiProto.Command.newBuilder()
.setType(COMMAND_TYPE)
.setSubtype(CMD_SET_CURRENT_LOCATION)
.setWeather(XiaomiProto.Weather.newBuilder().setCurrentLocation(
XiaomiProto.WeatherCurrentLocation.newBuilder()
.setLocation(XiaomiProto.WeatherLocation.newBuilder()
.setCode("accu:123456") // FIXME:AccuWeather code (we do not have it here)
.setName(weatherSpec.location)
)
))
.build()
);
}
getSupport().sendCommand(
"set current weather",
XiaomiProto.Command.newBuilder()
.setType(COMMAND_TYPE)
.setSubtype(CMD_SET_CURRENT_WEATHER)
.setWeather(XiaomiProto.Weather.newBuilder().setCurrent(
XiaomiProto.WeatherCurrent.newBuilder()
.setTimeLocation(XiaomiProto.WeatherCurrentTimeLocation.newBuilder()
.setTimestamp(timestamp)
.setUnk2("")
.setCurrentLocationString(weatherSpec.location)
.setCurrentLocationCode("accu:123456") // FIXME:AccuWeather code (we do not have it here)
.setUnk5(true)
)
.setWeatherCondition(HuamiWeatherConditions.mapToAmazfitBipWeatherCode(weatherSpec.currentConditionCode)) // *SEEMS* to work
.setTemperature(XiaomiProto.WeatherCurrentTemperature.newBuilder()
.setDegrees(weatherSpec.currentTemp - 273) // TODO: support inches for weather
.setSymbol("")
)
.setHumidity(XiaomiProto.WeatherCurrentHumidity.newBuilder()
.setHumidity(weatherSpec.currentHumidity)
.setSymbol("%")
)
.setWind(XiaomiProto.WeatherCurrentWind.newBuilder()
.setWind(weatherSpec.windSpeedAsBeaufort())
.setSymbol("")
)
.setUv(XiaomiProto.WeatherCurrentUVIndex.newBuilder()
.setUnk1("")
.setIndex(Math.round(weatherSpec.uvIndex)) // This is sent as an sint but seems to be displayed with a decimal point
)
.setAQI(XiaomiProto.WeatherCurrentAQI.newBuilder()
.setAQIText("Unknown") // some string like "Moderate"
.setAQI(weatherSpec.airQuality != null && weatherSpec.airQuality.aqi >= 0 ? weatherSpec.airQuality.aqi : 0)
)
.setWarning(XiaomiProto.WeatherCurrentWarning.newBuilder()
.addCurrentWarning1(XiaomiProto.WeatherCurrentWarning1.newBuilder()
.setCurrentWarningText("")
.setCurrentWarningSeverityText("")
)
)
.setPressure(weatherSpec.pressure)
))
.build()
);
if (weatherSpec.forecasts != null) {
XiaomiProto.WeatherDailyList.Builder dailyListBuilder = XiaomiProto.WeatherDailyList.newBuilder();
int daysToSend = Math.min(7, weatherSpec.forecasts.size());
for (int i = 0; i < daysToSend; i++) {
WeatherSpec.AirQuality airQuality = weatherSpec.forecasts.get(i).airQuality;
dailyListBuilder.addForecastDay(XiaomiProto.WeatherDailyForecastDay.newBuilder()
.setAQI(XiaomiProto.DailyAQI.newBuilder()
.setAQIText("")
.setAQI(airQuality != null && airQuality.aqi >= 0 ? airQuality.aqi : 0)
)
.setUnk2(XiaomiProto.DailyUnk2.newBuilder()
.setUnk1(HuamiWeatherConditions.mapToAmazfitBipWeatherCode(weatherSpec.forecasts.get(i).conditionCode)) // TODO: verify
.setUnk2(0)
)
.setHighLowTemp(XiaomiProto.DailyHighLowTemp.newBuilder()
.setHigh(weatherSpec.forecasts.get(i).maxTemp - 273)
.setLow(weatherSpec.forecasts.get(i).minTemp - 273)
)
.setTemperatureSymbol("")
.setSunriseSunset(XiaomiProto.DailySunriseSunset.newBuilder()
.setSunrise(weatherSpec.forecasts.get(i).sunRise != 0 ? unixTimetstampToISOWithColons(weatherSpec.forecasts.get(i).sunRise) : "")
.setSunset(weatherSpec.forecasts.get(i).sunSet != 0 ? unixTimetstampToISOWithColons(weatherSpec.forecasts.get(i).sunSet) : "")
)
);
}
getSupport().sendCommand(
"set daily forecast",
XiaomiProto.Command.newBuilder()
.setType(COMMAND_TYPE)
.setSubtype(CMD_SET_DAILY_WEATHER)
.setWeather(XiaomiProto.Weather.newBuilder().setDaily(
XiaomiProto.WeatherDaily.newBuilder()
.setTimeLocation(XiaomiProto.WeatherCurrentTimeLocation.newBuilder()
.setTimestamp(timestamp)
.setUnk2("")
.setCurrentLocationString(weatherSpec.location)
.setCurrentLocationCode("accu:123456") // FIXME:AccuWeather code (we do not have it here)
.setUnk5(true)
)
.setDailyList(dailyListBuilder)
))
.build()
);
}
}
private void setMeasurementSystem() {
final Prefs prefs = getDevicePrefs();
final String measurementSystem = prefs.getString(SettingsActivity.PREF_MEASUREMENT_SYSTEM, "metric");
LOG.info("Setting measurement system to {}", measurementSystem);
final int unitValue = "metric".equals(measurementSystem) ? 1 : 2;
getSupport().sendCommand(
"set temperature unit",
XiaomiProto.Command.newBuilder()
.setType(COMMAND_TYPE)
.setSubtype(CMD_TEMPERATURE_UNIT_SET)
.setWeather(XiaomiProto.Weather.newBuilder().setTemperatureUnit(
XiaomiProto.WeatherTemperatureUnit.newBuilder().setUnit(unitValue)
))
.build()
);
}
private String unixTimetstampToISOWithColons(int timestamp) {
return new StringBuilder(
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US)
.format(new Date(timestamp * 1000L)))
.insert(22, ':') // FIXME: I bet this fails for some, but all this java date craps sucks
.toString();
}
}