mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Marstek B2500: add status activity which shows current inputs, outputs and charge level
This commit is contained in:
parent
f77a60a14c
commit
6c8950209d
@ -868,6 +868,11 @@
|
||||
android:name=".devices.supercars.ControlActivity"
|
||||
android:exported="true" />
|
||||
|
||||
<activity
|
||||
android:name=".devices.marstek.SolarEquipmentStatusActivity"
|
||||
android:label="Solar Equipment Status"
|
||||
android:exported="true" />
|
||||
|
||||
<activity
|
||||
android:name=".devices.binary_sensor.activity.DataActivity"
|
||||
android:exported="true" />
|
||||
|
@ -17,14 +17,15 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.marstek;
|
||||
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import android.app.Activity;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.Device;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
@ -69,6 +70,16 @@ public class MarstekB2500DeviceCoordinator extends AbstractDeviceCoordinator {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsAppsManagement(final GBDevice device) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Activity> getAppsManagementActivity() {
|
||||
return SolarEquipmentStatusActivity.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBondingStyle() {
|
||||
return BONDING_STYLE_NONE;
|
||||
|
@ -0,0 +1,170 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.devices.marstek;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.gridlayout.widget.GridLayout;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.dashboard.GaugeDrawer;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class SolarEquipmentStatusActivity extends AbstractGBActivity {
|
||||
public static String ACTION_SEND_SOLAR_EQUIPMENT_STATUS = "send_solar_equipment_status";
|
||||
public static String EXTRA_BATTERY_PCT = "battery_pct";
|
||||
public static String EXTRA_PANEL1_WATT = "panel1_watt";
|
||||
public static String EXTRA_PANEL2_WATT = "panel2_watt";
|
||||
public static String EXTRA_OUTPUT1_WATT = "output1_watt";
|
||||
public static String EXTRA_OUTPUT2_WATT = "output2_watt";
|
||||
|
||||
private final Map<String, View> widgetMap = new HashMap<>();
|
||||
private GridLayout gridLayout;
|
||||
private SwipeRefreshLayout swipeLayout;
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (Objects.requireNonNull(action).equals(ACTION_SEND_SOLAR_EQUIPMENT_STATUS)) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras != null) {
|
||||
int battery_pct = extras.getInt(EXTRA_BATTERY_PCT);
|
||||
int panel1_watt = extras.getInt(EXTRA_PANEL1_WATT);
|
||||
int panel2_watt = extras.getInt(EXTRA_PANEL2_WATT);
|
||||
int output1_watt = extras.getInt(EXTRA_OUTPUT1_WATT);
|
||||
int output2_watt = extras.getInt(EXTRA_OUTPUT2_WATT);
|
||||
updateWidget("battery", battery_pct + "%", (float) (battery_pct / 100.0));
|
||||
updateWidget("panel1", panel1_watt + "W", (float) (panel1_watt / 380.0));
|
||||
updateWidget("panel2", panel2_watt + "W", (float) (panel1_watt / 380.0));
|
||||
updateWidget("output1", output1_watt + "W", (float) (output1_watt / 400.0));
|
||||
updateWidget("output2", output2_watt + "W", (float) (output1_watt / 400.0));
|
||||
|
||||
swipeLayout.setRefreshing(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
private GBDevice gBDevice = null;
|
||||
|
||||
public static int[] getColors() {
|
||||
return new int[]{
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_poor_color),
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_fair_color),
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_good_color),
|
||||
};
|
||||
}
|
||||
|
||||
public static int[] getColorsOutput() {
|
||||
return new int[]{
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_good_color),
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_fair_color),
|
||||
ContextCompat.getColor(GBApplication.getContext(), R.color.vo2max_value_poor_color),
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] getSegments() {
|
||||
return new float[]{
|
||||
0.1f,
|
||||
0.2f,
|
||||
0.7f,
|
||||
};
|
||||
}
|
||||
|
||||
public static float[] getSegmentsOutput() {
|
||||
return new float[]{
|
||||
0.33f,
|
||||
0.33f,
|
||||
0.34f,
|
||||
};
|
||||
}
|
||||
|
||||
private void updateWidget(String name, String value, float gaugeFill) {
|
||||
View view = widgetMap.get(name);
|
||||
if (view != null) {
|
||||
TextView gaugeValue = view.findViewById(R.id.gauge_value);
|
||||
gaugeValue.setText(value);
|
||||
GaugeDrawer gaugeDrawer = new GaugeDrawer();
|
||||
ImageView gaugeBar = view.findViewById(R.id.gauge_bar);
|
||||
gaugeDrawer.drawSegmentedGauge(gaugeBar, name.startsWith("output") ? getColorsOutput() : getColors(), name.startsWith("output") ? getSegmentsOutput() : getSegments(), gaugeFill, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
gBDevice = extras.getParcelable(GBDevice.EXTRA_DEVICE);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_solar_equipment_status);
|
||||
gridLayout = findViewById(R.id.solarequipmentview_gridlayout);
|
||||
createWidget("panel1", "Panel 1", 1);
|
||||
createWidget("panel2", "Panel 2", 1);
|
||||
createWidget("battery", "Battery", 2);
|
||||
createWidget("output1", "Output 1", 1);
|
||||
createWidget("output2", "Output 2", 1);
|
||||
|
||||
// Set pull-down-to-refresh action
|
||||
swipeLayout = findViewById(R.id.solarequipmentview_swipe_layout);
|
||||
swipeLayout.setOnRefreshListener(() -> {
|
||||
if (gBDevice == null || !gBDevice.isInitialized()) {
|
||||
swipeLayout.setRefreshing(false);
|
||||
GB.toast(getString(R.string.info_no_devices_connected), Toast.LENGTH_LONG, GB.WARN);
|
||||
return;
|
||||
}
|
||||
GBApplication.deviceService(gBDevice).onFetchRecordedData(0);
|
||||
});
|
||||
|
||||
IntentFilter filterLocal = new IntentFilter();
|
||||
filterLocal.addAction(ACTION_SEND_SOLAR_EQUIPMENT_STATUS);
|
||||
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filterLocal);
|
||||
}
|
||||
|
||||
private void createWidget(String name, String label, int columnSpan) {
|
||||
final float scale = getResources().getDisplayMetrics().density;
|
||||
|
||||
GridLayout.LayoutParams layoutParams = new GridLayout.LayoutParams(
|
||||
GridLayout.spec(GridLayout.UNDEFINED, GridLayout.FILL, 1f),
|
||||
GridLayout.spec(GridLayout.UNDEFINED, columnSpan, GridLayout.FILL, 1f)
|
||||
);
|
||||
layoutParams.width = 0;
|
||||
int pixels_8dp = (int) (8 * scale + 0.5f);
|
||||
layoutParams.setMargins(pixels_8dp, pixels_8dp, pixels_8dp, pixels_8dp);
|
||||
|
||||
MaterialCardView card = new MaterialCardView(this);
|
||||
int pixels_4dp = (int) (4 * scale + 0.5f);
|
||||
card.setRadius(pixels_4dp);
|
||||
card.setCardElevation(pixels_4dp);
|
||||
card.setContentPadding(pixels_4dp, pixels_4dp, pixels_4dp, pixels_4dp);
|
||||
card.setLayoutParams(layoutParams);
|
||||
LayoutInflater inflater = getLayoutInflater();
|
||||
|
||||
final View gaugeView = inflater.inflate(R.layout.dashboard_widget_generic_gauge, card, false);
|
||||
final TextView gaugeLabel = gaugeView.findViewById(R.id.gauge_label);
|
||||
gaugeLabel.setText(label);
|
||||
|
||||
card.addView(gaugeView);
|
||||
widgetMap.put(name, gaugeView);
|
||||
gridLayout.addView(card);
|
||||
}
|
||||
|
||||
}
|
@ -23,8 +23,11 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
|
||||
|
||||
import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -36,6 +39,7 @@ import java.util.SimpleTimeZone;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.marstek.SolarEquipmentStatusActivity;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
@ -148,6 +152,11 @@ public class MarstekB2500DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
sendCommand("set time", encodeSetCurrentTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFetchRecordedData(int dataTypes) {
|
||||
sendCommand("get infos 1", COMMAND_GET_INFOS1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSendConfiguration(final String config) {
|
||||
Prefs devicePrefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress()));
|
||||
@ -181,7 +190,7 @@ public class MarstekB2500DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
ByteBuffer buf = ByteBuffer.wrap(value);
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
buf.position(4); // skip header
|
||||
boolean p1_active = buf.get() != 0x00; // TODO: active = connected, or power incoming?
|
||||
boolean p1_active = buf.get() != 0x00;
|
||||
boolean p2_active = buf.get() != 0x00;
|
||||
int p1_watt = buf.getShort();
|
||||
int p2_watt = buf.getShort();
|
||||
@ -216,6 +225,15 @@ public class MarstekB2500DeviceSupport extends AbstractBTLEDeviceSupport {
|
||||
int battery_percentage = (int) Math.ceil((battery_charge_kwh / 2240.0f) * 100);
|
||||
getDevice().setBatteryLevel(battery_percentage);
|
||||
getDevice().sendDeviceUpdateIntent(getContext());
|
||||
|
||||
Intent intent = new Intent(SolarEquipmentStatusActivity.ACTION_SEND_SOLAR_EQUIPMENT_STATUS);
|
||||
intent.putExtra(SolarEquipmentStatusActivity.EXTRA_BATTERY_PCT, battery_percentage);
|
||||
intent.putExtra(SolarEquipmentStatusActivity.EXTRA_PANEL1_WATT, p1_watt);
|
||||
intent.putExtra(SolarEquipmentStatusActivity.EXTRA_PANEL2_WATT, p2_watt);
|
||||
intent.putExtra(SolarEquipmentStatusActivity.EXTRA_OUTPUT1_WATT, output_to_inverter_1_watt);
|
||||
intent.putExtra(SolarEquipmentStatusActivity.EXTRA_OUTPUT2_WATT, output_to_inverter_2_watt);
|
||||
LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
|
||||
getContext().sendBroadcast(intent);
|
||||
}
|
||||
|
||||
private void decodeDischargeIntervalsToPreferences(byte[] value) {
|
||||
|
21
app/src/main/res/layout/activity_solar_equipment_status.xml
Normal file
21
app/src/main/res/layout/activity_solar_equipment_status.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/solarequipmentview_swipe_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".devices.marstek.SolarEquipmentStatusActivity">
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.gridlayout.widget.GridLayout
|
||||
android:id="@+id/solarequipmentview_gridlayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:columnCount="2" />
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
Loading…
Reference in New Issue
Block a user