try this code,
package jsr.tvdiscovery;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.annotations.SimpleFunction;
import android.os.Handler;
import android.os.Looper;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
@DesignerComponent(
version = 1,
description = “Discover Smart TVs using SSDP”,
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = “”
)
@SimpleObject(external = true)
public class TVDiscovery extends AndroidNonvisibleComponent {
private Handler uiHandler;
public TVDiscovery(ComponentContainer container) {
super(container.$form());
uiHandler = new Handler(Looper.getMainLooper());
}
@SimpleFunction(description = "Start searching for TVs on the network")
public void SearchTVs() {
new Thread(new Runnable() {
@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket();
socket.setSoTimeout(4000);
String search =
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n" +
"ST: ssdp:all\r\n\r\n";
DatagramPacket requestPacket = new DatagramPacket(
search.getBytes(),
search.length(),
InetAddress.getByName("239.255.255.250"),
1900
);
socket.send(requestPacket);
byte[] buffer = new byte[2048];
DatagramPacket responsePacket = new DatagramPacket(buffer, buffer.length);
while (true) {
try {
socket.receive(responsePacket);
final String response = new String(
responsePacket.getData(),
0,
responsePacket.getLength()
);
final String ip = responsePacket.getAddress().getHostAddress();
// Simple filter for TVs
if (response.toLowerCase().contains("tv") ||
response.toLowerCase().contains("mediarenderer")) {
uiHandler.post(new Runnable() {
@Override
public void run() {
TVFound(ip, ExtractName(response));
}
});
}
} catch (SocketTimeoutException e) {
break;
}
}
socket.close();
uiHandler.post(new Runnable() {
@Override
public void run() {
SearchFinished();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
// Extract friendly name from response (basic)
private String ExtractName(String response) {
try {
String lower = response.toLowerCase();
if (lower.contains("server:")) {
int start = lower.indexOf("server:");
int end = response.indexOf("\n", start);
return response.substring(start + 7, end).trim();
}
} catch (Exception ignored) {}
return "Smart TV";
}
@SimpleEvent(description = "Triggered when a TV is found")
public void TVFound(String tvIP, String tvName) {
EventDispatcher.dispatchEvent(this, "TVFound", tvIP, tvName);
}
@SimpleEvent(description = "Triggered when search is finished")
public void SearchFinished() {
EventDispatcher.dispatchEvent(this, "SearchFinished");
}
}
jsr.tvdiscovery.aix (6.5 KB)