Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support new device "Blind" and suppy device statistics #34

Merged
merged 8 commits into from
Dec 4, 2021
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ publishing {
}
}


signing {
sign publishing.publications.mavenJava
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.github.kaklakariada.fritzbox.http.QueryParameters;
import com.github.kaklakariada.fritzbox.http.QueryParameters.Builder;
import com.github.kaklakariada.fritzbox.model.homeautomation.DeviceList;
import com.github.kaklakariada.fritzbox.model.homeautomation.DeviceStats;

public class HomeAutomation {

Expand Down Expand Up @@ -97,6 +98,10 @@ public Float getTemperature(String deviceAin) {
return centiDegree == null ? null : centiDegree / 10F;
}

public DeviceStats getBasicStatistics(String deviceAin) {
return executeDeviceCommand(deviceAin, "getbasicdevicestats", null, DeviceStats.class);
}

public Float getSwitchPowerWatt(String deviceAin) {
final Integer powerMilliWatt = executeDeviceCommand(deviceAin, "getswitchpower", null, Integer.class);
return powerMilliWatt == null ? null : powerMilliWatt / 1000F;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* A Java API for managing FritzBox HomeAutomation
* Copyright (C) 2017 Christoph Pirkl <christoph at users.sourceforge.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.kaklakariada.fritzbox;

public class MissingClassException extends RuntimeException {

private static final long serialVersionUID = 1L;

public MissingClassException(String message, Throwable cause) {
super(message, cause);
}

public MissingClassException(String message) {
super(message);
}
}
59 changes: 58 additions & 1 deletion src/main/java/com/github/kaklakariada/fritzbox/TestDriver.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,18 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.github.kaklakariada.fritzbox.EnergyStatisticsService.EnergyStatsTimeRange;
import com.github.kaklakariada.fritzbox.model.homeautomation.AbstractDeviceStatistics;
import com.github.kaklakariada.fritzbox.model.homeautomation.Device;
import com.github.kaklakariada.fritzbox.model.homeautomation.DeviceList;
import com.github.kaklakariada.fritzbox.model.homeautomation.MeasurementUnit;
import com.github.kaklakariada.fritzbox.model.homeautomation.PowerMeter;
import com.github.kaklakariada.fritzbox.model.homeautomation.Statistics;

public class TestDriver {
private static final Logger LOG = LoggerFactory.getLogger(TestDriver.class);
Expand Down Expand Up @@ -62,6 +65,8 @@ public static void main(String[] args) throws InterruptedException {
final String ain = ids.get(0);

// testEnergyStats(homeAutomation, devices.getDevices().get(0).getId());
testEnergyStatsNew(homeAutomation, ain);
testVoltageStatsNew(homeAutomation, ain);
testHomeAutomation(homeAutomation, ain);
}

Expand All @@ -73,6 +78,57 @@ private static void testEnergyStats(HomeAutomation homeAutomation, String device
}
}

private static void testEnergyStatsNew(HomeAutomation homeAutomation, String ain) {
final Optional<AbstractDeviceStatistics> energy = homeAutomation.getBasicStatistics(ain).getEnergy();
if (energy.isEmpty()) {
LOG.error("No Statistics for energy consumption gathered");
return;
}
Optional<Statistics> dailyEnergy = energy.get().getStatisticsByGrid(86400);
if (dailyEnergy.isEmpty()) {
LOG.error("No Statistics for energy consumption 'per day' gathered");
return;
}
MeasurementUnit measurementUnit = dailyEnergy.get().getMeasurementUnit();
List<Optional<Number>> dailyConsumption = dailyEnergy.get().getValues();

StringBuffer sb = new StringBuffer();
for (final Optional<Number> dailyValue : dailyConsumption) {
if (dailyValue.isPresent()) {
sb.append(dailyValue.get()).append(measurementUnit.getUnit()).append(" ");
} else {
sb.append("-").append(" ");
}
}
LOG.debug("Statistics daily energy consumption: {}", sb.toString());
}

private static void testVoltageStatsNew(HomeAutomation homeAutomation, String ain) {
final Optional<AbstractDeviceStatistics> power = homeAutomation.getBasicStatistics(ain).getPower();
if (power.isEmpty()) {
LOG.error("No Statistics for power consumption gathered");
return;
}
Optional<Statistics> sixMinsVoltage = power.get().getStatisticsByGrid(10);
if (sixMinsVoltage.isEmpty()) {
LOG.error("No Statistics for power consumption 'per 10 seconds interval' gathered");
return;
}
MeasurementUnit measurementUnit = sixMinsVoltage.get().getMeasurementUnit();
List<Optional<Number>> sixMinutestConsumption = sixMinsVoltage.get().getValues();

StringBuffer sb = new StringBuffer();
for (final Optional<Number> intervalValue : sixMinutestConsumption) {
if (intervalValue.isPresent()) {
sb.append(intervalValue.get()).append(measurementUnit.getUnit()).append(" ");
} else {
sb.append("-").append(" ");
}
}
LOG.debug("Statistics power detetcted: {}", sb.toString());
}


private static void testHomeAutomation(final HomeAutomation homeAutomation, final String ain)
throws InterruptedException {
homeAutomation.switchPowerState(ain, false);
Expand All @@ -83,6 +139,7 @@ private static void testHomeAutomation(final HomeAutomation homeAutomation, fina
LOG.info("Switch {} has used {}Wh", ain, homeAutomation.getSwitchEnergyWattHour(ain));
LOG.info("Switch {} has name '{}'", ain, homeAutomation.getSwitchName(ain));
LOG.info("Switch {} has temperature {}°C", ain, homeAutomation.getTemperature(ain));
LOG.info("Switch {} statistics '{}'", ain, homeAutomation.getBasicStatistics(ain));

while (true) {
final List<Device> devices = homeAutomation.getDeviceListInfos().getDevices();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/**
* A Java API for managing FritzBox HomeAutomation
* Copyright (C) 2017 Christoph Pirkl <christoph at users.sourceforge.net>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.kaklakariada.fritzbox.helper;

public class StringHelper {

/**
* <p>Note that the method does not allow for a leading sign, either positive or negative.</p>
*
* <pre>
* StringUtils.isIntegerNumber(null) = false
* StringHelper.isIntegerNumber("")) = false
* StringHelper.isIntegerNumber(" ") = false
* StringHelper.isIntegerNumber(" 1 ") = true
* StringHelper.isIntegerNumber("123") = true
* StringUtils.isIntegerNumber("\u0967\u0968\u0969") = true
* StringHelper.isIntegerNumber("1.1") = false
* StringHelper.isIntegerNumber("1.1D") = false
* </pre>
*
*
* @param cs the String to check, may be null
* @return {@code true} if only contains digits or is enclosed by blanks, and is non-null
*/
public static boolean isIntegerNumber(final String cs) {
if (isEmpty(cs) || !isNumeric(cs.trim())) {
return false;
}
try {
Integer.parseInt(cs.trim());
} catch (NumberFormatException nfe) {
return false;
}
return true;
}

/**
* <h4>Code copied 'as is' from apache-commons-lang3, class StringUtils.isNumeric()</h4>
*
* <p>Checks if the CharSequence contains only Unicode digits.
* A decimal point is not a Unicode digit and returns false.</p>
*
* <p>{@code null} will return {@code false}.
* An empty CharSequence (length()=0) will return {@code false}.</p>
*
* <p>Note that the method does not allow for a leading sign, either positive or negative.
* Also, if a String passes the numeric test, it may still generate a NumberFormatException
* when parsed by Integer.parseInt or Long.parseLong, e.g. if the value is outside the range
* for int or long respectively.</p>
*
* <pre>
* StringUtils.isNumeric(null) = false
* StringUtils.isNumeric("") = false
* StringUtils.isNumeric(" ") = false
* StringUtils.isNumeric("123") = true
* StringUtils.isNumeric("\u0967\u0968\u0969") = true
* StringUtils.isNumeric("12 3") = false
* StringUtils.isNumeric("ab2c") = false
* StringUtils.isNumeric("12-3") = false
* StringUtils.isNumeric("12.3") = false
* StringUtils.isNumeric("-123") = false
* StringUtils.isNumeric("+123") = false
* </pre>
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if only contains digits, and is non-null
* @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence)
* @since 3.0 Changed "" to return false and not true
*/
public static boolean isNumeric(final CharSequence cs) {
if (isEmpty(cs)) {
return false;
}
final int sz = cs.length();
for (int i = 0; i < sz; i++) {
if (!Character.isDigit(cs.charAt(i))) {
return false;
}
}
return true;
}


/**
* <h4>Code copied 'as is' from apache-commons-lang3, class StringUtils.isEmpty()</h4>
*
* <p>Checks if a CharSequence is empty ("") or null.</p>
*
* <pre>
* StringUtils.isEmpty(null) = true
* StringUtils.isEmpty("") = true
* StringUtils.isEmpty(" ") = false
* StringUtils.isEmpty("bob") = false
* StringUtils.isEmpty(" bob ") = false
* </pre>
*
* <p>NOTE: This method changed in Lang version 2.0.
* It no longer trims the CharSequence.
* That functionality is available in isBlank().</p>
*
* @param cs the CharSequence to check, may be null
* @return {@code true} if the CharSequence is empty or null
* @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
*/
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.github.kaklakariada.fritzbox.model.homeautomation;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public abstract class AbstractDeviceStatistics {

/**
* Supply grids used to gather statistics
* @return List<Integer>
*/
public List<Integer> getGridList() {
final List<Integer> gridList = getStats()
.stream()
.map(stats -> stats.getGrid())
.collect(Collectors.toList());

return gridList;
};

/**
* Supply the Statistics gathered for a chosen grid
* @param int grid
* @return Optional<Statistics> - avoid NPE is no statistics present
*/
public Optional<Statistics> getStatisticsByGrid(final int grid) {
Optional<Statistics> statisticsByGrid = getStats()
.stream()
.filter(stats -> stats.getGrid() == grid)
.findAny();

return statisticsByGrid;
};

/**
* All classes implementing this abstract class need to provide a "getStats"-method
* @return List<Statistics>
*/
public abstract List<Statistics> getStats();

/**
* AVM gathers just integer numbers. We know the precision only from documentation, it is never provided
* by returned responses from Fritz!Box.
* So we add this information here to the statistics.
* @param stats
* @param measurementUnit
* @return List<Statistics>
*/
protected List<Statistics> getStats(final List<Statistics> stats, final MeasurementUnit measurementUnit) {
return stats
.stream()
.map(stat -> {
stat.setMeasurementUnit(measurementUnit);
return stat;
})
.collect(Collectors.toList());
}

/**
* All classes implementing this abstract class need to provide a "statisticsToString"-method
* @return List<Statistics>
*/
abstract protected List<String> statisticsToString();

/**
* @return statistics as one line per grid
*/
protected List<String> statisticsToString(final String type) {
return getStats()
.stream()
.map(stats -> statisticsToString(type, stats))
.collect(Collectors.toList());
}

/**
* form a line from a single statistic
* @param type
* @param statistics
* @return statistic as a line
*/
protected String statisticsToString(final String type, final Statistics statistics) {
return String.format("[%s] count=%s,grid=%s values=[%s]", type, statistics.getCount(), statistics.getGrid(), statistics.getCsvValues());
}
}
Loading