Skip to content

Commit

Permalink
samsungtv: Updates for 2022 Frame TV
Browse files Browse the repository at this point in the history
Signed-off-by: Nick Waterton <n.waterton@outlook.com>
  • Loading branch information
NickWaterton committed Nov 22, 2022
1 parent 7b4d558 commit 1840bfd
Show file tree
Hide file tree
Showing 9 changed files with 772 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class SamsungTvBindingConstants {
public static final String KEY_CODE = "keyCode";
public static final String POWER = "power";
public static final String ART_MODE = "artMode";
public static final String SET_ART_MODE = "setArtMode";
public static final String SOURCE_APP = "sourceApp";

// List of all media renderer thing channel id's
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@ public class SamsungTvHandler extends BaseThingHandler implements RegistryListen

/* Store if art mode is supported to be able to skip switching power state to ON during initialization */
public boolean artModeSupported = false;
/* Art Mode on TV's >= 2022 is not properly supported - need workarounds for power */
public boolean artMode2022 = false;

private Optional<ScheduledFuture<?>> pollingJob = Optional.empty();
private WolSend wolTask = new WolSend(this);
Expand All @@ -122,6 +124,7 @@ class Device {
String countryCode;
String description;
String firmwareVersion;
String model;
String modelName;
String name;
String networkType;
Expand Down Expand Up @@ -153,6 +156,10 @@ public String getWifiMac() {
return Optional.ofNullable(device).map(a -> a.wifiMac).filter(m -> m.length() == 17).orElse("");
}

public String getModel() {
return Optional.ofNullable(device).map(a -> a.model).orElse("");
}

public String getModelName() {
return Optional.ofNullable(device).map(a -> a.modelName).orElse("");
}
Expand All @@ -175,6 +182,7 @@ public SamsungTvHandler(Thing thing, UpnpIOService upnpIOService, UpnpService up
* @return TVProperties
*/
public synchronized TVProperties fetchTVProperties() {
logger.trace("{}: getting TV properties", host);
TVProperties properties = new TVProperties();
try {
URI uri = new URI("http", null, host, PORT_DEFAULT_WEBSOCKET, HTTP_ENDPOINT_V2, null, null);
Expand Down Expand Up @@ -254,9 +262,14 @@ private void discoverConfiguration() {
public void updateSettings(TVProperties properties) {
setPowerState("on".equals(properties.getPowerState()));
setModelName(properties.getModelName());
setArtModeSupported(properties.getFrameTVSupport());
logger.debug("{}: Updated artModeSupported: {} and PowerState: {}", host, getArtModeSupported(),
getPowerState());
int year = Integer.parseInt(properties.getModel().substring(0, 2));
if (properties.getFrameTVSupport() && year >= 22) {
logger.warn("{}: Art Mode is NOT SUPPORTED on Frame TV's after 2021 model year", host);
setArtMode2022(true);
}
setArtModeSupported(properties.getFrameTVSupport() && year < 22);
logger.debug("{}: Updated artModeSupported: {} PowerState: {}({}) artMode2022: {}", host, getArtModeSupported(),
getPowerState(), properties.getPowerState(), getArtMode2022());
}

public void showConfiguration() {
Expand All @@ -278,6 +291,7 @@ public void showConfiguration() {
* @return String giving power state (Frame TV can be on or standby, off if unreachable)
*/
public String fetchPowerState() {
logger.trace("{}: fetching TV Power State", host);
TVProperties properties = fetchTVProperties();
String PowerState = properties.getPowerState();
setPowerState("on".equals(PowerState));
Expand Down Expand Up @@ -335,12 +349,21 @@ public String getModelName() {

public synchronized void setPowerState(boolean state) {
powerState = state;
logger.trace("{}: PowerState set to: {}", host, powerState ? "on" : "off");
}

public boolean getPowerState() {
return powerState;
}

public void setArtMode2022(boolean artmode) {
artMode2022 = artmode;
}

public boolean getArtMode2022() {
return artMode2022;
}

public boolean getArtModeSupported() {
return artModeSupported;
}
Expand Down Expand Up @@ -400,16 +423,24 @@ public void dispose() {
logger.debug("{}: Disposing SamsungTvHandler", host);
stopPolling();
wolTask.cancel();
setArtMode2022(false);
stopServices();
services.clear();
upnpService.getRegistry().removeListener(this);
}

private synchronized void stopServices() {
stopPolling();
if (!services.isEmpty()) {
logger.debug("{}: Shutdown all Samsung services", host);
services.stream().forEach(a -> stopService(a));
services.clear();
if (getArtMode2022()) {
logger.debug("{}: Shutdown all Samsung services except RemoteControllerService", host);
services.stream().filter(a -> !a.getServiceName().equals(RemoteControllerService.SERVICE_NAME))
.forEach(a -> stopService(a));
} else {
logger.debug("{}: Shutdown all Samsung services", host);
services.stream().forEach(a -> stopService(a));
services.clear();
}
}
}

Expand All @@ -423,8 +454,17 @@ private synchronized void putOnline() {
updateStatus(ThingStatus.ONLINE);
startPolling();
if (!getArtModeSupported()) {
setPowerState(true);
updateState(POWER, OnOffType.ON);
if (getArtMode2022()) {
// services.stream().filter(a -> a.getServiceName().equals(RemoteControllerService.SERVICE_NAME))
// .peek(a -> logger.debug("{}: Sendng SET_ART_MODE ON to {}", host, a.getServiceName()))
// .forEach(a -> a.handleCommand(SET_ART_MODE, (Command) OnOffType.ON));
setPowerState(false);
updateState(POWER, OnOffType.OFF);
updateState(ART_MODE, OnOffType.ON);
} else {
setPowerState(true);
updateState(POWER, OnOffType.ON);
}
}
logger.debug("{}: TV is {}", host, getThing().getStatus());
}
Expand All @@ -433,9 +473,18 @@ private synchronized void putOnline() {
public synchronized void putOffline() {
if (getThing().getStatus() != ThingStatus.OFFLINE) {
stopPolling();
setPowerState(false);
updateState(ART_MODE, OnOffType.OFF);
updateState(POWER, OnOffType.OFF);
if (getArtMode2022()) {
// services.stream().filter(a -> a.getServiceName().equals(RemoteControllerService.SERVICE_NAME))
// .peek(a -> logger.debug("{}: Sendng SET_ART_MODE OFF to {}", host, a.getServiceName()))
// .forEach(a -> a.handleCommand(SET_ART_MODE, (Command) OnOffType.OFF));
setPowerState(false);
updateState(ART_MODE, OnOffType.OFF);
updateState(POWER, OnOffType.OFF);
} else {
setPowerState(false);
updateState(ART_MODE, OnOffType.OFF);
updateState(POWER, OnOffType.OFF);
}
updateState(ART_IMAGE, UnDefType.NULL);
updateState(ART_LABEL, new StringType(""));
updateState(SOURCE_APP, new StringType(""));
Expand Down Expand Up @@ -511,7 +560,7 @@ private void checkAndCreateServices() {
}

// Websocket services and Smartthings service
if (isOnline && configuration.isWebsocketProtocol()) {
if ((isOnline | getArtMode2022()) && configuration.isWebsocketProtocol()) {
createService(RemoteControllerService.SERVICE_NAME, "");
if (!configuration.getSmartThingsApiKey().isBlank()) {
createService(SmartThingsApiService.SERVICE_NAME, "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ public synchronized void onWebSocketText(@Nullable String msgarg) {
case "ms.channel.ready":
logger.debug("{}: Art channel ready", host);
stateMap.clear();
if (remoteControllerWebSocket.callback.getArtMode2022()) {
remoteControllerWebSocket.callback.setArtMode2022(false);
logger.info("{}: Art Mode has been renabled on Frame TV's >= 2022", host);
}
getArtmodeStatus();
getArtmodeStatus("get_auto_rotation_status");
getArtmodeStatus("get_current_artwork");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@

import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
Expand Down Expand Up @@ -61,19 +63,20 @@ class WebSocketBase extends WebSocketAdapter {
public void onWebSocketClose(int statusCode, @Nullable String reason) {
logger.debug("{}: {} connection closed: {} - {}", host, className, statusCode, reason);
super.onWebSocketClose(statusCode, reason);
if (statusCode == 1001) {
// timeout
reconnect();
}
if (statusCode == 1006) {
// Disconnected
reconnect();
}
}

@Override
public void onWebSocketError(@Nullable Throwable error) {
if (logger.isTraceEnabled()) {
logger.trace("{}: {} connection error", host, className, error);
} else {
logger.debug("{}: {} connection error {}", host, className, error != null ? error.getMessage() : "");
}
logger.debug("{}: {} connection error {}", host, className, error != null ? error.getMessage() : "");
super.onWebSocketError(error);
if (error != null && !(error instanceof CancellationException)) {
reconnect();
}
}

void reconnect() {
Expand Down Expand Up @@ -115,6 +118,20 @@ public void onWebSocketConnect(@Nullable Session session) {
currentPolicy.setMaxBinaryMessageSize(bufferSize);
logger.trace("{}: {} Buffer Size set to {} Mb", host, className,
Math.round((bufferSize / 1048576.0) * 100.0) / 100.0);
// avoid 5 minute idle timeout
@Nullable
ScheduledExecutorService scheduler = remoteControllerWebSocket.callback.getScheduler();
if (scheduler != null) {
scheduler.scheduleWithFixedDelay(() -> {
try {
String data = "Ping";
ByteBuffer payload = ByteBuffer.wrap(data.getBytes());
session.getRemote().sendPing(payload);
} catch (IOException e) {
logger.warn("{} problem starting periodic Ping {} : {}", host, className, e.getMessage());
}
}, 4, 4, TimeUnit.MINUTES);
}
}
super.onWebSocketConnect(session);
count = 0;
Expand Down
Loading

0 comments on commit 1840bfd

Please sign in to comment.