/*
* AndFHEM - Open Source Android application to control a FHEM home automation
* server.
*
* Copyright (c) 2011, Matthias Klass or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLIC LICENSE, as published by the Free Software Foundation.
*
* 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 distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package li.klass.fhem.service.device;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import li.klass.fhem.domain.core.FhemDevice;
import li.klass.fhem.service.Command;
import li.klass.fhem.service.CommandExecutionService;
import li.klass.fhem.service.deviceConfiguration.DeviceConfiguration;
import li.klass.fhem.service.intent.RoomListUpdateIntentService;
import li.klass.fhem.service.room.RoomListUpdateService;
import li.klass.fhem.util.StateToSet;
import li.klass.fhem.util.Tasker;
import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Iterables.partition;
import static li.klass.fhem.constants.Actions.DO_REMOTE_UPDATE;
import static li.klass.fhem.constants.BundleExtraKeys.CONNECTION_ID;
import static li.klass.fhem.constants.BundleExtraKeys.DELAY;
import static li.klass.fhem.constants.BundleExtraKeys.DEVICE_NAME;
import static li.klass.fhem.service.deviceConfiguration.DeviceConfiguration.TO_DELAY_FOR_UPDATE_AFTER_COMMAND;
@Singleton
public class GenericDeviceService {
private static final Function<List<StateToSet>, String> FHT_CONCAT = new Function<List<StateToSet>, String>() {
@Override
public String apply(List<StateToSet> input) {
return fhtConcat(input);
}
private String fhtConcat(List<StateToSet> input) {
return from(input).transform(new Function<StateToSet, String>() {
@Override
public String apply(StateToSet input) {
return input.getKey() + " " + input.getValue();
}
}).join(Joiner.on(" "));
}
};
@Inject
CommandExecutionService commandExecutionService;
@Inject
RoomListUpdateService roomListUpdateService;
@Inject
public GenericDeviceService() {
}
public void setState(FhemDevice<?> device, String targetState, Optional<String> connectionId, Context context) {
setState(device, targetState, connectionId, context, true);
}
public void setState(final FhemDevice<?> device, String targetState, Optional<String> connectionId, final Context context, final boolean invokeUpdate) {
final String toSet = device.formatTargetState(targetState);
commandExecutionService.executeSafely(new Command("set " + device.getName() + " " + toSet, connectionId), context, invokePostCommandActions(device, context, invokeUpdate, "state", toSet, connectionId));
device.getXmlListDevice().setState("STATE", targetState);
device.getXmlListDevice().setInternal("STATE", targetState);
}
public void setSubState(FhemDevice<?> device, String subStateName, String value, Optional<String> connectionId, Context context, boolean invokeDeviceUpdate) {
if (device.getDeviceConfiguration().isPresent()) {
DeviceConfiguration configuration = device.getDeviceConfiguration().get();
Map<String, String> toReplace = configuration.getCommandReplaceFor(subStateName);
for (Map.Entry<String, String> entry : toReplace.entrySet()) {
value = value
.replaceAll("([ ,])" + entry.getKey(), "$1" + entry.getValue())
.replaceAll("^" + entry.getKey(), entry.getValue());
}
Optional<String> subStateReplaceForSubState = configuration.getSubStateReplaceFor(subStateName);
if (subStateReplaceForSubState.isPresent()) {
subStateName = subStateReplaceForSubState.get();
}
device.getXmlListDevice().setState(subStateName, value);
}
Command command = new Command("set " + device.getName() + " " + subStateName + " " + value, connectionId);
commandExecutionService.executeSafely(command, context,
invokePostCommandActions(device, context, invokeDeviceUpdate, subStateName, value, connectionId));
}
@NonNull
private CommandExecutionService.ResultListener invokePostCommandActions(final FhemDevice<?> device, final Context context, final boolean invokeUpdate, final String stateName, final String toSet, final Optional<String> connectionId) {
return new CommandExecutionService.SuccessfulResultListener() {
@Override
public void onResult(String result) {
if (invokeUpdate) {
update(device, context, connectionId);
}
Tasker.sendTaskerNotifyIntent(context, device.getName(), stateName, toSet);
Tasker.requestQuery(context);
device.getXmlListDevice().setState(stateName, toSet);
}
};
}
public void setSubStates(FhemDevice device, List<StateToSet> statesToSet, Optional<String> connectionId, Context context) {
if ("FHT".equalsIgnoreCase(device.getXmlListDevice().getType()) && statesToSet.size() > 1) {
setSubStatesForFHT(device, statesToSet, connectionId, context);
} else {
for (StateToSet toSet : statesToSet) {
setSubState(device, toSet.getKey(), toSet.getValue(), connectionId, context, false);
}
}
update(device, context, connectionId);
}
private void setSubStatesForFHT(FhemDevice device, List<StateToSet> statesToSet, Optional<String> connectionId, Context context) {
Iterable<List<StateToSet>> partitions = partition(statesToSet, 8);
ImmutableList<String> parts = from(partitions).transform(FHT_CONCAT).toList();
for (String toSet : parts) {
setState(device, toSet, connectionId, context, false);
}
}
public void update(FhemDevice<?> device, final Context context, Optional<String> connectionId) {
Integer delay = device.getDeviceConfiguration().transform(TO_DELAY_FOR_UPDATE_AFTER_COMMAND).or(0);
context.startService(new Intent(DO_REMOTE_UPDATE)
.putExtra(DEVICE_NAME, device.getName())
.putExtra(DELAY, delay)
.putExtra(CONNECTION_ID, connectionId.orNull())
.setClass(context, RoomListUpdateIntentService.class));
}
}