fork download
  1. /**
  2.  * Parent_ST_Anything_Ethernet.groovy
  3.  *
  4.  * Copyright 2017 Dan G Ogorchock
  5.  *
  6.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
  7.  * in compliance with the License. You may obtain a copy of the License at:
  8.  *
  9.  * http://w...content-available-to-author-only...e.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  12.  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  13.  * for the specific language governing permissions and limitations under the License.
  14.  *
  15.  * Change History:
  16.  *
  17.  * Date Who What
  18.  * ---- --- ----
  19.  * 2017-02-08 Dan Ogorchock Original Creation
  20.  * 2017-02-12 Dan Ogorchock Modified to work with Ethernet based devices instead of ThingShield
  21.  * 2017-02-24 Dan Ogorchock Created the new "Multiples" device handler as a new example
  22.  * 2017-04-16 Dan Ogorchock Updated to use the new Composite Device Handler feature
  23.  * 2017-06-10 Dan Ogorchock Added Dimmer Switch support
  24.  * 2017-07-09 Dan Ogorchock Added number of defined buttons tile
  25.  * 2017-08-24 Allan (vseven) Change the way values are pushed to child devices to allow a event to be executed allowing future customization
  26.  * 2007-09-24 Allan (vseven) Added RGB LED light support with a setColorRGB routine
  27.  * 2017-10-07 Dan Ogorchock Cleaned up formatting for readability
  28.  * 2017-09-24 Allan (vseven) Added RGBW LED strip support with a setColorRGBW routine
  29.  * 2017-12-29 Dan Ogorchock Added WiFi RSSI value per request from ST user @stevesell
  30.  * 2018-02-15 Dan Ogorchock Added @saif76's Ultrasonic Sensor
  31.  * 2018-02-25 Dan Ogorchock Added Child Presence Sensor
  32.  * 2018-03-03 Dan Ogorchock Added Child Power Meter
  33.  * 2018-06-05 Dan Ogorchock Simplified Parent & Child Device Handlers
  34.  * 2018-06-24 Dan Ogorchock Added Child Servo
  35.  * 2018-07-01 Dan Ogorchock Added Pressure Measurement
  36.  * 2018-08-06 Dan Ogorchock Added MAC Address formatting before setting deviceNetworkID
  37.  * 2019-02-05 Dan Ogorchock Added Child Energy Meter
  38.  * 2019-02-09 Dan Ogorchock Attempt to prevent duplicate devices from being created
  39.  *
  40.  */
  41.  
  42. metadata {
  43. definition (name: "Parent_ST_Anything_Ethernet", namespace: "ogiewon", author: "Dan Ogorchock") {
  44. capability "Configuration"
  45. capability "Refresh"
  46. capability "Button"
  47. capability "Holdable Button"
  48. capability "Signal Strength"
  49.  
  50. command "sendData", ["string"]
  51. }
  52.  
  53. simulator {
  54. }
  55.  
  56. // Preferences
  57. preferences {
  58. input "ip", "text", title: "Arduino IP Address", description: "IP Address in form 192.168.1.226", required: true, displayDuringSetup: true
  59. input "port", "text", title: "Arduino Port", description: "port in form of 8090", required: true, displayDuringSetup: true
  60. input "mac", "text", title: "Arduino MAC Addr", description: "MAC Address in form of 02A1B2C3D4E5", required: true, displayDuringSetup: true
  61. input "numButtons", "number", title: "Number of Buttons", description: "Number of Buttons, 0 to n", required: true, displayDuringSetup: true
  62. }
  63.  
  64. // Tile Definitions
  65. tiles (scale: 2){
  66. standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
  67. state "default", label:'Refresh', action: "refresh.refresh", icon: "st.secondary.refresh-icon"
  68. }
  69.  
  70. standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
  71. state "configure", label:'Configure', action:"configuration.configure", icon:"st.secondary.tools"
  72. }
  73.  
  74. valueTile("numberOfButtons", "device.numberOfButtons", inactiveLabel: false, width: 2, height: 2) {
  75. state "numberOfButtons", label:'${currentValue} buttons', unit:""
  76. }
  77.  
  78. valueTile("rssi", "device.rssi", width: 2, height: 2) {
  79. state("rssi", label:'RSSI ${currentValue}', unit:"",
  80. backgroundColors:[
  81. [value: -30, color: "#006600"],
  82. [value: -45, color: "#009900"],
  83. [value: -60, color: "#99cc00"],
  84. [value: -70, color: "#ff9900"],
  85. [value: -90, color: "#ff0000"]
  86. ]
  87. )
  88. }
  89.  
  90. childDeviceTiles("all")
  91. }
  92. }
  93.  
  94. // parse events into attributes
  95. def parse(String description) {
  96. //log.debug "Parsing '${description}'"
  97. def msg = parseLanMessage(description)
  98. def headerString = msg.header
  99.  
  100. if (!headerString) {
  101. //log.debug "headerstring was null for some reason :("
  102. }
  103.  
  104. def bodyString = msg.body
  105.  
  106. if(bodyString){
  107. log.debug "Parsing: $bodyString";
  108. def parts = bodyString.split(" ");
  109. def name = parts.length>0?parts[0].trim():null;
  110. def value = parts.length>1?parts[1].trim():null;
  111.  
  112. def nameparts = name.split("\\d+", 2);
  113. def namebase = nameparts.length>0?nameparts[0].trim():null;
  114. def namenum = name.substring(namebase.length()).trim();
  115.  
  116. def results = [];
  117.  
  118. if(name.startsWith("button")){
  119. //log.debug "In parse: name = ${name}, value = ${value}, " +
  120. // "btnName = ${name}, btnNum = ${namemun}"
  121. results = createEvent([
  122. name: namebase,
  123. value: value,
  124. data: [buttonNumber: namenum],
  125. descriptionText: "${namebase} ${namenum} was ${value} ",
  126. isStateChange: true,
  127. displayed: true]);
  128. log.debug results;
  129. return results;
  130. }
  131.  
  132. if(name.startsWith("rssi")){
  133. //log.debug "In parse: RSSI name = ${name}, value = ${value}";
  134. results = createEvent(name: name, value: value, displayed: false);
  135. log.debug results;
  136. return results;
  137. }
  138.  
  139.  
  140. def isChild = containsDigit(name);
  141. //log.debug "Name = ${name}, isChild = ${isChild}, " +
  142. // "namebase = ${namebase}, namenum = ${namenum}";
  143. //log.debug "parse() childDevices.size() = ${childDevices.size()}";
  144.  
  145. def childDevice = null;
  146.  
  147. try{
  148. childDevices.each{ // Iterate through each known child device
  149. try{ // Look for mathes
  150. //log.debug "Looking for child with deviceNetworkID = " +
  151. // "${device.deviceNetworkId}-${name} against " +
  152. // "${it.deviceNetworkId}";
  153. if( // A match is found
  154. it.deviceNetworkId ==
  155. "${device.deviceNetworkId}-${name}"
  156. ){
  157. childDevice = it;
  158. //log.debug "Found a match!!!";
  159. }
  160. }catch(e){
  161. //log.debug e;
  162. }
  163. }
  164.  
  165. if(isChild && childDevice == null){ // If a child should exist, but doesn't yet, automatically add it!
  166. log.debug "isChild = true, but no child found - Auto Add it!";
  167. //log.debug " Need a ${namebase} with id = ${namenum}";
  168.  
  169. def newChild = createChildDevice(namebase, namenum); // Create the child device
  170.  
  171. if(newChild){
  172. childDevice = newChild;
  173. }
  174.  
  175. /*childDevices.each{ // Look for the child again, since it should now exist!
  176.   try{
  177.   //log.debug "Looking for child with deviceNetworkID =" +
  178.   //" ${device.deviceNetworkId}-${name} against " +
  179.   //"${it.deviceNetworkId}"
  180.   if( // A match is found!
  181.   it.deviceNetworkId ==
  182.   "${device.deviceNetworkId}-${name}"
  183.   ){
  184.   childDevice = it;
  185.   //log.debug "Found a match!!!";
  186.   }
  187.   }catch(e){
  188.   //log.debug e;
  189.   }
  190.   }*/
  191. }
  192. log.debug state.childIds;
  193. if(childDevice != null){ // Handle cases where we know it's definitely a child device
  194. //log.debug "parse() found child device " +
  195. // "${childDevice.deviceNetworkId}";
  196. childDevice.parse("${namebase} ${value}"); // Parse the child device message
  197. log.debug "${childDevice.deviceNetworkId} - name: " +
  198. "${namebase}, value: ${value}";
  199. }else{ //It must not be a child, perform normal update
  200. results = createEvent(name: name, value: value);
  201. log.debug results;
  202. return results;
  203. }
  204. }catch(e){
  205. log.error "Error in parse() routine, error = ${e}";
  206. }
  207.  
  208. }
  209. }
  210.  
  211. private getHostAddress() {
  212. def ip = settings.ip
  213. def port = settings.port
  214.  
  215. log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
  216. return ip + ":" + port
  217. }
  218.  
  219. def sendData(message) {
  220. sendEthernet(message)
  221. }
  222.  
  223. def sendEthernet(message) {
  224. log.debug "Executing 'sendEthernet' ${message}"
  225. if (settings.ip != null && settings.port != null) {
  226. sendHubCommand(new physicalgraph.device.HubAction(
  227. method: "POST",
  228. path: "/${message}?",
  229. headers: [ HOST: "${getHostAddress()}" ]
  230. ))
  231. }
  232. else {
  233. state.alertMessage = "ST_Anything Parent Device has not yet been fully configured. Click the 'Gear' icon, enter data for all fields, and click 'Done'"
  234. runIn(2, "sendAlert")
  235. }
  236. }
  237.  
  238. // handle commands
  239. def configure() {
  240. log.debug "Executing 'configure()'"
  241. updateDeviceNetworkID()
  242. sendEvent(name: "numberOfButtons", value: numButtons)
  243. }
  244.  
  245. def refresh() {
  246. log.debug "Executing 'refresh()'"
  247. sendEthernet("refresh")
  248. sendEvent(name: "numberOfButtons", value: numButtons)
  249. }
  250.  
  251. def installed() {
  252. log.debug "Executing 'installed()'"
  253. if ( device.deviceNetworkId =~ /^[A-Z0-9]{12}$/)
  254. {
  255. }
  256. else
  257. {
  258. state.alertMessage = "ST_Anything Parent Device has not yet been fully configured. Click the 'Gear' icon, enter data for all fields, and click 'Done'"
  259. runIn(2, "sendAlert")
  260. }
  261. state.lastChildCreated = "none"
  262. }
  263.  
  264. def initialize() {
  265. log.debug "Executing 'initialize()'"
  266. sendEvent(name: "numberOfButtons", value: numButtons)
  267. }
  268.  
  269. def updated() {
  270. if (!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 5000) {
  271. state.updatedLastRanAt = now()
  272. log.debug "Executing 'updated()'"
  273. runIn(3, "updateDeviceNetworkID")
  274. sendEvent(name: "numberOfButtons", value: numButtons)
  275. log.debug "Hub IP Address = ${device.hub.getDataValue("localIP")}"
  276. log.debug "Hub Port = ${device.hub.getDataValue("localSrvPortTCP")}"
  277. }
  278. else {
  279. // log.trace "updated(): Ran within last 5 seconds so aborting."
  280. }
  281. }
  282.  
  283. def updateDeviceNetworkID() {
  284. log.debug "Executing 'updateDeviceNetworkID'"
  285. def formattedMac = mac.toUpperCase()
  286. formattedMac = formattedMac.replaceAll(":", "")
  287. if(device.deviceNetworkId!=formattedMac) {
  288. log.debug "setting deviceNetworkID = ${formattedMac}"
  289. device.setDeviceNetworkId("${formattedMac}")
  290. }
  291. //Need deviceNetworkID updated BEFORE we can create Child Devices
  292. //Have the Arduino send an updated value for every device attached. This will auto-created child devices!
  293. state.lastChildCreated = "none"
  294. refresh()
  295. }
  296.  
  297. private Object createChildDevice(String deviceName, String deviceNumber) {
  298. if(device.deviceNetworkId =~ /^[A-Z0-9]{12}$/){
  299. log.trace "createChildDevice: Creating Child Device " +
  300. "'${device.displayName} (${deviceName}${deviceNumber})'";
  301.  
  302. try{
  303. def deviceHandlerName = "";
  304.  
  305. switch(deviceName){
  306. case "contact":
  307. deviceHandlerName = "Child Contact Sensor"
  308. break;
  309. case "switch":
  310. deviceHandlerName = "Child Switch"
  311. break;
  312. case "dimmerSwitch":
  313. deviceHandlerName = "Child Dimmer Switch"
  314. break;
  315. case "rgbSwitch":
  316. deviceHandlerName = "Child RGB Switch"
  317. break;
  318. case "generic":
  319. deviceHandlerName = "Child Generic Sensor"
  320. break;
  321. case "rgbwSwitch":
  322. deviceHandlerName = "Child RGBW Switch"
  323. break;
  324. case "relaySwitch":
  325. deviceHandlerName = "Child Relay Switch"
  326. break;
  327. case "temperature":
  328. deviceHandlerName = "Child Temperature Sensor"
  329. break;
  330. case "humidity":
  331. deviceHandlerName = "Child Humidity Sensor"
  332. break;
  333. case "motion":
  334. deviceHandlerName = "Child Motion Sensor"
  335. break;
  336. case "water":
  337. deviceHandlerName = "Child Water Sensor"
  338. break;
  339. case "illuminance":
  340. deviceHandlerName = "Child Illuminance Sensor"
  341. break;
  342. case "illuminancergb":
  343. deviceHandlerName = "Child IlluminanceRGB Sensor"
  344. break;
  345. case "voltage":
  346. deviceHandlerName = "Child Voltage Sensor"
  347. break;
  348. case "smoke":
  349. deviceHandlerName = "Child Smoke Detector"
  350. break;
  351. case "carbonMonoxide":
  352. deviceHandlerName = "Child Carbon Monoxide Detector"
  353. break;
  354. case "alarm":
  355. deviceHandlerName = "Child Alarm"
  356. break;
  357. case "doorControl":
  358. deviceHandlerName = "Child Door Control"
  359. break;
  360. case "ultrasonic":
  361. deviceHandlerName = "Child Ultrasonic Sensor"
  362. break;
  363. case "presence":
  364. deviceHandlerName = "Child Presence Sensor"
  365. break;
  366. case "power":
  367. deviceHandlerName = "Child Power Meter"
  368. break;
  369. case "energy":
  370. deviceHandlerName = "Child Energy Meter"
  371. break;
  372. case "servo":
  373. deviceHandlerName = "Child Servo"
  374. break;
  375. case "pressure":
  376. deviceHandlerName = "Child Pressure Measurement"
  377. break;
  378. default:
  379. log.error "No Child Device Handler case for ${deviceName}"
  380. }
  381.  
  382. if(state.childIds == null){
  383. state.childIds = [];
  384. }
  385.  
  386. def stChildIds = [];
  387. childDevices.each{ // Iterate through each known child device
  388. stChildIds.add(it.deviceNetworkId);
  389. }
  390.  
  391. if(
  392. (deviceHandlerName != "") &&
  393. (!state.childIds.contains(
  394. "${device.deviceNetworkId}-${deviceName}${deviceNumber}")
  395. ) &&
  396. (!stChildIds.contains(
  397. "${device.deviceNetworkId}-${deviceName}${deviceNumber}")
  398. )
  399. ){
  400. def newChild = addChildDevice(
  401. deviceHandlerName,
  402. "${device.deviceNetworkId}-${deviceName}${deviceNumber}",
  403. null,
  404. [
  405. completedSetup: true,
  406. label: "${device.displayName} " +
  407. "(${deviceName}${deviceNumber})",
  408. isComponent: false,
  409. componentName: "${deviceName}${deviceNumber}",
  410. componentLabel: "${deviceName} ${deviceNumber}"
  411. ]
  412. );
  413.  
  414. //state.lastChildCreated = "${device.deviceNetworkId}-" +
  415. // "${deviceName}${deviceNumber}";
  416. state.childIds.add("${device.deviceNetworkId}-" + // Store a copy of this child device ID since we can't rely on ST
  417. "${deviceName}${deviceNumber}");
  418.  
  419. return newChild;
  420. }
  421. }catch(e){
  422. log.error "Child device creation failed with error = ${e}";
  423. state.alertMessage = "Child device creation failed. " +
  424. "Please make sure that the '${deviceHandlerName}' " +
  425. "is installed and published.";
  426. runIn(2, "sendAlert");
  427.  
  428. return false;
  429. }
  430. }else{
  431. state.alertMessage = "ST_Anything Parent Device has not yet been " +
  432. "fully configured. Click the 'Gear' icon, enter data for all " +
  433. "fields, and click 'Done'";
  434. runIn(2, "sendAlert");
  435.  
  436. return false;
  437. }
  438. }
  439.  
  440. private sendAlert() {
  441. sendEvent(
  442. descriptionText: state.alertMessage,
  443. eventType: "ALERT",
  444. name: "childDeviceCreation",
  445. value: "failed",
  446. displayed: true,
  447. )
  448. }
  449.  
  450. private boolean containsDigit(String s) {
  451. boolean containsDigit = false;
  452.  
  453. if (s != null && !s.isEmpty()) {
  454. // log.debug "containsDigit .matches = ${s.matches(".*\\d+.*")}"
  455. containsDigit = s.matches(".*\\d+.*")
  456. }
  457. return containsDigit
  458. }
Runtime error #stdin #stdout #stderr 0.99s 2382848KB
stdin
Standard input is empty
stdout
Standard output is empty
stderr
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
/home/hswC8K/prog.groovy: 226: unable to resolve class physicalgraph.device.HubAction 
 @ line 226, column 24.
           sendHubCommand(new physicalgraph.device.HubAction(
                          ^

1 error