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

[somfytahoma] New method to send a command to several devices in the same place #10347

Merged
merged 2 commits into from
Mar 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.binding.somfytahoma.internal.handler.SomfyTahomaBridgeHandler;
import org.openhab.binding.somfytahoma.internal.model.*;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaActionGroup;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaDevice;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaGateway;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaRootPlace;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaSetup;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaState;
import org.openhab.binding.somfytahoma.internal.model.SomfyTahomaSubPlace;
import org.openhab.core.config.discovery.AbstractDiscoveryService;
import org.openhab.core.config.discovery.DiscoveryResultBuilder;
import org.openhab.core.config.discovery.DiscoveryService;
Expand All @@ -41,20 +47,21 @@
* action groups associated with your TahomaLink cloud account.
*
* @author Ondrej Pecta - Initial contribution
* @author Laurent Garnier - Include the place into the inbox label (when defined for the device)
*/
@NonNullByDefault
public class SomfyTahomaItemDiscoveryService extends AbstractDiscoveryService
implements DiscoveryService, ThingHandlerService {

private static final int DISCOVERY_TIMEOUT_SEC = 10;
private static final int DISCOVERY_REFRESH_SEC = 3600;

private final Logger logger = LoggerFactory.getLogger(SomfyTahomaItemDiscoveryService.class);

private @Nullable SomfyTahomaBridgeHandler bridgeHandler;

private @Nullable ScheduledFuture<?> discoveryJob;

private static final int DISCOVERY_TIMEOUT_SEC = 10;
private static final int DISCOVERY_REFRESH_SEC = 3600;

public SomfyTahomaItemDiscoveryService() {
super(DISCOVERY_TIMEOUT_SEC);
logger.debug("Creating discovery service");
Expand Down Expand Up @@ -124,7 +131,7 @@ private synchronized void runDiscovery() {
}

for (SomfyTahomaDevice device : setup.getDevices()) {
discoverDevice(device);
discoverDevice(device, setup);
}
for (SomfyTahomaGateway gw : setup.getGateways()) {
gatewayDiscovered(gw);
Expand All @@ -144,163 +151,164 @@ private synchronized void runDiscovery() {
}
}

private void discoverDevice(SomfyTahomaDevice device) {
private void discoverDevice(SomfyTahomaDevice device, SomfyTahomaSetup setup) {
logger.debug("url: {}", device.getDeviceURL());
String place = getPlaceLabel(setup, device.getPlaceOID());
switch (device.getUiClass()) {
case CLASS_AWNING:
// widget: PositionableHorizontalAwning
deviceDiscovered(device, THING_TYPE_AWNING);
deviceDiscovered(device, THING_TYPE_AWNING, place);
break;
case CLASS_CONTACT_SENSOR:
// widget: ContactSensor
deviceDiscovered(device, THING_TYPE_CONTACTSENSOR);
deviceDiscovered(device, THING_TYPE_CONTACTSENSOR, place);
break;
case CLASS_CURTAIN:
deviceDiscovered(device, THING_TYPE_CURTAIN);
deviceDiscovered(device, THING_TYPE_CURTAIN, place);
break;
case CLASS_EXTERIOR_SCREEN:
// widget: PositionableScreen
deviceDiscovered(device, THING_TYPE_EXTERIORSCREEN);
deviceDiscovered(device, THING_TYPE_EXTERIORSCREEN, place);
break;
case CLASS_EXTERIOR_VENETIAN_BLIND:
// widget: PositionableExteriorVenetianBlind
deviceDiscovered(device, THING_TYPE_EXTERIORVENETIANBLIND);
deviceDiscovered(device, THING_TYPE_EXTERIORVENETIANBLIND, place);
break;
case CLASS_GARAGE_DOOR:
deviceDiscovered(device, THING_TYPE_GARAGEDOOR);
deviceDiscovered(device, THING_TYPE_GARAGEDOOR, place);
break;
case CLASS_LIGHT:
if ("DimmerLight".equals(device.getWidget())) {
// widget: DimmerLight
deviceDiscovered(device, THING_TYPE_DIMMER_LIGHT);
deviceDiscovered(device, THING_TYPE_DIMMER_LIGHT, place);
} else {
// widget: TimedOnOffLight
// widget: StatefulOnOffLight
deviceDiscovered(device, THING_TYPE_LIGHT);
deviceDiscovered(device, THING_TYPE_LIGHT, place);
}
break;
case CLASS_LIGHT_SENSOR:
deviceDiscovered(device, THING_TYPE_LIGHTSENSOR);
deviceDiscovered(device, THING_TYPE_LIGHTSENSOR, place);
break;
case CLASS_OCCUPANCY_SENSOR:
// widget: OccupancySensor
deviceDiscovered(device, THING_TYPE_OCCUPANCYSENSOR);
deviceDiscovered(device, THING_TYPE_OCCUPANCYSENSOR, place);
break;
case CLASS_ON_OFF:
// widget: StatefulOnOff
deviceDiscovered(device, THING_TYPE_ONOFF);
deviceDiscovered(device, THING_TYPE_ONOFF, place);
break;
case CLASS_ROLLER_SHUTTER:
if (isSilentRollerShutter(device)) {
// widget: PositionableRollerShutterWithLowSpeedManagement
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER_SILENT);
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER_SILENT, place);
} else if (isUnoRollerShutter(device)) {
// widget: PositionableRollerShutterUno
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER_UNO);
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER_UNO, place);
} else {
// widget: PositionableRollerShutter
// widget: PositionableTiltedRollerShutter
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER);
deviceDiscovered(device, THING_TYPE_ROLLERSHUTTER, place);
}
break;
case CLASS_SCREEN:
// widget: PositionableTiltedScreen
deviceDiscovered(device, THING_TYPE_SCREEN);
deviceDiscovered(device, THING_TYPE_SCREEN, place);
break;
case CLASS_SMOKE_SENSOR:
// widget: SmokeSensor
deviceDiscovered(device, THING_TYPE_SMOKESENSOR);
deviceDiscovered(device, THING_TYPE_SMOKESENSOR, place);
break;
case CLASS_VENETIAN_BLIND:
deviceDiscovered(device, THING_TYPE_VENETIANBLIND);
deviceDiscovered(device, THING_TYPE_VENETIANBLIND, place);
break;
case CLASS_WINDOW:
// widget: PositionableTiltedWindow
deviceDiscovered(device, THING_TYPE_WINDOW);
deviceDiscovered(device, THING_TYPE_WINDOW, place);
break;
case CLASS_ALARM:
if (device.getDeviceURL().startsWith("internal:")) {
// widget: TSKAlarmController
deviceDiscovered(device, THING_TYPE_INTERNAL_ALARM);
deviceDiscovered(device, THING_TYPE_INTERNAL_ALARM, place);
} else if ("MyFoxAlarmController".equals(device.getWidget())) {
// widget: MyFoxAlarmController
deviceDiscovered(device, THING_TYPE_MYFOX_ALARM);
deviceDiscovered(device, THING_TYPE_MYFOX_ALARM, place);
} else {
deviceDiscovered(device, THING_TYPE_EXTERNAL_ALARM);
deviceDiscovered(device, THING_TYPE_EXTERNAL_ALARM, place);
}
break;
case CLASS_POD:
if (hasState(device, CYCLIC_BUTTON_STATE)) {
deviceDiscovered(device, THING_TYPE_POD);
deviceDiscovered(device, THING_TYPE_POD, place);
}
break;
case CLASS_HEATING_SYSTEM:
if ("SomfyThermostat".equals(device.getWidget())) {
deviceDiscovered(device, THING_TYPE_THERMOSTAT);
deviceDiscovered(device, THING_TYPE_THERMOSTAT, place);
} else if ("ValveHeatingTemperatureInterface".equals(device.getWidget())) {
deviceDiscovered(device, THING_TYPE_VALVE_HEATING_SYSTEM);
deviceDiscovered(device, THING_TYPE_VALVE_HEATING_SYSTEM, place);
} else if (isOnOffHeatingSystem(device)) {
deviceDiscovered(device, THING_TYPE_ONOFF_HEATING_SYSTEM);
deviceDiscovered(device, THING_TYPE_ONOFF_HEATING_SYSTEM, place);
} else if (isZwaveHeatingSystem(device)) {
deviceDiscovered(device, THING_TYPE_ZWAVE_HEATING_SYSTEM);
deviceDiscovered(device, THING_TYPE_ZWAVE_HEATING_SYSTEM, place);
} else {
logUnsupportedDevice(device);
}
break;
case CLASS_EXTERIOR_HEATING_SYSTEM:
if ("DimmerExteriorHeating".equals(device.getWidget())) {
// widget: DimmerExteriorHeating
deviceDiscovered(device, THING_TYPE_EXTERIOR_HEATING_SYSTEM);
deviceDiscovered(device, THING_TYPE_EXTERIOR_HEATING_SYSTEM, place);
} else {
logUnsupportedDevice(device);
}
break;
case CLASS_HUMIDITY_SENSOR:
if (hasState(device, WATER_DETECTION_STATE)) {
deviceDiscovered(device, THING_TYPE_WATERSENSOR);
deviceDiscovered(device, THING_TYPE_WATERSENSOR, place);
} else {
// widget: RelativeHumiditySensor
deviceDiscovered(device, THING_TYPE_HUMIDITYSENSOR);
deviceDiscovered(device, THING_TYPE_HUMIDITYSENSOR, place);
}
case CLASS_DOOR_LOCK:
// widget: UnlockDoorLockWithUnknownPosition
deviceDiscovered(device, THING_TYPE_DOOR_LOCK);
deviceDiscovered(device, THING_TYPE_DOOR_LOCK, place);
break;
case CLASS_PERGOLA:
deviceDiscovered(device, THING_TYPE_PERGOLA);
deviceDiscovered(device, THING_TYPE_PERGOLA, place);
break;
case CLASS_WINDOW_HANDLE:
// widget: ThreeWayWindowHandle
deviceDiscovered(device, THING_TYPE_WINDOW_HANDLE);
deviceDiscovered(device, THING_TYPE_WINDOW_HANDLE, place);
break;
case CLASS_TEMPERATURE_SENSOR:
// widget: TemperatureSensor
deviceDiscovered(device, THING_TYPE_TEMPERATURESENSOR);
deviceDiscovered(device, THING_TYPE_TEMPERATURESENSOR, place);
break;
case CLASS_GATE:
deviceDiscovered(device, THING_TYPE_GATE);
deviceDiscovered(device, THING_TYPE_GATE, place);
break;
case CLASS_ELECTRICITY_SENSOR:
if (hasEnergyConsumption(device)) {
deviceDiscovered(device, THING_TYPE_ELECTRICITYSENSOR);
deviceDiscovered(device, THING_TYPE_ELECTRICITYSENSOR, place);
} else {
logUnsupportedDevice(device);
}
break;
case CLASS_DOCK:
// widget: Dock
deviceDiscovered(device, THING_TYPE_DOCK);
deviceDiscovered(device, THING_TYPE_DOCK, place);
break;
case CLASS_SIREN:
deviceDiscovered(device, THING_TYPE_SIREN);
deviceDiscovered(device, THING_TYPE_SIREN, place);
break;
case CLASS_ADJUSTABLE_SLATS_ROLLER_SHUTTER:
deviceDiscovered(device, THING_TYPE_ADJUSTABLE_SLATS_ROLLERSHUTTER);
deviceDiscovered(device, THING_TYPE_ADJUSTABLE_SLATS_ROLLERSHUTTER, place);
break;
case CLASS_CAMERA:
if (hasMyfoxShutter(device)) {
// widget: MyFoxSecurityCamera
deviceDiscovered(device, THING_TYPE_MYFOX_CAMERA);
deviceDiscovered(device, THING_TYPE_MYFOX_CAMERA, place);
} else {
logUnsupportedDevice(device);
}
Expand All @@ -315,6 +323,18 @@ private void discoverDevice(SomfyTahomaDevice device) {
}
}

