/tcp-driver/src/main/java/be/abollaert/domotics/light/drivers/tcp/TCPClient.java

https://github.com/abollaert/smartlights · Java · 525 lines · 259 code · 102 blank · 164 comment · 21 complexity · 040e42279814f9768ad626b1e3fe1a0a MD5 · raw file

  1. package be.abollaert.domotics.light.drivers.tcp;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.net.InetAddress;
  5. import java.util.Date;
  6. import java.util.concurrent.locks.Lock;
  7. import java.util.concurrent.locks.ReentrantLock;
  8. import org.apache.commons.httpclient.HttpClient;
  9. import org.apache.commons.httpclient.HttpException;
  10. import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
  11. import org.apache.commons.httpclient.methods.PostMethod;
  12. import be.abollaert.domotics.light.api.ChannelState;
  13. import be.abollaert.domotics.light.api.DigitalInputChannelConfiguration;
  14. import be.abollaert.domotics.light.api.DigitalModule;
  15. import be.abollaert.domotics.light.api.DigitalModuleConfiguration;
  16. import be.abollaert.domotics.light.api.DimMoodElement;
  17. import be.abollaert.domotics.light.api.DimmerDirection;
  18. import be.abollaert.domotics.light.api.DimmerInputChannelConfiguration;
  19. import be.abollaert.domotics.light.api.DimmerModuleConfiguration;
  20. import be.abollaert.domotics.light.api.Mood;
  21. import be.abollaert.domotics.light.api.SwitchMoodElement;
  22. import be.abollaert.domotics.light.protocolbuffers.Api;
  23. import be.abollaert.domotics.light.protocolbuffers.Api.GetDigitalModuleConfigResponse;
  24. import be.abollaert.domotics.light.protocolbuffers.Api.MessageResponse;
  25. import com.google.protobuf.Message;
  26. /**
  27. * The TCP client class provides access to the API.
  28. *
  29. * @author alex
  30. */
  31. public final class TCPClient {
  32. /** The URI for the GetModules request. */
  33. private static final String URI_GET_MODULES = "/api/GetModules";
  34. /** URI for the GetDigitalChannelConfig request. */
  35. private static final String URI_GET_DIGITAL_CHANNEL_CONFIG = "/api/GetDigitalInputChannelConfig";
  36. /** URI for the SwitchOutput request. */
  37. private static final String URI_SWITCH_OUTPUT ="/api/SwitchOutput";
  38. /** The URI to get the output channels. */
  39. private static final String URI_GET_OUTPUT_CHANNELS = "/api/GetOutputChannels";
  40. /** The URL to set an input channel configuration. */
  41. private static final String URI_SET_DIGITAL_INPUT_CONFIG = "/api/SetDigitalInputChannelConfig";
  42. /** The URL to set a digital module configuration. */
  43. private static final String URI_SET_DIGITAL_MODULE_CONFIG = "/api/SetDigitalModuleConfig";
  44. /** THe URL for saving the configuration of a {@link DigitalModule}. */
  45. private static final String URI_SAVE_DIGITAL_MODULE_CONFIG = "/api/SaveModuleConfig";
  46. /** URI to get the digital module configuration. */
  47. private static final String URI_GET_DIGITAL_MODULE_CONFIG = "/api/GetDigitalModuleConfiguration";
  48. /** URI used to get dimmer input channel configuration. */
  49. private static final String URI_GET_DIMMER_INPUT_CHANNEL_CONFIG = "/api/GetDimmerInputChannelConfig";
  50. /** URI for saving a mood. */
  51. private static final String URI_SAVE_MOOD = "/api/SaveMood";
  52. /** The URI for dimming. */
  53. private static final String URI_DIM = "/api/Dim";
  54. /** URI to set the dimmer module config. */
  55. private static final String URI_SET_DIMMER_MODULE_CONFIG = "/api/SetDimmerModuleConfig";
  56. /** URI to set the dimmer input configuration. */
  57. private static final String URI_SET_DIMMER_INPUT_CONFIG = "/api/SetDimmerInputConfiguration";
  58. /** URI to get the switch requests. */
  59. private static final String URI_GET_SWITCH_EVENTS = "/api/GetSwitchEvents";
  60. /** URI for getting the output state of a digital channel. */
  61. private static final String URI_GET_DIGITAL_OUTPUT_STATE = "/api/GetDigitalOutputState";
  62. /** URI for getting the output state of a dimmer channel. */
  63. private static final String URI_GET_DIMMER_OUTPUT_STATE = "/api/GetDimmerOutputState";
  64. /** URI for getting all moods. */
  65. private static final String URI_GET_MOODS = "/api/GetMoods";
  66. private static final String URI_ACTIVATE_MOOD = "/api/ActivateMood";
  67. private static final String URI_REMOVE_MOOD = "/api/RemoveMood";
  68. private static final String URI_ALL_LIGHTS_OFF = "/api/AllLightsOff";
  69. /** The HTTP client. */
  70. private HttpClient httpClient;
  71. /** The event listener. */
  72. private EventListener eventListener;
  73. /** The server address. */
  74. private String serverAddress;
  75. /** The server port. */
  76. private int port;
  77. /** The http client lock. */
  78. private final Lock httpClientLock = new ReentrantLock();
  79. /**
  80. * Connects the client.
  81. *
  82. * @param serverAddress The server address.
  83. * @param port The port.
  84. *
  85. * @throws IOException If an IO error occurs during the connect.
  86. */
  87. public void connect(final String serverAddress, final int port, final InetAddress iface) throws IOException {
  88. this.serverAddress = serverAddress;
  89. this.port = port;
  90. this.httpClient = new HttpClient();
  91. this.eventListener = new EventListener();
  92. this.eventListener.start(iface);
  93. }
  94. /**
  95. * Disconnects the client.
  96. *
  97. * @throws IOException If an IO error occurs while disconnecting.
  98. */
  99. public void disconnect() throws IOException {
  100. this.eventListener.stop();
  101. this.eventListener = null;
  102. this.httpClient = null;
  103. }
  104. /**
  105. * Gets the modules from the server.
  106. *
  107. * @return The modules from the server.
  108. *
  109. * @throws IOException If an IO error occurs.
  110. */
  111. public final Api.GetModulesResponse getModules() throws IOException {
  112. return (Api.GetModulesResponse)this.execute(URI_GET_MODULES, null, Api.GetModulesResponse.newBuilder());
  113. }
  114. /**
  115. * Returns the digital channel config for the requested channel.
  116. *
  117. * @param moduleId The module ID.
  118. * @param channelNumber The channel number.
  119. *
  120. * @return The config.
  121. *
  122. * @throws IOException
  123. */
  124. public final Api.GetDigitalInputChannelConfigResponse getDigitalChannelConfiguration(final int moduleId, final int channelNumber) throws IOException {
  125. final Api.GetDigitalInputChannelConfig.Builder builder = Api.GetDigitalInputChannelConfig.newBuilder();
  126. builder.setModuleId(moduleId);
  127. builder.setChannelNumber(channelNumber);
  128. return (Api.GetDigitalInputChannelConfigResponse)this.execute(URI_GET_DIGITAL_CHANNEL_CONFIG, builder.build(), Api.GetDigitalInputChannelConfigResponse.newBuilder());
  129. }
  130. public final void switchOutput(final int moduleId, final int channelNumber, final ChannelState desiredState) throws IOException {
  131. final Api.SwitchOutput.Builder builder = Api.SwitchOutput.newBuilder();
  132. builder.setModuleId(moduleId);
  133. builder.setChannelNumber(channelNumber);
  134. builder.setRequiredState(desiredState == ChannelState.ON ? true : false);
  135. this.executeVoidMessage(URI_SWITCH_OUTPUT, builder.build());
  136. }
  137. /**
  138. * Returns the switch events for the given module ID and channel number.
  139. *
  140. * @param moduleId
  141. * @param channelNumber
  142. * @param startDate
  143. * @param endDate
  144. * @return
  145. * @throws IOException
  146. */
  147. public final Api.SwitchEventList getSwitchEvents(final int moduleId, final int channelNumber, final Date startDate, final Date endDate) throws IOException {
  148. final Api.GetSwitchEvents.Builder requestBuilder = Api.GetSwitchEvents.newBuilder();
  149. requestBuilder.setModuleId(moduleId);
  150. requestBuilder.setChannelNumber(channelNumber);
  151. if (startDate != null) {
  152. requestBuilder.setStartDate(startDate.getTime());
  153. }
  154. if (endDate != null) {
  155. requestBuilder.setEndDate(endDate.getTime());
  156. }
  157. return (Api.SwitchEventList)this.execute(URI_GET_SWITCH_EVENTS, requestBuilder.build(), Api.SwitchEventList.newBuilder());
  158. }
  159. /**
  160. * Dim the given channel.
  161. *
  162. * @param moduleId The module ID.
  163. * @param channelNumber The channel number.
  164. * @param percentage The percentage.
  165. *
  166. * @throws IOException If an IO error occurs during the action.
  167. */
  168. public final void dim(final int moduleId, final int channelNumber, final int percentage) throws IOException {
  169. final Api.Dim.Builder messageBuilder = Api.Dim.newBuilder();
  170. messageBuilder.setModuleId(moduleId);
  171. messageBuilder.setChannelNumber(channelNumber);
  172. messageBuilder.setPercentage(percentage);
  173. this.executeVoidMessage(URI_DIM, messageBuilder.build());
  174. }
  175. public final Api.GetOutputChannelsResponse getOutputChannels() throws IOException {
  176. try {
  177. return (Api.GetOutputChannelsResponse)this.execute(URI_GET_OUTPUT_CHANNELS, null, Api.GetOutputChannelsResponse.newBuilder());
  178. } catch (HttpException e) {
  179. throw new IOException("HTTP error while getting modules : [" + e.getMessage() + "]");
  180. }
  181. }
  182. /**
  183. * Set the configuration for a particular digital input channel.
  184. *
  185. * @param newConfiguration The new configuration.
  186. *
  187. * @return The response.
  188. *
  189. * @throws IOException If an IO error occurs during the set.
  190. */
  191. public final void setDigitalInputChannelConfiguration(final DigitalInputChannelConfiguration newConfiguration) throws IOException {
  192. final Api.DigitalInputChannelConfig.Builder configurationBuilder = Api.DigitalInputChannelConfig.newBuilder();
  193. configurationBuilder.setCurrentOutputState(false);
  194. configurationBuilder.setCurrentSwitchState(false);
  195. configurationBuilder.setDefaultState(newConfiguration.getDefaultState() == ChannelState.ON ? true : false);
  196. configurationBuilder.setMappedOutputChannel(newConfiguration.getMappedOutputChannel());
  197. configurationBuilder.setTimerInSec(newConfiguration.getTimerInSeconds());
  198. if (newConfiguration.getName() != null) {
  199. configurationBuilder.setName(newConfiguration.getName());
  200. }
  201. configurationBuilder.setEnableLogging(newConfiguration.isLoggingEnabled());
  202. final Api.SetDigitalInputConfig.Builder messageBuilder = Api.SetDigitalInputConfig.newBuilder();
  203. messageBuilder.setModuleId(newConfiguration.getModuleId());
  204. messageBuilder.setChannelNumber(newConfiguration.getChannelNumber());
  205. messageBuilder.setConfig(configurationBuilder);
  206. this.executeVoidMessage(URI_SET_DIGITAL_INPUT_CONFIG, messageBuilder.build());
  207. }
  208. /**
  209. * Sets the configuration for a dimmer module.
  210. *
  211. * @param newConfiguration The new configuration.
  212. *
  213. * @throws IOException If an IO error occurs while setting.
  214. */
  215. public final void setDimmerModuleConfiguration(final DimmerModuleConfiguration newConfiguration) throws IOException {
  216. final Api.DimmerModuleConfig.Builder configBuilder = Api.DimmerModuleConfig.newBuilder();
  217. configBuilder.setDimmerDelay(newConfiguration.getDimmerDelay());
  218. configBuilder.setDimmerThresholdInMs(newConfiguration.getDimmerThreshold());
  219. configBuilder.setSwitchThresholdInMs(newConfiguration.getSwitchThreshold());
  220. final Api.SetDimmerModuleConfig.Builder messageBuilder = Api.SetDimmerModuleConfig.newBuilder();
  221. messageBuilder.setConfiguration(configBuilder);
  222. messageBuilder.setModuleId(newConfiguration.getModuleId());
  223. this.executeVoidMessage(URI_SET_DIMMER_MODULE_CONFIG, messageBuilder.build());
  224. }
  225. /**
  226. * Sets the configuration for a dimmer input.
  227. *
  228. * @param newConfiguration The new configuration.
  229. *
  230. * @throws IOException If an IO error occurs while setting.
  231. */
  232. public final void setDimmerInputConfiguration(final int moduleId, final int channelNumber, final DimmerInputChannelConfiguration newConfiguration) throws IOException {
  233. final Api.DimmerInputChannelConfig.Builder configBuilder = Api.DimmerInputChannelConfig.newBuilder();
  234. configBuilder.setCurrentDimmerPercentage(0);
  235. configBuilder.setCurrentOutputState(false);
  236. configBuilder.setCurrentSwitchState(false);
  237. configBuilder.setDefaultDirection(newConfiguration.getDefaultDirection() == DimmerDirection.UP ? true : false);
  238. configBuilder.setDefaultPercentage(newConfiguration.getDefaultPercentage());
  239. configBuilder.setDefaultState(newConfiguration.getDefaultState() == ChannelState.ON ? true : false);
  240. configBuilder.setMappedOutputChannel(newConfiguration.getMappedOutputChannel());
  241. configBuilder.setTimerInSec(newConfiguration.getTimerInSeconds());
  242. if (newConfiguration.getName() != null) {
  243. configBuilder.setName(newConfiguration.getName());
  244. }
  245. configBuilder.setEnableLogging(newConfiguration.isLoggingEnabled());
  246. final Api.SetDimmerInputConfig.Builder messageBuilder = Api.SetDimmerInputConfig.newBuilder();
  247. messageBuilder.setConfig(configBuilder);
  248. messageBuilder.setModuleId(moduleId);
  249. messageBuilder.setChannelNumber(channelNumber);
  250. this.executeVoidMessage(URI_SET_DIMMER_INPUT_CONFIG, messageBuilder.build());
  251. }
  252. /**
  253. * Sets the new digital module configuration.
  254. *
  255. * @param newConfiguration The new configuration.
  256. *
  257. * @return The response.
  258. *
  259. * @throws IOException If an IO error occurs.
  260. */
  261. public final void setDigitalModuleConfiguration(final DigitalModuleConfiguration newConfiguration) throws IOException {
  262. final Api.DigitalModuleConfig.Builder configurationBuilder = Api.DigitalModuleConfig.newBuilder();
  263. configurationBuilder.setSwitchThresholdInMs(newConfiguration.getSwitchThreshold());
  264. final Api.SetDigitalModuleConfig.Builder messageBuilder = Api.SetDigitalModuleConfig.newBuilder();
  265. messageBuilder.setModuleId(newConfiguration.getModuleId());
  266. messageBuilder.setConfiguration(configurationBuilder);
  267. this.executeVoidMessage(URI_SET_DIGITAL_MODULE_CONFIG, messageBuilder.build());
  268. }
  269. /**
  270. * Saves a digital module configuration.
  271. *
  272. * @param moduleId The module ID.
  273. *
  274. * @return The response.
  275. *
  276. * @throws IOException If an IO error occurs.
  277. */
  278. public final void saveModuleConfiguration(final int moduleId) throws IOException {
  279. final Api.SaveDigitalModuleConfig.Builder messageBuilder = Api.SaveDigitalModuleConfig.newBuilder();
  280. messageBuilder.setModuleId(moduleId);
  281. this.executeVoidMessage(URI_SAVE_DIGITAL_MODULE_CONFIG, messageBuilder.build());
  282. }
  283. public final int saveMood(final Mood mood) throws IOException {
  284. final Api.Mood.Builder moodBuilder = Api.Mood.newBuilder();
  285. moodBuilder.setName(mood.getName());
  286. moodBuilder.setMoodId(mood.getId());
  287. for (final SwitchMoodElement switchElement : mood.getSwitchMoodElements()) {
  288. final Api.SwitchMoodElement.Builder elementBuilder = Api.SwitchMoodElement.newBuilder();
  289. elementBuilder.setModuleId(switchElement.getModuleId());
  290. elementBuilder.setChannelNumber(switchElement.getChannelNumber());
  291. elementBuilder.setRequestedState(switchElement.getRequestedState() == ChannelState.ON);
  292. moodBuilder.addSwitchElements(elementBuilder.build());
  293. }
  294. for (final DimMoodElement dimmerElement : mood.getDimMoodElements()) {
  295. final Api.DimmerMoodElement.Builder elementBuilder = Api.DimmerMoodElement.newBuilder();
  296. elementBuilder.setModuleId(dimmerElement.getModuleId());
  297. elementBuilder.setChannelNumber(dimmerElement.getChannelNumber());
  298. elementBuilder.setPercentage(dimmerElement.getTargetPercentage());
  299. moodBuilder.addDimmerElements(elementBuilder.build());
  300. }
  301. final Api.SaveMood.Builder requestBuilder = Api.SaveMood.newBuilder();
  302. requestBuilder.setMood(moodBuilder.build());
  303. return ((Api.SaveMoodResponse)this.execute(URI_SAVE_MOOD, requestBuilder.build(), Api.SaveMoodResponse.newBuilder())).getMoodId();
  304. }
  305. /**
  306. * REturns the configuration of a digital module.
  307. *
  308. * @param moduleId The ID of the module.
  309. *
  310. * @return The configuration.
  311. *
  312. * @throws IOException
  313. */
  314. public final Api.DigitalModuleConfig getDigitalModuleConfig(final int moduleId) throws IOException {
  315. final Api.GetDigitalModuleConfig.Builder messageBuilder = Api.GetDigitalModuleConfig.newBuilder();
  316. messageBuilder.setModuleId(moduleId);
  317. final GetDigitalModuleConfigResponse response = (Api.GetDigitalModuleConfigResponse)this.execute(URI_GET_DIGITAL_MODULE_CONFIG, messageBuilder.build(), Api.GetDigitalModuleConfigResponse.newBuilder());
  318. return response.getConfig();
  319. }
  320. /**
  321. * Returns the event listener.
  322. *
  323. * @return The event listener.
  324. */
  325. public final EventListener getEventListener() {
  326. return this.eventListener;
  327. }
  328. /**
  329. * Generates the URL for the given URI.
  330. *
  331. * @param uri The URI to generate an URL for.
  332. *
  333. * @return The URL to use.
  334. */
  335. private final String generateURL(final String uri) {
  336. return new StringBuilder("http://").append(this.serverAddress).append(":").append(this.port).append(uri).toString();
  337. }
  338. /**
  339. * Get the configuration of an input channel on a dimmer module.
  340. *
  341. * @param moduleId The ID of the module.
  342. * @param channelNumber The channel number.
  343. *
  344. * @return The configuration of the channel.
  345. *
  346. * @throws IOException If an IO error occurs.
  347. */
  348. public final Api.DimmerInputChannelConfig getDimmerInputChannelConfiguration(final int moduleId, final int channelNumber) throws IOException {
  349. final Api.GetDimmerInputChannelConfig.Builder requestMessageBuilder = Api.GetDimmerInputChannelConfig.newBuilder();
  350. requestMessageBuilder.setModuleId(moduleId);
  351. requestMessageBuilder.setChannelNumber(channelNumber);
  352. final Api.GetDimmerInputChannelConfigResponse response = (Api.GetDimmerInputChannelConfigResponse)this.execute(URI_GET_DIMMER_INPUT_CHANNEL_CONFIG, requestMessageBuilder.build(), Api.GetDimmerInputChannelConfigResponse.newBuilder());
  353. return response.getConfig();
  354. }
  355. public final Api.DigitalChannelOutputState getDigitalChannelOutputState(final int moduleId, final int channelNumber) throws IOException {
  356. final Api.GetOutputChannelState.Builder requestBuilder = Api.GetOutputChannelState.newBuilder();
  357. requestBuilder.setModuleId(moduleId);
  358. requestBuilder.setChannelNumber(channelNumber);
  359. return (Api.DigitalChannelOutputState)this.execute(URI_GET_DIGITAL_OUTPUT_STATE, requestBuilder.build(), Api.DigitalChannelOutputState.newBuilder());
  360. }
  361. public final Api.DimmerChannelOutputState getDimmerChannelOutputState(final int moduleId, final int channelNumber) throws IOException {
  362. final Api.GetOutputChannelState.Builder requestBuilder = Api.GetOutputChannelState.newBuilder();
  363. requestBuilder.setModuleId(moduleId);
  364. requestBuilder.setChannelNumber(channelNumber);
  365. return (Api.DimmerChannelOutputState)this.execute(URI_GET_DIMMER_OUTPUT_STATE, requestBuilder.build(), Api.DimmerChannelOutputState.newBuilder());
  366. }
  367. /**
  368. * Executes the given message against the given URI, and returns the reult message if any.
  369. *
  370. * @param uri The URI.
  371. * @param message The message.
  372. * @param responseBuilder The response builder.
  373. *
  374. * @return The response message.
  375. *
  376. * @throws IOException If an IO error occurs.
  377. */
  378. private final Message execute(final String uri, final Message message, final Message.Builder responseBuilder) throws IOException {
  379. PostMethod method = new PostMethod(this.generateURL(uri));
  380. try {
  381. this.httpClientLock.lock();
  382. if (message != null) {
  383. method.setRequestEntity(new ByteArrayRequestEntity(message.toByteArray()));
  384. }
  385. this.httpClient.executeMethod(method);
  386. final InputStream responseBodyStream = method.getResponseBodyAsStream();
  387. if (responseBodyStream != null) {
  388. responseBuilder.mergeFrom(responseBodyStream);
  389. }
  390. return responseBuilder.build();
  391. } finally {
  392. method.releaseConnection();
  393. this.httpClientLock.unlock();
  394. }
  395. }
  396. private final void executeVoidMessage(final String uri, final Message message) throws IOException {
  397. final Api.MessageResponse response = (Api.MessageResponse)this.execute(uri, message, MessageResponse.newBuilder());
  398. if (response.getType() == Api.MessageResponse.Type.ERROR) {
  399. throw new IOException(response.getMessage());
  400. }
  401. }
  402. public final Api.MoodList getAllMoods() throws IOException {
  403. return (Api.MoodList)this.execute(URI_GET_MOODS, null, Api.MoodList.newBuilder());
  404. }
  405. public final void activateMood(final int moodId) throws IOException {
  406. final Api.ActivateMood.Builder requestBuilder = Api.ActivateMood.newBuilder();
  407. requestBuilder.setMoodId(moodId);
  408. this.executeVoidMessage(URI_ACTIVATE_MOOD, requestBuilder.build());
  409. }
  410. public final void removeMood(final int moodId) throws IOException {
  411. final Api.RemoveMood.Builder requestBuilder = Api.RemoveMood.newBuilder();
  412. requestBuilder.setMoodId(moodId);
  413. this.executeVoidMessage(URI_REMOVE_MOOD, requestBuilder.build());
  414. }
  415. /**
  416. * Switch all lights off.
  417. *
  418. * @throws IOException If an IO error occurs.
  419. */
  420. public final void allLightsOff() throws IOException {
  421. this.executeVoidMessage(URI_ALL_LIGHTS_OFF, null);
  422. }
  423. }