Skip to content

Commit 6ebfd84

Browse files
authored
[anthem] Add channel, refactor parser, add tests (openhab#14720)
* Add channel and refactor parser Signed-off-by: Mark Hilbush <[email protected]>
1 parent 891da2c commit 6ebfd84

File tree

12 files changed

+567
-137
lines changed

12 files changed

+567
-137
lines changed

bundles/org.openhab.binding.anthem/README.md

+18-14
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,24 @@ The Anthem AV processor supports the following channels (some zones/channels are
3030

3131
| Channel | Type | Description |
3232
|-------------------------|---------|--------------|
33-
| *Main Zone* | | |
34-
| 1#power | Switch | Power the zone on or off |
35-
| 1#volume | Dimmer | Increase or decrease the volume level |
36-
| 1#volumeDB | Number | The actual volume setting |
37-
| 1#mute | Switch | Mute the volume |
38-
| 1#activeInput | Number | The currently active input source |
33+
| *General* | | |
34+
| general#command | String | Send a custom command |
35+
| *Main Zone* | | |
36+
| 1#power | Switch | Power the zone on or off |
37+
| 1#volume | Dimmer | Increase or decrease the volume level |
38+
| 1#volumeDB | Number | The actual volume setting |
39+
| 1#mute | Switch | Mute the volume |
40+
| 1#activeInput | Number | The currently active input source |
3941
| 1#activeInputShortName | String | Short friendly name of the active input |
40-
| 1#activeInputLongName | String | Long friendly name of the active input |
41-
| *Zone 2* | | |
42-
| 2#power | Switch | Power the zone on or off |
43-
| 2#volume | Dimmer | Increase or decrease the volume level |
44-
| 2#volumeDB | Number | The actual volume setting |
45-
| 2#mute | Switch | Mute the volume |
46-
| 2#activeInput | Number | The currently active input source |
42+
| 1#activeInputLongName | String | Long friendly name of the active input |
43+
| *Zone 2* | | |
44+
| 2#power | Switch | Power the zone on or off |
45+
| 2#volume | Dimmer | Increase or decrease the volume level |
46+
| 2#volumeDB | Number | The actual volume setting |
47+
| 2#mute | Switch | Mute the volume |
48+
| 2#activeInput | Number | The currently active input source |
4749
| 2#activeInputShortName | String | Short friendly name of the active input |
48-
| 2#activeInputLongName | String | Long friendly name of the active input |
50+
| 2#activeInputLongName | String | Long friendly name of the active input |
4951

5052

5153
## Full Example
@@ -59,6 +61,8 @@ Thing anthem:anthem:mediaroom "Anthem AVM 60" [ host="192.168.1.100" ]
5961
### Items
6062

6163
```
64+
String Anthem_Command "Command [%s]" { channel="anthem:anthem:mediaroom:general#command" }
65+
6266
Switch Anthem_Z1_Power "Zone 1 Power [%s]" { channel="anthem:anthem:mediaroom:1#power" }
6367
Dimmer Anthem_Z1_Volume "Zone 1 Volume [%s]" { channel="anthem:anthem:mediaroom:1#volume" }
6468
Number Anthem_Z1_Volume_DB "Zone 1 Volume dB [%.0f]" { channel="anthem:anthem:mediaroom:1#volumeDB" }

bundles/org.openhab.binding.anthem/src/main/java/org/openhab/binding/anthem/internal/AnthemBindingConstants.java

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ public class AnthemBindingConstants {
3232
// List of all Thing Type UIDs
3333
public static final Set<ThingTypeUID> SUPPORTED_THING_TYPES_UIDS = Set.of(THING_TYPE_ANTHEM);
3434

35+
// Channel groups
36+
public static final String CHANNEL_GROUP_GENERAL = "general";
37+
3538
// Channel Ids
3639
public static final String CHANNEL_POWER = "power";
3740
public static final String CHANNEL_VOLUME = "volume";
@@ -40,11 +43,16 @@ public class AnthemBindingConstants {
4043
public static final String CHANNEL_ACTIVE_INPUT = "activeInput";
4144
public static final String CHANNEL_ACTIVE_INPUT_SHORT_NAME = "activeInputShortName";
4245
public static final String CHANNEL_ACTIVE_INPUT_LONG_NAME = "activeInputLongName";
46+
public static final String CHANNEL_COMMAND = "command";
4347

4448
// Connection-related configuration parameters
4549
public static final int DEFAULT_PORT = 14999;
4650
public static final int DEFAULT_RECONNECT_INTERVAL_MINUTES = 2;
4751
public static final int DEFAULT_COMMAND_DELAY_MSEC = 100;
4852

4953
public static final char COMMAND_TERMINATION_CHAR = ';';
54+
55+
public static final String PROPERTY_REGION = "region";
56+
public static final String PROPERTY_SOFTWARE_BUILD_DATE = "softwareBuildDate";
57+
public static final String PROPERTY_NUM_AVAILABLE_INPUTS = "numAvailableInputs";
5058
}

bundles/org.openhab.binding.anthem/src/main/java/org/openhab/binding/anthem/internal/handler/AnthemCommand.java

+4
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ public static AnthemCommand queryMacAddress() {
116116
return new AnthemCommand("IDN?");
117117
}
118118

119+
public static AnthemCommand customCommand(String customCommand) {
120+
return new AnthemCommand(customCommand);
121+
}
122+
119123
public String getCommand() {
120124
return command + COMMAND_TERMINATOR;
121125
}

bundles/org.openhab.binding.anthem/src/main/java/org/openhab/binding/anthem/internal/handler/AnthemCommandParser.java

+53-59
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import org.eclipse.jdt.annotation.Nullable;
2424
import org.openhab.core.library.types.DecimalType;
2525
import org.openhab.core.library.types.OnOffType;
26-
import org.openhab.core.library.types.StringType;
26+
import org.openhab.core.thing.Thing;
2727
import org.slf4j.Logger;
2828
import org.slf4j.LoggerFactory;
2929

@@ -35,7 +35,7 @@
3535
*/
3636
@NonNullByDefault
3737
public class AnthemCommandParser {
38-
private static final Pattern NUM_AVAILABLE_INPUTS_PATTERN = Pattern.compile("ICN([0-9])");
38+
private static final Pattern NUM_AVAILABLE_INPUTS_PATTERN = Pattern.compile("ICN([0-9]{1,2})");
3939
private static final Pattern INPUT_SHORT_NAME_PATTERN = Pattern.compile("ISN([0-9][0-9])(\\p{ASCII}*)");
4040
private static final Pattern INPUT_LONG_NAME_PATTERN = Pattern.compile("ILN([0-9][0-9])(\\p{ASCII}*)");
4141
private static final Pattern POWER_PATTERN = Pattern.compile("Z([0-9])POW([01])");
@@ -45,39 +45,27 @@ public class AnthemCommandParser {
4545

4646
private Logger logger = LoggerFactory.getLogger(AnthemCommandParser.class);
4747

48-
private AnthemHandler handler;
48+
private Map<String, String> inputShortNamesMap = new HashMap<>();
49+
private Map<String, String> inputLongNamesMap = new HashMap<>();
4950

50-
private Map<Integer, String> inputShortNamesMap = new HashMap<>();
51-
private Map<Integer, String> inputLongNamesMap = new HashMap<>();
52-
53-
private int numAvailableInputs;
54-
55-
public AnthemCommandParser(AnthemHandler anthemHandler) {
56-
this.handler = anthemHandler;
57-
}
58-
59-
public int getNumAvailableInputs() {
60-
return numAvailableInputs;
61-
}
62-
63-
public void parseMessage(String command) {
51+
public @Nullable AnthemUpdate parseCommand(String command) {
6452
if (!isValidCommand(command)) {
65-
return;
53+
return null;
6654
}
6755
// Strip off the termination char and any whitespace
6856
String cmd = command.substring(0, command.indexOf(COMMAND_TERMINATION_CHAR)).trim();
6957

7058
// Zone command
7159
if (cmd.startsWith("Z")) {
72-
parseZoneCommand(cmd);
60+
return parseZoneCommand(cmd);
7361
}
7462
// Information command
7563
else if (cmd.startsWith("ID")) {
76-
parseInformationCommand(cmd);
64+
return parseInformationCommand(cmd);
7765
}
7866
// Number of inputs
7967
else if (cmd.startsWith("ICN")) {
80-
parseNumberOfAvailableInputsCommand(cmd);
68+
return parseNumberOfAvailableInputsCommand(cmd);
8169
}
8270
// Input short name
8371
else if (cmd.startsWith("ISN")) {
@@ -95,6 +83,15 @@ else if (cmd.startsWith("!")) {
9583
else {
9684
logger.trace("Command parser doesn't know how to handle command: '{}'", cmd);
9785
}
86+
return null;
87+
}
88+
89+
public @Nullable String getInputShortName(String input) {
90+
return inputShortNamesMap.get(input);
91+
}
92+
93+
public @Nullable String getInputLongName(String input) {
94+
return inputLongNamesMap.get(input);
9895
}
9996

10097
private boolean isValidCommand(String command) {
@@ -106,45 +103,47 @@ private boolean isValidCommand(String command) {
106103
return true;
107104
}
108105

109-
private void parseZoneCommand(String command) {
106+
private @Nullable AnthemUpdate parseZoneCommand(String command) {
110107
// Power update
111108
if (command.contains("POW")) {
112-
parsePower(command);
109+
return parsePower(command);
113110
}
114111
// Volume update
115112
else if (command.contains("VOL")) {
116-
parseVolume(command);
113+
return parseVolume(command);
117114
}
118115
// Mute update
119116
else if (command.contains("MUT")) {
120-
parseMute(command);
117+
return parseMute(command);
121118
}
122119
// Active input
123120
else if (command.contains("INP")) {
124-
parseActiveInput(command);
121+
return parseActiveInput(command);
125122
}
123+
return null;
126124
}
127125

128-
private void parseInformationCommand(String command) {
126+
private @Nullable AnthemUpdate parseInformationCommand(String command) {
129127
String value = command.substring(3, command.length());
128+
AnthemUpdate update = null;
130129
switch (command.substring(2, 3)) {
131130
case "M":
132-
handler.setModel(value);
131+
update = AnthemUpdate.createPropertyUpdate(Thing.PROPERTY_MODEL_ID, value);
133132
break;
134133
case "R":
135-
handler.setRegion(value);
134+
update = AnthemUpdate.createPropertyUpdate(PROPERTY_REGION, value);
136135
break;
137136
case "S":
138-
handler.setSoftwareVersion(value);
137+
update = AnthemUpdate.createPropertyUpdate(Thing.PROPERTY_FIRMWARE_VERSION, value);
139138
break;
140139
case "B":
141-
handler.setSoftwareBuildDate(value);
140+
update = AnthemUpdate.createPropertyUpdate(PROPERTY_SOFTWARE_BUILD_DATE, value);
142141
break;
143142
case "H":
144-
handler.setHardwareVersion(value);
143+
update = AnthemUpdate.createPropertyUpdate(Thing.PROPERTY_HARDWARE_VERSION, value);
145144
break;
146145
case "N":
147-
handler.setMacAddress(value);
146+
update = AnthemUpdate.createPropertyUpdate(Thing.PROPERTY_MAC_ADDRESS, value);
148147
break;
149148
case "Q":
150149
// Ignore
@@ -153,21 +152,20 @@ private void parseInformationCommand(String command) {
153152
logger.debug("Unknown info type");
154153
break;
155154
}
155+
return update;
156156
}
157157

158-
private void parseNumberOfAvailableInputsCommand(String command) {
158+
private @Nullable AnthemUpdate parseNumberOfAvailableInputsCommand(String command) {
159159
Matcher matcher = NUM_AVAILABLE_INPUTS_PATTERN.matcher(command);
160160
if (matcher != null) {
161161
try {
162162
matcher.find();
163-
String numAvailableInputsStr = matcher.group(1);
164-
DecimalType numAvailableInputs = DecimalType.valueOf(numAvailableInputsStr);
165-
handler.setNumAvailableInputs(numAvailableInputs.intValue());
166-
this.numAvailableInputs = numAvailableInputs.intValue();
167-
} catch (NumberFormatException | IndexOutOfBoundsException | IllegalStateException e) {
163+
return AnthemUpdate.createPropertyUpdate(PROPERTY_NUM_AVAILABLE_INPUTS, matcher.group(1));
164+
} catch (IndexOutOfBoundsException | IllegalStateException e) {
168165
logger.debug("Parsing exception on command: {}", command, e);
169166
}
170167
}
168+
return null;
171169
}
172170

173171
private void parseInputShortNameCommand(String command) {
@@ -182,11 +180,11 @@ private void parseErrorCommand(String command) {
182180
logger.info("Command was not processed successfully by the device: '{}'", command);
183181
}
184182

185-
private void parseInputName(String command, @Nullable Matcher matcher, Map<Integer, String> map) {
183+
private void parseInputName(String command, @Nullable Matcher matcher, Map<String, String> map) {
186184
if (matcher != null) {
187185
try {
188186
matcher.find();
189-
int input = Integer.parseInt(matcher.group(1));
187+
String input = matcher.group(1);
190188
String inputName = matcher.group(2);
191189
map.putIfAbsent(input, inputName);
192190
} catch (NumberFormatException | IndexOutOfBoundsException | IllegalStateException e) {
@@ -195,69 +193,65 @@ private void parseInputName(String command, @Nullable Matcher matcher, Map<Integ
195193
}
196194
}
197195

198-
private void parsePower(String command) {
196+
private @Nullable AnthemUpdate parsePower(String command) {
199197
Matcher mmatcher = POWER_PATTERN.matcher(command);
200198
if (mmatcher != null) {
201199
try {
202200
mmatcher.find();
203201
String zone = mmatcher.group(1);
204202
String power = mmatcher.group(2);
205-
handler.updateChannelState(zone, CHANNEL_POWER, "1".equals(power) ? OnOffType.ON : OnOffType.OFF);
206-
handler.checkPowerStatusChange(zone, power);
203+
return AnthemUpdate.createStateUpdate(zone, CHANNEL_POWER,
204+
"1".equals(power) ? OnOffType.ON : OnOffType.OFF);
207205
} catch (IndexOutOfBoundsException | IllegalStateException e) {
208206
logger.debug("Parsing exception on command: {}", command, e);
209207
}
210208
}
209+
return null;
211210
}
212211

213-
private void parseVolume(String command) {
212+
private @Nullable AnthemUpdate parseVolume(String command) {
214213
Matcher matcher = VOLUME_PATTERN.matcher(command);
215214
if (matcher != null) {
216215
try {
217216
matcher.find();
218217
String zone = matcher.group(1);
219218
String volume = matcher.group(2);
220-
handler.updateChannelState(zone, CHANNEL_VOLUME_DB, DecimalType.valueOf(volume));
219+
return AnthemUpdate.createStateUpdate(zone, CHANNEL_VOLUME_DB, DecimalType.valueOf(volume));
221220
} catch (NumberFormatException | IndexOutOfBoundsException | IllegalStateException e) {
222221
logger.debug("Parsing exception on command: {}", command, e);
223222
}
224223
}
224+
return null;
225225
}
226226

227-
private void parseMute(String command) {
227+
private @Nullable AnthemUpdate parseMute(String command) {
228228
Matcher matcher = MUTE_PATTERN.matcher(command);
229229
if (matcher != null) {
230230
try {
231231
matcher.find();
232232
String zone = matcher.group(1);
233233
String mute = matcher.group(2);
234-
handler.updateChannelState(zone, CHANNEL_MUTE, "1".equals(mute) ? OnOffType.ON : OnOffType.OFF);
234+
return AnthemUpdate.createStateUpdate(zone, CHANNEL_MUTE,
235+
"1".equals(mute) ? OnOffType.ON : OnOffType.OFF);
235236
} catch (IndexOutOfBoundsException | IllegalStateException e) {
236237
logger.debug("Parsing exception on command: {}", command, e);
237238
}
238239
}
240+
return null;
239241
}
240242

241-
private void parseActiveInput(String command) {
243+
private @Nullable AnthemUpdate parseActiveInput(String command) {
242244
Matcher matcher = ACTIVE_INPUT_PATTERN.matcher(command);
243245
if (matcher != null) {
244246
try {
245247
matcher.find();
246248
String zone = matcher.group(1);
247249
DecimalType activeInput = DecimalType.valueOf(matcher.group(2));
248-
handler.updateChannelState(zone, CHANNEL_ACTIVE_INPUT, activeInput);
249-
String name;
250-
name = inputShortNamesMap.get(activeInput.intValue());
251-
if (name != null) {
252-
handler.updateChannelState(zone, CHANNEL_ACTIVE_INPUT_SHORT_NAME, new StringType(name));
253-
}
254-
name = inputShortNamesMap.get(activeInput.intValue());
255-
if (name != null) {
256-
handler.updateChannelState(zone, CHANNEL_ACTIVE_INPUT_LONG_NAME, new StringType(name));
257-
}
250+
return AnthemUpdate.createStateUpdate(zone, CHANNEL_ACTIVE_INPUT, activeInput);
258251
} catch (NumberFormatException | IndexOutOfBoundsException | IllegalStateException e) {
259252
logger.debug("Parsing exception on command: {}", command, e);
260253
}
261254
}
255+
return null;
262256
}
263257
}

0 commit comments

Comments
 (0)