-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
[Russound] Added discovery of new devices #2135
Conversation
@tmrobert8, thanks for your PR! By analyzing the history of the files in this pull request, we identified @kaikreuzer and @martinvw to be potential reviewers. |
@tmrobert8 can you take a look at the errors reported by the build? |
f45f2a1
to
c22024b
Compare
Martin - interesting decision enforcing IAE over NPE - but did the change... |
@tmrobert8 I did not look at the errors, what was the exact error? Was it that you should not throw your own NPE? I think I agree with this answer: http://stackoverflow.com/questions/3322638/is-it-okay-to-throw-nullpointerexception-programmatically#answer-3323006 |
Yep - I personally don't agree with that but I don't feel strongly enough to really care that much (nor care to waste my time arguing one way or the other) ;) At any rate, changed them all to IAE and it passes now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the big update of your/this binding. Great to see bindings being actively maintained by their authors!
I see that you reverted some changes which have been made in the meanwhile I don't know whether this was on purpose or not, but please keep them intact :-)
- So the prefered order of modifiers final static
- Loggers should be non-static and called logger.
I think that the code guidelines might have been updated since this binding was first developed. But it would be great if we could make your binding follow the current guidelines, if you really want we could make that a separate PR (before or after this one) but I prefer not to add new 'violations'.
So can you also please check the full report, you can find it in:
openhab2-addons\addons\binding\org.openhab.binding.russound\target\code-analysis\report.html
Besides that I added some other comments (but most of them are about the (current) guidelines). Questions? Please let me know I'll try to answer them ASAP.
@@ -33,11 +35,10 @@ | |||
* @author Tim Roberts | |||
*/ | |||
public class RussoundHandlerFactory extends BaseThingHandlerFactory { | |||
private final static Logger logger = LoggerFactory.getLogger(RussoundHandlerFactory.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loggers should non-static, can you check all of them?
logger.trace("Try to register Discovery service on BundleID: {} Service: {}", | ||
bundleContext.getBundle().getBundleId(), DiscoveryService.class.getName()); | ||
|
||
Hashtable<String, String> prop = new Hashtable<String, String>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hashtable should not be used anymore, see the following remark
If a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of Hashtable.
Source: https://docs.oracle.com/javase/7/docs/api/java/util/Hashtable.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately the bundleContext.registerService call requires a Dictionary<String, ?> - of which Hashtable is the only Dictionary implementation (there might be something in apache utils that I'm unaware of). Ignoring this one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pity, sorry for the false alarm 🙁
private final RioSystemHandler sysHandler; | ||
|
||
/** Pattern to identify controller notifications */ | ||
private final Pattern RSP_CONTROLLERNOTIFICATION = Pattern.compile("(?i)^[SN] C\\[(\\d+)\\]\\.(\\w+)=\"(.*)\"$"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All caps are reserved for constants. If this is one, can you then make it private final static
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure I got them all (over about 7 classes). Let me know if I didn't on the next commit
_listener = null; | ||
} | ||
|
||
// sysHandler.unregisterDiscoveryService(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this commented line of code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this - however, just food for thought. I fully disagree with this standard. Commented code has alot of value to someone reading the code (including the original author) since it's like code that was commented out either for a specific reason or for code that could potentially be used in the future. I see it as very much like documentation. But again, don't really care about this one - so I did it (any any others that I coudl find)
/** | ||
* The {@link SocketSession} that will be used to scan the device | ||
*/ | ||
private SocketSession _session; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please follow the java naming convention instance fields are camelCased and do not have underscores in them.
See bullat A.1 @ http://docs.openhab.org/developers/development/guidelines.html#a-code-style
/** | ||
* The minimum timespan between updates of source presets via {@link #refreshPresets(Integer)} | ||
*/ | ||
private static final long UPDATETIMESPAN = 60000; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: I think the correct name would be UPDATE_TIME_SPAN.
Just a suggestion did you know java allows underscore in number to be able to properly display large value, so you could do this:
private static final long UPDATE_TIME_SPAN_MS = 60_000;
/** | ||
* Sets the zone favorites to what is currently playing | ||
* | ||
* @param favJsona possibly null, possibly empty json for favorites to set |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: favJsona => missing space in docblock
|
||
final DiscoveryResult discoveryResult = DiscoveryResultBuilder.create(thingUID) | ||
.withProperty(RioZoneConfig.Zone, z).withBridge(controllerUID) | ||
.withLabel((StringUtils.isEmpty(name) || name.equalsIgnoreCase("null") ? "Zone" : name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You just checked name StringUtils.isNotEmpty(name)
on line 218. And can it really equals the string "null", then this should suffice: name.equalsIgnoreCase("null") ? "Zone" : name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the redundant isnotempty name - unfortunately Russound will put out a 'null' name
for (int i = 1; i < 255; i++) { | ||
addresses[i - 1] = subnet + i; | ||
} | ||
for (int i = 0; i < addresses.length; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you iterate twice? You should only need the first one with the body of the second one, does this work?
for (int i = 1; i < 255; i++) {
final String address = subnet + i;
scheduler.execute(new Runnable() {
@Override
public void run() {
scanAddress(address);
}
});
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol - use to be a ton of code between those statements and didn't notice that when I removed it all!
logger.trace("Starting scan of {}", subnet); | ||
|
||
final String[] addresses = new String[254]; | ||
for (int i = 1; i < 255; i++) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you aware that subnets can also be bigger?
You can have this complete range in a single home network: 192.168.0.0 - 192.168.255.255 or even bigger.
Besides that maybe a warning should be added before starting discovery like this, because some (company) networks might not like this port-scan. And if someone is connected to a vpn that network will be scanned as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In reverse - is there a way to 'warn' someone before starting a discovery? Not aware but let me know if there is one.
Of course they can be bigger, but I'm looking at a normal use case (ie home network) which, 99.9% of the time, will be a simple /24. Would be too onerous to try to do a full scan. For those people that fall into the .1% with complex HOME networks - they can simply define the system bridge directly (they'd certainly not need a discovery process).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the Network binding does the same, so most likely this is not a problem at all 👍 . I just happened to see that the Network binding uses a subnet calculation via an exernal jar maybe you could check it out too?
The only plausible ways to add a warning are:
- the Readme (most likely only read after any problem)
- or optionally in description of the binding it's displayed if you click from the PaperUI inbox to discover new devices, an impression of how this could look like.
Its up to you :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know about subnetutils - incorporated it (and the executor as well)
*/ | ||
@Override | ||
public void responseReceived(String response) { | ||
if (response == null || response == "") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You cannot compare a string like this, maybe you should use commons stringutils I saw that it was used in other places of this binding as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might have other occurrences of this same line in other places as well, can you search for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol - I work with too many languages and that occasionally slips through!
Although I do think that you should NOT throw NPE for things which non-null but empty. |
@SuppressWarnings("rawtypes") | ||
public RioSystemFavoritesProtocol getSystemFavoritesHandler() { | ||
final Bridge bridge = getBridge(); | ||
if (bridge != null && bridge.getHandler() instanceof AbstractBridgeHandler) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the difference with line 110
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Martin - btw just wanted to thank you for the time reviewing. I have no idea what you mean by this comment - would you mind explaining a bit more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that since line 109 and 125 are the same and line 111 and 127 are doing more or less the same (calling a method on the bridge its handler). I would expected the lines 110 and 125 to be resilient to the same amount of null
values. But I might miss some knowledge which would make it unnecessary.
Martin, I believe I've gotten all the changes (might have missed some). You'll see a few did change quite radically (mainly SocketChannelSession) because I took the opportunity to rewrite it a bit (to simplify) - always wanted to do that but never had the motivation to sit down and do it! Let me know if you see anything I missed |
01b61a4
to
7cea13e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad you're okay with my review. Great that you were able to fix all of it in such a short time!
I owed you an explanation of an earlier remark so I gave it. And you missed one thing, the order of some of the modifiers maybe you can search and replace all?
Did you btw also see my message about requireNonNull
?
@@ -15,6 +15,11 @@ | |||
*/ | |||
public class RioSourceConfig { | |||
/** | |||
* Constant defined for the "source" configuration field | |||
*/ | |||
public final static String Source = "source"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that you missed this one
@SuppressWarnings("rawtypes") | ||
public RioSystemFavoritesProtocol getSystemFavoritesHandler() { | ||
final Bridge bridge = getBridge(); | ||
if (bridge != null && bridge.getHandler() instanceof AbstractBridgeHandler) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect that since line 109 and 125 are the same and line 111 and 127 are doing more or less the same (calling a method on the bridge its handler). I would expected the lines 110 and 125 to be resilient to the same amount of null
values. But I might miss some knowledge which would make it unnecessary.
@@ -19,118 +19,134 @@ | |||
public class RioConstants { | |||
|
|||
// BRIDGE TYPE IDS | |||
public static final ThingTypeUID BRIDGE_TYPE_RIO = new ThingTypeUID(RussoundBindingConstants.BINDING_ID, "rio"); | |||
public static final ThingTypeUID BRIDGE_TYPE_CONTROLLER = new ThingTypeUID(RussoundBindingConstants.BINDING_ID, | |||
public final static ThingTypeUID BRIDGE_TYPE_RIO = new ThingTypeUID(RussoundBindingConstants.BINDING_ID, "rio"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some public final static
are still changed maybe you can do a replace-all 'final static' -> 'static final'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol - I did a global replace on private static final and missed the public ones. Not sure why I did that! (must have been caught up in the moment!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah - I see what you meant about the lines - don't know why they differ (probably copy/pasted and fixed one without the other). At any rate, I've updated to these latest comments and pushed.
Note: I've known about the requireNotNull and use it in current (other) bindings - but I didn't think about it when I made all these mods and tell you the truth - will probably leave it for a future date at this point (want to get this off my plate to get back to the sony binding)
7cea13e
to
9ace8a4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tmrobert8 thanks!
@kaikreuzer since its not a small PR on an existing binding its yours :-)
A lot of 'warnings' from the code guidelines were resolved, new functions were added and the readme was extended. So LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think @tmrobert8 just wants to avoid any code reviews by producing more code than anyone would be willing to review ;-) Amazing work...
So I only managed to scan very superfluously over it and came up with a few very unimportant comments.
discoverControllers(); | ||
discoverSources(); | ||
} catch (IOException e) { | ||
logger.warn("Trying to scan device but couldn't connect: {}", e.getMessage(), e); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that might be expected, it the device is not reachable, right? So better reduce this to debug.
this.scheduler.execute(new Runnable() { | ||
@Override | ||
public void run() { | ||
logger.info("Starting device discovery"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please reduce this to debug
* keypress/keyrelease/keyhold/keycode/event are advanced commands that will pass the related event string to Russound (i.e. "EVENT C[x].Z[y]!KeyPress [stringtype]"). Please see the "RIO Protocol for 3rd Party Integrators.pdf" (found at the Russound Portal) for proper string forms. | ||
* If you send a OnOffType to the volume will have the same affect as turning the zone on/off (ie sending OnOffType to "status") | ||
* The volume PercentType will be scaled to Russound's volume of 0-50 (ie 50% = volume of 25, 100% = volume of 50) | ||
##### System Favorites |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
| mminit | W | Switch**** | Whether to initial a media management session (ON) or close an existing one (OFF) | | ||
| mmcontextmenu | W | Switch**** | Whether to initial a media management context session (ON) or close an existing one (OFF) | | ||
|
||
#### Notes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
|
||
There are three different ways to use this channel: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
| name | RW | String | The name of the zone favorite (only saved when the 'savexxx' cmd is issued) | | ||
| valid | R | Switch | If favorite is valid or not ('on' when favorite is saved, 'off' when deleted | | ||
| cmd | W | String | The favorite command (see note below) | | ||
##### Zone Favorites |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
|
||
The favorite command channel ("cmd") supports the following | ||
There are two different ways to use this channel: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
| name | RW | String | The name of the preset (only saved when the 'save' preset cmd is issued) | | ||
| valid | R | Switch | If favorite is valid or not ('on' when a preset is saved, 'off' when preset is deleted) | | ||
| cmd | W | String | The preset command (see note below) | | ||
##### Zone Presets |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
|
||
The preset command channel ("cmd") supports the following | ||
There are two different ways to use this channel: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add an empty line after this
Kai - submitted your changes (note: the log.warn one is actually questionable - device discovery starts AFTER we successfully connected to the device - so if we couldn't connect in discovery, something really went wrong [unit turned off, etc]). Don't think it's big enough of an issue to argue however... |
@tmrobert8 can you squash your commits and change commit message to include a For squashing / rebasing see: |
Changes from Review Updated README.md and removed unnecessary functionality (converted NPE to IAE). Fixed turnonvolume and updated license headers Updated for autodiscovery and media management channels Signed-off-by: Tim Roberts <troberts@bigfoot.com> (github: tmrobert8)
4c700d3
to
c772fd7
Compare
Should be done |
Any idea when this will be merged? I wouldn't normally ask but I've got someone waiting on the merge to start writing the rnet protocol and he is waiting on the merge to start working on it Russound RNET |
@tmrobert8 Thanks for your quick updates and sorry to have kept you waiting! |
No problems - enjoy the foundation meeting! |
…b#2135) https://community.openhab.org/t/modbus-errors/114602 Signed-off-by: Michael Parment <michael.parment@gmail.com>
This pull request adds auto discovery of Russound devices (and all controllers/sources/zones on the device itself). Likewise, I've added all the media management functionality (to support UIs - like the HABPanel widget I've developed for this). Removed/rearranged alot of functionality as well.
Let me know what I've done wrong ;)