private @Nullable String getPlaceLabel(SomfyTahomaSetup setup, String oid) {
SomfyTahomaRootPlace root = setup.getRootPlace();
if (!oid.isEmpty() && root != null) {
for (SomfyTahomaSubPlace place : root.getSubPlaces()) {
if (oid.equals(place.getOid())) {
return place.getLabel();
}
}
}
return null;
}

private boolean isStateLess(SomfyTahomaDevice device) {
return device.getStates().isEmpty() || (device.getStates().size() == 1 && hasState(device, STATUS_STATE));
}
Expand Down Expand Up @@ -366,8 +386,12 @@ private boolean hasCommmand(SomfyTahomaDevice device, String command) {
return device.getDefinition().getCommands().stream().anyMatch(cmd -> command.equals(cmd.getCommandName()));
}

private void deviceDiscovered(SomfyTahomaDevice device, ThingTypeUID thingTypeUID) {
deviceDiscovered(device.getLabel(), device.getDeviceURL(), device.getOid(), thingTypeUID,
private void deviceDiscovered(SomfyTahomaDevice device, ThingTypeUID thingTypeUID, @Nullable String place) {
String label = device.getLabel();
if (place != null && !place.isBlank()) {
label += " (" + place + ")";
}
deviceDiscovered(label, device.getDeviceURL(), device.getOid(), thingTypeUID,
hasState(device, RSSI_LEVEL_STATE));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,17 @@ protected void sendCommand(String cmd, String param) {
}
}

protected void sendCommandToSameDevicesInPlace(String cmd) {
sendCommandToSameDevicesInPlace(cmd, "[]");
}

protected void sendCommandToSameDevicesInPlace(String cmd, String param) {
SomfyTahomaBridgeHandler handler = getBridgeHandler();
if (handler != null) {
handler.sendCommandToSameDevicesInPlace(url, cmd, param, EXEC_URL + "apply");
}
}

protected void refresh(String channel) {
SomfyTahomaBridgeHandler handler = getBridgeHandler();
String stateName = stateNames.get(channel);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ public class SomfyTahomaBridgeHandler extends BaseBridgeHandler {
*/
private String eventsId = "";

private Map<String, SomfyTahomaDevice> devicePlaces = new HashMap<>();

private ExpiringCache<List<SomfyTahomaDevice>> cachedDevices = new ExpiringCache<>(Duration.ofSeconds(30),
this::getDevices);

Expand Down Expand Up @@ -345,13 +347,19 @@ public List<SomfyTahomaActionGroup> listActionGroups() {
}

public @Nullable SomfyTahomaSetup getSetup() {
return invokeCallToURL(TAHOMA_API_URL + "setup", "", HttpMethod.GET, SomfyTahomaSetup.class);
SomfyTahomaSetup setup = invokeCallToURL(TAHOMA_API_URL + "setup", "", HttpMethod.GET, SomfyTahomaSetup.class);
if (setup != null) {
saveDevicePlaces(setup.getDevices());
}
return setup;
}

public List<SomfyTahomaDevice> getDevices() {
SomfyTahomaDevice[] response = invokeCallToURL(SETUP_URL + "devices", "", HttpMethod.GET,
SomfyTahomaDevice[].class);
return response != null ? List.of(response) : List.of();
List<SomfyTahomaDevice> devices = response != null ? List.of(response) : List.of();
saveDevicePlaces(devices);
return devices;
}

public synchronized @Nullable SomfyTahomaDevice getCachedDevice(String url) {
Expand All @@ -364,6 +372,18 @@ public List<SomfyTahomaDevice> getDevices() {
return null;
}

private void saveDevicePlaces(List<SomfyTahomaDevice> devices) {
devicePlaces.clear();
for (SomfyTahomaDevice device : devices) {
if (!device.getPlaceOID().isEmpty()) {
SomfyTahomaDevice newDevice = new SomfyTahomaDevice();
newDevice.setPlaceOID(device.getPlaceOID());
newDevice.setWidget(device.getWidget());
devicePlaces.put(device.getDeviceURL(), newDevice);
}
}
}

private void getTahomaUpdates() {
logger.debug("Getting Tahoma Updates...");
if (ThingStatus.OFFLINE == thing.getStatus() && !reLogin()) {
Expand Down Expand Up @@ -648,6 +668,20 @@ private void scheduleRetry(String io, String command, String params, String url,
}, thingConfig.getRetryDelay(), TimeUnit.MILLISECONDS));
}

public void sendCommandToSameDevicesInPlace(String io, String command, String params, String url) {
SomfyTahomaDevice device = devicePlaces.get(io);
if (device != null && !device.getPlaceOID().isEmpty()) {
devicePlaces.forEach((deviceUrl, devicePlace) -> {
if (device.getPlaceOID().equals(devicePlace.getPlaceOID())
&& device.getWidget().equals(devicePlace.getWidget())) {
sendCommand(deviceUrl, command, params, url);
}
});
} else {
sendCommand(io, command, params, url);
}
}

private String getThingLabelByURL(String io) {
Thing th = getThingByDeviceUrl(io);
if (th != null) {
Expand Down
Loading