From fd509c7d790991c6ce65c4613d3ab92de71e5113 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Fri, 23 Oct 2015 18:00:33 -0600 Subject: [PATCH] ozw: Initial implementation This module was developed using the OpenZWave library (v1.3): http://www.openzwave.com/ It was developed using a collection of devices (switches and a multi-sensor) connected via an Aeon Z-Stick Gen5 USB dongle. It can be used to query (and, where appropriate, set) Values on Nodes connected to the ZWave network. Checks are made in src/ozw/CMakeLists.txt to ensure that the libopenzwave library is installed (via pkg-config). If not present, then neither the module, nor the example will be built. Signed-off-by: Jon Trulson Signed-off-by: Mihai Tudor Panu --- examples/c++/CMakeLists.txt | 11 + examples/c++/ozw.cxx | 141 ++++++ examples/javascript/ozw.js | 128 +++++ examples/python/ozw.py | 126 +++++ src/ozw/CMakeLists.txt | 21 + src/ozw/javaupm_ozw.i | 12 + src/ozw/jsupm_ozw.i | 12 + src/ozw/ozw.cxx | 909 ++++++++++++++++++++++++++++++++++++ src/ozw/ozw.h | 574 +++++++++++++++++++++++ src/ozw/pyupm_ozw.i | 15 + src/ozw/zwNode.cxx | 168 +++++++ src/ozw/zwNode.h | 105 +++++ 12 files changed, 2222 insertions(+) create mode 100644 examples/c++/ozw.cxx create mode 100644 examples/javascript/ozw.js create mode 100644 examples/python/ozw.py create mode 100644 src/ozw/CMakeLists.txt create mode 100644 src/ozw/javaupm_ozw.i create mode 100644 src/ozw/jsupm_ozw.i create mode 100644 src/ozw/ozw.cxx create mode 100644 src/ozw/ozw.h create mode 100644 src/ozw/pyupm_ozw.i create mode 100644 src/ozw/zwNode.cxx create mode 100644 src/ozw/zwNode.h diff --git a/examples/c++/CMakeLists.txt b/examples/c++/CMakeLists.txt index ce4265fe..b004ff45 100644 --- a/examples/c++/CMakeLists.txt +++ b/examples/c++/CMakeLists.txt @@ -157,6 +157,10 @@ add_executable (mcp9808-example mcp9808.cxx) add_executable (groveultrasonic-example groveultrasonic.cxx) add_executable (sx1276-lora-example sx1276-lora.cxx) add_executable (sx1276-fsk-example sx1276-fsk.cxx) +# availability of libopenzwave is tested in src/ozw/CMakeLists.txt +if (OPENZWAVE_FOUND) + add_executable (ozw-example ozw.cxx) +endif () include_directories (${PROJECT_SOURCE_DIR}/src/hmc5883l) include_directories (${PROJECT_SOURCE_DIR}/src/grove) @@ -277,6 +281,10 @@ include_directories (${PROJECT_SOURCE_DIR}/src/dfrph) include_directories (${PROJECT_SOURCE_DIR}/src/mcp9808) include_directories (${PROJECT_SOURCE_DIR}/src/groveultrasonic) include_directories (${PROJECT_SOURCE_DIR}/src/sx1276) +if (OPENZWAVE_FOUND) + include_directories(${PROJECT_SOURCE_DIR}/src/ozw) + include_directories(${OPENZWAVE_INCLUDE_DIRS}) +endif () target_link_libraries (hmc5883l-example hmc5883l ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveled-example grove ${CMAKE_THREAD_LIBS_INIT}) @@ -435,3 +443,6 @@ target_link_libraries (mcp9808-example mcp9808 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (groveultrasonic-example groveultrasonic ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (sx1276-lora-example sx1276 ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries (sx1276-fsk-example sx1276 ${CMAKE_THREAD_LIBS_INIT}) +if (OPENZWAVE_FOUND) + target_link_libraries (ozw-example ozw ${CMAKE_THREAD_LIBS_INIT}) +endif () diff --git a/examples/c++/ozw.cxx b/examples/c++/ozw.cxx new file mode 100644 index 00000000..40c30f43 --- /dev/null +++ b/examples/c++/ozw.cxx @@ -0,0 +1,141 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include + +#include "ozw.h" + +using namespace std; + +int main(int argc, char **argv) +{ +//! [Interesting] + + string defaultDev = "/dev/ttyACM0"; + + // if an argument was specified, use it as the device instead + if (argc > 1) + defaultDev = string(argv[1]); + + // Instantiate an OZW instance + upm::OZW *sensor = new upm::OZW(); + + // The first thing to do is create options, then lock them when done. + sensor->optionsCreate(); + sensor->optionsLock(); + + // Next, initialize it. + cout << "Initializing, this may take awhile depending on your ZWave network" + << endl; + + if (!sensor->init(defaultDev)) + { + cerr << "Init failed." << endl; + return 0; + } + cout << "Initialization complete" << endl; + + cout << "Dumping nodes..." << endl; + + sensor->dumpNodes(); + + // The following is example output of dumpNodes: + // + // Dumping nodes... + // Node 1: Z-Stick Gen5 + // Node 2: Smart Switch 6 + // Index: 0, Type: bool, Label: Switch, Value: False + // Index: 2, Type: float, Label: Energy, Value: 1.190 kWh + // Index: 3, Type: float, Label: Previous Reading, Value: 1.190 kWh + // Index: 4, Type: int32, Label: Interval, Value: 1521 seconds + // Index: 5, Type: float, Label: Power, Value: 0.000 W + // Index: 6, Type: float, Label: Voltage, Value: 121.256 V + // Index: 7, Type: float, Label: Current, Value: 0.000 A + // Index: 8, Type: bool, Label: Exporting, Value: False + // Index: 45, Type: list, Label: Day, Value: Friday + // Index: 46, Type: byte, Label: Hour, Value: 5 + // Index: 47, Type: byte, Label: Minute, Value: 53 + // Node 3: Multi Sensor + // Index: 0, Type: bool, Label: Sensor, Value: True + // Index: 1, Type: float, Label: Temperature, Value: 72.8 F + // Index: 2, Type: float, Label: Luminance, Value: 4 lux + // Index: 3, Type: float, Label: Relative Humidity, Value: 22 % + // Index: 17, Type: byte, Label: Battery Level, Value: 98 % + // Node 5: Minimote + // Node 6: Smart Energy Switch + // Index: 0, Type: bool, Label: Switch, Value: False + // Index: 2, Type: float, Label: Power, Value: 0.000 W + // Index: 3, Type: float, Label: Energy, Value: 1.609 kWh + // Index: 4, Type: float, Label: Previous Reading, Value: 1.609 kWh + // Index: 5, Type: int32, Label: Interval, Value: 1521 seconds + // Index: 6, Type: float, Label: Power, Value: 0.000 W + // Index: 7, Type: float, Label: Previous Reading, Value: 1.609 W + // Index: 8, Type: int32, Label: Interval, Value: 1521 seconds + // Index: 9, Type: bool, Label: Exporting, Value: False + // Node 7: Smart Energy Switch + // Index: 0, Type: bool, Label: Switch, Value: False + // Index: 2, Type: float, Label: Power, Value: 0.000 W + // Index: 3, Type: float, Label: Energy, Value: 0.000 kWh + // Index: 4, Type: float, Label: Previous Reading, Value: 0.000 kWh + // Index: 5, Type: int32, Label: Interval, Value: 1521 seconds + // Index: 6, Type: float, Label: Power, Value: 0.000 W + // Index: 7, Type: float, Label: Previous Reading, Value: 0.000 W + // Index: 8, Type: int32, Label: Interval, Value: 1521 seconds + // Index: 9, Type: bool, Label: Exporting, Value: False + // + // So, with the above in mind: + // + // 1. Query the temperature on node 3 and print it out (as a + // string), along with the units of measure: + // + // cout << "Temperature: " << sensor->getValueAsString(3, 1) + // << " " << sensor->getValueUnits(3, 1) << endl; + // + // 2. query the same temperature as a float: + // + // float temperature = sensor->getValueAsFloat(3, 1); + // + // 3. Turn on the light plugged into the switch on Node 7, wait 5 + // seconds, then turn it back off again: + // + // cout << "Turning ON node 7" << endl; + // sensor->setValueAsBool(7, 0, true); + // + // cout << "Sleeping for 5 seconds" << endl; + // sleep(5); + // + // cout << "Turning OFF node 7" << endl; + // sensor->setValueAsBool(7, 0, false); + + +//! [Interesting] + + + cout << "Exiting..." << endl; + + delete sensor; + + return 0; +} diff --git a/examples/javascript/ozw.js b/examples/javascript/ozw.js new file mode 100644 index 00000000..5c5c7d56 --- /dev/null +++ b/examples/javascript/ozw.js @@ -0,0 +1,128 @@ +/*jslint node:true, vars:true, bitwise:true, unparam:true */ +/*jshint unused:true */ + +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +var sensorObj = require('jsupm_ozw'); + + +/************** Main code **************/ +// Instantiate an OZW instance +var sensor = new sensorObj.OZW(); + +var defaultDev = "/dev/ttyACM0"; + +// if an argument was specified, use it as the device instead +if (process.argv.length > 2) +{ + defaultDev = process.argv[2]; +} + +// The first thing to do is create options, then lock them when done. +sensor.optionsCreate(); +sensor.optionsLock(); + +// Next, initialize it. +console.log("Initializing, this may take awhile depending on your ZWave network"); + +if (!sensor.init(defaultDev)) +{ + console.log("Init failed."); + process.exit(0); +} +console.log("Initialization complete"); + +console.log("Dumping nodes..."); + +sensor.dumpNodes(); + +// The following is example output of dumpNodes: +// +// Dumping nodes... +// Node 1: Z-Stick Gen5 +// Node 2: Smart Switch 6 +// Index: 0, Type: bool, Label: Switch, Value: False +// Index: 2, Type: float, Label: Energy, Value: 1.190 kWh +// Index: 3, Type: float, Label: Previous Reading, Value: 1.190 kWh +// Index: 4, Type: int32, Label: Interval, Value: 1521 seconds +// Index: 5, Type: float, Label: Power, Value: 0.000 W +// Index: 6, Type: float, Label: Voltage, Value: 121.256 V +// Index: 7, Type: float, Label: Current, Value: 0.000 A +// Index: 8, Type: bool, Label: Exporting, Value: False +// Index: 45, Type: list, Label: Day, Value: Friday +// Index: 46, Type: byte, Label: Hour, Value: 5 +// Index: 47, Type: byte, Label: Minute, Value: 53 +// Node 3: Multi Sensor +// Index: 0, Type: bool, Label: Sensor, Value: True +// Index: 1, Type: float, Label: Temperature, Value: 72.8 F +// Index: 2, Type: float, Label: Luminance, Value: 4 lux +// Index: 3, Type: float, Label: Relative Humidity, Value: 22 % +// Index: 17, Type: byte, Label: Battery Level, Value: 98 % +// Node 5: Minimote +// Node 6: Smart Energy Switch +// Index: 0, Type: bool, Label: Switch, Value: False +// Index: 2, Type: float, Label: Power, Value: 0.000 W +// Index: 3, Type: float, Label: Energy, Value: 1.609 kWh +// Index: 4, Type: float, Label: Previous Reading, Value: 1.609 kWh +// Index: 5, Type: int32, Label: Interval, Value: 1521 seconds +// Index: 6, Type: float, Label: Power, Value: 0.000 W +// Index: 7, Type: float, Label: Previous Reading, Value: 1.609 W +// Index: 8, Type: int32, Label: Interval, Value: 1521 seconds +// Index: 9, Type: bool, Label: Exporting, Value: False +// Node 7: Smart Energy Switch +// Index: 0, Type: bool, Label: Switch, Value: False +// Index: 2, Type: float, Label: Power, Value: 0.000 W +// Index: 3, Type: float, Label: Energy, Value: 0.000 kWh +// Index: 4, Type: float, Label: Previous Reading, Value: 0.000 kWh +// Index: 5, Type: int32, Label: Interval, Value: 1521 seconds +// Index: 6, Type: float, Label: Power, Value: 0.000 W +// Index: 7, Type: float, Label: Previous Reading, Value: 0.000 W +// Index: 8, Type: int32, Label: Interval, Value: 1521 seconds +// Index: 9, Type: bool, Label: Exporting, Value: False +// +// So, with the above in mind: +// +// 1. Query the temperature on node 3 and print it out (as a +// string), along with the units of measure: +// +// console.log("Temperature: " + sensor.getValueAsString(3, 1) + +// " " + sensor.getValueUnits(3, 1)); +// +// 2. query the same temperature as a float: +// +// var temperature = sensor.getValueAsFloat(3, 1); +// +// 3. Turn on the light plugged into the switch on Node 7 +// +// console.log("Turning ON node 7"); +// sensor.setValueAsBool(7, 0, true); +// + +sensor = null; +sensorObj.cleanUp(); +sensorObj = null; +console.log("Exiting..."); +process.exit(0); diff --git a/examples/python/ozw.py b/examples/python/ozw.py new file mode 100644 index 00000000..010c1fc1 --- /dev/null +++ b/examples/python/ozw.py @@ -0,0 +1,126 @@ +#!/usr/bin/python +# Author: Jon Trulson +# Copyright (c) 2015 Intel Corporation. +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import time, sys, signal, atexit +import pyupm_ozw as sensorObj + +# Instantiate an OZW instance +sensor = sensorObj.OZW() + +# This function lets you run code on exit +def exitHandler(): + print "Exiting" + sys.exit(0) + +# Register exit handlers +atexit.register(exitHandler) + +defaultDev = "/dev/ttyACM0" +if (len(sys.argv) > 1): + defaultDev = sys.argv[1] + +# The first thing to do is create options, then lock them when done. +sensor.optionsCreate() +sensor.optionsLock() + +# Next, initialize it. +print "Initializing, this may take awhile depending on your ZWave network" + +if (not sensor.init(defaultDev)): + print "Init failed." + sys.exit(1) + +print "Initialization complete" + +print "Dumping nodes..." + +sensor.dumpNodes() + +# The following is example output of dumpNodes: +# +# Dumping nodes... +# Node 1: Z-Stick Gen5 +# Node 2: Smart Switch 6 +# Index: 0, Type: bool, Label: Switch, Value: False +# Index: 2, Type: float, Label: Energy, Value: 1.190 kWh +# Index: 3, Type: float, Label: Previous Reading, Value: 1.190 kWh +# Index: 4, Type: int32, Label: Interval, Value: 1521 seconds +# Index: 5, Type: float, Label: Power, Value: 0.000 W +# Index: 6, Type: float, Label: Voltage, Value: 121.256 V +# Index: 7, Type: float, Label: Current, Value: 0.000 A +# Index: 8, Type: bool, Label: Exporting, Value: False +# Index: 45, Type: list, Label: Day, Value: Friday +# Index: 46, Type: byte, Label: Hour, Value: 5 +# Index: 47, Type: byte, Label: Minute, Value: 53 +# Node 3: Multi Sensor +# Index: 0, Type: bool, Label: Sensor, Value: True +# Index: 1, Type: float, Label: Temperature, Value: 72.8 F +# Index: 2, Type: float, Label: Luminance, Value: 4 lux +# Index: 3, Type: float, Label: Relative Humidity, Value: 22 % +# Index: 17, Type: byte, Label: Battery Level, Value: 98 % +# Node 5: Minimote +# Node 6: Smart Energy Switch +# Index: 0, Type: bool, Label: Switch, Value: False +# Index: 2, Type: float, Label: Power, Value: 0.000 W +# Index: 3, Type: float, Label: Energy, Value: 1.609 kWh +# Index: 4, Type: float, Label: Previous Reading, Value: 1.609 kWh +# Index: 5, Type: int32, Label: Interval, Value: 1521 seconds +# Index: 6, Type: float, Label: Power, Value: 0.000 W +# Index: 7, Type: float, Label: Previous Reading, Value: 1.609 W +# Index: 8, Type: int32, Label: Interval, Value: 1521 seconds +# Index: 9, Type: bool, Label: Exporting, Value: False +# Node 7: Smart Energy Switch +# Index: 0, Type: bool, Label: Switch, Value: False +# Index: 2, Type: float, Label: Power, Value: 0.000 W +# Index: 3, Type: float, Label: Energy, Value: 0.000 kWh +# Index: 4, Type: float, Label: Previous Reading, Value: 0.000 kWh +# Index: 5, Type: int32, Label: Interval, Value: 1521 seconds +# Index: 6, Type: float, Label: Power, Value: 0.000 W +# Index: 7, Type: float, Label: Previous Reading, Value: 0.000 W +# Index: 8, Type: int32, Label: Interval, Value: 1521 seconds +# Index: 9, Type: bool, Label: Exporting, Value: False +# +# So, with the above in mind: +# +# 1. Query the temperature on node 3 and print it out (as a +# string), along with the units of measure: +# +# print "Temperature:", sensor.getValueAsString(3, 1), +# sensor->getValueUnits(3, 1) +# +# 2. query the same temperature as a float: +# +# temperature = sensor.getValueAsFloat(3, 1) +# +# 3. Turn on the light plugged into the switch on Node 7, wait 5 +# seconds, then turn it back off again: +# +# print "Turning ON node 7" +# sensor.setValueAsBool(7, 0, true) +# +# print "Sleeping for 5 seconds"; +# time.sleep(5) +# +# print "Turning OFF node 7" +# sensor.setValueAsBool(7, 0, false); + diff --git a/src/ozw/CMakeLists.txt b/src/ozw/CMakeLists.txt new file mode 100644 index 00000000..6c671578 --- /dev/null +++ b/src/ozw/CMakeLists.txt @@ -0,0 +1,21 @@ +set (libname "ozw") +set (libdescription "upm module for the OpenZWave library interface") +set (module_src ${libname}.cxx zwNode.cxx) +set (module_h ${libname}.h) + +pkg_search_module(OPENZWAVE libopenzwave) +if (OPENZWAVE_FOUND) + set (reqlibname "libopenzwave") + include_directories(${OPENZWAVE_INCLUDE_DIRS}) + upm_module_init() + add_dependencies(${libname} ${OPENZWAVE_LIBRARIES}) + target_link_libraries(${libname} ${OPENZWAVE_LIBRARIES}) + if (BUILDSWIG) + if (BUILDSWIGNODE) + swig_link_libraries (jsupm_${libname} ${OPENZWAVE_LIBRARIES} ${MRAA_LIBRARIES} ${NODE_LIBRARIES}) + endif() + if (BUILDSWIGPYTHON) + swig_link_libraries (pyupm_${libname} ${OPENZWAVE_LIBRARIES} ${PYTHON_LIBRARIES} ${MRAA_LIBRARIES}) + endif() + endif() +endif () diff --git a/src/ozw/javaupm_ozw.i b/src/ozw/javaupm_ozw.i new file mode 100644 index 00000000..dcb7f3a6 --- /dev/null +++ b/src/ozw/javaupm_ozw.i @@ -0,0 +1,12 @@ +%module javaupm_ozw +%include "../upm.i" +%include "cpointer.i" +%include "typemaps.i" +%include "arrays_java.i"; +%include "../java_buffer.i" + +%{ + #include "ozw.h" +%} + +%include "ozw.h" diff --git a/src/ozw/jsupm_ozw.i b/src/ozw/jsupm_ozw.i new file mode 100644 index 00000000..209fe621 --- /dev/null +++ b/src/ozw/jsupm_ozw.i @@ -0,0 +1,12 @@ +%module jsupm_ozw +%include "../upm.i" +%include "cpointer.i" +%include "stdint.i" + +%pointer_functions(float, floatp); + +%include "ozw.h" +%{ + #include "ozw.h" +%} + diff --git a/src/ozw/ozw.cxx b/src/ozw/ozw.cxx new file mode 100644 index 00000000..4f8df346 --- /dev/null +++ b/src/ozw/ozw.cxx @@ -0,0 +1,909 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include + +#include "platform/Log.h" + +#include "zwNode.h" + +#include "ozw.h" + +using namespace upm; +using namespace std; +using namespace OpenZWave; + + +OZW::OZW() +{ + m_mgrCreated = false; + m_driverFailed = false; + m_homeId = 0; + + pthread_mutexattr_t mutexAttrib; + pthread_mutexattr_init(&mutexAttrib); + pthread_mutexattr_settype(&mutexAttrib, PTHREAD_MUTEX_RECURSIVE); + + if (pthread_mutex_init(&m_nodeLock, &mutexAttrib)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": pthread_mutex_init(nodeLock) failed"); + } + + pthread_mutexattr_destroy(&mutexAttrib); + + if (pthread_mutex_init(&m_initLock, NULL)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": pthread_mutex_init(initLock) failed"); + } + + // initialize our init conditional + if (pthread_cond_init(&m_initCond, NULL)) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": pthread_cond_init() failed"); + } + + setDebug(false); +} + +OZW::~OZW() +{ + if (m_mgrCreated) + { + // remove the driver + if (m_driverIsHID) + Manager::Get()->RemoveDriver("HID"); + else + Manager::Get()->RemoveDriver(m_devicePath); + + // remove the notification handler + Manager::Get()->RemoveWatcher(notificationHandler, this); + + // now destroy Manager and Options. Options must be destroyed + // after the Manager is destroyed. + Manager::Destroy(); + Options::Destroy(); + } + + pthread_mutex_destroy(&m_nodeLock); + pthread_mutex_destroy(&m_initLock); + pthread_cond_destroy(&m_initCond); + + // delete any nodes. This should be safe after deleting the node + // mutex since the handler is no longer registered. + for (zwNodeMap_t::iterator it = m_zwNodeMap.begin(); + it != m_zwNodeMap.end(); ++it) + { + // delete the zwNode pointer + delete (*it).second; + } + // empty the map + m_zwNodeMap.clear(); +} + +void OZW::optionsCreate(std::string configPath, + std::string userConfigDir, + std::string cmdLine) +{ + Options::Create(configPath, userConfigDir, cmdLine); +} + +void OZW::optionAddInt(std::string name, int val) +{ + Options::Get()->AddOptionInt(name, val); +} + +void OZW::optionAddBool(std::string name, bool val) +{ + Options::Get()->AddOptionBool(name, val); +} + +void OZW::optionAddString(std::string name, std::string val, bool append) +{ + Options::Get()->AddOptionString(name, val, append); +} + +void OZW::optionsLock() +{ + // lock the options if not already locked + if (!Options::Get()->AreLocked()) + Options::Get()->Lock(); +} + +bool OZW::init(string devicePath, bool isHID) +{ + // make sure options are locked + optionsLock(); + + pthread_mutex_lock(&m_initLock); + + // the fun begins + Manager::Create(); + + // add our event handler + Manager::Get()->AddWatcher(notificationHandler, this); + + // now add the driver + m_devicePath = devicePath; + if (isHID) + { + m_driverIsHID = true; + Manager::Get()->AddDriver("HID", + Driver::ControllerInterface_Hid); + } + else + Manager::Get()->AddDriver(devicePath); + + m_mgrCreated = true; + + // now we block here waiting for the driver to get far enough along + // (or fail) to proceed further + pthread_cond_wait(&m_initCond, &m_initLock); + + if (m_driverFailed) + { + throw std::runtime_error(std::string(__FUNCTION__) + + ": driver initialization failed"); + return false; + } + + return true; +} + +void OZW::notificationHandler(Notification const* notification, void *ctx) +{ + upm::OZW *This = (upm::OZW *)ctx; + + This->lockNodes(); + + if (This->m_debugging) + fprintf(stderr, "### %s: homeId %08x, nodeId %d, type %x\n", + __FUNCTION__, + notification->GetHomeId(), + notification->GetNodeId(), + notification->GetType()); + + const uint32_t homeId = notification->GetHomeId(); + const uint8_t nodeId = notification->GetNodeId(); + + switch (notification->GetType()) + { + + case Notification::Type_NodeAdded: + case Notification::Type_NodeNew: + { + if (This->m_debugging) + cerr << "### ### ADDING NODE: " << int(nodeId) << endl; + zwNode *node = new zwNode(homeId, nodeId); + This->m_zwNodeMap.insert(std::pair(nodeId, node)); + + break; + } + + case Notification::Type_NodeRemoved: + { + if (This->m_debugging) + cerr << "### ### REMOVING NODE: " << int(nodeId) << endl; + if (This->m_zwNodeMap.count(nodeId) != 0) + { + delete This->m_zwNodeMap[nodeId]; + This->m_zwNodeMap.erase(nodeId); + } + + break; + } + + case Notification::Type_ValueAdded: + { + if (This->m_debugging) + cerr << "### ### VALUE ADDED " << endl; + This->m_zwNodeMap[nodeId]->addValueID(notification->GetValueID()); + + break; + } + + case Notification::Type_ValueRemoved: + { + if (This->m_debugging) + cerr << "### ### VALUE DELETED " << endl; + This->m_zwNodeMap[nodeId]->removeValueID(notification->GetValueID()); + + break; + } + + case Notification::Type_ValueChanged: + { + // might be able to do something with this someday... + break; + } + + case Notification::Type_DriverReset: + { + // all nodes deleted. According to OZW docs, this happens + // when a driver is reset, instead of sending potentially + // hundreds of ValueRemoved/NodeRemoved events. + for (zwNodeMap_t::iterator it = This->m_zwNodeMap.begin(); + it != This->m_zwNodeMap.end(); ++it) + { + // delete the zwNode pointer + delete (*it).second; + } + // empty the map + This->m_zwNodeMap.clear(); + + break; + } + + case Notification::Type_DriverReady: + { + if (This->m_debugging) + fprintf(stderr, "### DriverReady, homeID = %08x\n", This->m_homeId); + This->m_homeId = notification->GetHomeId(); + break; + } + + case Notification::Type_DriverFailed: + { + if (This->m_debugging) + cerr << "### Driver FAILED" << endl; + This->m_driverFailed = true; + // wake up init() + pthread_cond_broadcast(&(This->m_initCond)); + break; + } + + case Notification::Type_AwakeNodesQueried: + case Notification::Type_AllNodesQueried: + case Notification::Type_AllNodesQueriedSomeDead: + { + if (This->m_debugging) + cerr << "### Awake/All/SomeDead complete" << endl; + // wake up init() + pthread_cond_broadcast(&(This->m_initCond)); + break; + } + + // might be able to do something with these someday too + case Notification::Type_Notification: + case Notification::Type_NodeNaming: + case Notification::Type_NodeProtocolInfo: + case Notification::Type_NodeQueriesComplete: + case Notification::Type_PollingEnabled: + case Notification::Type_PollingDisabled: + case Notification::Type_NodeEvent: + case Notification::Type_Group: + default: + { + break; + } + } + + This->unlockNodes(); +} + +void OZW::dumpNodes(bool all) +{ + // iterate through all the nodes and dump various info on them + + lockNodes(); + + for (zwNodeMap_t::iterator it = m_zwNodeMap.begin(); + it != m_zwNodeMap.end(); ++it) + { + uint8_t nodeId = (*it).first; + + cerr << "Node " << int(nodeId) + << ": " + << Manager::Get()->GetNodeProductName(m_homeId, nodeId) + << endl; + (*it).second->dumpNode(all); + } + + unlockNodes(); +} + +bool OZW::getValueID(int nodeId, int index, ValueID *vid) +{ + // nodeId's are uint8_t's in OpenZWave, but we want to use int's to + // avoid hassles when dealing with SWIG, so here we just throw away + // everything except the first byte. + nodeId &= 0xff; + + lockNodes(); + + zwNodeMap_t::iterator it; + + it = m_zwNodeMap.find(nodeId); + + if (it == m_zwNodeMap.end()) + { + cerr << __FUNCTION__ << ": Node " << nodeId + << " does not exist" << endl; + unlockNodes(); + return false; + } + + // now get the ValueID + if (!(*it).second->indexToValueID(index, vid)) + { + cerr << __FUNCTION__ << ": Index " << index + << " for node " << nodeId + << " does not exist" << endl; + unlockNodes(); + return false; + } + + unlockNodes(); + return true; +} + +string OZW::getValueAsString(int nodeId, int index) +{ + // we have to play this game since there is no default ctor for ValueID + ValueID vid(m_homeId, (uint64)0); + + string rv; + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + Manager::Get()->GetValueAsString(vid, &rv); + + unlockNodes(); + + return rv; +} + +string OZW::getValueUnits(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + string rv; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->GetValueUnits(vid); + + unlockNodes(); + + return rv; +} + +void OZW::setValueUnits(int nodeId, int index, string text) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + Manager::Get()->SetValueUnits(vid, text); + + unlockNodes(); +} + +string OZW::getValueLabel(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + string rv; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->GetValueLabel(vid); + + unlockNodes(); + + return rv; +} + +void OZW::setValueLabel(int nodeId, int index, string text) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + Manager::Get()->SetValueLabel(vid, text); + + unlockNodes(); +} + +string OZW::getValueHelp(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + string rv; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->GetValueHelp(vid); + + unlockNodes(); + + return rv; +} + +void OZW::setValueHelp(int nodeId, int index, string text) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + Manager::Get()->SetValueHelp(vid, text); + + unlockNodes(); +} + +void OZW::setValueAsBool(int nodeId, int index, bool val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + cerr << __FUNCTION__ << ": Value is not a bool type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsByte(int nodeId, int index, uint8_t val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + cerr << __FUNCTION__ << ": Value is not a byte type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsFloat(int nodeId, int index, float val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + cerr << __FUNCTION__ << ": Value is not a float type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsInt32(int nodeId, int index, int32_t val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + cerr << __FUNCTION__ << ": Value is not a int32 type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsInt16(int nodeId, int index, int16_t val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + cerr << __FUNCTION__ << ": Value is not a int16 type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsBytes(int nodeId, int index, uint8_t *val, uint8_t len) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val, len)) + { + cerr << __FUNCTION__ << ": Value is not a bytes type" << endl; + } + } + + unlockNodes(); +} + +void OZW::setValueAsString(int nodeId, int index, string val) +{ + if (isValueReadOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is ReadOnly" << endl; + return; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->SetValue(vid, val)) + { + // this should always succeed, but for consistancy... + cerr << __FUNCTION__ << ": Value is not a string type" << endl; + } + } + + unlockNodes(); +} + +void OZW::refreshValue(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + if (getValueID(nodeId, index, &vid)) + Manager::Get()->RefreshValue(vid); + + unlockNodes(); +} + +int OZW::getValueMin(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + int rv = 0; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->GetValueMin(vid); + + unlockNodes(); + return rv; +} + +int OZW::getValueMax(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + int rv = 0; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->GetValueMax(vid); + + unlockNodes(); + return rv; +} + +bool OZW::isValueReadOnly(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + bool rv = false; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->IsValueReadOnly(vid); + + unlockNodes(); + return rv; +} + +bool OZW::isValueWriteOnly(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + bool rv = false; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->IsValueWriteOnly(vid); + + unlockNodes(); + return rv; +} + +bool OZW::isValueSet(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + bool rv = false; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->IsValueSet(vid); + + unlockNodes(); + return rv; +} + +bool OZW::isValuePolled(int nodeId, int index) +{ + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + bool rv = false; + if (getValueID(nodeId, index, &vid)) + rv = Manager::Get()->IsValuePolled(vid); + + unlockNodes(); + return rv; +} + +bool OZW::getValueAsBool(int nodeId, int index) +{ + if (isValueWriteOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is WriteOnly" << endl; + return false; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + bool rv = false; + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->GetValueAsBool(vid, &rv)) + { + cerr << __FUNCTION__ << ": Value is not a bool type, returning " + << rv << endl; + } + } + + unlockNodes(); + return rv; +} + +uint8_t OZW::getValueAsByte(int nodeId, int index) +{ + if (isValueWriteOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is WriteOnly" << endl; + return 0; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + uint8_t rv = false; + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->GetValueAsByte(vid, &rv)) + { + cerr << __FUNCTION__ << ": Value is not a byte type, returning " + << int(rv) << endl; + } + } + + unlockNodes(); + return rv; +} + +float OZW::getValueAsFloat(int nodeId, int index) +{ + if (isValueWriteOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is WriteOnly" << endl; + return 0.0; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + float rv = 0.0; + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->GetValueAsFloat(vid, &rv)) + { + cerr << __FUNCTION__ << ": Value is not a float type, returning " + << rv << endl; + } + } + + unlockNodes(); + return rv; +} + +int OZW::getValueAsInt32(int nodeId, int index) +{ + if (isValueWriteOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is WriteOnly" << endl; + return 0; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + int32_t rv = 0; + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->GetValueAsInt(vid, &rv)) + { + cerr << __FUNCTION__ << ": Value is not an int32 type, returning " + << rv << endl; + } + } + + unlockNodes(); + return int(rv); +} + +int OZW::getValueAsInt16(int nodeId, int index) +{ + if (isValueWriteOnly(nodeId, index)) + { + cerr << __FUNCTION__ << ": Node " << nodeId << " index " << index + << " is WriteOnly" << endl; + return 0; + } + + ValueID vid(m_homeId, (uint64)0); + + lockNodes(); + + int16_t rv = 0; + if (getValueID(nodeId, index, &vid)) + { + if (!Manager::Get()->GetValueAsShort(vid, &rv)) + { + cerr << __FUNCTION__ << ": Value is not an int16 type, returning " + << rv << endl; + } + } + + unlockNodes(); + return int(rv); +} + +bool OZW::isNodeListeningDevice(int nodeId) +{ + nodeId &= 0xff; + + lockNodes(); + + bool rv = Manager::Get()->IsNodeListeningDevice(m_homeId, nodeId); + + unlockNodes(); + return rv; +} + +bool OZW::isNodeFrequentListeningDevice(int nodeId) +{ + nodeId &= 0xff; + + lockNodes(); + + bool rv = Manager::Get()->IsNodeFrequentListeningDevice(m_homeId, nodeId); + + unlockNodes(); + return rv; +} + +bool OZW::isNodeAwake(int nodeId) +{ + nodeId &= 0xff; + + lockNodes(); + + bool rv = Manager::Get()->IsNodeAwake(m_homeId, nodeId); + + unlockNodes(); + return rv; +} + +void OZW::setDebug(bool enable) +{ + m_debugging = enable; + + // To bad the following does not seem to affect anything. The only + // way I've found to control it is via the options.xml file. + + // Log::SetLoggingState(enable); +} diff --git a/src/ozw/ozw.h b/src/ozw/ozw.h new file mode 100644 index 00000000..e4f4152e --- /dev/null +++ b/src/ozw/ozw.h @@ -0,0 +1,574 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include + +#include "Manager.h" +#include "Notification.h" +#include "Options.h" +#include "Driver.h" +#include "Node.h" +#include "Group.h" +#include "platform/Log.h" + +namespace upm { + + /** + * @brief OZW OpenZWave library + * @defgroup ozw libupm-ozw + * @ingroup uart wifi + */ + + /** + * @library ozw + * @sensor ozw + * @comname UPM wrapper for the OpenZWave library + * @type wifi + * @man other + * @con uart + * @web http://www.openzwave.com/ + * + * @brief UPM API for the OpenZWave library + * + * This module implements a wrapper around the OpenZWave library. + * OpenZWave must be compiled and installed on your machine in order + * to use this library. + * + * This module was developed with OpenZWave 1.3, and an Aeon Z-Stick + * Gen5 configured as a Primary Controller. It provides the ability + * to query and set various values that can be used to control ZWave + * devices. It does not concern itself with configuration of + * devices. It is assumed that you have already setup your ZWave + * network using a tool like the OpenZWave control panel, and have + * already configured your devices as appropriate. + * + * To avoid exposing some of the internals of OpenZWave, devices + * (nodes) and their values, are accessed via a nodeId and a value + * index number. The example will run dumpNodes() which will list + * the currently connected devices and the values that are available + * to them, along with an index number for that value. It is + * through these values (nodeId and index) that you can query and + * set device values. + * + * In addition to querying values from a device (such as state + * (on/off), or temperature, etc), methods are provided to allow you + * to control these devices to the extent they allow, for example, + * using a ZWave connected switch to turn on a lamp. + * + * Since it's likely no two ZWave networks are going to be the same, + * the example will just initialize OpenZWave and run the + * dumpNodes() method to allow you to see what devices are present, + * the values they support and their current content, along with the + * per-node index number you can use to address them. There will be + * commented out code examples showing you how to query or set a + * specific value for a device. + * + * See the ozw example code comments for an example of the ouput of + * running dumpNodes(). + * + * In most of the methods below, You will need the NodeId (Node + * number), and the Index number to access or otherwise affect these + * values. + * + * @snippet openzwave.cxx Interesting + */ + + // forward declaration of private zwNode data + class zwNode; + + class OZW { + public: + + typedef std::map zwNodeMap_t; + + /** + * OZW constructor + */ + OZW(); + + /** + * OZW Destructor + */ + ~OZW(); + + /** + * Start configuration with basic options. This must be called + * prior to init(), after the OZW() contructor is called. + * + * @param configPath Set the location of the OpenZWave config + * directory, default is /etc/openzwave + * @param userConfigDir Set the path to the user configuration + * directory. This is the location of the zwcfg*.xml and + * option.xml files for the user (probably created by the + * OpenZWave Control Panel example application). The default is + * the current directory (""). + * @param cmdLine Specify command line formatted options to + * OpenZWave. The default is "". + */ + void optionsCreate(std::string configPath="/etc/openzwave", + std::string userConfigDir="", + std::string cmdLine=""); + + /** + * Add an integer Option. See the OpenZWave documentation for + * valid values. + * + * @param name The name of the configuration option + * @param val The value to set it to + */ + void optionAddInt(std::string name, int val); + + /** + * Add a boolean Option. See the OpenZWave documentation for + * valid values. + * + * @param name The name of the configuration option + * @param val The value to set it to + */ + void optionAddBool(std::string name, bool val); + + /** + * Add a string Option. See the OpenZWave documentation for valid + * values. + * + * @param name The name of the configuration option + * @param val The value to set it to + * @append true to append to the option, false to override + */ + void optionAddString(std::string name, std::string val, bool append); + + /** + * Lock the Options. This must be called after all options have + * been set, and before init() is called. If init() is called + * without locking the Options, init() will lock them itself. + * After the options have been locked, no further options can be + * specified. + */ + void optionsLock(); + + /** + * Initialize the ZWave network. This method will start a probe + * of all defined devices on the ZWave network and query essential + * information about them. This function will not return until + * either initialization has failed, or has succeeded far enough + * along for the following methods to work. Depending on the size + * an complexity of the ZWave network, this may take anywhere from + * seconds to several minutes to complete. + * + * All Options (via option*()) must have been specified before + * this function is called. If the Options have not been locked + * via optionsLock() prior to calling init(), this method will + * lock them for you before proceeding. + * + * @param devicePath The device path for the ZWave controller, + * typically something like /dev/ttyACM0, or similiar + * @param isHID true if this is a HID device, false otherwise (ie: + * a serial port like /dev/ttyACM0, /dev/ttyUSB0, etc). Default + * is false. + * @return true if init succeeded, false otherwise + */ + bool init(std::string devicePath, bool isHID=false); + + /** + * Dump information about all configured nodes and their values to + * stdout. This is useful to determine what nodes are available, + * and the index (used for querying and seting values for them). + * In addition, it includes information about each value (type, + * current value, etc). + * + * @param all set to true to dump information about all values + * available for each node. If false, only information about + * 'user' values (ignoring 'system' and 'configuration') are + * output. The default is false ('user' values only). + */ + void dumpNodes(bool all=false); + + /** + * Return a string (which may be empty) indicating the Units of + * measure for a given value. For example, querying a temperature + * value may return "F" to indicate Fahrenheit. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @return A string containing the Unit of measure for the value + */ + std::string getValueUnits(int nodeId, int index); + + /** + * Set the text for the Units of measure for a value. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @param text The text to set + */ + void setValueUnits(int nodeId, int index, std::string text); + + /** + * Return a string (which may be empty) containing the + * user-freindly Label for a value. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @return A string containing the Value's label + */ + std::string getValueLabel(int nodeId, int index); + + /** + * Set the text for a Value's label. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @param text The text to set + */ + void setValueLabel(int nodeId, int index, std::string text); + + /** + * Return a string (which may be empty) indicating the Help text + * of a value, if available. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @return A string containing the Help text, if available + */ + std::string getValueHelp(int nodeId, int index); + + /** + * Set the text for a Value's help text. + * + * @param nodeId The node ID to query + * @param index The value index (see dumpNodes()) of the value to query. + * @param text The text to set + */ + void setValueHelp(int nodeId, int index, std::string text); + + /** + * Set the contents of a Value to a string. This should always + * succeed if the supplied content makes sense for a given value, + * regardless of the value's actual type. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the content to assign to the value referenced by + * nodeId, and index. + */ + void setValueAsString(int nodeId, int index, std::string val); + + /** + * Set the contents of a Value, to a bool. This will fail, and an + * error message printed if the value type is not a boolean value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the boolean content to assign to the value referenced + * by nodeId, and index. + */ + void setValueAsBool(int nodeId, int index, bool val); + + /** + * Set the contents of a Value, to a byte. This will fail, and an + * error message printed if the value type is not a byte value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the byte content to assign to the value referenced + * by nodeId, and index. + */ + void setValueAsByte(int nodeId, int index, uint8_t val); + + /** + * Set the contents of a Value, to a float. This will fail, and an + * error message printed if the value type is not a float value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the float content to assign to the value referenced + * by nodeId, and index. + */ + void setValueAsFloat(int nodeId, int index, float val); + + /** + * Set the contents of a Value, to a 32 bit integer (int32). This + * will fail, and an error message printed if the value type is + * not an int32. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the int32 content to assign to the value referenced + * by nodeId, and index. + */ + void setValueAsInt32(int nodeId, int index, int32_t val); + + /** + * Set the contents of a Value, to a 16 bit integer (int16). This + * will fail, and an error message printed if the value type is + * not an int16. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the int16 content to assign to the value referenced + * by nodeId, and index. + */ + void setValueAsInt16(int nodeId, int index, int16_t val); + + /** + * Set the contents of a Value, to an array of bytes. This will + * fail, and an error message printed if the value type is not + * settable as an array of bytes. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param val the byte array content to assign to the value referenced + * by nodeId, and index. + * @param len The length of the byte array + */ + void setValueAsBytes(int nodeId, int index, uint8_t *val, uint8_t len); + + /** + * Get the minimum allowed value for a node's Value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return The minumum allowed value + */ + int getValueMin(int nodeId, int index); + + /** + * Get the maximum allowed value for a node's Value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return The maximum allowed value + */ + int getValueMax(int nodeId, int index); + + /** + * Test whether a value is read-only. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return true if the value is read-only, false otherwise + */ + bool isValueReadOnly(int nodeId, int index); + + /** + * Test whether a value is write only. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return true if the value is write-only, false otherwise + */ + bool isValueWriteOnly(int nodeId, int index); + + /** + * Test whether a value is really set on a node, and not a default + * value chosen by the OpenZWave library. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return true if the value is really set, false if a default value is + * being reported + */ + bool isValueSet(int nodeId, int index); + + /** + * Test whether a value is being manually polled by the OpenZWave + * library. Most modern devices are never polled, rather they are + * configured to report changing values to the controller on their + * own at device specific intervals or when appropriate events + * (depending the device) have occured. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return true if the value is being maually polled, false otherwise + * being reported + */ + bool isValuePolled(int nodeId, int index); + + /** + * Return the content of a value as a string. This should always + * succeed, regardless of the actual value type. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return A string representing the current contents of a value. + */ + std::string getValueAsString(int nodeId, int index); + + /** + * Return the content of a value as a booleang. This will fail, + * and an error message printed if the value type is not boolean. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return A boolean representing the current contents of a value. + */ + bool getValueAsBool(int nodeId, int index); + + /** + * Return the content of a value as a byte. This will fail, and + * an error message printed if the value type is not a byte. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return A byte representing the current contents of a value. + */ + uint8_t getValueAsByte(int nodeId, int index); + + /** + * Return the content of a value as a float. This will fail, and + * an error message printed if the value type is not a floating + * point value. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return A float representing the current contents of a value. + */ + float getValueAsFloat(int nodeId, int index); + + /** + * Return the content of a value as an int32. This will fail, and + * an error message printed if the value type is not an int32. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return An int32 representing the current contents of a value. + */ + int getValueAsInt32(int nodeId, int index); + + /** + * Return the content of a value as an int16. This will fail, and + * an error message printed if the value type is not an int16. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @return An int16 representing the current contents of a value. + */ + int getValueAsInt16(int nodeId, int index); + + /** + * Issue a refresh request for a value on a node. OpenZWave will + * query the value and update it's internal state when the device + * responds. Note, this happens asynchronously - it may take some + * time before the current value is reported to OpenZWave by the + * node. If the node is asleep, you may not get a current value + * for some time (or at all, depending on the device). This + * method will return immediately after the request has been + * queued. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + */ + void refreshValue(int nodeId, int index); + + /** + * Enable or disable some debugging output. Note, this will not + * affect OpenZWave's own debugging, which is usually set in the + * option.xml file. + * + * @param enable true to enable debugging, false otherwise + */ + void setDebug(bool enable); + + /** + * Determine if a node is a listening device -- in other words, the + * node never sleeps. + * + * @param nodeId The node ID + * @return true if the node never sleeps, false otherwise + */ + bool isNodeListeningDevice(int nodeId); + + /** + * Determine if a node is a frequent listening device -- in other + * words, if the node is asleep, can it be woken by a beam. + * + * @param nodeId The node ID + * @return true if the node is a frequent listening device, false + * otherwise + */ + bool isNodeFrequentListeningDevice(int nodeId); + + /** + * Determine if a node is awake. + * + * @param nodeId The node ID + * @return true if the node is awake, false otherwise + */ + bool isNodeAwake(int nodeId); + + protected: + /** + * Based on a nodeId and a value index, lookup the corresponding + * OpenZWave ValueID. + * + * @param nodeId The node ID + * @param index The value index (see dumpNodes()) of the value to query. + * @param A pointer to a ValueID that will be returned if successful + * @return true of the nodeId and index was found, false otherwise + */ + bool getValueID(int nodeId, int index, OpenZWave::ValueID *vid); + + /** + * Lock the m_zwNodeMap mutex to protect against changes made to + * the the the map by the OpenZWave notification handler. Always + * lock this mutex when acessing anything in the zwNodeMap map. + */ + + void lockNodes() { pthread_mutex_lock(&m_nodeLock); }; + /** + * Unlock the m_zwNodeMap mutex after lockNodes() has been called. + */ + void unlockNodes() { pthread_mutex_unlock(&m_nodeLock); }; + + private: + uint32_t m_homeId; + bool m_mgrCreated; + bool m_driverFailed; + bool m_debugging; + + bool m_driverIsHID; + std::string m_devicePath; + + // our notification handler, called by OpenZWave for events on the + // network. + static void notificationHandler(OpenZWave::Notification + const* notification, + void *ctx); + + // a map of added nodes + zwNodeMap_t m_zwNodeMap; + + // for coordinating access to the node list + pthread_mutex_t m_nodeLock; + + // We use these to determine init failure or success (if OpenZWave + // has successfully queried essential data about the network). + pthread_mutex_t m_initLock; + pthread_cond_t m_initCond; + }; +} + + diff --git a/src/ozw/pyupm_ozw.i b/src/ozw/pyupm_ozw.i new file mode 100644 index 00000000..ad6d19e4 --- /dev/null +++ b/src/ozw/pyupm_ozw.i @@ -0,0 +1,15 @@ +%module pyupm_ozw +%include "../upm.i" +%include "cpointer.i" + +%include "stdint.i" + +%feature("autodoc", "3"); + +%pointer_functions(float, floatp); + +%include "ozw.h" +%{ + #include "ozw.h" +%} + diff --git a/src/ozw/zwNode.cxx b/src/ozw/zwNode.cxx new file mode 100644 index 00000000..a55729b5 --- /dev/null +++ b/src/ozw/zwNode.cxx @@ -0,0 +1,168 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "zwNode.h" + +#include "Node.h" + +using namespace upm; +using namespace std; +using namespace OpenZWave; + +zwNode::zwNode(uint32_t homeId, uint8_t nodeId) +{ + m_homeId = homeId; + m_nodeId = nodeId; + + m_vindex = 0; +} + +zwNode::~zwNode() +{ +} + +uint8_t zwNode::nodeId() +{ + return m_nodeId; +} + +uint32_t zwNode::homeId() +{ + return m_homeId; +} + +void zwNode::addValueID(ValueID vid) +{ + // We need to use insert since ValueID's default ctor is private + m_values.insert(std::pair(m_vindex++, vid)); +} + +void zwNode::removeValueID(ValueID vid) +{ + //we have to get a little complicated here since we need to delete + //the value id, but the map is indexed by m_vindex + for (valueMap_t::iterator it = m_values.begin(); + it != m_values.end(); ++it) + { + if ((*it).second == vid) + { + m_values.erase((*it).first); + break; + } + } +} + +bool zwNode::indexToValueID(int index, ValueID *vid) +{ + valueMap_t::iterator it; + + it = m_values.find(index); + + if (it == m_values.end()) + { + // not found, return false + return false; + } + else + *vid = (*it).second; + + return true; +} + +void zwNode::dumpNode(bool all) +{ + for (valueMap_t::iterator it = m_values.begin(); + it != m_values.end(); ++it) + { + int vindex = (*it).first; + ValueID vid = (*it).second; + string label = Manager::Get()->GetValueLabel(vid); + string valueAsStr; + Manager::Get()->GetValueAsString(vid, &valueAsStr); + string valueUnits = Manager::Get()->GetValueUnits(vid); + ValueID::ValueType vType = vid.GetType(); + string vTypeStr; + + switch (vType) + { + case ValueID::ValueType_Bool: + vTypeStr = "bool"; + break; + + case ValueID::ValueType_Byte: + vTypeStr = "byte"; + break; + + case ValueID::ValueType_Decimal: + vTypeStr = "float"; + break; + + case ValueID::ValueType_Int: + vTypeStr = "int32"; + break; + + case ValueID::ValueType_List: + vTypeStr = "list"; + break; + + case ValueID::ValueType_Schedule: + vTypeStr = "schedule"; + break; + + case ValueID::ValueType_Short: + vTypeStr = "int16"; + break; + + case ValueID::ValueType_String: + vTypeStr = "string"; + break; + + case ValueID::ValueType_Button: + vTypeStr = "button"; + break; + + case ValueID::ValueType_Raw: + vTypeStr = "raw"; + break; + + default: + vTypeStr = "undefined"; + break; + } + + // by default we only want user values, unless 'all' is true + if (all || (vid.GetGenre() == ValueID::ValueGenre_User)) + fprintf(stderr, "\t Index: %2d, Type: %s, Label: %s, Value: %s %s\n", + vindex, + vTypeStr.c_str(), + label.c_str(), + valueAsStr.c_str(), + valueUnits.c_str()); + } +} + + + diff --git a/src/ozw/zwNode.h b/src/ozw/zwNode.h new file mode 100644 index 00000000..0b01dc61 --- /dev/null +++ b/src/ozw/zwNode.h @@ -0,0 +1,105 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include + +#include "Manager.h" + +namespace upm { + + class zwNode { + public: + typedef std::map valueMap_t; + + /** + * zwNode contructor. + * + * @param homeId the homeId os the network controller + * @param nodeId the node index + */ + zwNode(uint32_t homeId, uint8_t nodeId); + + ~zwNode(); + + /** + * get the nodeId + * + * @return The nodeId for this node + */ + uint8_t nodeId(); + + /** + * get the homeId + * + * @return The homeId for this node + */ + uint32_t homeId(); + + /** + * Add an OpenZWave ValueID and index to the value map, + * incrementing m_vindex. + * + * @param vid The OpenZWave ValueID + */ + void addValueID(OpenZWave::ValueID vid); + + /** + * Remove an OpenZWave ValueID from the value map. + * + * @param vid The OpenZWave ValueID + */ + void removeValueID(OpenZWave::ValueID vid); + + /** + * Lookup and return a ValueID corresponding to an index. + * + * @param index the index to look up + * @param vid The pointer to the returned ValueID, if it exists + * @return true if the index was found, false otherwise + */ + bool indexToValueID(int index, OpenZWave::ValueID *vid); + + /** + * Dump various information about the ValueIDs stored in this + * node. + * + * @param all true to dump all values, false to limit dumping only + * 'user' values. + */ + void dumpNode(bool all=false); + + protected: + + private: + uint32_t m_homeId; + uint8_t m_nodeId; + + valueMap_t m_values; + + // we increment this index for every ValueID we add + unsigned int m_vindex; + }; + +}