[tivo] Implement search channel (#20787)

* Implement search channel

Signed-off-by: Michael Lobstein <michael.lobstein@gmail.com>
This commit is contained in:
mlobstein
2026-05-25 20:47:36 +02:00
committed by GitHub
parent bc54c3f2ec
commit a8f5d8ff5c
6 changed files with 69 additions and 11 deletions
+5 -4
View File
@@ -60,6 +60,7 @@ All devices support the following channels:
| irCommand | String | Remote Control Button (IRCOMMAND) | Send a simulated button push from the remote control to the TiVo. See below for available IR COMMANDS. |
| kbdCommand | String | Keyboard Command (KEYBOARD) | Sends a code corresponding to a keyboard key press to the TiVo e.g. A-Z. See Appendix A in document TCP Remote Protocol 1.1 for supported characters and special character codes. |
| dvrStatus | String | TiVo Status | Action return code / channel information returned by the TiVo. |
| search | String | Search | Write-only channel that executes a search in the TiVo menu for the given string. |
- To change channels simply post/send the number of the channel to channelSet or channelForce. For OTA channels, a decimal for the sub-channel must be specified (ie: 2.1), for all others just send the channel as a whole number (ie: 100).
- Keyboard commands must currently be issued one character at a time to the item (this is how the TiVo natively supports this command).
@@ -144,7 +145,7 @@ Number TiVo_ForceChannel "Force Channel" {channel="tivo:sckt:Living_Roo
Number TiVo_Recording "Recording [MAP(tivo.map):rec-%s]" {channel="tivo:sckt:Living_Room:isRecording"}
String TiVo_IRCmd "Ir Cmd" {channel="tivo:sckt:Living_Room:irCommand"}
String TiVo_KbdCmd "Keyboard Cmd" {channel="tivo:sckt:Living_Room:kbdCommand"}
String TiVo_KeyboardStr "Search String"
String TiVo_SearchStr "Search String" {channel="tivo:sckt:Living_Room:search"}
```
- The item `TiVo_SetChannelName` depends upon a valid `tivo.map` file to translate channel numbers to channel names. The openHAB **MAP** transformation service must also be installed.
@@ -169,7 +170,7 @@ sitemap tivo label="Tivo Central" {
Switch item=TiVo_IRCmd label="Likes" icon="screen" mappings=["THUMBSUP"="Thumbs Up", "THUMBSDOWN"="Thumbs Down"]
Switch item=TiVo_IRCmd label="Remote" icon="screen" mappings=["FIND_REMOTE"="Find Remote"]
Switch item=TiVo_IRCmd label="Standby" icon="screen" mappings=["STANDBY"="Standby","TIVO"="Wake Up"]
Input item=TiVo_KeyboardStr label="Search" staticIcon=zoom inputHint="text"
Input item=TiVo_SearchStr label="Search" staticIcon=zoom inputHint="text"
Buttongrid item=TiVo_IRCmd label="Remote Control" staticIcon=material:tv_remote buttons=[1:1:GUIDE="Guide", 1:2:TIVO="Home", 1:3:LIVETV="LiveTV", 2:2:UP="Up"=f7:arrowtriangle_up, 3:1:LEFT="Left"=f7:arrowtriangle_left, 3:2:SELECT="OK", 3:3:RIGHT="Right"=f7:arrowtriangle_right, 4:2:DOWN="Down"=f7:arrowtriangle_down, 5:1:BACK="Back", 5:2:INFO="Info", 5:3:EXIT="Exit", 6:1:THUMBSUP="Thumbs Up"=f7:hand_thumbsup, 6:3:CHANNELUP="Channel +", 7:1:THUMBSDOWN="Thumbs Down"=f7:hand_thumbsdown, 7:3:CHANNELDOWN="Channel -", 8:2:PLAY="Play"=f7:play, 9:1:REVERSE="Reverse"=f7:backward, 9:2:PAUSE="Pause"=f7:pause, 9:3:FORWARD="Forward"=f7:forward, 10:2:SLOW="Slow"=f7:play_circle, 11:1:REPLAY="Replay", 11:2:RECORD="Record"=f7:circle, 11:3:ADVANCE="Advance", 12:1:ACTION_A="A (Yellow)", 12:2:ACTION_B="B (Blue)", 12:3:ACTION_C="C (Red)", 13:1:ACTION_D="D (Green)", 13:2:CC_ON="CC On", 13:3:CC_OFF="CC Off", 14:1:NUM1="1", 14:2:NUM2="2", 14:3:NUM3="3", 15:1:NUM4="4", 15:2:NUM5="5", 15:3:NUM6="6", 16:1:NUM7="7", 16:2:NUM8="8", 16:3:NUM9="9", 17:1:CLEAR="Clear", 17:2:NUM0="0", 17:3:ENTER="Enter", 18:1:STANDBY="Stand By", 18:3:FIND_REMOTE="Find Remote"]
}
}
@@ -201,12 +202,12 @@ etc...
### `tivo.rules` Example
- The following rule shows how a string change to the item `TiVo_KeyboardStr` is split into individual characters and sent to the Tivo.
- The following rule is no longer needed as the `search` channel implements this functionality internally. It remains here as a reference for sending KEYBOARD commands to the TiVo.
```java
rule "TiVo Search"
when
Item TiVo_KeyboardStr received update
Item TiVo_SearchStr received update
then
if (newState != NULL && newState.toString.length > 0) {
@@ -42,8 +42,11 @@ public class TiVoBindingConstants {
public static final String CHANNEL_TIVO_IRCMD = "irCommand";
public static final String CHANNEL_TIVO_KBDCMD = "kbdCommand";
public static final String CHANNEL_TIVO_STATUS = "dvrStatus";
public static final String CHANNEL_TIVO_SEARCH = "search";
// List of all configuration Properties
public static final String CONFIG_HOST = "host";
public static final String CONFIG_PORT = "tcpPort";
public static final String KEYBOARD = "KEYBOARD";
}
@@ -14,6 +14,7 @@ package org.openhab.binding.tivo.internal.handler;
import static org.openhab.binding.tivo.internal.TiVoBindingConstants.*;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@@ -82,7 +83,7 @@ public class TiVoHandler extends BaseThingHandler {
TivoStatusData currentStatus = tivoConnection.get().getServiceStatus();
String commandKeyword = "";
String commandParameter = command.toString().toUpperCase();
String commandParameter = command.toString().toUpperCase(Locale.ENGLISH);
if (command instanceof RefreshType) {
// Future enhancement, if we can come up with a sensible set of actions when a REFRESH is issued
logger.debug("TiVo '{}' skipping REFRESH command for channel: '{}'.", getThing().getUID(),
@@ -104,13 +105,51 @@ public class TiVoHandler extends BaseThingHandler {
commandKeyword = "IRCODE";
break;
case CHANNEL_TIVO_KBDCMD:
commandKeyword = "KEYBOARD";
commandKeyword = KEYBOARD;
break;
case CHANNEL_TIVO_SEARCH:
if (commandParameter.isBlank()) {
logger.debug("Search string was blank");
return;
}
try {
sendCommand("TELEPORT", "SEARCH", currentStatus);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
// Wait 1 second for the SEARCH screen to load
scheduler.schedule(() -> {
try {
for (int i = 0; i < commandParameter.length(); i++) {
if (commandParameter.charAt(i) >= 'A' && commandParameter.charAt(i) <= 'Z') {
sendCommand(KEYBOARD, String.valueOf(commandParameter.charAt(i)), currentStatus);
} else if (Character.isDigit(commandParameter.charAt(i))) {
sendCommand(KEYBOARD, "NUM" + commandParameter.charAt(i), currentStatus);
} else if (Character.isSpaceChar(commandParameter.charAt(i))) {
sendCommand(KEYBOARD, "SPACE", currentStatus);
} else {
logger.debug("Search character not supported: {}",
String.valueOf(commandParameter.charAt(i)));
}
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, 1, TimeUnit.SECONDS);
return;
default:
logger.debug("TiVo '{}' ignoring command '{}' for unsupported channel '{}'.", getThing().getUID(),
command, channelUID.getId());
return;
}
try {
sendCommand(commandKeyword, commandParameter, currentStatus);
} catch (InterruptedException e) {
// TiVo handler disposed or openHAB exiting, do nothing
Thread.currentThread().interrupt();
}
}
@@ -190,7 +229,7 @@ public class TiVoHandler extends BaseThingHandler {
try {
tivoConnection.get().connTivoDisconnect();
} catch (InterruptedException e) {
// TiVo handler disposed or openHAB exiting, do nothing
Thread.currentThread().interrupt();
}
tivoConnection = Optional.empty();
}
@@ -207,7 +246,7 @@ public class TiVoHandler extends BaseThingHandler {
try {
connection.statusRefresh();
} catch (InterruptedException e) {
// TiVo handler disposed or openHAB exiting, do nothing
Thread.currentThread().interrupt();
}
});
};
@@ -229,7 +268,7 @@ public class TiVoHandler extends BaseThingHandler {
try {
connection.statusRefresh();
} catch (InterruptedException e) {
// TiVo handler disposed or openHAB exiting, do nothing
Thread.currentThread().interrupt();
}
});
}
@@ -102,3 +102,5 @@ channel-type.tivo.menuTeleport.state.option.GUIDE = GUIDE
channel-type.tivo.menuTeleport.state.option.NOWPLAYING = NOWPLAYING
channel-type.tivo.menuTeleport.state.option.SEARCH = SEARCH
channel-type.tivo.menuTeleport.state.option.NETFLIX = NETFLIX
channel-type.tivo.search.label = Search
channel-type.tivo.search.description = Executes a search in the TiVo menu for the given string (write-only)
@@ -20,10 +20,11 @@
<channel id="irCommand" typeId="irCommand"/>
<channel id="kbdCommand" typeId="kbdCommand"/>
<channel id="dvrStatus" typeId="dvrStatus"/>
<channel id="search" typeId="search"/>
</channels>
<properties>
<property name="thingTypeVersion">1</property>
<property name="thingTypeVersion">2</property>
</properties>
<representation-property>host</representation-property>
@@ -211,4 +212,10 @@
<description>Action return code / channel information returned by the TiVo. Type: String (read-only)</description>
<state readOnly="true" pattern="%s"/>
</channel-type>
<channel-type id="search">
<item-type>String</item-type>
<label>Search</label>
<description>Executes a search in the TiVo menu for the given string (write-only)</description>
<autoUpdatePolicy>veto</autoUpdatePolicy>
</channel-type>
</thing:thing-descriptions>
@@ -12,6 +12,12 @@
<type>tivo:channelForce</type>
</update-channel>
</instruction-set>
<instruction-set targetVersion="2">
<add-channel id="search">
<type>tivo:search</type>
</add-channel>
</instruction-set>
</thing-type>
</update:update-descriptions>