Skip to content

Commit

Permalink
Merge pull request #34 from JunkerMartin/master
Browse files Browse the repository at this point in the history
Support new device "Blind" and suppy device statistics
  • Loading branch information
kaklakariada authored Dec 4, 2021
2 parents e140a5c + f665c0d commit e06cb65
Show file tree
Hide file tree
Showing 25 changed files with 1,269 additions and 37 deletions.
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

0 comments on commit e06cb65

Please sign in to comment.