From 60f5783a268127146dd83ff4b0843f448d18303b Mon Sep 17 00:00:00 2001 From: matst80 Date: Fri, 21 Nov 2025 18:12:55 +0100 Subject: [PATCH] random ai stuff --- Dockerfile | 34 + go.mod | 7 + go.sum | 4 + main.go | 89 + .../3rdparty/openbsd-getopt/CMakeLists.txt | 14 + telldus-core/3rdparty/openbsd-getopt/err.h | 9 + telldus-core/3rdparty/openbsd-getopt/getopt.h | 78 + .../3rdparty/openbsd-getopt/getopt_long.c | 511 +++ .../3rdparty/openbsd-getopt/machine/_types.h | 10 + .../3rdparty/openbsd-getopt/machine/cdefs.h | 12 + .../3rdparty/openbsd-getopt/machine/endian.h | 0 .../3rdparty/openbsd-getopt/sys/_types.h | 71 + .../3rdparty/openbsd-getopt/sys/cdefs.h | 388 ++ .../3rdparty/openbsd-getopt/sys/types.h | 243 ++ .../3rdparty/openbsd-getopt/sys/unistd.h | 159 + telldus-core/3rdparty/openbsd-getopt/unistd.h | 274 ++ telldus-core/AUTHORS | 7 + telldus-core/CMakeLists.txt | 75 + telldus-core/CMakeLists.txt.orig | 80 + telldus-core/INSTALL | 51 + telldus-core/LICENSE | 502 +++ telldus-core/README | 30 + telldus-core/client/CMakeLists.txt | 131 + telldus-core/client/CallbackDispatcher.cpp | 139 + telldus-core/client/CallbackDispatcher.h | 129 + .../client/CallbackMainDispatcher.cpp | 95 + telldus-core/client/CallbackMainDispatcher.h | 35 + telldus-core/client/Client.cpp | 269 ++ telldus-core/client/Client.h | 47 + telldus-core/client/libtelldus-core.def | 60 + telldus-core/client/telldus-core.cpp | 1296 +++++++ telldus-core/client/telldus-core.h | 166 + telldus-core/client/telldus-core.rc.in | 18 + telldus-core/cmake/FindSignTool.cmake | 19 + telldus-core/common/CMakeLists.txt | 89 + telldus-core/common/Event.cpp | 88 + telldus-core/common/Event.h | 80 + telldus-core/common/EventHandler.h | 39 + telldus-core/common/EventHandler_unix.cpp | 96 + telldus-core/common/EventHandler_win.cpp | 76 + telldus-core/common/Event_unix.cpp | 36 + telldus-core/common/Event_win.cpp | 40 + telldus-core/common/Message.cpp | 131 + telldus-core/common/Message.h | 36 + telldus-core/common/Mutex.cpp | 88 + telldus-core/common/Mutex.h | 45 + telldus-core/common/Socket.h | 39 + telldus-core/common/Socket_unix.cpp | 144 + telldus-core/common/Socket_win.cpp | 187 + telldus-core/common/Strings.cpp | 276 ++ telldus-core/common/Strings.h | 39 + telldus-core/common/Thread.cpp | 98 + telldus-core/common/Thread.h | 37 + telldus-core/common/common.h | 108 + telldus-core/common/stdlibc_workaround.cpp | 73 + telldus-core/debian/changelog | 5 + telldus-core/debian/compat | 1 + telldus-core/debian/control | 40 + telldus-core/debian/copyright | 44 + telldus-core/debian/libtelldus-dev.install | 2 + telldus-core/debian/libtelldus0.install | 1 + .../patches/01_add_missing_doxyfile.patch | 1740 +++++++++ .../02_fix_link_ordering_problem.patch | 46 + telldus-core/debian/patches/series | 2 + telldus-core/debian/rules | 15 + telldus-core/debian/source/format | 1 + telldus-core/debian/tdtool.docs | 1 + telldus-core/debian/tdtool.install | 5 + telldus-core/service/CMakeLists.txt | 278 ++ .../service/ClientCommunicationHandler.cpp | 266 ++ .../service/ClientCommunicationHandler.h | 41 + telldus-core/service/ConnectionListener.h | 37 + .../service/ConnectionListener_unix.cpp | 100 + .../service/ConnectionListener_win.cpp | 153 + telldus-core/service/Controller.cpp | 99 + telldus-core/service/Controller.h | 38 + telldus-core/service/ControllerListener.h | 32 + .../service/ControllerListener_mac.cpp | 187 + telldus-core/service/ControllerManager.cpp | 374 ++ telldus-core/service/ControllerManager.h | 40 + telldus-core/service/ControllerMessage.cpp | 95 + telldus-core/service/ControllerMessage.h | 36 + telldus-core/service/Device.cpp | 272 ++ telldus-core/service/Device.h | 51 + telldus-core/service/DeviceManager.cpp | 790 ++++ telldus-core/service/DeviceManager.h | 68 + telldus-core/service/EventUpdateManager.cpp | 259 ++ telldus-core/service/EventUpdateManager.h | 53 + telldus-core/service/Log.cpp | 183 + telldus-core/service/Log.h | 40 + telldus-core/service/Messages.mc | Bin 0 -> 1158 bytes telldus-core/service/Protocol.cpp | 267 ++ telldus-core/service/Protocol.h | 46 + telldus-core/service/ProtocolBrateck.cpp | 53 + telldus-core/service/ProtocolBrateck.h | 19 + telldus-core/service/ProtocolComen.cpp | 23 + telldus-core/service/ProtocolComen.h | 21 + telldus-core/service/ProtocolEverflourish.cpp | 133 + telldus-core/service/ProtocolEverflourish.h | 24 + telldus-core/service/ProtocolFineoffset.cpp | 52 + telldus-core/service/ProtocolFineoffset.h | 19 + telldus-core/service/ProtocolFuhaote.cpp | 60 + telldus-core/service/ProtocolFuhaote.h | 19 + telldus-core/service/ProtocolGroup.cpp | 16 + telldus-core/service/ProtocolGroup.h | 22 + telldus-core/service/ProtocolHasta.cpp | 201 + telldus-core/service/ProtocolHasta.h | 27 + telldus-core/service/ProtocolIkea.cpp | 151 + telldus-core/service/ProtocolIkea.h | 19 + telldus-core/service/ProtocolMandolyn.cpp | 46 + telldus-core/service/ProtocolMandolyn.h | 19 + telldus-core/service/ProtocolNexa.cpp | 279 ++ telldus-core/service/ProtocolNexa.h | 39 + telldus-core/service/ProtocolOregon.cpp | 347 ++ telldus-core/service/ProtocolOregon.h | 27 + telldus-core/service/ProtocolRisingSun.cpp | 115 + telldus-core/service/ProtocolRisingSun.h | 24 + telldus-core/service/ProtocolSartano.cpp | 108 + telldus-core/service/ProtocolSartano.h | 24 + telldus-core/service/ProtocolScene.cpp | 16 + telldus-core/service/ProtocolScene.h | 22 + telldus-core/service/ProtocolSilvanChip.cpp | 146 + telldus-core/service/ProtocolSilvanChip.h | 22 + telldus-core/service/ProtocolUpm.cpp | 78 + telldus-core/service/ProtocolUpm.h | 19 + telldus-core/service/ProtocolWaveman.cpp | 77 + telldus-core/service/ProtocolWaveman.h | 26 + telldus-core/service/ProtocolX10.cpp | 185 + telldus-core/service/ProtocolX10.h | 22 + telldus-core/service/ProtocolYidong.cpp | 31 + telldus-core/service/ProtocolYidong.h | 18 + telldus-core/service/Sensor.cpp | 73 + telldus-core/service/Sensor.h | 33 + telldus-core/service/Settings.cpp | 140 + telldus-core/service/Settings.h | 60 + telldus-core/service/SettingsConfuse.cpp | 451 +++ .../SettingsCoreFoundationPreferences.cpp | 310 ++ telldus-core/service/SettingsWinRegistry.cpp | 270 ++ telldus-core/service/TellStick.cpp | 79 + telldus-core/service/TellStick.h | 54 + telldus-core/service/TellStick_ftd2xx.cpp | 343 ++ telldus-core/service/TellStick_libftdi.cpp | 327 ++ telldus-core/service/TelldusMain.cpp | 154 + telldus-core/service/TelldusMain.h | 28 + .../service/TelldusWinService_win.cpp | 181 + telldus-core/service/TelldusWinService_win.h | 39 + telldus-core/service/Timer.cpp | 120 + telldus-core/service/Timer.h | 30 + .../service/com.telldus.service.plist | 14 + telldus-core/service/config.h.in | 11 + telldus-core/service/ftd2xx.h | 15 + telldus-core/service/main_mac.cpp | 34 + telldus-core/service/main_unix.cpp | 166 + telldus-core/service/main_win.cpp | 25 + telldus-core/service/osx/WinTypes.h | 87 + telldus-core/service/osx/ftd2xx.h | 1008 +++++ telldus-core/service/telldus-core.conf | 1 + telldus-core/service/tellstick.conf | 13 + telldus-core/service/win/ftd2xx.h | 963 +++++ telldus-core/tdadmin/05-tellstick.rules | 3 + telldus-core/tdadmin/CMakeLists.txt | 79 + telldus-core/tdadmin/main.cpp | 88 + telldus-core/tdadmin/udev.sh | 9 + telldus-core/tdtool/CMakeLists.txt | 56 + telldus-core/tdtool/main.cpp | 623 +++ telldus-core/tests/CMakeLists.txt | 51 + telldus-core/tests/common/CMakeLists.txt | 9 + telldus-core/tests/common/CommonTests.h | 11 + telldus-core/tests/common/StringsTest.cpp | 16 + telldus-core/tests/common/StringsTest.h | 21 + telldus-core/tests/cpplint.py | 3369 +++++++++++++++++ telldus-core/tests/cppunit.cpp | 46 + telldus-core/tests/service/CMakeLists.txt | 9 + .../service/ProtocolEverflourishTest.cpp | 30 + .../tests/service/ProtocolEverflourishTest.h | 25 + .../tests/service/ProtocolHastaTest.cpp | 40 + .../tests/service/ProtocolHastaTest.h | 25 + .../tests/service/ProtocolNexaTest.cpp | 40 + telldus-core/tests/service/ProtocolNexaTest.h | 25 + .../tests/service/ProtocolOregonTest.cpp | 67 + .../tests/service/ProtocolOregonTest.h | 25 + .../tests/service/ProtocolSartanoTest.cpp | 25 + .../tests/service/ProtocolSartanoTest.h | 25 + .../tests/service/ProtocolX10Test.cpp | 30 + telldus-core/tests/service/ProtocolX10Test.h | 25 + telldus-core/tests/service/ServiceTests.h | 21 + telldus/telldus.go | 221 ++ 187 files changed, 25197 insertions(+) create mode 100644 Dockerfile create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 telldus-core/3rdparty/openbsd-getopt/CMakeLists.txt create mode 100644 telldus-core/3rdparty/openbsd-getopt/err.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/getopt.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/getopt_long.c create mode 100644 telldus-core/3rdparty/openbsd-getopt/machine/_types.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/machine/cdefs.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/machine/endian.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/sys/_types.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/sys/cdefs.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/sys/types.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/sys/unistd.h create mode 100644 telldus-core/3rdparty/openbsd-getopt/unistd.h create mode 100644 telldus-core/AUTHORS create mode 100644 telldus-core/CMakeLists.txt create mode 100644 telldus-core/CMakeLists.txt.orig create mode 100644 telldus-core/INSTALL create mode 100644 telldus-core/LICENSE create mode 100644 telldus-core/README create mode 100644 telldus-core/client/CMakeLists.txt create mode 100644 telldus-core/client/CallbackDispatcher.cpp create mode 100644 telldus-core/client/CallbackDispatcher.h create mode 100644 telldus-core/client/CallbackMainDispatcher.cpp create mode 100644 telldus-core/client/CallbackMainDispatcher.h create mode 100644 telldus-core/client/Client.cpp create mode 100644 telldus-core/client/Client.h create mode 100644 telldus-core/client/libtelldus-core.def create mode 100644 telldus-core/client/telldus-core.cpp create mode 100644 telldus-core/client/telldus-core.h create mode 100644 telldus-core/client/telldus-core.rc.in create mode 100644 telldus-core/cmake/FindSignTool.cmake create mode 100644 telldus-core/common/CMakeLists.txt create mode 100644 telldus-core/common/Event.cpp create mode 100644 telldus-core/common/Event.h create mode 100644 telldus-core/common/EventHandler.h create mode 100644 telldus-core/common/EventHandler_unix.cpp create mode 100644 telldus-core/common/EventHandler_win.cpp create mode 100644 telldus-core/common/Event_unix.cpp create mode 100644 telldus-core/common/Event_win.cpp create mode 100644 telldus-core/common/Message.cpp create mode 100644 telldus-core/common/Message.h create mode 100644 telldus-core/common/Mutex.cpp create mode 100644 telldus-core/common/Mutex.h create mode 100644 telldus-core/common/Socket.h create mode 100644 telldus-core/common/Socket_unix.cpp create mode 100644 telldus-core/common/Socket_win.cpp create mode 100644 telldus-core/common/Strings.cpp create mode 100644 telldus-core/common/Strings.h create mode 100644 telldus-core/common/Thread.cpp create mode 100644 telldus-core/common/Thread.h create mode 100644 telldus-core/common/common.h create mode 100644 telldus-core/common/stdlibc_workaround.cpp create mode 100644 telldus-core/debian/changelog create mode 100644 telldus-core/debian/compat create mode 100644 telldus-core/debian/control create mode 100644 telldus-core/debian/copyright create mode 100644 telldus-core/debian/libtelldus-dev.install create mode 100644 telldus-core/debian/libtelldus0.install create mode 100644 telldus-core/debian/patches/01_add_missing_doxyfile.patch create mode 100644 telldus-core/debian/patches/02_fix_link_ordering_problem.patch create mode 100644 telldus-core/debian/patches/series create mode 100755 telldus-core/debian/rules create mode 100644 telldus-core/debian/source/format create mode 100644 telldus-core/debian/tdtool.docs create mode 100644 telldus-core/debian/tdtool.install create mode 100644 telldus-core/service/CMakeLists.txt create mode 100644 telldus-core/service/ClientCommunicationHandler.cpp create mode 100644 telldus-core/service/ClientCommunicationHandler.h create mode 100644 telldus-core/service/ConnectionListener.h create mode 100644 telldus-core/service/ConnectionListener_unix.cpp create mode 100644 telldus-core/service/ConnectionListener_win.cpp create mode 100644 telldus-core/service/Controller.cpp create mode 100644 telldus-core/service/Controller.h create mode 100644 telldus-core/service/ControllerListener.h create mode 100644 telldus-core/service/ControllerListener_mac.cpp create mode 100644 telldus-core/service/ControllerManager.cpp create mode 100644 telldus-core/service/ControllerManager.h create mode 100644 telldus-core/service/ControllerMessage.cpp create mode 100644 telldus-core/service/ControllerMessage.h create mode 100644 telldus-core/service/Device.cpp create mode 100644 telldus-core/service/Device.h create mode 100644 telldus-core/service/DeviceManager.cpp create mode 100644 telldus-core/service/DeviceManager.h create mode 100644 telldus-core/service/EventUpdateManager.cpp create mode 100644 telldus-core/service/EventUpdateManager.h create mode 100644 telldus-core/service/Log.cpp create mode 100644 telldus-core/service/Log.h create mode 100644 telldus-core/service/Messages.mc create mode 100644 telldus-core/service/Protocol.cpp create mode 100644 telldus-core/service/Protocol.h create mode 100644 telldus-core/service/ProtocolBrateck.cpp create mode 100644 telldus-core/service/ProtocolBrateck.h create mode 100644 telldus-core/service/ProtocolComen.cpp create mode 100644 telldus-core/service/ProtocolComen.h create mode 100644 telldus-core/service/ProtocolEverflourish.cpp create mode 100644 telldus-core/service/ProtocolEverflourish.h create mode 100644 telldus-core/service/ProtocolFineoffset.cpp create mode 100644 telldus-core/service/ProtocolFineoffset.h create mode 100644 telldus-core/service/ProtocolFuhaote.cpp create mode 100644 telldus-core/service/ProtocolFuhaote.h create mode 100644 telldus-core/service/ProtocolGroup.cpp create mode 100644 telldus-core/service/ProtocolGroup.h create mode 100644 telldus-core/service/ProtocolHasta.cpp create mode 100644 telldus-core/service/ProtocolHasta.h create mode 100644 telldus-core/service/ProtocolIkea.cpp create mode 100644 telldus-core/service/ProtocolIkea.h create mode 100644 telldus-core/service/ProtocolMandolyn.cpp create mode 100644 telldus-core/service/ProtocolMandolyn.h create mode 100644 telldus-core/service/ProtocolNexa.cpp create mode 100644 telldus-core/service/ProtocolNexa.h create mode 100644 telldus-core/service/ProtocolOregon.cpp create mode 100644 telldus-core/service/ProtocolOregon.h create mode 100644 telldus-core/service/ProtocolRisingSun.cpp create mode 100644 telldus-core/service/ProtocolRisingSun.h create mode 100644 telldus-core/service/ProtocolSartano.cpp create mode 100644 telldus-core/service/ProtocolSartano.h create mode 100644 telldus-core/service/ProtocolScene.cpp create mode 100644 telldus-core/service/ProtocolScene.h create mode 100644 telldus-core/service/ProtocolSilvanChip.cpp create mode 100644 telldus-core/service/ProtocolSilvanChip.h create mode 100644 telldus-core/service/ProtocolUpm.cpp create mode 100644 telldus-core/service/ProtocolUpm.h create mode 100644 telldus-core/service/ProtocolWaveman.cpp create mode 100644 telldus-core/service/ProtocolWaveman.h create mode 100644 telldus-core/service/ProtocolX10.cpp create mode 100644 telldus-core/service/ProtocolX10.h create mode 100644 telldus-core/service/ProtocolYidong.cpp create mode 100644 telldus-core/service/ProtocolYidong.h create mode 100644 telldus-core/service/Sensor.cpp create mode 100644 telldus-core/service/Sensor.h create mode 100644 telldus-core/service/Settings.cpp create mode 100644 telldus-core/service/Settings.h create mode 100644 telldus-core/service/SettingsConfuse.cpp create mode 100644 telldus-core/service/SettingsCoreFoundationPreferences.cpp create mode 100644 telldus-core/service/SettingsWinRegistry.cpp create mode 100644 telldus-core/service/TellStick.cpp create mode 100644 telldus-core/service/TellStick.h create mode 100644 telldus-core/service/TellStick_ftd2xx.cpp create mode 100644 telldus-core/service/TellStick_libftdi.cpp create mode 100644 telldus-core/service/TelldusMain.cpp create mode 100644 telldus-core/service/TelldusMain.h create mode 100644 telldus-core/service/TelldusWinService_win.cpp create mode 100644 telldus-core/service/TelldusWinService_win.h create mode 100644 telldus-core/service/Timer.cpp create mode 100644 telldus-core/service/Timer.h create mode 100644 telldus-core/service/com.telldus.service.plist create mode 100644 telldus-core/service/config.h.in create mode 100644 telldus-core/service/ftd2xx.h create mode 100644 telldus-core/service/main_mac.cpp create mode 100644 telldus-core/service/main_unix.cpp create mode 100644 telldus-core/service/main_win.cpp create mode 100755 telldus-core/service/osx/WinTypes.h create mode 100755 telldus-core/service/osx/ftd2xx.h create mode 100644 telldus-core/service/telldus-core.conf create mode 100644 telldus-core/service/tellstick.conf create mode 100644 telldus-core/service/win/ftd2xx.h create mode 100644 telldus-core/tdadmin/05-tellstick.rules create mode 100644 telldus-core/tdadmin/CMakeLists.txt create mode 100644 telldus-core/tdadmin/main.cpp create mode 100755 telldus-core/tdadmin/udev.sh create mode 100644 telldus-core/tdtool/CMakeLists.txt create mode 100644 telldus-core/tdtool/main.cpp create mode 100644 telldus-core/tests/CMakeLists.txt create mode 100644 telldus-core/tests/common/CMakeLists.txt create mode 100644 telldus-core/tests/common/CommonTests.h create mode 100644 telldus-core/tests/common/StringsTest.cpp create mode 100644 telldus-core/tests/common/StringsTest.h create mode 100755 telldus-core/tests/cpplint.py create mode 100644 telldus-core/tests/cppunit.cpp create mode 100644 telldus-core/tests/service/CMakeLists.txt create mode 100644 telldus-core/tests/service/ProtocolEverflourishTest.cpp create mode 100644 telldus-core/tests/service/ProtocolEverflourishTest.h create mode 100644 telldus-core/tests/service/ProtocolHastaTest.cpp create mode 100644 telldus-core/tests/service/ProtocolHastaTest.h create mode 100644 telldus-core/tests/service/ProtocolNexaTest.cpp create mode 100644 telldus-core/tests/service/ProtocolNexaTest.h create mode 100644 telldus-core/tests/service/ProtocolOregonTest.cpp create mode 100644 telldus-core/tests/service/ProtocolOregonTest.h create mode 100644 telldus-core/tests/service/ProtocolSartanoTest.cpp create mode 100644 telldus-core/tests/service/ProtocolSartanoTest.h create mode 100644 telldus-core/tests/service/ProtocolX10Test.cpp create mode 100644 telldus-core/tests/service/ProtocolX10Test.h create mode 100644 telldus-core/tests/service/ServiceTests.h create mode 100644 telldus/telldus.go diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c8c962c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM golang:1.25.4-bookworm + +# Install dependencies for telldus-core +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + libftdi-dev \ + libconfuse-dev \ + wget \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +COPY ./telldus-core /usr/src/telldus-core +WORKDIR /usr/src/telldus-core +RUN cmake . \ + && make \ + && make install \ + && ldconfig + +# Set workdir for the project +WORKDIR /go/src/app + +# Copy go mod and download dependencies +COPY go.mod go.sum ./ +RUN go mod download + +# Copy source code +COPY . . + +# Build the application +RUN go build -o main . + +# Run the application +CMD ["./main"] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..ff51d96 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module git.k6n.net/mats/go-telldus-matter + +go 1.25.4 + +require github.com/tom-code/gomat v0.0.0-20231210134906-5413b3e13054 + +require golang.org/x/crypto v0.13.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6c585af --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/tom-code/gomat v0.0.0-20231210134906-5413b3e13054 h1:+MMrzc6oLOvnCmG60XkWTUwUejzIfZvfNEzBOth/adc= +github.com/tom-code/gomat v0.0.0-20231210134906-5413b3e13054/go.mod h1:emj5ejTh9ctVZmkrAMGEuh7WFwSpOHCGNNGpl8nfWZE= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5141d02 --- /dev/null +++ b/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "fmt" + "log" + "net" + + "git.k6n.net/mats/go-telldus-matter/telldus" + "github.com/tom-code/gomat" +) + +const ( + fabricID = 0x100 + adminUser = 5 + deviceID = 10 + matterIP = "192.168.1.100" // Replace with actual Matter device IP + matterPort = 5540 +) + +var fabric *gomat.Fabric + +func main() { + // Initialize Telldus + telldus.Init() + defer telldus.Close() + + // Register callbacks + telldus.RegisterDeviceEvent(deviceEventHandler) + telldus.RegisterSensorEvent(sensorEventHandler) + + // Initialize Matter certificate manager + cm := gomat.NewFileCertManager(fabricID) + cm.Load() + + fabric = gomat.NewFabric(fabricID, cm) + + // Keep the program running + fmt.Println("Telldus-Matter bridge running. Press Ctrl+C to exit.") + select {} +} + +func deviceEventHandler(deviceId, method int, data string, callbackId int) { + fmt.Printf("Device event: ID=%d, Method=%d, Data=%s\n", deviceId, method, data) + + // Example: If a Telldus device is turned on, turn on a Matter device + if method == telldus.MethodTurnOn { + sendMatterCommand("on") + } +} + +func sensorEventHandler(protocol, model string, id, dataType int, value string, timestamp, callbackId int) { + fmt.Printf("Sensor event: Protocol=%s, Model=%s, ID=%d, Type=%d, Value=%s, Timestamp=%d\n", + protocol, model, id, dataType, value, timestamp) + + // Example: Log sensor data, or react to temperature, etc. +} + +func sendMatterCommand(command string) { + secureChannel, err := gomat.StartSecureChannel(net.ParseIP(matterIP), matterPort, 55555) + if err != nil { + log.Printf("Failed to start secure channel: %v", err) + return + } + defer secureChannel.Close() + + secureChannel, err = gomat.SigmaExchange(fabric, adminUser, deviceID, secureChannel) + if err != nil { + log.Printf("Failed to exchange sigma: %v", err) + return + } + + var invokeCommand []byte + switch command { + case "on": + // invokeCommand = gomat.EncodeInvokeCommand(1, 6, 1, []byte{}) + invokeCommand = []byte{} // Placeholder + case "off": + // invokeCommand = gomat.EncodeInvokeCommand(1, 6, 0, []byte{}) + invokeCommand = []byte{} // Placeholder + } + + secureChannel.Send(invokeCommand) + resp, err := secureChannel.Receive() + if err != nil { + log.Printf("Failed to receive response: %v", err) + return + } + fmt.Printf("Matter command response: %v\n", resp) +} diff --git a/telldus-core/3rdparty/openbsd-getopt/CMakeLists.txt b/telldus-core/3rdparty/openbsd-getopt/CMakeLists.txt new file mode 100644 index 0000000..89080c4 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/CMakeLists.txt @@ -0,0 +1,14 @@ +SET( SRCS + getopt_long.c +) + +SET( HDRS + getopt.h +) + +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ) + +ADD_LIBRARY(openbsd-getopt STATIC + ${SRCS} + ${HDRS} +) diff --git a/telldus-core/3rdparty/openbsd-getopt/err.h b/telldus-core/3rdparty/openbsd-getopt/err.h new file mode 100644 index 0000000..b7d57bb --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/err.h @@ -0,0 +1,9 @@ +#include +#include + +__inline void warnx(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} diff --git a/telldus-core/3rdparty/openbsd-getopt/getopt.h b/telldus-core/3rdparty/openbsd-getopt/getopt.h new file mode 100644 index 0000000..0311b07 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/getopt.h @@ -0,0 +1,78 @@ +/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#include + +/* + * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +__BEGIN_DECLS +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED_ +#define _GETOPT_DEFINED_ +int getopt(int, char * const *, const char *); +int getsubopt(char **, char * const *, char **); + +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif +__END_DECLS + +#endif /* !_GETOPT_H_ */ diff --git a/telldus-core/3rdparty/openbsd-getopt/getopt_long.c b/telldus-core/3rdparty/openbsd-getopt/getopt_long.c new file mode 100644 index 0000000..e149fe0 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/getopt_long.c @@ -0,0 +1,511 @@ +/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */ +/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ + +/* + * Copyright (c) 2002 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +int opterr = 1; /* if error message should be printed */ +int optind = 1; /* index into parent argv vector */ +int optopt = '?'; /* character checked for validity */ +int optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define PRINT_ERROR ((opterr) && (*options != ':')) + +#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ +#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ +#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ + +/* return values */ +#define BADCH (int)'?' +#define BADARG ((*options == ':') ? (int)':' : (int)'?') +#define INORDER (int)1 + +#define EMSG "" + +static int getopt_internal(int, char * const *, const char *, + const struct option *, int *, int); +static int parse_long_options(char * const *, const char *, + const struct option *, int *, int); +static int gcd(int, int); +static void permute_args(int, int, int, char * const *); + +static char *place = EMSG; /* option letter processing */ + +/* XXX: set optreset to 1 rather than these two */ +static int nonopt_start = -1; /* first non option argument (for permute) */ +static int nonopt_end = -1; /* first option after non options (for permute) */ + +/* Error messages */ +static const char recargchar[] = "option requires an argument -- %c"; +static const char recargstring[] = "option requires an argument -- %s"; +static const char ambig[] = "ambiguous option -- %.*s"; +static const char noarg[] = "option doesn't take an argument -- %.*s"; +static const char illoptchar[] = "unknown option -- %c"; +static const char illoptstring[] = "unknown option -- %s"; + +/* + * Compute the greatest common divisor of a and b. + */ +static int +gcd(int a, int b) +{ + int c; + + c = a % b; + while (c != 0) { + a = b; + b = c; + c = a % b; + } + + return (b); +} + +/* + * Exchange the block from nonopt_start to nonopt_end with the block + * from nonopt_end to opt_end (keeping the same order of arguments + * in each block). + */ +static void +permute_args(int panonopt_start, int panonopt_end, int opt_end, + char * const *nargv) +{ + int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; + char *swap; + + /* + * compute lengths of blocks and number and size of cycles + */ + nnonopts = panonopt_end - panonopt_start; + nopts = opt_end - panonopt_end; + ncycle = gcd(nnonopts, nopts); + cyclelen = (opt_end - panonopt_start) / ncycle; + + for (i = 0; i < ncycle; i++) { + cstart = panonopt_end+i; + pos = cstart; + for (j = 0; j < cyclelen; j++) { + if (pos >= panonopt_end) + pos -= nnonopts; + else + pos += nopts; + swap = nargv[pos]; + /* LINTED const cast */ + ((char **) nargv)[pos] = nargv[cstart]; + /* LINTED const cast */ + ((char **)nargv)[cstart] = swap; + } + } +} + +/* + * parse_long_options -- + * Parse long options in argc/argv argument vector. + * Returns -1 if short_too is set and the option does not match long_options. + */ +static int +parse_long_options(char * const *nargv, const char *options, + const struct option *long_options, int *idx, int short_too) +{ + char *current_argv, *has_equal; + size_t current_argv_len; + int i, match; + + current_argv = place; + match = -1; + + optind++; + + if ((has_equal = strchr(current_argv, '=')) != NULL) { + /* argument found (--option=arg) */ + current_argv_len = has_equal - current_argv; + has_equal++; + } else + current_argv_len = strlen(current_argv); + + for (i = 0; long_options[i].name; i++) { + /* find matching long option */ + if (strncmp(current_argv, long_options[i].name, + current_argv_len)) + continue; + + if (strlen(long_options[i].name) == current_argv_len) { + /* exact match */ + match = i; + break; + } + /* + * If this is a known short option, don't allow + * a partial match of a single character. + */ + if (short_too && current_argv_len == 1) + continue; + + if (match == -1) /* partial match */ + match = i; + else { + /* ambiguous abbreviation */ + if (PRINT_ERROR) + warnx(ambig, (int)current_argv_len, + current_argv); + optopt = 0; + return (BADCH); + } + } + if (match != -1) { /* option found */ + if (long_options[match].has_arg == no_argument + && has_equal) { + if (PRINT_ERROR) + warnx(noarg, (int)current_argv_len, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + return (BADARG); + } + if (long_options[match].has_arg == required_argument || + long_options[match].has_arg == optional_argument) { + if (has_equal) + optarg = has_equal; + else if (long_options[match].has_arg == + required_argument) { + /* + * optional argument doesn't use next nargv + */ + optarg = nargv[optind++]; + } + } + if ((long_options[match].has_arg == required_argument) + && (optarg == NULL)) { + /* + * Missing argument; leading ':' indicates no error + * should be generated. + */ + if (PRINT_ERROR) + warnx(recargstring, + current_argv); + /* + * XXX: GNU sets optopt to val regardless of flag + */ + if (long_options[match].flag == NULL) + optopt = long_options[match].val; + else + optopt = 0; + --optind; + return (BADARG); + } + } else { /* unknown option */ + if (short_too) { + --optind; + return (-1); + } + if (PRINT_ERROR) + warnx(illoptstring, current_argv); + optopt = 0; + return (BADCH); + } + if (idx) + *idx = match; + if (long_options[match].flag) { + *long_options[match].flag = long_options[match].val; + return (0); + } else + return (long_options[match].val); +} + +/* + * getopt_internal -- + * Parse argc/argv argument vector. Called by user level routines. + */ +static int +getopt_internal(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx, int flags) +{ + char *oli; /* option letter list index */ + int optchar, short_too; + static int posixly_correct = -1; + + if (options == NULL) + return (-1); + + /* + * XXX Some GNU programs (like cvs) set optind to 0 instead of + * XXX using optreset. Work around this braindamage. + */ + if (optind == 0) + optind = optreset = 1; + + /* + * Disable GNU extensions if POSIXLY_CORRECT is set or options + * string begins with a '+'. + */ + if (posixly_correct == -1 || optreset) + posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); + if (*options == '-') + flags |= FLAG_ALLARGS; + else if (posixly_correct || *options == '+') + flags &= ~FLAG_PERMUTE; + if (*options == '+' || *options == '-') + options++; + + optarg = NULL; + if (optreset) + nonopt_start = nonopt_end = -1; +start: + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc) { /* end of argument vector */ + place = EMSG; + if (nonopt_end != -1) { + /* do permutation, if we have to */ + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + else if (nonopt_start != -1) { + /* + * If we skipped non-options, set optind + * to the first of them. + */ + optind = nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + if (*(place = nargv[optind]) != '-' || + (place[1] == '\0' && strchr(options, '-') == NULL)) { + place = EMSG; /* found non-option */ + if (flags & FLAG_ALLARGS) { + /* + * GNU extension: + * return non-option as argument to option 1 + */ + optarg = nargv[optind++]; + return (INORDER); + } + if (!(flags & FLAG_PERMUTE)) { + /* + * If no permutation wanted, stop parsing + * at first non-option. + */ + return (-1); + } + /* do permutation */ + if (nonopt_start == -1) + nonopt_start = optind; + else if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + nonopt_start = optind - + (nonopt_end - nonopt_start); + nonopt_end = -1; + } + optind++; + /* process next argument */ + goto start; + } + if (nonopt_start != -1 && nonopt_end == -1) + nonopt_end = optind; + + /* + * If we have "-" do nothing, if "--" we are done. + */ + if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { + optind++; + place = EMSG; + /* + * We found an option (--), so if we skipped + * non-options, we have to permute. + */ + if (nonopt_end != -1) { + permute_args(nonopt_start, nonopt_end, + optind, nargv); + optind -= nonopt_end - nonopt_start; + } + nonopt_start = nonopt_end = -1; + return (-1); + } + } + + /* + * Check long options if: + * 1) we were passed some + * 2) the arg is not just "-" + * 3) either the arg starts with -- we are getopt_long_only() + */ + if (long_options != NULL && place != nargv[optind] && + (*place == '-' || (flags & FLAG_LONGONLY))) { + short_too = 0; + if (*place == '-') + place++; /* --foo long option */ + else if (*place != ':' && strchr(options, *place) != NULL) + short_too = 1; /* could be short option too */ + + optchar = parse_long_options(nargv, options, long_options, + idx, short_too); + if (optchar != -1) { + place = EMSG; + return (optchar); + } + } + + if ((optchar = (int)*place++) == (int)':' || + (optchar == (int)'-' && *place != '\0') || + (oli = strchr(options, optchar)) == NULL) { + /* + * If the user specified "-" and '-' isn't listed in + * options, return -1 (non-option) as per POSIX. + * Otherwise, it is an unknown option character (or ':'). + */ + if (optchar == (int)'-' && *place == '\0') + return (-1); + if (!*place) + ++optind; + if (PRINT_ERROR) + warnx(illoptchar, optchar); + optopt = optchar; + return (BADCH); + } + if (long_options != NULL && optchar == 'W' && oli[1] == ';') { + /* -W long-option */ + if (*place) /* no space */ + /* NOTHING */; + else if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else /* white space */ + place = nargv[optind]; + optchar = parse_long_options(nargv, options, long_options, + idx, 0); + place = EMSG; + return (optchar); + } + if (*++oli != ':') { /* doesn't take argument */ + if (!*place) + ++optind; + } else { /* takes (optional) argument */ + optarg = NULL; + if (*place) /* no white space */ + optarg = place; + else if (oli[1] != ':') { /* arg not optional */ + if (++optind >= nargc) { /* no arg */ + place = EMSG; + if (PRINT_ERROR) + warnx(recargchar, optchar); + optopt = optchar; + return (BADARG); + } else + optarg = nargv[optind]; + } + place = EMSG; + ++optind; + } + /* dump back option letter */ + return (optchar); +} + +/* + * getopt -- + * Parse argc/argv argument vector. + * + * [eventually this will replace the BSD getopt] + */ +int +getopt(int nargc, char * const *nargv, const char *options) +{ + + /* + * We don't pass FLAG_PERMUTE to getopt_internal() since + * the BSD getopt(3) (unlike GNU) has never done this. + * + * Furthermore, since many privileged programs call getopt() + * before dropping privileges it makes sense to keep things + * as simple (and bug-free) as possible. + */ + return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); +} + +/* + * getopt_long -- + * Parse argc/argv argument vector. + */ +int +getopt_long(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE)); +} + +/* + * getopt_long_only -- + * Parse argc/argv argument vector. + */ +int +getopt_long_only(int nargc, char * const *nargv, const char *options, + const struct option *long_options, int *idx) +{ + + return (getopt_internal(nargc, nargv, options, long_options, idx, + FLAG_PERMUTE|FLAG_LONGONLY)); +} diff --git a/telldus-core/3rdparty/openbsd-getopt/machine/_types.h b/telldus-core/3rdparty/openbsd-getopt/machine/_types.h new file mode 100644 index 0000000..3799f02 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/machine/_types.h @@ -0,0 +1,10 @@ +#include + +typedef __int8 __int8_t; +typedef unsigned __int8 __uint8_t; +typedef __int16 __int16_t; +typedef unsigned __int16 __uint16_t; +typedef __int32 __int32_t; +typedef unsigned __int32 __uint32_t; +typedef __int64 __int64_t; +typedef unsigned __int64 __uint64_t; diff --git a/telldus-core/3rdparty/openbsd-getopt/machine/cdefs.h b/telldus-core/3rdparty/openbsd-getopt/machine/cdefs.h new file mode 100644 index 0000000..7294625 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/machine/cdefs.h @@ -0,0 +1,12 @@ +#include + +#define _ANSI_SOURCE 1 +#define NO_ANSI_KEYWORDS 1 + +#define _CLOCK_T_DEFINED_ +#define _CLOCKID_T_DEFINED_ +#define _SIZE_T_DEFINED_ +#define _SSIZE_T_DEFINED_ +#define _TIME_T_DEFINED_ +#define _TIMER_T_DEFINED_ +#define _OFF_T_DEFINED_ diff --git a/telldus-core/3rdparty/openbsd-getopt/machine/endian.h b/telldus-core/3rdparty/openbsd-getopt/machine/endian.h new file mode 100644 index 0000000..e69de29 diff --git a/telldus-core/3rdparty/openbsd-getopt/sys/_types.h b/telldus-core/3rdparty/openbsd-getopt/sys/_types.h new file mode 100644 index 0000000..f31ae95 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/sys/_types.h @@ -0,0 +1,71 @@ +/* $OpenBSD: _types.h,v 1.2 2008/03/16 19:42:57 otto Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)types.h 8.3 (Berkeley) 1/5/94 + */ + +#ifndef _SYS__TYPES_H_ +#define _SYS__TYPES_H_ + +#include + +typedef unsigned long __cpuid_t; /* CPU id */ +typedef __int32_t __dev_t; /* device number */ +typedef __uint32_t __fixpt_t; /* fixed point number */ +typedef __uint32_t __gid_t; /* group id */ +typedef __uint32_t __id_t; /* may contain pid, uid or gid */ +typedef __uint32_t __in_addr_t; /* base type for internet address */ +typedef __uint16_t __in_port_t; /* IP port type */ +typedef __uint32_t __ino_t; /* inode number */ +typedef long __key_t; /* IPC key (for Sys V IPC) */ +typedef __uint32_t __mode_t; /* permissions */ +typedef __uint32_t __nlink_t; /* link count */ +typedef __int32_t __pid_t; /* process id */ +typedef __uint64_t __rlim_t; /* resource limit */ +typedef __uint8_t __sa_family_t; /* sockaddr address family type */ +typedef __int32_t __segsz_t; /* segment size */ +typedef __uint32_t __socklen_t; /* length type for network syscalls */ +typedef __int32_t __swblk_t; /* swap offset */ +typedef __uint32_t __uid_t; /* user id */ +typedef __uint32_t __useconds_t; /* microseconds */ +typedef __int32_t __suseconds_t; /* microseconds (signed) */ +typedef __uint64_t __fsblkcnt_t; /* file system block count */ +typedef __uint64_t __fsfilcnt_t; /* file system file count */ + +/* + * mbstate_t is an opaque object to keep conversion state, during multibyte + * stream conversions. The content must not be referenced by user programs. + */ +typedef union { + char __mbstate8[128]; + __int64_t __mbstateL; /* for alignment */ +} __mbstate_t; + +#endif /* !_SYS__TYPES_H_ */ diff --git a/telldus-core/3rdparty/openbsd-getopt/sys/cdefs.h b/telldus-core/3rdparty/openbsd-getopt/sys/cdefs.h new file mode 100644 index 0000000..65c06d8 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/sys/cdefs.h @@ -0,0 +1,388 @@ +/* $OpenBSD: cdefs.h,v 1.32 2012/01/03 16:56:58 kettenis Exp $ */ +/* $NetBSD: cdefs.h,v 1.16 1996/04/03 20:46:39 christos Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Berkeley Software Design, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cdefs.h 8.7 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_CDEFS_H_ +#define _SYS_CDEFS_H_ + +#include + +#if defined(__cplusplus) +#define __BEGIN_DECLS extern "C" { +#define __END_DECLS } +#else +#define __BEGIN_DECLS +#define __END_DECLS +#endif + +/* + * Macro to test if we're using a specific version of gcc or later. + */ +#ifdef __GNUC__ +#define __GNUC_PREREQ__(ma, mi) \ + ((__GNUC__ > (ma)) || (__GNUC__ == (ma) && __GNUC_MINOR__ >= (mi))) +#else +#define __GNUC_PREREQ__(ma, mi) 0 +#endif + +/* + * The __CONCAT macro is used to concatenate parts of symbol names, e.g. + * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. + * The __CONCAT macro is a bit tricky -- make sure you don't put spaces + * in between its arguments. __CONCAT can also concatenate double-quoted + * strings produced by the __STRING macro, but this only works with ANSI C. + */ +#if defined(__STDC__) || defined(__cplusplus) +#define __P(protos) protos /* full-blown ANSI C */ +#define __CONCAT(x,y) x ## y +#define __STRING(x) #x + +#define __const const /* define reserved names to standard */ +#define __signed signed +#define __volatile volatile +#if defined(__cplusplus) || defined(__PCC__) +#define __inline inline /* convert to C++ keyword */ +#else +#if !defined(__GNUC__) && !defined(lint) +#define __inline /* delete GCC keyword */ +#endif /* !__GNUC__ && !lint */ +#endif /* !__cplusplus */ + +#else /* !(__STDC__ || __cplusplus) */ +#define __P(protos) () /* traditional C preprocessor */ +#define __CONCAT(x,y) x/**/y +#define __STRING(x) "x" + +#if defined(_MSC_VER) +#include +#elif !defined(__GNUC__) && !defined(lint) +#define __const /* delete pseudo-ANSI C keywords */ +#define __inline +#define __signed +#define __volatile +#endif /* !__GNUC__ && !lint */ + +/* + * In non-ANSI C environments, new programs will want ANSI-only C keywords + * deleted from the program and old programs will want them left alone. + * Programs using the ANSI C keywords const, inline etc. as normal + * identifiers should define -DNO_ANSI_KEYWORDS. + */ +#ifndef NO_ANSI_KEYWORDS +#define const __const /* convert ANSI C keywords */ +#define inline __inline +#define signed __signed +#define volatile __volatile +#endif /* !NO_ANSI_KEYWORDS */ +#endif /* !(__STDC__ || __cplusplus) */ + +/* + * GCC1 and some versions of GCC2 declare dead (non-returning) and + * pure (no side effects) functions using "volatile" and "const"; + * unfortunately, these then cause warnings under "-ansi -pedantic". + * GCC >= 2.5 uses the __attribute__((attrs)) style. All of these + * work for GNU C++ (modulo a slight glitch in the C++ grammar in + * the distribution version of 2.5.5). + */ + +#if !__GNUC_PREREQ__(2, 5) && !defined(__PCC__) +#define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */ +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define __dead __volatile +#define __pure __const +#elif defined(lint) +#define __dead /* NORETURN */ +#endif +#elif !defined(__STRICT_ANSI__) +#define __dead __attribute__((__noreturn__)) +#define __pure __attribute__((__const__)) +#endif + +#if __GNUC_PREREQ__(2, 7) +#define __unused __attribute__((__unused__)) +#else +#define __unused /* delete */ +#endif + +#if __GNUC_PREREQ__(3, 1) +#define __used __attribute__((__used__)) +#else +#define __used __unused /* suppress -Wunused warnings */ +#endif + +/* + * __returns_twice makes the compiler not assume the function + * only returns once. This affects registerisation of variables: + * even local variables need to be in memory across such a call. + * Example: setjmp() + */ +#if __GNUC_PREREQ__(4, 1) +#define __returns_twice __attribute__((returns_twice)) +#else +#define __returns_twice +#endif + +/* + * __only_inline makes the compiler only use this function definition + * for inlining; references that can't be inlined will be left as + * external references instead of generating a local copy. The + * matching library should include a simple extern definition for + * the function to handle those references. c.f. ctype.h + */ +#ifdef __GNUC__ +# if __GNUC_PREREQ__(4, 2) +#define __only_inline extern __inline __attribute__((__gnu_inline__)) +# else +#define __only_inline extern __inline +# endif +#else +#define __only_inline static __inline +#endif + +/* + * GNU C version 2.96 adds explicit branch prediction so that + * the CPU back-end can hint the processor and also so that + * code blocks can be reordered such that the predicted path + * sees a more linear flow, thus improving cache behavior, etc. + * + * The following two macros provide us with a way to utilize this + * compiler feature. Use __predict_true() if you expect the expression + * to evaluate to true, and __predict_false() if you expect the + * expression to evaluate to false. + * + * A few notes about usage: + * + * * Generally, __predict_false() error condition checks (unless + * you have some _strong_ reason to do otherwise, in which case + * document it), and/or __predict_true() `no-error' condition + * checks, assuming you want to optimize for the no-error case. + * + * * Other than that, if you don't know the likelihood of a test + * succeeding from empirical or other `hard' evidence, don't + * make predictions. + * + * * These are meant to be used in places that are run `a lot'. + * It is wasteful to make predictions in code that is run + * seldomly (e.g. at subsystem initialization time) as the + * basic block reordering that this affects can often generate + * larger code. + */ +#if __GNUC_PREREQ__(2, 96) +#define __predict_true(exp) __builtin_expect(((exp) != 0), 1) +#define __predict_false(exp) __builtin_expect(((exp) != 0), 0) +#else +#define __predict_true(exp) ((exp) != 0) +#define __predict_false(exp) ((exp) != 0) +#endif + +/* Delete pseudo-keywords wherever they are not available or needed. */ +#ifndef __dead +#define __dead +#define __pure +#endif + +#if __GNUC_PREREQ__(2, 7) || defined(__PCC__) +#define __packed __attribute__((__packed__)) +#elif defined(lint) +#define __packed +#endif + +#if !__GNUC_PREREQ__(2, 8) +#define __extension__ +#endif + +#if __GNUC_PREREQ__(2, 8) || defined(__PCC__) +#define __statement(x) __extension__(x) +#elif defined(lint) +#define __statement(x) (0) +#else +#define __statement(x) (x) +#endif + +#if __GNUC_PREREQ__(3, 0) +#define __malloc __attribute__((__malloc__)) +#else +#define __malloc +#endif + +/* + * "The nice thing about standards is that there are so many to choose from." + * There are a number of "feature test macros" specified by (different) + * standards that determine which interfaces and types the header files + * should expose. + * + * Because of inconsistencies in these macros, we define our own + * set in the private name space that end in _VISIBLE. These are + * always defined and so headers can test their values easily. + * Things can get tricky when multiple feature macros are defined. + * We try to take the union of all the features requested. + * + * The following macros are guaranteed to have a value after cdefs.h + * has been included: + * __POSIX_VISIBLE + * __XPG_VISIBLE + * __ISO_C_VISIBLE + * __BSD_VISIBLE + */ + +/* + * X/Open Portability Guides and Single Unix Specifications. + * _XOPEN_SOURCE XPG3 + * _XOPEN_SOURCE && _XOPEN_VERSION = 4 XPG4 + * _XOPEN_SOURCE && _XOPEN_SOURCE_EXTENDED = 1 XPG4v2 + * _XOPEN_SOURCE == 500 XPG5 + * _XOPEN_SOURCE == 520 XPG5v2 + * _XOPEN_SOURCE == 600 POSIX 1003.1-2001 with XSI + * _XOPEN_SOURCE == 700 POSIX 1003.1-2008 with XSI + * + * The XPG spec implies a specific value for _POSIX_C_SOURCE. + */ +#ifdef _XOPEN_SOURCE +# if (_XOPEN_SOURCE - 0 >= 700) +# define __XPG_VISIBLE 700 +# undef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200809L +# elif (_XOPEN_SOURCE - 0 >= 600) +# define __XPG_VISIBLE 600 +# undef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 200112L +# elif (_XOPEN_SOURCE - 0 >= 520) +# define __XPG_VISIBLE 520 +# undef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 199506L +# elif (_XOPEN_SOURCE - 0 >= 500) +# define __XPG_VISIBLE 500 +# undef _POSIX_C_SOURCE +# define _POSIX_C_SOURCE 199506L +# elif (_XOPEN_SOURCE_EXTENDED - 0 == 1) +# define __XPG_VISIBLE 420 +# elif (_XOPEN_VERSION - 0 >= 4) +# define __XPG_VISIBLE 400 +# else +# define __XPG_VISIBLE 300 +# endif +#endif + +/* + * POSIX macros, these checks must follow the XOPEN ones above. + * + * _POSIX_SOURCE == 1 1003.1-1988 (superseded by _POSIX_C_SOURCE) + * _POSIX_C_SOURCE == 1 1003.1-1990 + * _POSIX_C_SOURCE == 2 1003.2-1992 + * _POSIX_C_SOURCE == 199309L 1003.1b-1993 + * _POSIX_C_SOURCE == 199506L 1003.1c-1995, 1003.1i-1995, + * and the omnibus ISO/IEC 9945-1:1996 + * _POSIX_C_SOURCE == 200112L 1003.1-2001 + * _POSIX_C_SOURCE == 200809L 1003.1-2008 + * + * The POSIX spec implies a specific value for __ISO_C_VISIBLE, though + * this may be overridden by the _ISOC99_SOURCE macro later. + */ +#ifdef _POSIX_C_SOURCE +# if (_POSIX_C_SOURCE - 0 >= 200809) +# define __POSIX_VISIBLE 200809 +# define __ISO_C_VISIBLE 1999 +# elif (_POSIX_C_SOURCE - 0 >= 200112) +# define __POSIX_VISIBLE 200112 +# define __ISO_C_VISIBLE 1999 +# elif (_POSIX_C_SOURCE - 0 >= 199506) +# define __POSIX_VISIBLE 199506 +# define __ISO_C_VISIBLE 1990 +# elif (_POSIX_C_SOURCE - 0 >= 199309) +# define __POSIX_VISIBLE 199309 +# define __ISO_C_VISIBLE 1990 +# elif (_POSIX_C_SOURCE - 0 >= 2) +# define __POSIX_VISIBLE 199209 +# define __ISO_C_VISIBLE 1990 +# else +# define __POSIX_VISIBLE 199009 +# define __ISO_C_VISIBLE 1990 +# endif +#elif defined(_POSIX_SOURCE) +# define __POSIX_VISIBLE 198808 +# define __ISO_C_VISIBLE 0 +#endif + +/* + * _ANSI_SOURCE means to expose ANSI C89 interfaces only. + * If the user defines it in addition to one of the POSIX or XOPEN + * macros, assume the POSIX/XOPEN macro(s) should take precedence. + */ +#if defined(_ANSI_SOURCE) && !defined(__POSIX_VISIBLE) && \ + !defined(__XPG_VISIBLE) +# define __POSIX_VISIBLE 0 +# define __XPG_VISIBLE 0 +# define __ISO_C_VISIBLE 1990 +#endif + +/* + * _ISOC99_SOURCE and __STDC_VERSION__ override any of the other macros since + * they are non-exclusive. + */ +#if defined(_ISOC99_SOURCE) || \ + (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901) || \ + (defined(__cplusplus) && __cplusplus >= 201103) +# undef __ISO_C_VISIBLE +# define __ISO_C_VISIBLE 1999 +#endif + +/* + * Finally deal with BSD-specific interfaces that are not covered + * by any standards. We expose these when none of the POSIX or XPG + * macros is defined or if the user explicitly asks for them. + */ +#if !defined(_BSD_SOURCE) && \ + (defined(_ANSI_SOURCE) || defined(__XPG_VISIBLE) || defined(__POSIX_VISIBLE)) +# define __BSD_VISIBLE 0 +#endif + +/* + * Default values. + */ +#ifndef __XPG_VISIBLE +# define __XPG_VISIBLE 700 +#endif +#ifndef __POSIX_VISIBLE +# define __POSIX_VISIBLE 200809 +#endif +#ifndef __ISO_C_VISIBLE +# define __ISO_C_VISIBLE 1999 +#endif +#ifndef __BSD_VISIBLE +# define __BSD_VISIBLE 1 +#endif + +#endif /* !_SYS_CDEFS_H_ */ diff --git a/telldus-core/3rdparty/openbsd-getopt/sys/types.h b/telldus-core/3rdparty/openbsd-getopt/sys/types.h new file mode 100644 index 0000000..c2ebbb1 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/sys/types.h @@ -0,0 +1,243 @@ +/* $OpenBSD: types.h,v 1.32 2011/03/19 18:26:06 deraadt Exp $ */ +/* $NetBSD: types.h,v 1.29 1996/11/15 22:48:25 jtc Exp $ */ + +/*- + * Copyright (c) 1982, 1986, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)types.h 8.4 (Berkeley) 1/21/94 + */ + +#ifndef _SYS_TYPES_H_ +#define _SYS_TYPES_H_ + +#include +#include +#include + +#if __BSD_VISIBLE +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; + +typedef unsigned char unchar; /* Sys V compatibility */ +typedef unsigned short ushort; /* Sys V compatibility */ +typedef unsigned int uint; /* Sys V compatibility */ +typedef unsigned long ulong; /* Sys V compatibility */ + +typedef __cpuid_t cpuid_t; /* CPU id */ +typedef __register_t register_t; /* register-sized type */ +#endif /* __BSD_VISIBLE */ + +/* + * XXX The exact-width bit types should only be exposed if __BSD_VISIBLE + * but the rest of the includes are not ready for that yet. + */ +#ifndef __BIT_TYPES_DEFINED__ +#define __BIT_TYPES_DEFINED__ +#endif + +#ifndef _INT8_T_DEFINED_ +#define _INT8_T_DEFINED_ +typedef __int8_t int8_t; +#endif + +#ifndef _UINT8_T_DEFINED_ +#define _UINT8_T_DEFINED_ +typedef __uint8_t uint8_t; +#endif + +#ifndef _INT16_T_DEFINED_ +#define _INT16_T_DEFINED_ +typedef __int16_t int16_t; +#endif + +#ifndef _UINT16_T_DEFINED_ +#define _UINT16_T_DEFINED_ +typedef __uint16_t uint16_t; +#endif + +#ifndef _INT32_T_DEFINED_ +#define _INT32_T_DEFINED_ +typedef __int32_t int32_t; +#endif + +#ifndef _UINT32_T_DEFINED_ +#define _UINT32_T_DEFINED_ +typedef __uint32_t uint32_t; +#endif + +#ifndef _INT64_T_DEFINED_ +#define _INT64_T_DEFINED_ +typedef __int64_t int64_t; +#endif + +#ifndef _UINT64_T_DEFINED_ +#define _UINT64_T_DEFINED_ +typedef __uint64_t uint64_t; +#endif + +/* BSD-style unsigned bits types */ +typedef __uint8_t u_int8_t; +typedef __uint16_t u_int16_t; +typedef __uint32_t u_int32_t; +typedef __uint64_t u_int64_t; + +/* quads, deprecated in favor of 64 bit int types */ +typedef __int64_t quad_t; +typedef __uint64_t u_quad_t; +typedef quad_t * qaddr_t; + +#if __BSD_VISIBLE +/* VM system types */ +typedef __vaddr_t vaddr_t; +typedef __paddr_t paddr_t; +typedef __vsize_t vsize_t; +typedef __psize_t psize_t; +#endif /* __BSD_VISIBLE */ + +/* Standard system types */ +typedef char * caddr_t; /* core address */ +typedef __int32_t daddr32_t; /* 32-bit disk address */ +typedef __int64_t daddr_t; /* 64-bit disk address */ +typedef __int64_t daddr64_t; /* 64-bit disk address */ +typedef __dev_t dev_t; /* device number */ +typedef __fixpt_t fixpt_t; /* fixed point number */ +typedef __gid_t gid_t; /* group id */ +typedef __id_t id_t; /* may contain pid, uid or gid */ +typedef __ino_t ino_t; /* inode number */ +typedef __key_t key_t; /* IPC key (for Sys V IPC) */ +typedef __mode_t mode_t; /* permissions */ +typedef __nlink_t nlink_t; /* link count */ +typedef __pid_t pid_t; /* process id */ +typedef __rlim_t rlim_t; /* resource limit */ +typedef __segsz_t segsz_t; /* segment size */ +typedef __swblk_t swblk_t; /* swap offset */ +typedef __uid_t uid_t; /* user id */ +typedef __useconds_t useconds_t; /* microseconds */ +typedef __suseconds_t suseconds_t; /* microseconds (signed) */ +typedef __fsblkcnt_t fsblkcnt_t; /* file system block count */ +typedef __fsfilcnt_t fsfilcnt_t; /* file system file count */ + +/* + * XPG4.2 states that inclusion of must pull these + * in and that inclusion of must pull in sa_family_t. + * We put these here because there are other headers that require + * these types and and will indirectly + * include . + * XXX - now that we have protected versions these should move. + */ +typedef __in_addr_t in_addr_t; /* base type for internet address */ +typedef __in_port_t in_port_t; /* IP port type */ +typedef __sa_family_t sa_family_t; /* sockaddr address family type */ +typedef __socklen_t socklen_t; /* length type for network syscalls */ + +/* + * The following types may be defined in multiple header files. + */ +#ifndef _CLOCK_T_DEFINED_ +#define _CLOCK_T_DEFINED_ +typedef __clock_t clock_t; +#endif + +#ifndef _CLOCKID_T_DEFINED_ +#define _CLOCKID_T_DEFINED_ +typedef __clockid_t clockid_t; +#endif + +#ifndef _SIZE_T_DEFINED_ +#define _SIZE_T_DEFINED_ +typedef __size_t size_t; +#endif + +#ifndef _SSIZE_T_DEFINED_ +#define _SSIZE_T_DEFINED_ +typedef __ssize_t ssize_t; +#endif + +#ifndef _TIME_T_DEFINED_ +#define _TIME_T_DEFINED_ +typedef __time_t time_t; +#endif + +#ifndef _TIMER_T_DEFINED_ +#define _TIMER_T_DEFINED_ +typedef __timer_t timer_t; +#endif + +#ifndef _OFF_T_DEFINED_ +#define _OFF_T_DEFINED_ +typedef __off_t off_t; +#endif + +/* + * These belong in unistd.h, but are placed here too to ensure that + * long arguments will be promoted to off_t if the program fails to + * include that header or explicitly cast them to off_t. + */ +#if __BSD_VISIBLE && !defined(_KERNEL) +__BEGIN_DECLS +off_t lseek(int, off_t, int); +int ftruncate(int, off_t); +int truncate(const char *, off_t); +__END_DECLS +#endif /* __BSD_VISIBLE && !_KERNEL */ + +#if __BSD_VISIBLE +/* Major, minor numbers, dev_t's. */ +#define major(x) ((int32_t)(((u_int32_t)(x) >> 8) & 0xff)) +#define minor(x) ((int32_t)((x) & 0xff) | (((x) & 0xffff0000) >> 8)) +#define makedev(x,y) ((dev_t)((((x) & 0xff) << 8) | ((y) & 0xff) | (((y) & 0xffff00) << 8))) +#endif + +#if __BSD_VISIBLE +#include /* must be after type declarations */ +#endif + +#if defined(__STDC__) && defined(_KERNEL) +/* + * Forward structure declarations for function prototypes. We include the + * common structures that cross subsystem boundaries here; others are mostly + * used in the same place that the structure is defined. + */ +struct proc; +struct pgrp; +struct ucred; +struct rusage; +struct file; +struct buf; +struct tty; +struct uio; +#endif + +#endif /* !_SYS_TYPES_H_ */ diff --git a/telldus-core/3rdparty/openbsd-getopt/sys/unistd.h b/telldus-core/3rdparty/openbsd-getopt/sys/unistd.h new file mode 100644 index 0000000..52b8545 --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/sys/unistd.h @@ -0,0 +1,159 @@ +/* $OpenBSD: unistd.h,v 1.19 2011/10/15 23:35:29 guenther Exp $ */ +/* $NetBSD: unistd.h,v 1.10 1994/06/29 06:46:06 cgd Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)unistd.h 8.2 (Berkeley) 1/7/94 + */ + +#ifndef _SYS_UNISTD_H_ +#define _SYS_UNISTD_H_ + +#include + +/* compile-time symbolic constants */ + /* implementation supports job control */ +#define _POSIX_JOB_CONTROL 1 + /* saved set-user-ID and set-group-ID */ +#define _POSIX_SAVED_IDS 1 + +#define _POSIX_VERSION 199009L +#define _POSIX2_VERSION 199212L + +/* execution-time symbolic constants */ + /* chown requires appropriate privileges */ +#define _POSIX_CHOWN_RESTRICTED 1 + /* too-long path components generate errors */ +#define _POSIX_NO_TRUNC 1 + /* may disable terminal special characters */ +#define _POSIX_VDISABLE (0377) + /* file synchronization is available */ +#define _POSIX_FSYNC 1 + +/* access function */ +#define F_OK 0 /* test for existence of file */ +#define X_OK 0x01 /* test for execute or search permission */ +#define W_OK 0x02 /* test for write permission */ +#define R_OK 0x04 /* test for read permission */ + +/* whence values for lseek(2) */ +#define SEEK_SET 0 /* set file offset to offset */ +#define SEEK_CUR 1 /* set file offset to current plus offset */ +#define SEEK_END 2 /* set file offset to EOF plus offset */ + +#if __BSD_VISIBLE +/* old BSD whence values for lseek(2); renamed by POSIX 1003.1 */ +#define L_SET SEEK_SET +#define L_INCR SEEK_CUR +#define L_XTND SEEK_END + +/* the parameters argument passed to the __tfork() syscall */ +struct __tfork { + void *tf_tcb; + pid_t *tf_tid; + int tf_flags; +}; +#endif + +/* configurable pathname variables */ +#define _PC_LINK_MAX 1 +#define _PC_MAX_CANON 2 +#define _PC_MAX_INPUT 3 +#define _PC_NAME_MAX 4 +#define _PC_PATH_MAX 5 +#define _PC_PIPE_BUF 6 +#define _PC_CHOWN_RESTRICTED 7 +#define _PC_NO_TRUNC 8 +#define _PC_VDISABLE 9 + +/* configurable system variables */ +#define _SC_ARG_MAX 1 +#define _SC_CHILD_MAX 2 +#define _SC_CLK_TCK 3 +#define _SC_NGROUPS_MAX 4 +#define _SC_OPEN_MAX 5 +#define _SC_JOB_CONTROL 6 +#define _SC_SAVED_IDS 7 +#define _SC_VERSION 8 +#define _SC_BC_BASE_MAX 9 +#define _SC_BC_DIM_MAX 10 +#define _SC_BC_SCALE_MAX 11 +#define _SC_BC_STRING_MAX 12 +#define _SC_COLL_WEIGHTS_MAX 13 +#define _SC_EXPR_NEST_MAX 14 +#define _SC_LINE_MAX 15 +#define _SC_RE_DUP_MAX 16 +#define _SC_2_VERSION 17 +#define _SC_2_C_BIND 18 +#define _SC_2_C_DEV 19 +#define _SC_2_CHAR_TERM 20 +#define _SC_2_FORT_DEV 21 +#define _SC_2_FORT_RUN 22 +#define _SC_2_LOCALEDEF 23 +#define _SC_2_SW_DEV 24 +#define _SC_2_UPE 25 +#define _SC_STREAM_MAX 26 +#define _SC_TZNAME_MAX 27 +#define _SC_PAGESIZE 28 +#define _SC_PAGE_SIZE _SC_PAGESIZE /* 1170 compatibility */ +#define _SC_FSYNC 29 +#define _SC_XOPEN_SHM 30 +#define _SC_SEM_NSEMS_MAX 31 +#define _SC_SEM_VALUE_MAX 32 +#define _SC_HOST_NAME_MAX 33 + +/* P1003.1c */ +#define _SC_GETGR_R_SIZE_MAX 100 +#define _SC_GETPW_R_SIZE_MAX 101 +#define _SC_LOGIN_NAME_MAX 102 +#define _SC_THREAD_SAFE_FUNCTIONS 103 +#ifdef notyet +#define _SC_THREAD_DESTRUCTOR_ITERATIONS +#define _SC_THREAD_KEYS_MAX +#define _SC_THREAD_STACK_MIN +#define _SC_THREAD_THREADS_MAX +#define _SC_TTY_NAME_MAX +#define _SC_THREADS +#define _SC_THREAD_ATTR_STACKADDR +#define _SC_THREAD_ATTR_STACKSIZE +#define _SC_THREAD_PRIORITY_SCHEDULING +#define _SC_THREAD_PRIO_INHERIT +#define _SC_THREAD_PRIO_PROTECT +#define _SC_THREAD_PROCESS_SHARED +#endif + +#define _SC_PHYS_PAGES 500 +#define _SC_AVPHYS_PAGES 501 +#define _SC_NPROCESSORS_CONF 502 +#define _SC_NPROCESSORS_ONLN 503 + +/* configurable system strings */ +#define _CS_PATH 1 + +#endif /* !_SYS_UNISTD_H_ */ diff --git a/telldus-core/3rdparty/openbsd-getopt/unistd.h b/telldus-core/3rdparty/openbsd-getopt/unistd.h new file mode 100644 index 0000000..b013d3e --- /dev/null +++ b/telldus-core/3rdparty/openbsd-getopt/unistd.h @@ -0,0 +1,274 @@ +/* $OpenBSD: unistd.h,v 1.67 2012/01/13 13:16:44 nigel Exp $ */ +/* $NetBSD: unistd.h,v 1.26.4.1 1996/05/28 02:31:51 mrg Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)unistd.h 5.13 (Berkeley) 6/17/91 + */ + +#ifndef _UNISTD_H_ +#define _UNISTD_H_ + +#include +#include +#include + +#define STDIN_FILENO 0 /* standard input file descriptor */ +#define STDOUT_FILENO 1 /* standard output file descriptor */ +#define STDERR_FILENO 2 /* standard error file descriptor */ + +#if __XPG_VISIBLE || __POSIX_VISIBLE >= 200112 +#define F_ULOCK 0 /* unlock locked section */ +#define F_LOCK 1 /* lock a section for exclusive use */ +#define F_TLOCK 2 /* test and lock a section for exclusive use */ +#define F_TEST 3 /* test a section for locks by other procs */ +#endif + +#if __POSIX_VISIBLE +#define _POSIX_REENTRANT_FUNCTIONS 1 +#define _POSIX_THREAD_SAFE_FUNCTIONS 200112L +#endif + +#ifndef NULL +#ifdef __GNUG__ +#define NULL __null +#elif defined(__cplusplus) +#define NULL 0L +#else +#define NULL ((void *)0) +#endif +#endif + +__BEGIN_DECLS +//__dead void _exit(int); +//int access(const char *, int); +unsigned int alarm(unsigned int); +//int chdir(const char *); +//int chown(const char *, uid_t, gid_t); +int close(int); +int dup(int); +int dup2(int, int); +//int execl(const char *, const char *, ...) +// __attribute__((sentinel)); +//int execle(const char *, const char *, ...); +//int execlp(const char *, const char *, ...) +// __attribute__((sentinel)); +//int execv(const char *, char * const *); +//int execve(const char *, char * const *, char * const *); +//int execvp(const char *, char * const *); +pid_t fork(void); +long fpathconf(int, int); +char *getcwd(char *, size_t) + __attribute__((__bounded__(__string__,1,2))); +gid_t getegid(void); +uid_t geteuid(void); +gid_t getgid(void); +int getgroups(int, gid_t *); +char *getlogin(void); +pid_t getpgrp(void); +pid_t getpid(void); +pid_t getppid(void); +uid_t getuid(void); +int isatty(int); +//int link(const char *, const char *); +//off_t lseek(int, off_t, int); +//long pathconf(const char *, int); +int pause(void); +int pipe(int *); +//ssize_t read(int, void *, size_t) +// __attribute__((__bounded__(__buffer__,2,3))); +//int rmdir(const char *); +int setgid(gid_t); +int setuid(uid_t); +unsigned int sleep(unsigned int); +long sysconf(int); +pid_t tcgetpgrp(int); +int tcsetpgrp(int, pid_t); +char *ttyname(int); +//int unlink(const char *); +//ssize_t write(int, const void *, size_t) +// __attribute__((__bounded__(__buffer__,2,3))); + +#if __POSIX_VISIBLE || __XPG_VISIBLE >= 300 +pid_t setsid(void); +int setpgid(pid_t, pid_t); +#endif + +#if __POSIX_VISIBLE >= 199209 || __XPG_VISIBLE +size_t confstr(int, char *, size_t) + __attribute__((__bounded__(__string__,2,3))); +#ifndef _GETOPT_DEFINED_ +#define _GETOPT_DEFINED_ +int getopt(int, char * const *, const char *); +extern char *optarg; /* getopt(3) external variables */ +extern int opterr, optind, optopt, optreset; +/* XXX - getsubopt does not belong here */ +int getsubopt(char **, char * const *, char **); +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif /* _GETOPT_DEFINED_ */ +#endif + +#if __POSIX_VISIBLE >= 199506 || __XPG_VISIBLE +int fsync(int); +int ftruncate(int, off_t); +int getlogin_r(char *, size_t) + __attribute__((__bounded__(__string__,1,2))); +#endif + +#if __XPG_VISIBLE || __BSD_VISIBLE +char *crypt(const char *, const char *); +int encrypt(char *, int); +int fchdir(int); +int fchown(int, uid_t, gid_t); +long gethostid(void); +char *getwd(char *) + __attribute__ ((__bounded__(__minbytes__,1,1024))); +int lchown(const char *, uid_t, gid_t); +int mkstemp(char *); +char *mktemp(char *); +int nice(int); +int readlink(const char *, char *, size_t) + __attribute__ ((__bounded__(__string__,2,3))); +int setkey(const char *); +int setpgrp(pid_t pid, pid_t pgrp); /* obsoleted by setpgid() */ +int setregid(gid_t, gid_t); +int setreuid(uid_t, uid_t); +void swab(const void *, void *, size_t); +void sync(void); +int truncate(const char *, off_t); +unsigned int ualarm(unsigned int, unsigned int); +int usleep(useconds_t); +pid_t vfork(void); +#endif + +#if __XPG_VISIBLE >= 420 +pid_t getpgid(pid_t); +pid_t getsid(pid_t); +#endif + +#if __XPG_VISIBLE >= 500 +ssize_t pread(int, void *, size_t, off_t); +ssize_t pwrite(int, const void *, size_t, off_t); +int ttyname_r(int, char *, size_t) + __attribute__((__bounded__(__string__,2,3))); +#endif + +#if __BSD_VISIBLE || __XPG_VISIBLE <= 500 +/* Interfaces withdrawn by X/Open Issue 5 Version 0 */ +int brk(void *); +//int chroot(const char *); +int getdtablesize(void); +int getpagesize(void); +//char *getpass(const char *); +void *sbrk(int); +#endif + +#if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE >= 420 +int lockf(int, int, off_t); +#endif + +#if __POSIX_VISIBLE >= 200112 || __XPG_VISIBLE >= 420 || __BSD_VISIBLE +int symlink(const char *, const char *); +int gethostname(char *, size_t) + __attribute__ ((__bounded__(__string__,1,2))); +int setegid(gid_t); +int seteuid(uid_t); +#endif + +#if __POSIX_VISIBLE >= 200809 +int faccessat(int, const char *, int, int); +int fchownat(int, const char *, uid_t, gid_t, int); +int linkat(int, const char *, int, const char *, int); +ssize_t readlinkat(int, const char *, char *, size_t); +int symlinkat(const char *, int, const char *); +int unlinkat(int, const char *, int); +#endif + +#if __BSD_VISIBLE +int acct(const char *); +int closefrom(int); +int des_cipher(const char *, char *, int32_t, int); +int des_setkey(const char *); +void endusershell(void); +int exect(const char *, char * const *, char * const *); +char *fflagstostr(u_int32_t); +int getdomainname(char *, size_t) + __attribute__ ((__bounded__(__string__,1,2))); +int getgrouplist(const char *, gid_t, gid_t *, int *); +mode_t getmode(const void *, mode_t); +int getresgid(gid_t *, gid_t *, gid_t *); +int getresuid(uid_t *, uid_t *, uid_t *); +char *getusershell(void); +int initgroups(const char *, gid_t); +int iruserok(u_int32_t, int, const char *, const char *); +int iruserok_sa(const void *, int, int, const char *, const char *); +int issetugid(void); +char *mkdtemp(char *); +int mkstemps(char *, int); +int nfssvc(int, void *); +int profil(char *, size_t, unsigned long, unsigned int) + __attribute__ ((__bounded__(__string__,1,2))); +int quotactl(const char *, int, int, char *); +int rcmd(char **, int, const char *, + const char *, const char *, int *); +int rcmd_af(char **, int, const char *, + const char *, const char *, int *, int); +int rcmdsh(char **, int, const char *, + const char *, const char *, char *); +char *re_comp(const char *); +int re_exec(const char *); +int reboot(int); +int revoke(const char *); +int rfork(int opts); +int rresvport(int *); +int rresvport_af(int *, int); +int ruserok(const char *, int, const char *, const char *); +#ifndef _SELECT_DEFINED_ +#define _SELECT_DEFINED_ +struct timeval; +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); +#endif +int setdomainname(const char *, size_t); +int setgroups(int, const gid_t *); +int sethostid(long); +int sethostname(const char *, size_t); +int setlogin(const char *); +void *setmode(const char *); +int setresgid(gid_t, gid_t, gid_t); +int setresuid(uid_t, uid_t, uid_t); +int setrgid(gid_t); +int setruid(uid_t); +void setusershell(void); +int strtofflags(char **, u_int32_t *, u_int32_t *); +int swapctl(int cmd, const void *arg, int misc); +int syscall(int, ...); +#endif /* __BSD_VISIBLE */ +__END_DECLS + +#endif /* !_UNISTD_H_ */ diff --git a/telldus-core/AUTHORS b/telldus-core/AUTHORS new file mode 100644 index 0000000..58bd0e4 --- /dev/null +++ b/telldus-core/AUTHORS @@ -0,0 +1,7 @@ +telldus-core has been developed by : + + Micke Prag + Fredrik Jacobsson + Stefan Persson + +The package is maintained by Micke Prag diff --git a/telldus-core/CMakeLists.txt b/telldus-core/CMakeLists.txt new file mode 100644 index 0000000..0937e3e --- /dev/null +++ b/telldus-core/CMakeLists.txt @@ -0,0 +1,75 @@ +PROJECT( telldus-core ) + +CMAKE_MINIMUM_REQUIRED( VERSION 2.6.0 ) + +CMAKE_POLICY(SET CMP0003 NEW) + +SET(PACKAGE_MAJOR_VERSION 2) +SET(PACKAGE_MINOR_VERSION 1) +SET(PACKAGE_PATCH_VERSION 2) +SET(PACKAGE_VERSION "${PACKAGE_MAJOR_VERSION}.${PACKAGE_MINOR_VERSION}.${PACKAGE_PATCH_VERSION}") +SET(PACKAGE_SUBVERSION "") +SET(PACKAGE_SOVERSION 2) + +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +IF (PACKAGE_SUBVERSION) + SET(DISPLAYED_VERSION "${PACKAGE_VERSION}_${PACKAGE_SUBVERSION}") +ELSE (PACKAGE_SUBVERSION) + SET(DISPLAYED_VERSION ${PACKAGE_VERSION}) +ENDIF(PACKAGE_SUBVERSION) + +SET(BUILD_LIBTELLDUS-CORE TRUE CACHE BOOL "Build libtelldus-core") + +IF (WIN32) + SET(TDADMIN_DEFAULT FALSE) +ELSEIF(APPLE) + SET(TDADMIN_DEFAULT FALSE) +ELSE (WIN32) + SET(TDADMIN_DEFAULT TRUE) +ENDIF (WIN32) + +IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + INCLUDE_DIRECTORIES(/usr/local/include) + LINK_DIRECTORIES(/usr/local/lib) +ENDIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + +SET(BUILD_TDTOOL TRUE CACHE BOOL "Build tdtool") +SET(BUILD_TDADMIN ${TDADMIN_DEFAULT} CACHE BOOL "Build tdadmin") + +SET(GENERATE_MAN FALSE CACHE BOOL "Enable generation of man-files") + +ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(service) +ADD_SUBDIRECTORY(client) + +IF(BUILD_TDTOOL) + IF(WIN32) + ADD_SUBDIRECTORY(3rdparty/openbsd-getopt) + ENDIF() + ADD_SUBDIRECTORY(tdtool) +ENDIF(BUILD_TDTOOL) +IF(BUILD_TDADMIN) + ADD_SUBDIRECTORY(tdadmin) +ENDIF(BUILD_TDADMIN) + +ENABLE_TESTING() +ADD_SUBDIRECTORY(tests) + +FIND_PACKAGE(Doxygen) + +IF(DOXYGEN_FOUND) + SET(DOXY_CONFIG ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" + ${DOXY_CONFIG} @ONLY + ) + + ADD_CUSTOM_TARGET(docs + ${DOXYGEN_EXECUTABLE} ${DOXY_CONFIG} + DEPENDS ${DOXY_CONFIG} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating doxygen documentation" VERBATIM + ) +ENDIF() diff --git a/telldus-core/CMakeLists.txt.orig b/telldus-core/CMakeLists.txt.orig new file mode 100644 index 0000000..43d8da5 --- /dev/null +++ b/telldus-core/CMakeLists.txt.orig @@ -0,0 +1,80 @@ +PROJECT( telldus-core ) + +CMAKE_MINIMUM_REQUIRED( VERSION 2.6.0 ) + +CMAKE_POLICY(SET CMP0003 NEW) + +OPTION(FORCE_COMPILE_FROM_TRUNK FALSE "Accept compiling source from trunk. This is unsupported and highly unrecommended") +IF(NOT FORCE_COMPILE_FROM_TRUNK) + MESSAGE(FATAL_ERROR "You are compiling sources from trunk. Don't do that!") +ENDIF(NOT FORCE_COMPILE_FROM_TRUNK) + +SET(PACKAGE_MAJOR_VERSION 2) +SET(PACKAGE_MINOR_VERSION 1) +SET(PACKAGE_PATCH_VERSION 2) +SET(PACKAGE_VERSION "${PACKAGE_MAJOR_VERSION}.${PACKAGE_MINOR_VERSION}.${PACKAGE_PATCH_VERSION}") +SET(PACKAGE_SUBVERSION "") +SET(PACKAGE_SOVERSION 2) + +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +IF (PACKAGE_SUBVERSION) + SET(DISPLAYED_VERSION "${PACKAGE_VERSION}_${PACKAGE_SUBVERSION}") +ELSE (PACKAGE_SUBVERSION) + SET(DISPLAYED_VERSION ${PACKAGE_VERSION}) +ENDIF(PACKAGE_SUBVERSION) + +SET(BUILD_LIBTELLDUS-CORE TRUE CACHE BOOL "Build libtelldus-core") + +IF (WIN32) + SET(TDADMIN_DEFAULT FALSE) +ELSEIF(APPLE) + SET(TDADMIN_DEFAULT FALSE) +ELSE (WIN32) + SET(TDADMIN_DEFAULT TRUE) +ENDIF (WIN32) + +IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + INCLUDE_DIRECTORIES(/usr/local/include) + LINK_DIRECTORIES(/usr/local/lib) +ENDIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + +SET(BUILD_TDTOOL TRUE CACHE BOOL "Build tdtool") +SET(BUILD_TDADMIN ${TDADMIN_DEFAULT} CACHE BOOL "Build tdadmin") + +SET(GENERATE_MAN FALSE CACHE BOOL "Enable generation of man-files") + +ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(service) +ADD_SUBDIRECTORY(client) + +IF(BUILD_TDTOOL) + IF(WIN32) + ADD_SUBDIRECTORY(3rdparty/openbsd-getopt) + ENDIF() + ADD_SUBDIRECTORY(tdtool) +ENDIF(BUILD_TDTOOL) +IF(BUILD_TDADMIN) + ADD_SUBDIRECTORY(tdadmin) +ENDIF(BUILD_TDADMIN) + +ENABLE_TESTING() +ADD_SUBDIRECTORY(tests) + +FIND_PACKAGE(Doxygen) + +IF(DOXYGEN_FOUND) + SET(DOXY_CONFIG ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + + CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" + ${DOXY_CONFIG} @ONLY + ) + + ADD_CUSTOM_TARGET(docs + ${DOXYGEN_EXECUTABLE} ${DOXY_CONFIG} + DEPENDS ${DOXY_CONFIG} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating doxygen documentation" VERBATIM + ) +ENDIF() diff --git a/telldus-core/INSTALL b/telldus-core/INSTALL new file mode 100644 index 0000000..9d2b0b4 --- /dev/null +++ b/telldus-core/INSTALL @@ -0,0 +1,51 @@ +Unpacking +--------- + +Unpack the archive if you have not done so already: + + cd /usr/src + gunzip telldus-core-{version}.tar.gz #Change {version} to the downloaded version + tar xvf telldus-core-{version}.tar + +This creates the directory /usr/src/telldus-core-{version} containing the files +from the archive. We only support the GNU version of tar archiving utility. Note +on some systems it is called gtar. + +Configuring +----------- + +Telldus Core is built using CMake (http://www.cmake.org), version 2.4.0 is the +minimum required version. This means there is no configure-script, but you +need to run cmake. + +To configure telldus-core for your machine type: + + cmake . + +in the package directory. + +By default, Telldus Core is configured for installation in the /usr/local +directory, but this can be changed by using the -DCMAKE_INSTALL_PREFIX=/usr +option. Alternatively, the DESTDIR="/opt" option can be used to specify a "local" +installation within the source directory when running "make install". + +Building +-------- + +To create the library and compile all the tools type: + + make + +If you did not configure Telldus Core using the -DCMAKE_INSTALL_PREFIX=/usr +option, you need to install the library and tools in the appropriate place. To +do this, type: + + su -c "make install" + +and enter the root password. + +If you want to install in another directory, type: + + su -c "make DESTDIR=/opt install" + +Note that on some systems the make utility is named differently, e.g. gmake. diff --git a/telldus-core/LICENSE b/telldus-core/LICENSE new file mode 100644 index 0000000..4362b49 --- /dev/null +++ b/telldus-core/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/telldus-core/README b/telldus-core/README new file mode 100644 index 0000000..dd80b5b --- /dev/null +++ b/telldus-core/README @@ -0,0 +1,30 @@ +This is Telldus Core version {version} + +Telldus Core is the driver and tools for controlling a Telldus Technologies +TellStick. It does not containing any GUI tools which makes it suitable for +server use. + + +INSTALLING Telldus Core + +On Windows, if you want to install the precompiles binary packages, simply +launch the package and follow the instructions in the installation wizard. + +If you have a source package (a .tar.gz file), follow the instruction in the +INSTALL file. + + +CONFIGURATION AND TOOLS + +Once Telldus Core is installed, we suggest that you start by adding the devices +you want to control. + +On Windows, this is done by installing TelldusCenter. On Linux it's done by +editing the file /etc/tellstick.conf directly, or in TelldusCenter. + +Telldus Core installs the tool tdtool for controlling devices with TellStick. +Have a look in the man page for a description how to use it: + + man tdtool + +TellStick is a trademark of Telldus Technologies AB diff --git a/telldus-core/client/CMakeLists.txt b/telldus-core/client/CMakeLists.txt new file mode 100644 index 0000000..3336d29 --- /dev/null +++ b/telldus-core/client/CMakeLists.txt @@ -0,0 +1,131 @@ +IF(COMMAND cmake_policy) + cmake_policy(SET CMP0003 NEW) +ENDIF(COMMAND cmake_policy) + +FIND_PACKAGE( SignTool REQUIRED ) + +######## Non configurable options ######## +SET( telldus-core_SRCS + CallbackDispatcher.cpp + CallbackMainDispatcher.cpp + Client.cpp + telldus-core.cpp +) + +SET( telldus-core_HDRS + CallbackDispatcher.h + CallbackMainDispatcher.cpp + Client.h +) +SET( telldus-core_PUB_HDRS + telldus-core.h +) + +FIND_PACKAGE(Threads) +LIST(APPEND telldus-core_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) + +######## Configurable options for the platform ######## + + + +######## Platforms-specific, non configurable ######## + +IF (APPLE) + #### Mac OS X #### + SET( telldus-core_TARGET TelldusCore ) + ADD_DEFINITIONS( + -D_MACOSX + ) + LIST(APPEND telldus-core_LIBRARIES + TelldusCommon + ) +ELSEIF (WIN32) + #### Windows #### + ADD_DEFINITIONS( -DUNICODE ) + ADD_DEFINITIONS( /Zc:wchar_t- ) # Treat wchar_t as Built-in Type' = No + SET( telldus-core_TARGET TelldusCore ) + LIST(APPEND telldus-core_LIBRARIES + TelldusCommon + ) + CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/telldus-core.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/telldus-core.rc + ) + LIST(APPEND telldus-core_SRCS + libtelldus-core.def + ${CMAKE_CURRENT_SOURCE_DIR}/telldus-core.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/telldus-core.rc + ) + ADD_DEFINITIONS( + -D_WINDOWS + -DTELLDUSCORE_EXPORTS + ) + IF (CMAKE_CL_64) + ADD_DEFINITIONS(-D_CL64) + ENDIF(CMAKE_CL_64) +ELSE (APPLE) + #### Linux #### + SET( telldus-core_TARGET telldus-core ) + LIST(APPEND telldus-core_LIBRARIES + TelldusCommon + ) + + ADD_DEFINITIONS( + -D_LINUX + ) +ENDIF (APPLE) + + + +######## Configuring ######## + +ADD_LIBRARY(${telldus-core_TARGET} SHARED + ${telldus-core_SRCS} + ${telldus-core_HDRS} + ${telldus-core_PUB_HDRS} +) + +#Copy public headers files on windows +IF (WIN32) + FOREACH(_FILE ${telldus-core_PUB_HDRS}) + ADD_CUSTOM_COMMAND( TARGET ${telldus-core_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy \"${CMAKE_CURRENT_SOURCE_DIR}/${_FILE}\" \"${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\" + COMMENT "Copy ${_FILE}" + ) + ENDFOREACH(_FILE) +ENDIF () + +ADD_DEPENDENCIES(${telldus-core_TARGET} TelldusCommon) + +IF (UNIX) + SET_TARGET_PROPERTIES( ${telldus-core_TARGET} PROPERTIES COMPILE_FLAGS "-fPIC -fvisibility=hidden") +ENDIF (UNIX) + +TARGET_LINK_LIBRARIES( ${telldus-core_TARGET} ${telldus-core_LIBRARIES} ) + +SET(telldus-core_TARGET ${telldus-core_TARGET} PARENT_SCOPE) +SET_TARGET_PROPERTIES(${telldus-core_TARGET} PROPERTIES + FRAMEWORK TRUE + INSTALL_NAME_DIR "/Library/Frameworks" + PUBLIC_HEADER ${telldus-core_PUB_HDRS} + VERSION ${PACKAGE_VERSION} + SOVERSION ${PACKAGE_SOVERSION} +) +SIGN(${telldus-core_TARGET}) + +IF (NOT LIB_INSTALL_DIR) + SET(LIB_INSTALL_DIR "lib") +ENDIF (NOT LIB_INSTALL_DIR) + +IF (UNIX) + INSTALL(TARGETS ${telldus-core_TARGET} + LIBRARY DESTINATION ${LIB_INSTALL_DIR} + ARCHIVE DESTINATION ${LIB_INSTALL_DIR} + FRAMEWORK DESTINATION "/Library/Frameworks" + PUBLIC_HEADER DESTINATION include + ) +ENDIF (UNIX) + diff --git a/telldus-core/client/CallbackDispatcher.cpp b/telldus-core/client/CallbackDispatcher.cpp new file mode 100644 index 0000000..a1f7226 --- /dev/null +++ b/telldus-core/client/CallbackDispatcher.cpp @@ -0,0 +1,139 @@ +/* + * CallbackDispatcher.cpp + * telldus-core + * + * Created by Micke Prag on 2010-11-02. + * Copyright 2010 Telldus Technologies AB. All rights reserved. + * + */ + +#include "client/CallbackDispatcher.h" +#include "common/Event.h" +#include "common/EventHandler.h" + +namespace TelldusCore { + +class TDEventDispatcher::PrivateData { +public: + EventHandler eventHandler; + EventRef stopEvent, callbackEvent; + int id; + void *func, *context; +}; + +TDEventDispatcher::TDEventDispatcher(int id, void *func, void *context) + :Thread() { + d = new PrivateData; + d->stopEvent = d->eventHandler.addEvent(); + d->callbackEvent = d->eventHandler.addEvent(); + d->id = id; + d->func = func; + d->context = context; + this->start(); +} + +TDEventDispatcher::~TDEventDispatcher() { + d->stopEvent->signal(); + this->wait(); + delete d; +} + +int TDEventDispatcher::id() const { + return d->id; +} + +void TDEventDispatcher::queue(EventDataRef eventData) { + d->callbackEvent->signal(eventData); +} + +void TDEventDispatcher::run() { + while (!d->stopEvent->isSignaled()) { + d->eventHandler.waitForAny(); + if (d->callbackEvent->isSignaled()) { + TelldusCore::EventDataRef eventData = d->callbackEvent->takeSignal(); + this->execute(eventData); + } + } +} + +TDDeviceEventDispatcher::TDDeviceEventDispatcher(int id, void *func, void *context) + :TDEventDispatcher(id, func, context) +{} + +void TDDeviceEventDispatcher::execute(EventDataRef eventData) { + DeviceEventCallbackData *data = dynamic_cast(eventData.get()); + if (!data) { + return; + } + ((TDDeviceEvent)d->func)(data->deviceId, data->deviceState, data->deviceStateValue.c_str(), d->id, d->context); +} + +CallbackStruct::CallbackType TDDeviceEventDispatcher::type() { + return CallbackStruct::DeviceEvent; +} + +TDDeviceChangeEventDispatcher::TDDeviceChangeEventDispatcher(int id, void *func, void *context) + :TDEventDispatcher(id, func, context) +{} + +void TDDeviceChangeEventDispatcher::execute(EventDataRef eventData) { + DeviceChangeEventCallbackData *data = dynamic_cast(eventData.get()); + if (!data) { + return; + } + ((TDDeviceChangeEvent)d->func)(data->deviceId, data->changeEvent, data->changeType, d->id, d->context); +} + +CallbackStruct::CallbackType TDDeviceChangeEventDispatcher::type() { + return CallbackStruct::DeviceChangeEvent; +} + +TDRawDeviceEventDispatcher::TDRawDeviceEventDispatcher(int id, void *func, void *context) + :TDEventDispatcher(id, func, context) +{} + +void TDRawDeviceEventDispatcher::execute(EventDataRef eventData) { + RawDeviceEventCallbackData *data = dynamic_cast(eventData.get()); + if (!data) { + return; + } + ((TDRawDeviceEvent)d->func)(data->data.c_str(), data->controllerId, d->id, d->context); +} + +CallbackStruct::CallbackType TDRawDeviceEventDispatcher::type() { + return CallbackStruct::RawDeviceEvent; +} + +TDSensorEventDispatcher::TDSensorEventDispatcher(int id, void *func, void *context) + :TDEventDispatcher(id, func, context) +{} + +void TDSensorEventDispatcher::execute(EventDataRef eventData) { + SensorEventCallbackData *data = dynamic_cast(eventData.get()); + if (!data) { + return; + } + ((TDSensorEvent)d->func)(data->protocol.c_str(), data->model.c_str(), data->id, data->dataType, data->value.c_str(), data->timestamp, d->id, d->context); +} + +CallbackStruct::CallbackType TDSensorEventDispatcher::type() { + return CallbackStruct::SensorEvent; +} + +TDControllerEventDispatcher::TDControllerEventDispatcher(int id, void *func, void *context) + :TDEventDispatcher(id, func, context) +{} + +void TDControllerEventDispatcher::execute(EventDataRef eventData) { + ControllerEventCallbackData *data = dynamic_cast(eventData.get()); + if (!data) { + return; + } + ((TDControllerEvent)d->func)(data->controllerId, data->changeEvent, data->changeType, data->newValue.c_str(), d->id, d->context); +} + +CallbackStruct::CallbackType TDControllerEventDispatcher::type() { + return CallbackStruct::ControllerEvent; +} + +} // namespace TelldusCore diff --git a/telldus-core/client/CallbackDispatcher.h b/telldus-core/client/CallbackDispatcher.h new file mode 100644 index 0000000..4b38790 --- /dev/null +++ b/telldus-core/client/CallbackDispatcher.h @@ -0,0 +1,129 @@ +/* + * CallbackDispatcher.h + * telldus-core + * + * Created by Micke Prag on 2010-11-02. + * Copyright 2010 Telldus Technologies AB. All rights reserved. + * + */ + +#ifndef TELLDUS_CORE_CLIENT_CALLBACKDISPATCHER_H_ +#define TELLDUS_CORE_CLIENT_CALLBACKDISPATCHER_H_ + +#include +#include "common/common.h" +#include "common/Event.h" +#include "common/Thread.h" +#include "common/Mutex.h" +#include "client/telldus-core.h" + +namespace TelldusCore { + + struct CallbackStruct { + enum CallbackType { DeviceEvent, DeviceChangeEvent, RawDeviceEvent, SensorEvent, ControllerEvent }; + CallbackType type; + void *event; + int id; + void *context; + TelldusCore::Mutex mutex; + }; + + class CallbackData: public EventDataBase { + public: + explicit CallbackData(CallbackStruct::CallbackType t) : EventDataBase(), type(t) {} + CallbackStruct::CallbackType type; + }; + + class DeviceEventCallbackData : public CallbackData { + public: + DeviceEventCallbackData() : CallbackData(CallbackStruct::DeviceEvent) {} + int deviceId; + int deviceState; + std::string deviceStateValue; + }; + class DeviceChangeEventCallbackData : public CallbackData { + public: + DeviceChangeEventCallbackData() : CallbackData(CallbackStruct::DeviceChangeEvent) {} + int deviceId; + int changeEvent; + int changeType; + }; + + class RawDeviceEventCallbackData : public CallbackData { + public: + RawDeviceEventCallbackData() : CallbackData(CallbackStruct::RawDeviceEvent) {} + std::string data; + int controllerId; + }; + + class SensorEventCallbackData : public CallbackData { + public: + SensorEventCallbackData() : CallbackData(CallbackStruct::SensorEvent) {} + std::string protocol; + std::string model; + int id; + int dataType; + std::string value; + int timestamp; + }; + class ControllerEventCallbackData : public CallbackData { + public: + ControllerEventCallbackData() : CallbackData(CallbackStruct::ControllerEvent) {} + int controllerId; + int changeEvent; + int changeType; + std::string newValue; + }; + + class TDEventDispatcher : public Thread { + public: + TDEventDispatcher(int id, void *func, void *context); + virtual ~TDEventDispatcher(); + int id() const; + void queue(EventDataRef eventData); + virtual CallbackStruct::CallbackType type() = 0; + protected: + class PrivateData; + PrivateData *d; + + virtual void run(); + virtual void execute(EventDataRef eventData) = 0; + }; + class TDDeviceEventDispatcher : public TDEventDispatcher { + public: + TDDeviceEventDispatcher(int id, void *func, void *context); + virtual CallbackStruct::CallbackType type(); + protected: + virtual void execute(EventDataRef eventData); + }; + class TDDeviceChangeEventDispatcher : public TDEventDispatcher { + public: + TDDeviceChangeEventDispatcher(int id, void *func, void *context); + virtual CallbackStruct::CallbackType type(); + protected: + virtual void execute(EventDataRef eventData); + }; + class TDRawDeviceEventDispatcher : public TDEventDispatcher { + public: + TDRawDeviceEventDispatcher(int id, void *func, void *context); + virtual CallbackStruct::CallbackType type(); + protected: + virtual void execute(EventDataRef eventData); + }; + class TDSensorEventDispatcher : public TDEventDispatcher { + public: + TDSensorEventDispatcher(int id, void *func, void *context); + virtual CallbackStruct::CallbackType type(); + protected: + virtual void execute(EventDataRef eventData); + }; + class TDControllerEventDispatcher : public TDEventDispatcher { + public: + TDControllerEventDispatcher(int id, void *func, void *context); + virtual CallbackStruct::CallbackType type(); + protected: + virtual void execute(EventDataRef eventData); + }; +} + +#endif // TELLDUS_CORE_CLIENT_CALLBACKDISPATCHER_H_ diff --git a/telldus-core/client/CallbackMainDispatcher.cpp b/telldus-core/client/CallbackMainDispatcher.cpp new file mode 100644 index 0000000..5b49544 --- /dev/null +++ b/telldus-core/client/CallbackMainDispatcher.cpp @@ -0,0 +1,95 @@ +/* + * CallbackMainDispatcher.cpp + * telldus-core + * + * Created by Stefan Persson on 2012-02-23. + * Copyright 2012 Telldus Technologies AB. All rights reserved. + * + */ + +#include "client/CallbackMainDispatcher.h" + +#include + +namespace TelldusCore { + +typedef std::list CallbackList; + +class CallbackMainDispatcher::PrivateData { +public: + Mutex mutex; + CallbackList callbackList; + int lastCallbackId; +}; + +CallbackMainDispatcher::CallbackMainDispatcher() { + d = new PrivateData; + d->lastCallbackId = 0; +} + +CallbackMainDispatcher::~CallbackMainDispatcher(void) { + { + TelldusCore::MutexLocker locker(&d->mutex); + for(CallbackList::iterator it = d->callbackList.begin(); it != d->callbackList.end(); ++it) { + delete (*it); + } + } + delete d; +} + +void CallbackMainDispatcher::execute(CallbackStruct::CallbackType type, EventData *eventData) { + { + TelldusCore::MutexLocker locker(&d->mutex); + EventDataRef eventDataRef(eventData); + for(CallbackList::iterator callback_it = d->callbackList.begin(); callback_it != d->callbackList.end(); ++callback_it) { + if ( (*callback_it)->type() != type ) { + continue; + } + (*callback_it)->queue(eventDataRef); + } + } +} + +int CallbackMainDispatcher::registerCallback(CallbackStruct::CallbackType type, void *eventFunction, void *context) { + TelldusCore::MutexLocker locker(&d->mutex); + int id = ++d->lastCallbackId; + TelldusCore::TDEventDispatcher *callback; + if (type == CallbackStruct::DeviceEvent) { + callback = new TelldusCore::TDDeviceEventDispatcher(id, eventFunction, context); + } else if (type == CallbackStruct::DeviceChangeEvent) { + callback = new TelldusCore::TDDeviceChangeEventDispatcher(id, eventFunction, context); + } else if (type == CallbackStruct::RawDeviceEvent) { + callback = new TelldusCore::TDRawDeviceEventDispatcher(id, eventFunction, context); + } else if (type == CallbackStruct::SensorEvent) { + callback = new TelldusCore::TDSensorEventDispatcher(id, eventFunction, context); + } else if (type == CallbackStruct::ControllerEvent) { + callback = new TelldusCore::TDControllerEventDispatcher(id, eventFunction, context); + } else { + return -1; + } + d->callbackList.push_back(callback); + return id; +} + +int CallbackMainDispatcher::unregisterCallback(int callbackId) { + CallbackList newEventList; + { + TelldusCore::MutexLocker locker(&d->mutex); + for(CallbackList::iterator callback_it = d->callbackList.begin(); callback_it != d->callbackList.end(); ++callback_it) { + if ( (*callback_it)->id() != callbackId ) { + continue; + } + newEventList.splice(newEventList.begin(), d->callbackList, callback_it); + break; + } + } + if (newEventList.size()) { + CallbackList::iterator it = newEventList.begin(); + delete (*it); + newEventList.erase(it); + return TELLSTICK_SUCCESS; + } + return TELLSTICK_ERROR_NOT_FOUND; +} + +} // namespace TelldusCore diff --git a/telldus-core/client/CallbackMainDispatcher.h b/telldus-core/client/CallbackMainDispatcher.h new file mode 100644 index 0000000..911d21a --- /dev/null +++ b/telldus-core/client/CallbackMainDispatcher.h @@ -0,0 +1,35 @@ +/* + * CallbackMainDispatcher.h + * telldus-core + * + * Created by Stefan Persson on 2012-02-23. + * Copyright 2012 Telldus Technologies AB. All rights reserved. + * + */ + +#ifndef CALLBACKMAINDISPATCHER_H +#define CALLBACKMAINDISPATCHER_H + +#include "client/CallbackDispatcher.h" +#include "common/Thread.h" + +namespace TelldusCore { + + class CallbackMainDispatcher + { + public: + CallbackMainDispatcher(void); + ~CallbackMainDispatcher(void); + + void execute(TelldusCore::CallbackStruct::CallbackType type, EventData *eventData); + + int registerCallback( TelldusCore::CallbackStruct::CallbackType type, void *eventFunction, void *context ); + int unregisterCallback( int callbackId ); + + private: + class PrivateData; + PrivateData *d; + }; +} + +#endif //CALLBACKMAINDISPATCHER_H diff --git a/telldus-core/client/Client.cpp b/telldus-core/client/Client.cpp new file mode 100644 index 0000000..3cd095c --- /dev/null +++ b/telldus-core/client/Client.cpp @@ -0,0 +1,269 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "client/Client.h" + +#include + +#include "client/CallbackDispatcher.h" +#include "client/CallbackMainDispatcher.h" +#include "common/Socket.h" +#include "common/Strings.h" +#include "common/Mutex.h" + +namespace TelldusCore { + +class Client::PrivateData { +public: + Socket eventSocket; + bool running, sensorCached, controllerCached; + std::wstring sensorCache, controllerCache; + TelldusCore::Mutex mutex; + CallbackMainDispatcher callbackMainDispatcher; +}; + +Client *Client::instance = 0; + +Client::Client() + : Thread() { + d = new PrivateData; + d->running = true; + d->sensorCached = false; + d->controllerCached = false; + start(); +} + +Client::~Client(void) { + stopThread(); + wait(); + { + TelldusCore::MutexLocker locker(&d->mutex); + } + delete d; +} + +void Client::close() { + if (Client::instance != 0) { + delete Client::instance; + Client::instance = 0; + } +} + +Client *Client::getInstance() { + if (Client::instance == 0) { + Client::instance = new Client(); + } + return Client::instance; +} + +bool Client::getBoolFromService(const Message &msg) { + return getIntegerFromService(msg) == TELLSTICK_SUCCESS; +} + +int Client::getIntegerFromService(const Message &msg) { + std::wstring response = sendToService(msg); + if (response.compare(L"") == 0) { + return TELLSTICK_ERROR_COMMUNICATING_SERVICE; + } + return Message::takeInt(&response); +} + +std::wstring Client::getWStringFromService(const Message &msg) { + std::wstring response = sendToService(msg); + return Message::takeString(&response); +} + +int Client::registerEvent( CallbackStruct::CallbackType type, void *eventFunction, void *context ) { + return d->callbackMainDispatcher.registerCallback(type, eventFunction, context ); +} + +void Client::run() { + // listen here + d->eventSocket.connect(L"TelldusEvents"); + + while(d->running) { + if(!d->eventSocket.isConnected()) { + d->eventSocket.connect(L"TelldusEvents"); // try to reconnect to service + if(!d->eventSocket.isConnected()) { + // reconnect didn't succeed, wait a while and try again + msleep(2000); + continue; + } + } + + std::wstring clientMessage = d->eventSocket.read(1000); // testing 5 second timeout + + while(clientMessage != L"") { + // a message arrived + std::wstring type = Message::takeString(&clientMessage); + if(type == L"TDDeviceChangeEvent") { + DeviceChangeEventCallbackData *data = new DeviceChangeEventCallbackData(); + data->deviceId = Message::takeInt(&clientMessage); + data->changeEvent = Message::takeInt(&clientMessage); + data->changeType = Message::takeInt(&clientMessage); + d->callbackMainDispatcher.execute(CallbackStruct::DeviceChangeEvent, data); + + } else if(type == L"TDDeviceEvent") { + DeviceEventCallbackData *data = new DeviceEventCallbackData(); + data->deviceId = Message::takeInt(&clientMessage); + data->deviceState = Message::takeInt(&clientMessage); + data->deviceStateValue = TelldusCore::wideToString(Message::takeString(&clientMessage)); + d->callbackMainDispatcher.execute(CallbackStruct::DeviceEvent, data); + + } else if(type == L"TDRawDeviceEvent") { + RawDeviceEventCallbackData *data = new RawDeviceEventCallbackData(); + data->data = TelldusCore::wideToString(Message::takeString(&clientMessage)); + data->controllerId = Message::takeInt(&clientMessage); + d->callbackMainDispatcher.execute(CallbackStruct::RawDeviceEvent, data); + + } else if(type == L"TDSensorEvent") { + SensorEventCallbackData *data = new SensorEventCallbackData(); + data->protocol = TelldusCore::wideToString(Message::takeString(&clientMessage)); + data->model = TelldusCore::wideToString(Message::takeString(&clientMessage)); + data->id = Message::takeInt(&clientMessage); + data->dataType = Message::takeInt(&clientMessage); + data->value = TelldusCore::wideToString(Message::takeString(&clientMessage)); + data->timestamp = Message::takeInt(&clientMessage); + d->callbackMainDispatcher.execute(CallbackStruct::SensorEvent, data); + + } else if(type == L"TDControllerEvent") { + ControllerEventCallbackData *data = new ControllerEventCallbackData(); + data->controllerId = Message::takeInt(&clientMessage); + data->changeEvent = Message::takeInt(&clientMessage); + data->changeType = Message::takeInt(&clientMessage); + data->newValue = TelldusCore::wideToString(Message::takeString(&clientMessage)); + d->callbackMainDispatcher.execute(CallbackStruct::ControllerEvent, data); + + } else { + clientMessage = L""; // cleanup, if message contained garbage/unhandled data + } + } + } +} + +std::wstring Client::sendToService(const Message &msg) { + int tries = 0; + std::wstring readData; + while(tries < 20) { + tries++; + if(tries == 20) { + TelldusCore::Message msg; + msg.addArgument(TELLSTICK_ERROR_CONNECTING_SERVICE); + return msg; + } + Socket s; + s.connect(L"TelldusClient"); + if (!s.isConnected()) { // sConnection failed + msleep(500); + continue; // retry + } + s.write(msg.data()); + if (!s.isConnected()) { // Connection failed sometime during operation... (better check here, instead of 5 seconds timeout later) + msleep(500); + continue; // retry + } + readData = s.read(1000); + if(readData == L"") { + msleep(500); + continue; + } + + if (!s.isConnected()) { // Connection failed sometime during operation... + msleep(500); + continue; // retry + } + break; + } + + return readData; +} + +void Client::stopThread() { + d->running = false; + d->eventSocket.stopReadWait(); +} + +int Client::unregisterCallback( int callbackId ) { + return d->callbackMainDispatcher.unregisterCallback(callbackId); +} + +int Client::getSensor(char *protocol, int protocolLen, char *model, int modelLen, int *sensorId, int *dataTypes) { + if (!d->sensorCached) { + Message msg(L"tdSensor"); + std::wstring response = Client::getWStringFromService(msg); + int count = Message::takeInt(&response); + d->sensorCached = true; + d->sensorCache = L""; + if (count > 0) { + d->sensorCache = response; + } + } + + if (d->sensorCache == L"") { + d->sensorCached = false; + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + std::wstring p = Message::takeString(&d->sensorCache); + std::wstring m = Message::takeString(&d->sensorCache); + int id = Message::takeInt(&d->sensorCache); + int dt = Message::takeInt(&d->sensorCache); + + if (protocol && protocolLen) { + strncpy(protocol, TelldusCore::wideToString(p).c_str(), protocolLen); + } + if (model && modelLen) { + strncpy(model, TelldusCore::wideToString(m).c_str(), modelLen); + } + if (sensorId) { + (*sensorId) = id; + } + if (dataTypes) { + (*dataTypes) = dt; + } + + return TELLSTICK_SUCCESS; +} + +int Client::getController(int *controllerId, int *controllerType, char *name, int nameLen, int *available) { + if (!d->controllerCached) { + Message msg(L"tdController"); + std::wstring response = Client::getWStringFromService(msg); + int count = Message::takeInt(&response); + d->controllerCached = true; + d->controllerCache = L""; + if (count > 0) { + d->controllerCache = response; + } + } + + if (d->controllerCache == L"") { + d->controllerCached = false; + return TELLSTICK_ERROR_NOT_FOUND; + } + + int id = Message::takeInt(&d->controllerCache); + int type = Message::takeInt(&d->controllerCache); + std::wstring n = Message::takeString(&d->controllerCache); + int a = Message::takeInt(&d->controllerCache); + + if (controllerId) { + (*controllerId) = id; + } + if (controllerType) { + (*controllerType) = type; + } + if (name && nameLen) { + strncpy(name, TelldusCore::wideToString(n).c_str(), nameLen); + } + if (available) { + (*available) = a; + } + + return TELLSTICK_SUCCESS; +} + +} // namespace TelldusCore diff --git a/telldus-core/client/Client.h b/telldus-core/client/Client.h new file mode 100644 index 0000000..ce47a54 --- /dev/null +++ b/telldus-core/client/Client.h @@ -0,0 +1,47 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_CLIENT_CLIENT_H_ +#define TELLDUS_CORE_CLIENT_CLIENT_H_ + +#include "client/telldus-core.h" +#include "client/CallbackDispatcher.h" +#include "common/Message.h" +#include "common/Thread.h" + +namespace TelldusCore { + class Client : public Thread { + public: + ~Client(void); + + static Client *getInstance(); + static void close(); + + int registerEvent(CallbackStruct::CallbackType type, void *eventFunction, void *context ); + void stopThread(void); + int unregisterCallback( int callbackId ); + + int getSensor(char *protocol, int protocolLen, char *model, int modelLen, int *id, int *dataTypes); + int getController(int *controllerId, int *controllerType, char *name, int nameLen, int *available); + + static bool getBoolFromService(const Message &msg); + static int getIntegerFromService(const Message &msg); + static std::wstring getWStringFromService(const Message &msg); + + protected: + void run(void); + + private: + Client(); + static std::wstring sendToService(const Message &msg); + + class PrivateData; + PrivateData *d; + static Client *instance; + }; +} + +#endif // TELLDUS_CORE_CLIENT_CLIENT_H_ diff --git a/telldus-core/client/libtelldus-core.def b/telldus-core/client/libtelldus-core.def new file mode 100644 index 0000000..57c3976 --- /dev/null +++ b/telldus-core/client/libtelldus-core.def @@ -0,0 +1,60 @@ +LIBRARY tellduscore +EXPORTS + tdGetNumberOfDevices @1 + tdGetDeviceId @2 + + tdGetName @3 + tdGetProtocol @4 + tdGetModel @5 + tdGetDeviceParameter @6 + + tdSetName @7 + tdSetProtocol @8 + tdSetModel @9 + tdSetDeviceParameter @10 + + tdAddDevice @11 + tdRemoveDevice @12 + + tdMethods @13 + tdTurnOn @14 + tdTurnOff @15 + tdBell @16 + tdDim @17 + + tdGetErrorString @18 + + tdClose @19 + + tdInit @20 + tdRegisterDeviceEvent @21 + tdLastSentCommand @22 + tdGetDeviceType @23 + + tdSendRawCommand @24 + tdRegisterRawDeviceEvent @25 + + tdLearn @26 + tdLastSentValue @27 + + tdReleaseString @28 + tdUnregisterCallback @29 + + tdConnectTellStickController @30 + tdDisconnectTellStickController @31 + + tdRegisterDeviceChangeEvent @32 + tdExecute @33 + tdUp @34 + tdDown @35 + tdStop @36 + + tdRegisterSensorEvent @37 + tdSensor @38 + tdSensorValue @39 + + tdController @40 + tdControllerValue @41 + tdSetControllerValue @42 + tdRemoveController @43 + tdRegisterControllerEvent @44 diff --git a/telldus-core/client/telldus-core.cpp b/telldus-core/client/telldus-core.cpp new file mode 100644 index 0000000..c9a977b --- /dev/null +++ b/telldus-core/client/telldus-core.cpp @@ -0,0 +1,1296 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +/** + * @defgroup core telldus-core + * Telldus Core is the base module used to interface a Telldus TellStick. + * @{ + */ + +#include +#include +#include "client/telldus-core.h" +#include "client/Client.h" +#include "common/common.h" +#include "common/Message.h" +#include "common/Socket.h" + +/** + * @name Device method flags + * Flags for the different methods/commands a device can support. Can be used + * as bit flags in e.g. tdMethods(). + * @{ + * + * @def TELLSTICK_TURNON + * Device-flag for devices supporting the tdTurnOn() call. + * @def TELLSTICK_TURNOFF + * Device-flag for devices supporting the tdTurnOff() call. + * @def TELLSTICK_BELL + * Device-flag for devices supporting the tdBell() call. + * @def TELLSTICK_TOGGLE + * This method is currently unimplemented. + * @def TELLSTICK_DIM + * Device-flag for devices supporting the tdDim() call. + * @def TELLSTICK_LEARN + * Device-flag for devices supporting the tdLearn() call. + * @def TELLSTICK_EXECUTE + * Device-flag for devices (scenes) supporting the tdExecute() call. + * @def TELLSTICK_UP + * Device-flag for devices supporting the tdUp() call. + * @def TELLSTICK_DOWN + * Device-flag for devices supporting the tdDown() call. + * @def TELLSTICK_STOP + * Device-flag for devices supporting the tdStop() call. + * + **//* @} */ + +/** + * @name Device types + * The device type as returned from tdGetDeviceType(). + * @{ + * + * @def TELLSTICK_TYPE_DEVICE + * Device type of a single device. + * @def TELLSTICK_TYPE_GROUP + * Device type of a device which contains other devices. + * @def TELLSTICK_TYPE_SCENE + * Device type of a device which contains other devices that are sent + * different commands when executed (i.e. one device turns on, another turns + * off). + * + **//* @} */ + +/** + * @name Sensor value types + * The supported sensor value types are returned from tdSensor() and used + * when querying a sensor for a specific value in tdSensorValue(). + * @{ + * + * @def TELLSTICK_TEMPERATURE + * The sensor can report the temperature. + * @def TELLSTICK_HUMIDITY + * The sensor can report the humidity. + * @def TELLSTICK_RAINRATE + * The sensor can report rain fall rate. + * @def TELLSTICK_RAINTOTAL + * The sensor can report total rain fall. + * @def TELLSTICK_WINDDIRECTION + * The sensor can report wind direction. + * @def TELLSTICK_WINDAVERAGE + * The sensor can report average wind speed. + * @def TELLSTICK_WINDGUST + * The sensor can report gust wind speed. + * + **//* @} */ + +/** + * @name Controller type + * The controller type as returned from tdController(). + * @{ + * + * @def TELLSTICK_CONTROLLER_TELLSTICK + * The controller is a TellStick. + * @def TELLSTICK_CONTROLLER_TELLSTICK_DUO + * The controller is a TellStick Duo. + * @def TELLSTICK_CONTROLLER_TELLSTICK_NET + * The controller is a TellStick Net. + * + **//* @} */ + +/** + * @name Device changes + * Flags used in event callbacks. + * @{ + * + * @def TELLSTICK_DEVICE_ADDED + * A new device was added. + * @def TELLSTICK_DEVICE_CHANGED + * A device was changed. + * @def TELLSTICK_DEVICE_REMOVED + * A device was removed. + * @def TELLSTICK_DEVICE_STATE_CHANGED + * The state changed for a device. + * + **//* @} */ + +/** + * @name Change types + * Flags used in event callbacks. + * @{ + * + * @def TELLSTICK_CHANGE_NAME + * The name has changed. + * @def TELLSTICK_CHANGE_PROTOCOL + * The protocol has changed. + * @def TELLSTICK_CHANGE_MODEL + * The model has changed. + * @def TELLSTICK_CHANGE_METHOD + * The method has changed. + * @def TELLSTICK_CHANGE_AVAILABLE + * The controller is now available or unavailable. + * @def TELLSTICK_CHANGE_FIRMWARE + * The firmware has changed. + * + **//* @} */ + +/** + * @name Error codes + * The error codes returned from some API functions. + * @{ + * + * @def TELLSTICK_SUCCESS + * Error code. Returned when the command succeeded. + * @def TELLSTICK_ERROR_NOT_FOUND + * Error code. Returned if a TellStick was not found on the system. + * @def TELLSTICK_ERROR_PERMISSION_DENIED + * Error code. Returned if the user doesn't have privileges to open the + * TellStick device. + * @def TELLSTICK_ERROR_DEVICE_NOT_FOUND + * Error code. The supplied device id was not found. + * @def TELLSTICK_ERROR_METHOD_NOT_SUPPORTED + * Error code. The requested method is not supported by the device. This + * should be avoided by a call to tdMethods(). + * @def TELLSTICK_ERROR_COMMUNICATION + * Error code. An error occurred when communicating with TellStick. + * @def TELLSTICK_ERROR_CONNECTING_SERVICE + * Error code. The client library could not connect to the service. Maybe it + * is not running? + * @def TELLSTICK_ERROR_UNKNOWN_RESPONSE + * Error code. The client library received a response from the service it did + * not understand. + * @def TELLSTICK_ERROR_SYNTAX + * Error code. Input/command could not be parsed or didn't follow input + * rules. + * @def TELLSTICK_ERROR_BROKEN_PIPE + * Error code. Pipe broken during communication. + * @def TELLSTICK_ERROR_COMMUNICATING_SERVICE + * Error code. Timeout waiting for response from the Telldus Service. + * @def TELLSTICK_ERROR_UNKNOWN + * Error code. An unknown error has occurred. + * + **//* @} */ + +/** + * @name Callback typedefs + * @{ + * + * @typedef TDDeviceEvent + * The callback type for device events. + * + * @attention + * The callback will be called by another thread than the thread used by the + * application and some measures must be taken to synchronize it with the + * main thread. + * + * @param deviceId + * The id of the device that changed. + * @param method + * The new device state. Can be @ref TELLSTICK_TURNON, @ref + * TELLSTICK_TURNOFF, etc. + * @param data + * If @a method is @ref TELLSTICK_DIM this holds the current value as a human + * readable string, example "128" for 50%. + * @param callbackId + * The id of the callback. + * @param context + * The pointer passed when registering for the event. + * + * @sa tdRegisterDeviceEvent + * + ****************************************************************************** + * + * @typedef TDDeviceChangeEvent + * The callback type for device change events. + * + * @attention + * The callback will be called by another thread than the thread used by the + * application and some measures must be taken to synchronize it with the + * main thread. + * + * @param deviceId + * The id of the device that was added, changed or removed. + * @param changeEvent + * One of the constants @ref TELLSTICK_DEVICE_ADDED, @ref + * TELLSTICK_DEVICE_CHANGED or @ref TELLSTICK_DEVICE_REMOVED. + * @param changeType + * If @a changeEvent is @ref TELLSTICK_DEVICE_CHANGED, this parameter + * indicates what has changed (e.g @ref TELLSTICK_CHANGE_NAME, @ref + * TELLSTICK_CHANGE_PROTOCOL, @ref TELLSTICK_CHANGE_MODEL or @ref + * TELLSTICK_CHANGE_METHOD). + * @param callbackId + * The id of the callback. + * @param context + * The pointer passed when registering for the event. + * + * @sa tdRegisterDeviceChangeEvent + * + ****************************************************************************** + * + * @typedef TDRawDeviceEvent + * The callback type for raw events. + * + * @attention + * The callback will be called by another thread than the thread used by the + * application and some measures must be taken to synchronize it with the + * main thread. + * + * @param data + * Raw device data. + * @param controllerId + * Id of receiving controller. + * @param callbackId + * The id of the callback. + * @param context + * The pointer passed when registering for the event. + * + * @sa tdRegisterRawDeviceEvent + * + ****************************************************************************** + * + * @typedef TDSensorEvent + * The callback type for sensor events. + * + * @attention + * The callback will be called by another thread than the thread used by the + * application and some measures must be taken to synchronize it with the + * main thread. + * + * @param protocol + * The sensor's protocol. + * @param model + * The model of the sensor. + * @param id + * The unique id for the sensor. + * @param dataType + * The type that @a value is. Can be one of @ref TELLSTICK_TEMPERATURE, + * @ref TELLSTICK_HUMIDITY, @ref TELLSTICK_RAINTOTAL, + * @ref TELLSTICK_RAINRATE, @ref TELLSTICK_WINDDIRECTION, + * @ref TELLSTICK_WINDAVERAGE or @ref TELLSTICK_WINDGUST. + * @param value + * A human readable string of the data. + * @param timestamp + * The time (as returned by the time() system call) when the value was + * received. + * @param callbackId + * The id of the callback. + * @param context + * The pointer passed when registering for the event. + * + * @sa tdRegisterSensorEvent + * + ****************************************************************************** + * + * @typedef TDControllerEvent + * The callback type for controller events. + * + * @attention + * The callback will be called by another thread than the thread used by the + * application and some measures must be taken to synchronize it with the + * main thread. + * + * @param controllerId + * The id of the controller that was added, changed or removed. + * @param changeEvent + * One of the constants @ref TELLSTICK_DEVICE_ADDED, @ref + * TELLSTICK_DEVICE_CHANGED, @ref TELLSTICK_DEVICE_STATE_CHANGED or @ref + * TELLSTICK_DEVICE_REMOVED. + + * @param changeType + * If @a changeEvent is: + * @arg @ref TELLSTICK_DEVICE_ADDED this is the controller's type + * (e.g. @ref TELLSTICK_CONTROLLER_TELLSTICK or @ref + * TELLSTICK_CONTROLLER_TELLSTICK_DUO), + * @arg @ref TELLSTICK_DEVICE_CHANGED this indicates what has changed + * (e.g. @ref TELLSTICK_CHANGE_NAME or @ref TELLSTICK_CHANGE_FIRMWARE), + * @arg @ref TELLSTICK_DEVICE_STATE_CHANGED this indicates which state that + * has changed (e.g. @ref TELLSTICK_CHANGE_AVAILABLE), + * @arg @ref TELLSTICK_DEVICE_REMOVED this is unused. + * @param newValue + * If @a changeEvent is: + * @arg @ref TELLSTICK_DEVICE_CHANGED this is the property's new value, + * @arg @ref TELLSTICK_DEVICE_STATE_CHANGED this is the new state. For @ref + * TELLSTICK_CHANGE_AVAILABLE this is either @c "0" or @c "1". + * @param callbackId + * The id of the callback. + * @param context + * The pointer passed when registering for the event. + * + * @sa tdRegisterControllerEvent + * + **//* @} */ + +/** + * This function initiates the library. Call this function before any other + * call to a function in telldus-core. + * + * @since Version 2.0.0 + **/ +void WINAPI tdInit(void) { + TelldusCore::Client::getInstance(); // Create the manager-object +} + +/** + * Register a callback that will receive device events. + * + * @param eventFunction + * Callback function. + * @param context + * Pointer that will be passed back in the callback. + * + * @returns + * An id identifying the callback. Pass this id to tdUnregisterCallback() to + * stop receiving callbacks. + * + * @sa @ref sec_events_registering + * @since Version 2.0.0 + **/ +int WINAPI tdRegisterDeviceEvent( TDDeviceEvent eventFunction, void *context ) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->registerEvent( TelldusCore::CallbackStruct::DeviceEvent, reinterpret_cast(eventFunction), context ); +} + +/** + * Register a callback that will receive raw (i.e. all) events. + * + * @param eventFunction + * Callback function. + * @param context + * Pointer that will be passed back in the callback. + * + * @returns + * An id identifying the callback. Pass this id to tdUnregisterCallback() to + * stop receiving callbacks. + * + * @sa @ref sec_events_registering + * @since Version 2.0.0 + **/ +int WINAPI tdRegisterRawDeviceEvent( TDRawDeviceEvent eventFunction, void *context ) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->registerEvent( TelldusCore::CallbackStruct::RawDeviceEvent, reinterpret_cast(eventFunction), context ); +} + +/** + * Register a callback that will receive device change events. This includes + * addition of new devices, changes of device properties (e.g. name) and + * removal of existing devices. + * + * @param eventFunction + * Callback function. + * @param context + * Pointer that will be passed back in the callback. + * + * @returns + * An id identifying the callback. Pass this id to tdUnregisterCallback() to + * stop receiving callbacks. + * + * @sa @ref sec_events_registering + * @since Version 2.1.0 + **/ +int WINAPI tdRegisterDeviceChangeEvent( TDDeviceChangeEvent eventFunction, void *context) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->registerEvent( TelldusCore::CallbackStruct::DeviceChangeEvent, reinterpret_cast(eventFunction), context ); +} + +/** + * Register a callback that will receive sensor events. + * + * @param eventFunction + * Callback function. + * @param context + * Pointer that will be passed back in the callback. + * + * @returns + * An id identifying the callback. Pass this id to tdUnregisterCallback() to + * stop receiving callbacks. + * + * @sa @ref sec_events_registering + * @since Version 2.1.0 + **/ +int WINAPI tdRegisterSensorEvent( TDSensorEvent eventFunction, void *context) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->registerEvent( TelldusCore::CallbackStruct::SensorEvent, reinterpret_cast(eventFunction), context ); +} + +/** + * Register a callback that will receive controller events. + * + * @param eventFunction + * Callback function. + * @param context + * Pointer that will be passed back in the callback. + * + * @returns + * An id identifying the callback. Pass this id to tdUnregisterCallback() to + * stop receiving callbacks. + * + * @sa @ref sec_events_registering + * @since Version 2.1.2 + **/ +int WINAPI tdRegisterControllerEvent( TDControllerEvent eventFunction, void *context) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->registerEvent( TelldusCore::CallbackStruct::ControllerEvent, reinterpret_cast(eventFunction), context ); +} + +/** + * Unregister a callback. + * + * @param callbackId + * The returned id from one of the tdRegister* functions. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @sa @ref sec_events_registering + * @since Version 2.1.0 + **/ +int WINAPI tdUnregisterCallback( int callbackId ) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->unregisterCallback( callbackId ); +} + +/** + * Close the library and clean up the cache it uses. This should be called + * when the library is not supposed to be used anymore. + * + * @since Version 2.0.0 + **/ +void WINAPI tdClose(void) { + TelldusCore::Client::close(); +} + +/** + * This method releases resources allocated by telldus-core. It should be + * called on the returned value from all functions returning char *. + * + * @param thestring + * A string returned from a td* function. + * + * @since Version 2.0.1 + **/ +void WINAPI tdReleaseString(char *thestring) { +#ifdef _WINDOWS + SysFreeString((BSTR)thestring); +#else + free(thestring); +#endif +} + +/** + * Turns a device on. Make sure the device supports this by calling + * tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to turn on. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.0.0 + **/ +int WINAPI tdTurnOn(int intDeviceId) { + TelldusCore::Message msg(L"tdTurnOn"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Turns a device off. Make sure the device supports this by calling + * tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to turn off. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.0.0 + **/ +int WINAPI tdTurnOff(int intDeviceId) { + TelldusCore::Message msg(L"tdTurnOff"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Sends bell command to devices supporting this. Make sure the device + * supports this by calling tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to send bell to. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.0.0 + **/ +int WINAPI tdBell(int intDeviceId) { + TelldusCore::Message msg(L"tdBell"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Dims a device. Make sure the device supports this by calling tdMethods() + * before any call to this function. + * + * @param intDeviceId + * The device id to dim. + * @param level + * The level the device should dim to. This value should be 0 - 255. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.0.0 + **/ +int WINAPI tdDim(int intDeviceId, unsigned char level) { + TelldusCore::Message msg(L"tdDim"); + msg.addArgument(intDeviceId); + msg.addArgument(level); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Execute a scene action. Make sure the device supports this by calling + * tdMethods() before any call to this function. + * + * @param intDeviceId + * The id to execute. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.1.0 + **/ +int WINAPI tdExecute(int intDeviceId) { + TelldusCore::Message msg(L"tdExecute"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Send "up" command to device. Make sure the device supports this by calling + * tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to send the command to. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.1.0 + **/ +int WINAPI tdUp(int intDeviceId) { + TelldusCore::Message msg(L"tdUp"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Send "down" command to device. Make sure the device supports this by + * calling tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to send the command to. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.1.0 + **/ +int WINAPI tdDown(int intDeviceId) { + TelldusCore::Message msg(L"tdDown"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Send "stop" command to device. Make sure the device supports this by + * calling tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to stop. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.1.0 + */ +int WINAPI tdStop(int intDeviceId) { + TelldusCore::Message msg(L"tdStop"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Sends a special learn command to some devices that need a special + * learn-command to be used from TellStick. Make sure the device supports this + * by calling tdMethods() before any call to this function. + * + * @param intDeviceId + * The device id to learn. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or appropriate error code on failure. + * + * @since Version 2.0.0 + **/ +int WINAPI tdLearn(int intDeviceId) { + TelldusCore::Message msg(L"tdLearn"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Returns the last sent command to a specific device + * + * @param intDeviceId + * The device id to query. + * @param methodsSupported + * The methods supported by the client. See tdMethods() for more information. + * + * @returns + * The last sent command as integer, example @ref TELLSTICK_TURNON or @ref + * TELLSTICK_TURNOFF + * + * @since Version 2.0.0 + **/ +int WINAPI tdLastSentCommand(int intDeviceId, int methodsSupported ) { + TelldusCore::Message msg(L"tdLastSentCommand"); + msg.addArgument(intDeviceId); + msg.addArgument(methodsSupported); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * If the last sent command it @ref TELLSTICK_DIM this returns the dimmed + * value. + * + * @param intDeviceId + * The device id to query. + * + * @returns + * The the value as a human readable string, example "128" for 50%. The + * returned string must be freed by calling tdReleaseString(). + * + * @since Version 2.0.0 + **/ +char * WINAPI tdLastSentValue( int intDeviceId ) { + TelldusCore::Message msg(L"tdLastSentValue"); + msg.addArgument(intDeviceId); + std::wstring strReturn = TelldusCore::Client::getWStringFromService(msg); + return wrapStdWstring(strReturn); +} + +/** + * This function returns the number of devices configured. + * + * @returns + * An integer of the total number of devices configured. + * + * @since Version 2.0.0 + **/ +int WINAPI tdGetNumberOfDevices(void) { + return TelldusCore::Client::getIntegerFromService(TelldusCore::Message(L"tdGetNumberOfDevices")); +} + +/** + * This function returns the unique id of a device with a specific index. + * + * To get all the id numbers you should loop over all the devices: + * @code + * int intNumberOfDevices = tdGetNumberOfDevices(); + * for (int i = 0; i < intNumberOfDevices; i++) { + * int id = tdGetDeviceId( i ); + * // id now contains the id number of the device with index of i + * } + * @endcode + * + * @param intDeviceIndex + * The device index to query. The index starts from 0. + * + * @returns + * The unique id for the device or @c -1 if the device is not found. + * + * @since Version 2.0.0 + **/ +int WINAPI tdGetDeviceId(int intDeviceIndex) { + TelldusCore::Message msg(L"tdGetDeviceId"); + msg.addArgument(intDeviceIndex); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Get the device type. + * + * @param intDeviceId + * The unique id of the device to query. + * + * @returns + * Which type the device is. The device could be either @ref + * TELLSTICK_TYPE_DEVICE, @ref TELLSTICK_TYPE_GROUP or @ref + * TELLSTICK_TYPE_SCENE + * + * @since Version 2.0.0 + **/ +int WINAPI tdGetDeviceType(int intDeviceId) { + TelldusCore::Message msg(L"tdGetDeviceType"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Query a device for it's name. + * + * @param intDeviceId + * The unique id of the device to query. + * + * @returns + * The name of the device or an empty string if the device is not found. The + * returned string must be freed by calling tdReleaseString(). + * + * @since Version 2.0.0 + **/ +char * WINAPI tdGetName(int intDeviceId) { + TelldusCore::Message msg(L"tdGetName"); + msg.addArgument(intDeviceId); + std::wstring strReturn = TelldusCore::Client::getWStringFromService(msg); + return wrapStdWstring(strReturn); +} + +/** + * Sets a new name for a device. The devices are global for all application, + * changing this will change the name for other applications as well. + * + * @param intDeviceId + * The device id to change the name for. + * @param strNewName + * The new name for the devices. + * + * @returns + * @c true on success, @c false otherwise. + * + * @since Version 2.0.0 + **/ +bool WINAPI tdSetName(int intDeviceId, const char* strNewName) { + TelldusCore::Message msg(L"tdSetName"); + msg.addArgument(intDeviceId); + msg.addArgument(strNewName); + return TelldusCore::Client::getBoolFromService(msg); +} + +/** + * Get the device protocol. + * + * @param intDeviceId + * The device id to query. + * + * @returns + * The protocol used by a specific device. The returned string must be freed + * by calling tdReleaseString(). + * + * @since Version 2.0.0 + **/ +char* WINAPI tdGetProtocol(int intDeviceId) { + TelldusCore::Message msg(L"tdGetProtocol"); + msg.addArgument(intDeviceId); + std::wstring strReturn = TelldusCore::Client::getWStringFromService(msg); + return wrapStdWstring(strReturn); +} + +/** + * This changes the current protocol used by a device. After changing the + * protocol, setting new parameters is required. + * + * @param intDeviceId + * The device to change. + * @param strProtocol + * The new protocol to use. + * + * @returns + * @c true on success, @c false otherwise. + * + * @sa tdSetModel() + * @sa tdSetDeviceParameter() + * + * @since Version 2.0.0 + **/ +bool WINAPI tdSetProtocol(int intDeviceId, const char* strProtocol) { + TelldusCore::Message msg(L"tdSetProtocol"); + msg.addArgument(intDeviceId); + msg.addArgument(strProtocol); + return TelldusCore::Client::getBoolFromService(msg); +} + +/** + * Get the device model. + * + * @param intDeviceId + * The device to query. + * + * @returns + * The model for a device. Not all protocols uses this. The returned string + * must be freed by calling tdReleaseString(). + * + * @since Version 2.0.0 + **/ +char* WINAPI tdGetModel(int intDeviceId) { + TelldusCore::Message msg(L"tdGetModel"); + msg.addArgument(intDeviceId); + std::wstring strReturn = TelldusCore::Client::getWStringFromService(msg); + return wrapStdWstring(strReturn); +} + +/** + * Sets a new model for a device. Which model to set depends on the + * current protocol. + * + * @param intDeviceId + * The device to change. + * @param strModel + * The new model. + * + * @returns + * @c true on success, @c false otherwise. + * + * @since Version 2.0.0 + **/ +bool WINAPI tdSetModel(int intDeviceId, const char *strModel) { + TelldusCore::Message msg(L"tdSetModel"); + msg.addArgument(intDeviceId); + msg.addArgument(strModel); + return TelldusCore::Client::getBoolFromService(msg); +} + +/** + * Sets a new protocol specific parameter. Please see the documentation of the + * protocols before setting any parameter. + * + * @param intDeviceId + * The device to change. + * @param strName + * The parameter to change. + * @param strValue + * The new value for the parameter. + * + * @returns + * @c true on success, @c false otherwise. + * + * @since Version 2.0.0 + **/ +bool WINAPI tdSetDeviceParameter(int intDeviceId, const char *strName, const char *strValue) { + TelldusCore::Message msg(L"tdSetDeviceParameter"); + msg.addArgument(intDeviceId); + msg.addArgument(strName); + msg.addArgument(strValue); + return TelldusCore::Client::getBoolFromService(msg); +} + +/** + * Get a protocol specific parameter for a device. + * + * @param intDeviceId + * The device to query. + * @param strName + * The name of the parameter to query. + * @param defaultValue + * A default value to return if the current parameter hasn't previously been + * set. + * + * @returns + * The protocol specific parameter specified by @a strName. The returned + * string must be freed by calling tdReleaseString(). + * + * @since Version 2.0.0 + **/ +char * WINAPI tdGetDeviceParameter(int intDeviceId, const char *strName, const char *defaultValue) { + TelldusCore::Message msg(L"tdGetDeviceParameter"); + msg.addArgument(intDeviceId); + msg.addArgument(strName); + msg.addArgument(defaultValue); + std::wstring strReturn = TelldusCore::Client::getWStringFromService(msg); + return wrapStdWstring(strReturn); +} + +/** + * Add a new device to the global database of devices. This function must be + * called first before any call to tdSetName(), tdSetProtocol() and similar + * functions. + * + * @returns + * The new device id for the newly created device. If the creation fails it + * returns an error (i.e. negative) value. + * + * @since Version 2.0.0 + **/ +int WINAPI tdAddDevice() { + TelldusCore::Message msg(L"tdAddDevice"); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Removes a device. + * + * @param intDeviceId + * The device to query. + * + * @returns + * @c true on success, @c false otherwise. + * + * @since Version 2.0.0 + **/ +bool WINAPI tdRemoveDevice(int intDeviceId) { + TelldusCore::Message msg(L"tdRemoveDevice"); + msg.addArgument(intDeviceId); + return TelldusCore::Client::getBoolFromService(msg); +} + +/** + * Query a device for which methods it supports. By supplying the methods you + * support the library could remap the methods a device support for better fit + * the application. + * + * Example of querying a device supporting TELLSTICK_BELL: + * @code + * int methods = tdMethods( + * id, TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_BELL); + * // methods is now TELLSTICK_BELL + * int methods = tdMethods(id, TELLSTICK_TURNON | TELLSTICK_TURNOFF); + * // methods is now TELLSTICK_TURNON because the client application doesn't + * // support TELLSTICK_BELL + * @endcode + * + * @param id + * The device id to query. + * @param methodsSupported + * The methods the client application supports. + * + * @returns + * The method-flags OR'ed into an integer. + * + * @sa TELLSTICK_TURNON + * @sa TELLSTICK_TURNOFF + * @sa TELLSTICK_BELL + * @sa TELLSTICK_TOGGLE + * @sa TELLSTICK_DIM + * @sa TELLSTICK_EXECUTE + * @sa TELLSTICK_UP + * @sa TELLSTICK_DOWN + * @sa TELLSTICK_STOP + * + * @since Version 2.0.0 + **/ +int WINAPI tdMethods(int id, int methodsSupported) { + TelldusCore::Message msg(L"tdMethods"); + msg.addArgument(id); + msg.addArgument(methodsSupported); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Get a human readable string from an error code returned from a function in + * telldus-core. + * + * @param intErrorNo + * The error code to translate. + * + * @returns + * A string ready to show to the user. The returned string must be freed by + * calling tdReleaseString(). + * + * @sa TELLSTICK_SUCCESS + * @sa TELLSTICK_ERROR_NOT_FOUND + * @sa TELLSTICK_ERROR_PERMISSION_DENIED + * @sa TELLSTICK_ERROR_DEVICE_NOT_FOUND + * @sa TELLSTICK_ERROR_METHOD_NOT_SUPPORTED + * @sa TELLSTICK_ERROR_COMMUNICATION + * @sa TELLSTICK_ERROR_CONNECTING_SERVICE + * @sa TELLSTICK_ERROR_UNKNOWN_RESPONSE + * @sa TELLSTICK_ERROR_SYNTAX + * @sa TELLSTICK_ERROR_BROKEN_PIPE + * @sa TELLSTICK_ERROR_COMMUNICATING_SERVICE + * @sa TELLSTICK_ERROR_UNKNOWN + * + * @since Version 2.0.0 + **/ +char * WINAPI tdGetErrorString(int intErrorNo) { + const int numResponses = 12; + const char *responses[numResponses] = { + "Success", + "TellStick not found", + "Permission denied", + "Device not found", + "The method you tried to use is not supported by the device", + "An error occurred while communicating with TellStick", + "Could not connect to the Telldus Service", + "Received an unknown response", + "Syntax error", + "Broken pipe", + "An error occurred while communicating with the Telldus Service", + "Syntax error in the configuration file" + }; + std::string strReturn; + intErrorNo = abs(intErrorNo); // We don't use negative values here. + if (intErrorNo >= numResponses) { + strReturn = "Unknown error"; + } else { + // Copy the error string to strReturn + strReturn = responses[intErrorNo]; + } + return wrapStdString(strReturn); +} + +/** + * Send a raw command to TellStick. Please read the TellStick protocol + * definition on how the command should be constructed. + * + * @param command + * The command for TellStick in its native format. + * @param reserved + * Reserved for future use. + * + * @returns + * @ref TELLSTICK_SUCCESS on success or one of the error codes on failure + * + * @since Version 2.0.0 + **/ +int WINAPI tdSendRawCommand(const char *command, int reserved) { + std::wstring wcommand; + for(int i = 0; i < strlen(command); ++i) { + wcommand.append(1, (unsigned char)command[i]); + } + TelldusCore::Message msg(L"tdSendRawCommand"); + msg.addArgument(wcommand); + msg.addArgument(reserved); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * Connect a TellStick controller. + * + * @param vid + * The vendor id. + * @param pid + * The product id. + * @param serial + * The serial. + * + * @since Version 2.1.0 + **/ +void WINAPI tdConnectTellStickController(int vid, int pid, const char *serial) { + TelldusCore::Message msg(L"tdConnectTellStickController"); + msg.addArgument(vid); + msg.addArgument(pid); + msg.addArgument(serial); + TelldusCore::Client::getWStringFromService(msg); +} + +/** + * Disconnect a TellStick controller. + * + * @param vid + * The vendor id. + * @param pid + * The product id. + * @param serial + * The serial. + * + * @since Version 2.1.0 + **/ +void WINAPI tdDisconnectTellStickController(int vid, int pid, const char *serial) { + TelldusCore::Message msg(L"tdDisconnectTellStickController"); + msg.addArgument(vid); + msg.addArgument(pid); + msg.addArgument(serial); + TelldusCore::Client::getWStringFromService(msg); +} + +/** + * Use this function to iterate over all sensors. Iterate until @ref + * TELLSTICK_SUCCESS is not returned. + * + * @param[out] protocol + * A by ref string where the protocol of the sensor will be placed. + * @param[in] protocolLen + * The length of the @a protocol parameter. + * @param[out] model + * A by ref string where the model of the sensor will be placed. + * @param[in] modelLen + * The length of the @a model parameter. + * @param[out] id + * A by ref int where the id of the sensor will be placed. + * @param[out] dataTypes + * A by ref int with flags for the supported sensor values. + * + * @returns + * @ref TELLSTICK_SUCCESS if there is more sensors to be fetched. + * + * @since Version 2.1.0 + */ +int WINAPI tdSensor(char *protocol, int protocolLen, char *model, int modelLen, int *id, int *dataTypes) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->getSensor(protocol, protocolLen, model, modelLen, id, dataTypes); +} + +/** + * Get one of the supported sensor values from a sensor. Make sure it support + * the value type first by calling tdSensor(). The triplet @a protocol, @a + * model and @a id together identifies a sensor. + * + * @param[in] protocol + * The protocol for the sensor. + * @param[in] model + * The model for the sensor. + * @param[in] id + * The id of the sensor. + * @param[in] dataType + * Which sensor value to retrive (one of @ref TELLSTICK_TEMPERATURE, + * @ref TELLSTICK_HUMIDITY, @ref TELLSTICK_RAINTOTAL, + * @ref TELLSTICK_RAINRATE, @ref TELLSTICK_WINDDIRECTION, + * @ref TELLSTICK_WINDAVERAGE or @ref TELLSTICK_WINDGUST. + * @param[out] value + * A by ref string where the value will be placed. + * @param[in] len + * The length of the @a value parameter. + * @param[out] timestamp + * A by ref int where the timestamp of the value will be placed. + * + * @returns + * @ref TELLSTICK_SUCCESS if the value could be fetched or one of the error + * codes on failure. + * + * @since Version 2.1.0 + */ +int WINAPI tdSensorValue(const char *protocol, const char *model, int id, int dataType, char *value, int len, int *timestamp) { + TelldusCore::Message msg(L"tdSensorValue"); + msg.addArgument(protocol); + msg.addArgument(model); + msg.addArgument(id); + msg.addArgument(dataType); + std::wstring retval = TelldusCore::Client::getWStringFromService(msg); + if (retval.length() == 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + + std::wstring v = TelldusCore::Message::takeString(&retval); + int t = TelldusCore::Message::takeInt(&retval); + if (value && len) { + strncpy(value, TelldusCore::wideToString(v).c_str(), len); + } + if (timestamp) { + (*timestamp) = t; + } + return TELLSTICK_SUCCESS; +} + +/** + * Use this function to iterate over all controllers. Iterate until + * @ref TELLSTICK_SUCCESS is not returned + * + * @param[out] controllerId + * A by ref int where the id of the controller will be placed. + * @param[out] controllerType + * A by ref int where the type of the controller will be placed. + * @param[out] name + * A by ref string where the name of the controller will be placed. + * @param[in] nameLen + * The length of the @a name parameter. + * @param[out] available + * A by ref int if the controller is currently available or maybe + * disconnected. + * + * @returns + * @ref TELLSTICK_SUCCESS if there is more sensors to be fetched. + * + * @sa TELLSTICK_CONTROLLER_TELLSTICK + * @sa TELLSTICK_CONTROLLER_TELLSTICK_DUO + * @sa TELLSTICK_CONTROLLER_TELLSTICK_NET + * + * @since Version 2.1.2 + **/ +int WINAPI tdController(int *controllerId, int *controllerType, char *name, int nameLen, int *available) { + TelldusCore::Client *client = TelldusCore::Client::getInstance(); + return client->getController(controllerId, controllerType, name, nameLen, available); +} + +/** + * This function gets a parameter on a controller. + * Valid parameters are: \c serial, \c name, \c available and \c firmware + * + * @param[in] controllerId + * The controller to change. + * @param[in] name + * The parameter to get. + * @param[out] value + * A by ref string where the value of the parameter will be placed. + * @param[in] valueLen + * The length of the @a value parameter. + * + * @returns + * @ref TELLSTICK_SUCCESS on success, or an error code on failure. + * + * @since Version 2.1.2 + **/ +int WINAPI tdControllerValue(int controllerId, const char *name, char *value, int valueLen) { + TelldusCore::Message msg(L"tdControllerValue"); + msg.addArgument(controllerId); + msg.addArgument(name); + std::wstring retval = TelldusCore::Client::getWStringFromService(msg); + if (retval.length() == 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + + if (value && valueLen) { + strncpy(value, TelldusCore::wideToString(retval).c_str(), valueLen); + } + return TELLSTICK_SUCCESS; +} + +/** + * This function sets a parameter on a controller. Valid parameters are: @c + * name. + * + * @param controllerId + * The controller to change. + * @param name + * The parameter to change. + * @param value + * The new value for the parameter. + * + * @returns + * @ref TELLSTICK_SUCCESS on success, or an error code on failure. + * + * @since Version 2.1.2 + **/ +int WINAPI tdSetControllerValue(int controllerId, const char *name, const char *value) { + TelldusCore::Message msg(L"tdSetControllerValue"); + msg.addArgument(controllerId); + msg.addArgument(name); + msg.addArgument(value); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/** + * This function removes a controller from the list of controllers. The + * controller must not be available (disconnected) for this to work. + * + * @param controllerId + * The controller to remove. + * + * @returns + * @ref TELLSTICK_SUCCESS if the controller was removed, @ref + * TELLSTICK_ERROR_NOT_FOUND if the controller was not found, and @ref + * TELLSTICK_ERROR_PERMISSION_DENIED if the controller is still connected. + * + * @since Version 2.1.2 + **/ +int WINAPI tdRemoveController(int controllerId) { + TelldusCore::Message msg(L"tdRemoveController"); + msg.addArgument(controllerId); + return TelldusCore::Client::getIntegerFromService(msg); +} + +/* @} */ diff --git a/telldus-core/client/telldus-core.h b/telldus-core/client/telldus-core.h new file mode 100644 index 0000000..21a9bf9 --- /dev/null +++ b/telldus-core/client/telldus-core.h @@ -0,0 +1,166 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_CLIENT_TELLDUS_CORE_H_ +#define TELLDUS_CORE_CLIENT_TELLDUS_CORE_H_ + +// The following ifdef block is the standard way of creating macros +// which make exporting from a DLL simpler. All files within this DLL +// are compiled with the TELLDUSCORE_EXPORTS symbol defined on the command line. +// This symbol should not be defined on any project that uses this DLL. +// This way any other project whose source files include this file see +// TELLSTICK_API functions as being imported from a DLL, whereas this DLL +// sees symbols defined with this macro as being exported. + +#ifdef _WINDOWS + #if defined(TELLDUSCORE_EXPORTS) + #if defined(_CL64) + #define TELLSTICK_API + #else + #define TELLSTICK_API __declspec(dllexport) + #endif + #else + #define TELLSTICK_API __declspec(dllimport) + #endif + #define WINAPI __stdcall +#else + #define WINAPI + #define TELLSTICK_API __attribute__ ((visibility("default"))) +#endif + +typedef void (WINAPI *TDDeviceEvent)(int deviceId, int method, const char *data, int callbackId, void *context); +typedef void (WINAPI *TDDeviceChangeEvent)(int deviceId, int changeEvent, int changeType, int callbackId, void *context); +typedef void (WINAPI *TDRawDeviceEvent)(const char *data, int controllerId, int callbackId, void *context); +typedef void (WINAPI *TDSensorEvent)(const char *protocol, const char *model, int id, int dataType, const char *value, int timestamp, int callbackId, void *context); +typedef void (WINAPI *TDControllerEvent)(int controllerId, int changeEvent, int changeType, const char *newValue, int callbackId, void *context); + +#ifndef __cplusplus + #define bool char +#endif + +#ifdef __cplusplus +extern "C" { +#endif + TELLSTICK_API void WINAPI tdInit(void); + TELLSTICK_API int WINAPI tdRegisterDeviceEvent( TDDeviceEvent eventFunction, void *context ); + TELLSTICK_API int WINAPI tdRegisterDeviceChangeEvent( TDDeviceChangeEvent eventFunction, void *context); + TELLSTICK_API int WINAPI tdRegisterRawDeviceEvent( TDRawDeviceEvent eventFunction, void *context ); + TELLSTICK_API int WINAPI tdRegisterSensorEvent( TDSensorEvent eventFunction, void *context ); + TELLSTICK_API int WINAPI tdRegisterControllerEvent( TDControllerEvent eventFunction, void *context); + TELLSTICK_API int WINAPI tdUnregisterCallback( int callbackId ); + TELLSTICK_API void WINAPI tdClose(void); + TELLSTICK_API void WINAPI tdReleaseString(char *thestring); + + TELLSTICK_API int WINAPI tdTurnOn(int intDeviceId); + TELLSTICK_API int WINAPI tdTurnOff(int intDeviceId); + TELLSTICK_API int WINAPI tdBell(int intDeviceId); + TELLSTICK_API int WINAPI tdDim(int intDeviceId, unsigned char level); + TELLSTICK_API int WINAPI tdExecute(int intDeviceId); + TELLSTICK_API int WINAPI tdUp(int intDeviceId); + TELLSTICK_API int WINAPI tdDown(int intDeviceId); + TELLSTICK_API int WINAPI tdStop(int intDeviceId); + TELLSTICK_API int WINAPI tdLearn(int intDeviceId); + TELLSTICK_API int WINAPI tdMethods(int id, int methodsSupported); + TELLSTICK_API int WINAPI tdLastSentCommand( int intDeviceId, int methodsSupported ); + TELLSTICK_API char *WINAPI tdLastSentValue( int intDeviceId ); + + TELLSTICK_API int WINAPI tdGetNumberOfDevices(); + TELLSTICK_API int WINAPI tdGetDeviceId(int intDeviceIndex); + TELLSTICK_API int WINAPI tdGetDeviceType(int intDeviceId); + + TELLSTICK_API char * WINAPI tdGetErrorString(int intErrorNo); + + TELLSTICK_API char * WINAPI tdGetName(int intDeviceId); + TELLSTICK_API bool WINAPI tdSetName(int intDeviceId, const char* chNewName); + TELLSTICK_API char * WINAPI tdGetProtocol(int intDeviceId); + TELLSTICK_API bool WINAPI tdSetProtocol(int intDeviceId, const char* strProtocol); + TELLSTICK_API char * WINAPI tdGetModel(int intDeviceId); + TELLSTICK_API bool WINAPI tdSetModel(int intDeviceId, const char *intModel); + + TELLSTICK_API char * WINAPI tdGetDeviceParameter(int intDeviceId, const char *strName, const char *defaultValue); + TELLSTICK_API bool WINAPI tdSetDeviceParameter(int intDeviceId, const char *strName, const char* strValue); + + TELLSTICK_API int WINAPI tdAddDevice(); + TELLSTICK_API bool WINAPI tdRemoveDevice(int intDeviceId); + + TELLSTICK_API int WINAPI tdSendRawCommand(const char *command, int reserved); + + TELLSTICK_API void WINAPI tdConnectTellStickController(int vid, int pid, const char *serial); + TELLSTICK_API void WINAPI tdDisconnectTellStickController(int vid, int pid, const char *serial); + + TELLSTICK_API int WINAPI tdSensor(char *protocol, int protocolLen, char *model, int modelLen, int *id, int *dataTypes); + TELLSTICK_API int WINAPI tdSensorValue(const char *protocol, const char *model, int id, int dataType, char *value, int len, int *timestamp); + + TELLSTICK_API int WINAPI tdController(int *controllerId, int *controllerType, char *name, int nameLen, int *available); + TELLSTICK_API int WINAPI tdControllerValue(int controllerId, const char *name, char *value, int valueLen); + TELLSTICK_API int WINAPI tdSetControllerValue(int controllerId, const char *name, const char *value); + TELLSTICK_API int WINAPI tdRemoveController(int controllerId); + +#ifdef __cplusplus +} +#endif + +// Device methods +#define TELLSTICK_TURNON 1 +#define TELLSTICK_TURNOFF 2 +#define TELLSTICK_BELL 4 +#define TELLSTICK_TOGGLE 8 +#define TELLSTICK_DIM 16 +#define TELLSTICK_LEARN 32 +#define TELLSTICK_EXECUTE 64 +#define TELLSTICK_UP 128 +#define TELLSTICK_DOWN 256 +#define TELLSTICK_STOP 512 + +// Sensor value types +#define TELLSTICK_TEMPERATURE 1 +#define TELLSTICK_HUMIDITY 2 +#define TELLSTICK_RAINRATE 4 +#define TELLSTICK_RAINTOTAL 8 +#define TELLSTICK_WINDDIRECTION 16 +#define TELLSTICK_WINDAVERAGE 32 +#define TELLSTICK_WINDGUST 64 + +// Error codes +#define TELLSTICK_SUCCESS 0 +#define TELLSTICK_ERROR_NOT_FOUND -1 +#define TELLSTICK_ERROR_PERMISSION_DENIED -2 +#define TELLSTICK_ERROR_DEVICE_NOT_FOUND -3 +#define TELLSTICK_ERROR_METHOD_NOT_SUPPORTED -4 +#define TELLSTICK_ERROR_COMMUNICATION -5 +#define TELLSTICK_ERROR_CONNECTING_SERVICE -6 +#define TELLSTICK_ERROR_UNKNOWN_RESPONSE -7 +#define TELLSTICK_ERROR_SYNTAX -8 +#define TELLSTICK_ERROR_BROKEN_PIPE -9 +#define TELLSTICK_ERROR_COMMUNICATING_SERVICE -10 +#define TELLSTICK_ERROR_CONFIG_SYNTAX -11 +#define TELLSTICK_ERROR_UNKNOWN -99 + +// Device typedef +#define TELLSTICK_TYPE_DEVICE 1 +#define TELLSTICK_TYPE_GROUP 2 +#define TELLSTICK_TYPE_SCENE 3 + +// Controller typedef +#define TELLSTICK_CONTROLLER_TELLSTICK 1 +#define TELLSTICK_CONTROLLER_TELLSTICK_DUO 2 +#define TELLSTICK_CONTROLLER_TELLSTICK_NET 3 + +// Device changes +#define TELLSTICK_DEVICE_ADDED 1 +#define TELLSTICK_DEVICE_CHANGED 2 +#define TELLSTICK_DEVICE_REMOVED 3 +#define TELLSTICK_DEVICE_STATE_CHANGED 4 + +// Change types +#define TELLSTICK_CHANGE_NAME 1 +#define TELLSTICK_CHANGE_PROTOCOL 2 +#define TELLSTICK_CHANGE_MODEL 3 +#define TELLSTICK_CHANGE_METHOD 4 +#define TELLSTICK_CHANGE_AVAILABLE 5 +#define TELLSTICK_CHANGE_FIRMWARE 6 + +#endif // TELLDUS_CORE_CLIENT_TELLDUS_CORE_H_ diff --git a/telldus-core/client/telldus-core.rc.in b/telldus-core/client/telldus-core.rc.in new file mode 100644 index 0000000..23b3763 --- /dev/null +++ b/telldus-core/client/telldus-core.rc.in @@ -0,0 +1,18 @@ +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION ${PACKAGE_MAJOR_VERSION},${PACKAGE_MINOR_VERSION},${PACKAGE_PATCH_VERSION},0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "04090000" + BEGIN + VALUE "FileDescription", "Utilities and driver to control wireless receivers through a TellStick" + VALUE "FileVersion", "${PACKAGE_VERSION}" + VALUE "LegalCopyright", "Copyright (C) 2010 Telldus Technologies AB" + VALUE "OriginalFilename", "TelldusCore.dll" + VALUE "ProductName", "TelldusCore" + VALUE "ProductVersion", "${PACKAGE_MAJOR_VERSION}.${PACKAGE_MINOR_VERSION}" + END + END +END \ No newline at end of file diff --git a/telldus-core/cmake/FindSignTool.cmake b/telldus-core/cmake/FindSignTool.cmake new file mode 100644 index 0000000..ff7dfc0 --- /dev/null +++ b/telldus-core/cmake/FindSignTool.cmake @@ -0,0 +1,19 @@ + +IF(WIN32) + SET(SIGN_FILES FALSE CACHE BOOL "Sign the generated files. This requires a certificate to be installed on the computer!") +ENDIF() + +FUNCTION(SIGN TARGET) + IF (NOT WIN32) + RETURN() + ENDIF() + IF (NOT SIGN_FILES) + RETURN() + ENDIF() + GET_TARGET_PROPERTY(file ${TARGET} LOCATION) + GET_FILENAME_COMPONENT(filename ${file} NAME) + ADD_CUSTOM_COMMAND( TARGET ${TARGET} POST_BUILD + COMMAND signtool.exe sign /a /t http://timestamp.verisign.com/scripts/timstamp.dll ${file} + COMMENT "Signing file ${filename}" + ) +ENDFUNCTION() diff --git a/telldus-core/common/CMakeLists.txt b/telldus-core/common/CMakeLists.txt new file mode 100644 index 0000000..13aafa8 --- /dev/null +++ b/telldus-core/common/CMakeLists.txt @@ -0,0 +1,89 @@ +IF(COMMAND cmake_policy) + CMAKE_POLICY(SET CMP0003 NEW) +ENDIF(COMMAND cmake_policy) + +######## Non configurable options ######## +SET( telldus-common_SRCS + Event.cpp + Message.cpp + Mutex.cpp + Strings.cpp + Thread.cpp +) + +SET( telldus-common_HDRS + common.h + Event.h + EventHandler.h + Message.h + Mutex.h + Socket.h + Strings.h + Thread.h +) + +INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR} ) + +######## Configurable options for the platform ######## + + +######## Platforms-specific, non configurable ######## + +IF (APPLE) + #### Mac OS X #### + FIND_LIBRARY(ICONV_LIBRARY iconv) + ADD_DEFINITIONS( -D_MACOSX ) + LIST(APPEND telldus-common_SRCS + Event_unix.cpp + EventHandler_unix.cpp + Socket_unix.cpp + stdlibc_workaround.cpp #Remove this when we drop support for 10.5 + ) + LIST(APPEND telldus-common_LIBRARIES + ${ICONV_LIBRARY} + ) +ELSEIF (WIN32) + #### Windows #### + ADD_DEFINITIONS( -DUNICODE ) + ADD_DEFINITIONS( /Zc:wchar_t- ) # Treat wchar_t as Built-in Type' = No + ADD_DEFINITIONS( -D_WINDOWS ) + LIST(APPEND telldus-common_SRCS + Event_win.cpp + EventHandler_win.cpp + Socket_win.cpp + ) +ELSEIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + #### FreeBSD #### + FIND_LIBRARY(ICONV_LIBRARY iconv) + ADD_DEFINITIONS( -D_FREEBSD ) + LIST(APPEND telldus-common_SRCS + Event_unix.cpp + EventHandler_unix.cpp + Socket_unix.cpp + ) + LIST(APPEND telldus-common_LIBRARIES + ${ICONV_LIBRARY} + ) +ELSE (APPLE) + #### Linux #### + ADD_DEFINITIONS( -D_LINUX ) + LIST(APPEND telldus-common_SRCS + Event_unix.cpp + EventHandler_unix.cpp + Socket_unix.cpp + ) +ENDIF (APPLE) + + +######## Configuring ######## + +ADD_LIBRARY(TelldusCommon STATIC + ${telldus-common_SRCS} + ${telldus-common_HDRS} +) + +IF (UNIX) + SET_TARGET_PROPERTIES( TelldusCommon PROPERTIES COMPILE_FLAGS "-fPIC -fvisibility=hidden") +ENDIF (UNIX) + +TARGET_LINK_LIBRARIES( TelldusCommon ${telldus-common_LIBRARIES} ) diff --git a/telldus-core/common/Event.cpp b/telldus-core/common/Event.cpp new file mode 100644 index 0000000..7cb3055 --- /dev/null +++ b/telldus-core/common/Event.cpp @@ -0,0 +1,88 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "common/Event.h" +#include +#include "common/EventHandler.h" +#include "common/Mutex.h" + +namespace TelldusCore { + +EventData::~EventData() { +} + +bool EventData::isValid() const { + return false; +}; + +bool EventDataBase::isValid() const { + return true; +}; + +class EventBase::PrivateData { +public: + TelldusCore::Mutex mutex; + EventHandler *handler; + std::list eventDataList; +}; + +EventBase::EventBase(TelldusCore::EventHandler *handler) { + d = new PrivateData; + d->handler = handler; +} + +EventBase::~EventBase(void) { + delete d; +} + +void EventBase::clearHandler() { + TelldusCore::MutexLocker locker(&d->mutex); + d->handler = 0; +} + +void EventBase::popSignal() { + this->takeSignal(); +} + +EventHandler *EventBase::handler() const { + return d->handler; +} + +bool EventBase::isSignaled() { + TelldusCore::MutexLocker locker(&d->mutex); + return (d->eventDataList.size() > 0); +} + +void EventBase::signal() { + signal(new EventData()); +} + +void EventBase::signal(EventData *eventData) { + this->signal(EventDataRef(eventData)); +} + +void EventBase::signal(EventDataRef eventData) { + { + TelldusCore::MutexLocker locker(&d->mutex); + d->eventDataList.push_back(eventData); + } + sendSignal(); +} + +EventDataRef EventBase::takeSignal() { + TelldusCore::MutexLocker locker(&d->mutex); + if (d->eventDataList.size() == 0) { + return EventDataRef(new EventData()); + } + EventDataRef data = d->eventDataList.front(); + d->eventDataList.pop_front(); + if (d->eventDataList.size() == 0) { + this->clearSignal(); + } + return data; +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Event.h b/telldus-core/common/Event.h new file mode 100644 index 0000000..8612d72 --- /dev/null +++ b/telldus-core/common/Event.h @@ -0,0 +1,80 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_EVENT_H_ +#define TELLDUS_CORE_COMMON_EVENT_H_ + + +#ifndef _WINDOWS + #include + typedef void* EVENT_T; +#else + #include + #include + typedef HANDLE EVENT_T; +#endif +#include "common/Thread.h" + +namespace TelldusCore { + class EventHandler; + + class EventData { + public: + virtual ~EventData(); + virtual bool isValid() const; + }; + + class EventDataBase : public EventData { + public: + virtual bool isValid() const; + }; + + typedef std::tr1::shared_ptr EventDataRef; + + class EventBase { + public: + virtual ~EventBase(); + + void popSignal(); + bool isSignaled(); + void signal(); + void signal(EventData *eventData); + void signal(EventDataRef eventData); + EventDataRef takeSignal(); + + protected: + explicit EventBase(EventHandler *handler); + void clearHandler(); + virtual void clearSignal() = 0; + EventHandler *handler() const; + virtual void sendSignal() = 0; + + private: + class PrivateData; + PrivateData *d; + }; + + class Event : public EventBase { + public: + virtual ~Event(); + + protected: + explicit Event(EventHandler *handler); + EVENT_T retrieveNative(); + virtual void clearSignal(); + virtual void sendSignal(); + + private: + class PrivateData; + PrivateData *d; + + friend class EventHandler; + }; + + typedef std::tr1::shared_ptr EventRef; +} + +#endif // TELLDUS_CORE_COMMON_EVENT_H_ diff --git a/telldus-core/common/EventHandler.h b/telldus-core/common/EventHandler.h new file mode 100644 index 0000000..620d370 --- /dev/null +++ b/telldus-core/common/EventHandler.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_EVENTHANDLER_H_ +#define TELLDUS_CORE_COMMON_EVENTHANDLER_H_ + +#ifdef _MSC_VER +typedef unsigned __int64 uint64_t; +#else +#include +#endif +#include "common/Event.h" + +namespace TelldusCore { + + class EventHandler { + public: + EventHandler(); + virtual ~EventHandler(void); + + EventRef addEvent(); + + bool waitForAny(); + + protected: + void signal(Event *event); + + private: + class PrivateData; + PrivateData *d; + bool listIsSignalled(); + friend class Event; + }; +} + +#endif // TELLDUS_CORE_COMMON_EVENTHANDLER_H_ diff --git a/telldus-core/common/EventHandler_unix.cpp b/telldus-core/common/EventHandler_unix.cpp new file mode 100644 index 0000000..6c65339 --- /dev/null +++ b/telldus-core/common/EventHandler_unix.cpp @@ -0,0 +1,96 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include +#include "common/EventHandler.h" +#include "common/Event.h" +#include "common/Mutex.h" +#include "common/Thread.h" + +namespace TelldusCore { + +class EventHandler::PrivateData { +public: + pthread_cond_t event; + pthread_mutex_t mutex; + std::list eventList; + TelldusCore::Mutex listMutex; + bool isSignalled; +}; + +EventHandler::EventHandler() { + d = new PrivateData; + pthread_cond_init(&d->event, NULL); + pthread_cond_init(&d->event, NULL); + pthread_mutex_init(&d->mutex, NULL); + d->isSignalled = false; +} + +EventHandler::~EventHandler(void) { + pthread_mutex_destroy(&d->mutex); + pthread_cond_destroy(&d->event); + + std::list::const_iterator it = d->eventList.begin(); + for(; it != d->eventList.end(); ++it) { + // We clear the handler if someone else still has a reference to the event + (*it)->clearHandler(); + } + + delete d; +} + +EventRef EventHandler::addEvent() { + EventRef event(new Event(this)); + TelldusCore::MutexLocker locker(&d->listMutex); + d->eventList.push_back(event); + return event; +} + +bool EventHandler::listIsSignalled() { + TelldusCore::MutexLocker locker(&d->listMutex); + + std::list::const_iterator it = d->eventList.begin(); + for(; it != d->eventList.end(); ++it) { + if((*it)->isSignaled()) { + return true; + } + } + return false; +} + +void EventHandler::signal(Event *event) { + pthread_mutex_lock(&d->mutex); + d->isSignalled = true; + // event->setSignaled(); + pthread_cond_signal(&d->event); + pthread_mutex_unlock(&d->mutex); +} + +bool EventHandler::waitForAny() { + pthread_mutex_lock(&d->mutex); + int ret; + while (!d->isSignalled) { + timeval now; + gettimeofday(&now, NULL); + uint64_t abstime_ns_large = now.tv_usec*1000 + 60000000000; // add 60 seconds wait (5 seconds before)? + timespec abstime = { now.tv_sec + (abstime_ns_large / 1000000000), abstime_ns_large % 1000000000 }; + ret = pthread_cond_timedwait(&d->event, &d->mutex, &abstime); + if (ret == ETIMEDOUT) { + continue; + } + } + if (!listIsSignalled()) { + d->isSignalled = false; + } + pthread_mutex_unlock(&d->mutex); + return listIsSignalled(); +} + +} // namespace TelldusCore diff --git a/telldus-core/common/EventHandler_win.cpp b/telldus-core/common/EventHandler_win.cpp new file mode 100644 index 0000000..57833bd --- /dev/null +++ b/telldus-core/common/EventHandler_win.cpp @@ -0,0 +1,76 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "common/EventHandler.h" +#include +#include + +#include "common/Event.h" +#include "common/Mutex.h" + +namespace TelldusCore { + +class EventHandler::PrivateData { +public: + HANDLE *eventArray; + EventRef *eventObjectArray; + TelldusCore::Mutex mutex; + int eventCount; +}; + +EventHandler::EventHandler() { + d = new PrivateData; + d->eventCount = 0; + d->eventArray = new HANDLE[0]; + d->eventObjectArray = new EventRef[0]; +} + +EventHandler::~EventHandler(void) { + delete[] d->eventObjectArray; + delete[] d->eventArray; + delete d; +} + +EventRef EventHandler::addEvent() { + EventRef event(new Event(this)); + + TelldusCore::MutexLocker locker(&d->mutex); + + HANDLE *newArray = new HANDLE[d->eventCount+1]; + EventRef *newObjectArray = new EventRef[d->eventCount+1]; + for (int i = 0; i < d->eventCount; ++i) { + newArray[i] = d->eventArray[i]; + newObjectArray[i] = d->eventObjectArray[i]; + } + delete[] d->eventArray; + delete[] d->eventObjectArray; + d->eventArray = newArray; + d->eventObjectArray = newObjectArray; + d->eventArray[d->eventCount] = event->retrieveNative(); + d->eventObjectArray[d->eventCount] = event; + ++d->eventCount; + return event; +} + +void EventHandler::signal(Event *event) { +} + +bool EventHandler::waitForAny() { + while(1) { + int result = WaitForMultipleObjects(d->eventCount, d->eventArray, FALSE, 1000); + if (result == WAIT_TIMEOUT) { + continue; + } + TelldusCore::MutexLocker locker(&d->mutex); + int eventIndex = result - WAIT_OBJECT_0; + if (eventIndex >= d->eventCount) { + return false; + } + return true; + } +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Event_unix.cpp b/telldus-core/common/Event_unix.cpp new file mode 100644 index 0000000..301b6b2 --- /dev/null +++ b/telldus-core/common/Event_unix.cpp @@ -0,0 +1,36 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "common/Event.h" +#include "common/EventHandler.h" +#include "common/Thread.h" + +namespace TelldusCore { + +class Event::PrivateData { +public: +}; + +Event::Event(EventHandler *handler) + :EventBase(handler) { + d = new PrivateData; +} + +Event::~Event(void) { + delete d; +} + +void Event::clearSignal() { +} + +void Event::sendSignal() { + EventHandler *handler = this->handler(); + if (handler) { + handler->signal(this); + } +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Event_win.cpp b/telldus-core/common/Event_win.cpp new file mode 100644 index 0000000..e116d38 --- /dev/null +++ b/telldus-core/common/Event_win.cpp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "common/Event.h" +#include "common/Thread.h" + +namespace TelldusCore { + +class Event::PrivateData { +public: + EVENT_T event; +}; + +Event::Event(EventHandler *handler) + :EventBase(handler) { + d = new PrivateData; + d->event = CreateEvent(NULL, true, false, NULL); +} + +Event::~Event(void) { + CloseHandle(d->event); + delete d; +} + +EVENT_T Event::retrieveNative() { + return d->event; +} + +void Event::clearSignal() { + ResetEvent(d->event); +} + +void Event::sendSignal() { + SetEvent(d->event); +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Message.cpp b/telldus-core/common/Message.cpp new file mode 100644 index 0000000..6fbea00 --- /dev/null +++ b/telldus-core/common/Message.cpp @@ -0,0 +1,131 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "common/Message.h" +#include +#include +#include +#include "common/Socket.h" +#include "common/Strings.h" + +namespace TelldusCore { + +Message::Message() + : std::wstring() { +} + +Message::Message(const std::wstring &functionName) + :std::wstring() { + this->addArgument(functionName); +} + +Message::~Message(void) { +} + +void Message::addArgument(const std::wstring &value) { + // std::wstringstream st; + // st << (int)value.size(); + this->append(TelldusCore::intToWstring(value.size())); // st.str()); + this->append(L":"); + this->append(value); +} + +void Message::addArgument(int value) { + // std::wstringstream st; + // st << (int)value; + this->append(L"i"); + this->append(TelldusCore::intToWstring(value)); // st.str()); + this->append(L"s"); +} + +/* +void Message::addSpecialArgument(const std::wstring &value){ + int i = 0; + while(i<1000000){ + i++; + + char numstr[21]; // enough to hold all numbers up to 64-bits + //sprintf(numstr, "%d", value.size()); + //this->append(TelldusCore::charToWstring(numstr)); //.str()); + + itoa(value.size(), numstr, 10); + std::string test(numstr); + std::wstring temp(test.length(), L' '); + std::copy(test.begin(), test.end(), temp.begin()); + + this->append(temp); + this->append(L":"); + this->append(value); + + + std::wstringstream st; + st << (int)value.size(); + this->append(st.str()); + this->append(L":"); + this->append(value); + + } +} + +void Message::addSpecialArgument(int value){ + int i = 0; + while(i<1000000){ + i++; + //std::wstringstream st; + //st << (int)value; + this->append(L"i"); + //this->append(st.str()); + this->append(L"s"); + + } +} +*/ +/* +void Message::addSpecialArgument(const char *value){ + this->addSpecialArgument(TelldusCore::charToWstring(value)); +} +*/ + +void Message::addArgument(const char *value) { + this->addArgument(TelldusCore::charToWstring(value)); +} + +bool Message::nextIsInt(const std::wstring &message) { + if (message.length() == 0) { + return false; + } + return (message.at(0) == 'i'); +} + +bool Message::nextIsString(const std::wstring &message) { + if (message.length() == 0) { + return false; + } + return (iswdigit(message.at(0)) != 0); +} + +std::wstring Message::takeString(std::wstring *message) { + if (!Message::nextIsString(*message)) { + return L""; + } + size_t index = message->find(':'); + int length = wideToInteger(message->substr(0, index)); + std::wstring retval(message->substr(index+1, length)); + message->erase(0, index+length+1); + return retval; +} + +int Message::takeInt(std::wstring *message) { + if (!Message::nextIsInt(*message)) { + return 0; + } + size_t index = message->find('s'); + int value = wideToInteger(message->substr(1, index - 1)); + message->erase(0, index+1); + return value; +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Message.h b/telldus-core/common/Message.h new file mode 100644 index 0000000..f0ca593 --- /dev/null +++ b/telldus-core/common/Message.h @@ -0,0 +1,36 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_MESSAGE_H_ +#define TELLDUS_CORE_COMMON_MESSAGE_H_ + +#include + +namespace TelldusCore { + class Message : public std::wstring { + public: + Message(); + explicit Message(const std::wstring &functionName); + ~Message(void); + + void addArgument(const std::wstring &value); + // void addSpecialArgument(const std::wstring &); + // void addSpecialArgument(int); + // void addSpecialArgument(const char *); + void addArgument(int value); + void addArgument(const char *value); + + static bool nextIsInt(const std::wstring &message); + static bool nextIsString(const std::wstring &message); + + static std::wstring takeString(std::wstring *message); + static int takeInt(std::wstring *message); + + private: + }; +} + +#endif // TELLDUS_CORE_COMMON_MESSAGE_H_ diff --git a/telldus-core/common/Mutex.cpp b/telldus-core/common/Mutex.cpp new file mode 100644 index 0000000..003a0f8 --- /dev/null +++ b/telldus-core/common/Mutex.cpp @@ -0,0 +1,88 @@ +// +// C++ Implementation: Thread +// +// Description: +// +// +// Author: Micke Prag , (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "common/Mutex.h" + +#ifdef _WINDOWS + #include + typedef HANDLE MUTEX_T; +#else + #include + typedef pthread_mutex_t MUTEX_T; +#endif + +#include "common/common.h" + +namespace TelldusCore { + +class Mutex::PrivateData { +public: + MUTEX_T mutex; +}; + +Mutex::Mutex() { + d = new PrivateData; +#ifdef _WINDOWS + d->mutex = CreateMutex(NULL, FALSE, NULL); +#else + pthread_mutex_init(&d->mutex, NULL); +#endif +} + +Mutex::~Mutex() { +#ifdef _WINDOWS + CloseHandle(d->mutex); +#else + pthread_mutex_destroy(&d->mutex); +#endif + delete d; +} + +void Mutex::lock() { +#ifdef _WINDOWS + WaitForSingleObject(d->mutex, INFINITE); +#else + pthread_mutex_lock(&d->mutex); +#endif +} + +void Mutex::unlock() { +#ifdef _WINDOWS + ReleaseMutex(d->mutex); +#else + pthread_mutex_unlock(&d->mutex); +#endif +} + + +void LoggedMutex::lock() { + debuglog(0, "Locking"); + Mutex::lock(); + debuglog(0, "Locked"); +} + +void LoggedMutex::unlock() { + debuglog(0, "Unlocking"); + Mutex::unlock(); + debuglog(0, "Unlocked"); +} + +MutexLocker::MutexLocker(Mutex *m) + :mutex(m) { + mutex->lock(); +} + +MutexLocker::~MutexLocker() { + mutex->unlock(); +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Mutex.h b/telldus-core/common/Mutex.h new file mode 100644 index 0000000..082ee17 --- /dev/null +++ b/telldus-core/common/Mutex.h @@ -0,0 +1,45 @@ +// +// C++ Interface: Thread +// +// Description: +// +// +// Author: Micke Prag , (C) 2010 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_MUTEX_H_ +#define TELLDUS_CORE_COMMON_MUTEX_H_ + +namespace TelldusCore { + class Mutex { + public: + Mutex(); + virtual ~Mutex(); + + virtual void lock(); + virtual void unlock(); + + private: + Mutex(const Mutex&); // Disable copy + Mutex& operator = (const Mutex&); + class PrivateData; + PrivateData *d; + }; + class LoggedMutex : public Mutex { + public: + void lock(); + void unlock(); + }; + + class MutexLocker { + public: + explicit MutexLocker(Mutex *m); + ~MutexLocker(); + private: + Mutex *mutex; + }; +} + +#endif // TELLDUS_CORE_COMMON_MUTEX_H_ diff --git a/telldus-core/common/Socket.h b/telldus-core/common/Socket.h new file mode 100644 index 0000000..98fdbc5 --- /dev/null +++ b/telldus-core/common/Socket.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_SOCKET_H_ +#define TELLDUS_CORE_COMMON_SOCKET_H_ + +#ifdef _WINDOWS + #include + typedef HANDLE SOCKET_T; +#else + typedef int SOCKET_T; +#endif + +#include + +namespace TelldusCore { + class Socket { + public: + Socket(); + explicit Socket(SOCKET_T hPipe); + virtual ~Socket(void); + + void connect(const std::wstring &server); + bool isConnected(); + std::wstring read(); + std::wstring read(int timeout); + void stopReadWait(); + void write(const std::wstring &msg); + + private: + class PrivateData; + PrivateData *d; + }; +} + +#endif // TELLDUS_CORE_COMMON_SOCKET_H_ diff --git a/telldus-core/common/Socket_unix.cpp b/telldus-core/common/Socket_unix.cpp new file mode 100644 index 0000000..2ee1d7c --- /dev/null +++ b/telldus-core/common/Socket_unix.cpp @@ -0,0 +1,144 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include +#include +#include +#include +#include +#include +#include + +#include "common/Socket.h" +#include "common/Mutex.h" +#include "common/Strings.h" + +#define BUFSIZE 512 +#if defined(_MACOSX) && !defined(SOCK_CLOEXEC) + #define SOCK_CLOEXEC 0 +#endif + +namespace TelldusCore { + +int connectWrapper(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + return connect(sockfd, addr, addrlen); +} + +class Socket::PrivateData { +public: + SOCKET_T socket; + bool connected; + fd_set infds; + Mutex mutex; +}; + +Socket::Socket() { + d = new PrivateData; + d->socket = 0; + d->connected = false; + FD_ZERO(&d->infds); +} + +Socket::Socket(SOCKET_T socket) { + d = new PrivateData; + d->socket = socket; + FD_ZERO(&d->infds); + d->connected = true; +} + +Socket::~Socket(void) { + if(d->socket) { + close(d->socket); + } + delete d; +} + +void Socket::connect(const std::wstring &server) { + struct sockaddr_un remote; + socklen_t len; + + if ((d->socket = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1) { + return; + } +#if defined(_MACOSX) + int op = fcntl(d->socket, F_GETFD); + fcntl(d->socket, F_SETFD, op | FD_CLOEXEC); // OS X doesn't support SOCK_CLOEXEC yet +#endif + std::string name = "/tmp/" + std::string(server.begin(), server.end()); + remote.sun_family = AF_UNIX; + snprintf(remote.sun_path, sizeof(remote.sun_path), "%s", name.c_str()); + + len = SUN_LEN(&remote); + if (connectWrapper(d->socket, (struct sockaddr *)&remote, len) == -1) { + return; + } + + TelldusCore::MutexLocker locker(&d->mutex); + d->connected = true; +} + +bool Socket::isConnected() { + TelldusCore::MutexLocker locker(&d->mutex); + return d->connected; +} + +std::wstring Socket::read() { + return this->read(0); +} + +std::wstring Socket::read(int timeout) { + struct timeval tv; + char inbuf[BUFSIZE]; + + FD_SET(d->socket, &d->infds); + std::string msg; + while(isConnected()) { + tv.tv_sec = floor(timeout / 1000.0); + tv.tv_usec = timeout % 1000; + + int response = select(d->socket+1, &d->infds, NULL, NULL, &tv); + if (response == 0 && timeout > 0) { + return L""; + } else if (response <= 0) { + FD_SET(d->socket, &d->infds); + continue; + } + + int received = BUFSIZE; + while(received >= (BUFSIZE - 1)) { + memset(inbuf, '\0', sizeof(inbuf)); + received = recv(d->socket, inbuf, BUFSIZE - 1, 0); + if(received > 0) { + msg.append(std::string(inbuf)); + } + } + if (received < 0) { + TelldusCore::MutexLocker locker(&d->mutex); + d->connected = false; + } + break; + } + + return TelldusCore::charToWstring(msg.c_str()); +} + +void Socket::stopReadWait() { + TelldusCore::MutexLocker locker(&d->mutex); + d->connected = false; + // TODO(stefan): somehow signal the socket here? +} + +void Socket::write(const std::wstring &msg) { + std::string newMsg(TelldusCore::wideToString(msg)); + int sent = send(d->socket, newMsg.c_str(), newMsg.length(), 0); + if (sent < 0) { + TelldusCore::MutexLocker locker(&d->mutex); + d->connected = false; + } +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Socket_win.cpp b/telldus-core/common/Socket_win.cpp new file mode 100644 index 0000000..23d87ac --- /dev/null +++ b/telldus-core/common/Socket_win.cpp @@ -0,0 +1,187 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include + +#include "common/common.h" +#include "common/Socket.h" + +#define BUFSIZE 512 + +namespace TelldusCore { + +class Socket::PrivateData { +public: + HANDLE hPipe; + HANDLE readEvent; + bool connected; + bool running; +}; + +Socket::Socket() { + d = new PrivateData; + d->hPipe = INVALID_HANDLE_VALUE; + d->connected = false; + d->running = true; +} + +Socket::Socket(SOCKET_T hPipe) { + d = new PrivateData; + d->hPipe = hPipe; + d->connected = true; + d->running = true; +} + + +Socket::~Socket(void) { + d->running = false; + SetEvent(d->readEvent); // signal for break + if (d->hPipe != INVALID_HANDLE_VALUE) { + CloseHandle(d->hPipe); + d->hPipe = 0; + } + delete d; +} + +void Socket::connect(const std::wstring &server) { + BOOL fSuccess = false; + + std::wstring name(L"\\\\.\\pipe\\" + server); + d->hPipe = CreateFile( + (const wchar_t *)name.c_str(), // pipe name + GENERIC_READ | // read and write access + GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // opens existing pipe + FILE_FLAG_OVERLAPPED, // default attributes + NULL); // no template file + + if (d->hPipe == INVALID_HANDLE_VALUE) { + return; + } + + DWORD dwMode = PIPE_READMODE_MESSAGE; + fSuccess = SetNamedPipeHandleState( + d->hPipe, // pipe handle + &dwMode, // new pipe mode + NULL, // don't set maximum bytes + NULL); // don't set maximum time + + if (!fSuccess) { + return; + } + d->connected = true; +} + +void Socket::stopReadWait() { + d->running = false; + SetEvent(d->readEvent); +} + +std::wstring Socket::read() { + return read(INFINITE); +} + +std::wstring Socket::read(int timeout) { + wchar_t buf[BUFSIZE]; + int result; + DWORD cbBytesRead = 0; + OVERLAPPED oOverlap; + + memset(&oOverlap, 0, sizeof(OVERLAPPED)); + + d->readEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + oOverlap.hEvent = d->readEvent; + BOOL fSuccess = false; + std::wstring returnString; + bool moreData = true; + + while(moreData) { + moreData = false; + memset(&buf, 0, sizeof(buf)); + + ReadFile( d->hPipe, &buf, sizeof(buf)-sizeof(wchar_t), &cbBytesRead, &oOverlap); + + result = WaitForSingleObject(oOverlap.hEvent, timeout); + + if(!d->running) { + CancelIo(d->hPipe); + WaitForSingleObject(oOverlap.hEvent, INFINITE); + d->readEvent = 0; + CloseHandle(oOverlap.hEvent); + return L""; + } + + if (result == WAIT_TIMEOUT) { + CancelIo(d->hPipe); + // Cancel, we still need to cleanup + } + fSuccess = GetOverlappedResult(d->hPipe, &oOverlap, &cbBytesRead, true); + + if (!fSuccess) { + DWORD err = GetLastError(); + + if(err == ERROR_MORE_DATA) { + moreData = true; + } else { + buf[0] = 0; + } + if (err == ERROR_BROKEN_PIPE) { + d->connected = false; + break; + } + } + returnString.append(buf); + } + d->readEvent = 0; + CloseHandle(oOverlap.hEvent); + return returnString; +} + +void Socket::write(const std::wstring &msg) { + OVERLAPPED oOverlap; + DWORD bytesWritten = 0; + int result; + BOOL fSuccess = false; + + memset(&oOverlap, 0, sizeof(OVERLAPPED)); + + HANDLE writeEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + oOverlap.hEvent = writeEvent; + + BOOL writeSuccess = WriteFile(d->hPipe, msg.data(), (DWORD)msg.length()*sizeof(wchar_t), &bytesWritten, &oOverlap); + result = GetLastError(); + if (writeSuccess || result == ERROR_IO_PENDING) { + result = WaitForSingleObject(writeEvent, 30000); + if (result == WAIT_TIMEOUT) { + CancelIo(d->hPipe); + WaitForSingleObject(oOverlap.hEvent, INFINITE); + CloseHandle(writeEvent); + CloseHandle(d->hPipe); + d->hPipe = 0; + d->connected = false; + return; + } + fSuccess = GetOverlappedResult(d->hPipe, &oOverlap, &bytesWritten, TRUE); + } + + CloseHandle(writeEvent); + if (!fSuccess) { + CloseHandle(d->hPipe); + d->hPipe = 0; + d->connected = false; + return; + } +} + +bool Socket::isConnected() { + return d->connected; +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Strings.cpp b/telldus-core/common/Strings.cpp new file mode 100644 index 0000000..dc15f81 --- /dev/null +++ b/telldus-core/common/Strings.cpp @@ -0,0 +1,276 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "common/Strings.h" +#include +#include + +#ifdef _WINDOWS +#include +#else +#include +#endif +#include +#include +#include + + +#ifdef _MACOSX +#define WCHAR_T_ENCODING "UCS-4-INTERNAL" +#else +#define WCHAR_T_ENCODING "WCHAR_T" +#endif + +#ifndef va_copy +#ifdef __va_copy +#define va_copy(a, b) __va_copy(a, b) +#else /* !__va_copy */ +#define va_copy(a, b) ((a)=(b)) +#endif /* __va_copy */ +#endif /* va_copy */ + +std::wstring TelldusCore::charToWstring(const char *value) { +#ifdef _WINDOWS + // Determine size + int size = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); + if (size == 0) { + return L""; + } + wchar_t *buffer; + buffer = new wchar_t[size]; + memset(buffer, 0, sizeof(wchar_t)*(size)); + + int bytes = MultiByteToWideChar(CP_UTF8, 0, value, -1, buffer, size); + std::wstring retval(buffer); + delete[] buffer; + return retval; + +#else + size_t utf8Length = strlen(value); + size_t outbytesLeft = utf8Length*sizeof(wchar_t); + + // Copy the instring + char *inString = new char[utf8Length+1]; + snprintf(inString, utf8Length+1, "%s", value); + + // Create buffer for output + char *outString = reinterpret_cast(new wchar_t[utf8Length+1]); + memset(outString, 0, sizeof(wchar_t)*(utf8Length+1)); + +#ifdef _FREEBSD + const char *inPointer = inString; +#else + char *inPointer = inString; +#endif + char *outPointer = outString; + + iconv_t convDesc = iconv_open(WCHAR_T_ENCODING, "UTF-8"); + iconv(convDesc, &inPointer, &utf8Length, &outPointer, &outbytesLeft); + iconv_close(convDesc); + + std::wstring retval( reinterpret_cast(outString) ); + + // Cleanup + delete[] inString; + delete[] outString; + + return retval; +#endif +} + +int TelldusCore::charToInteger(const char *input) { + std::stringstream inputstream; + inputstream << input; + int retval; + inputstream >> retval; + return retval; +} + +std::wstring TelldusCore::charUnsignedToWstring(const unsigned char value) { + std::wstringstream st; + st << value; + return st.str(); +} + +/** +* This method doesn't support all locales +*/ +bool TelldusCore::comparei(std::wstring stringA, std::wstring stringB) { + transform(stringA.begin(), stringA.end(), stringA.begin(), toupper); + transform(stringB.begin(), stringB.end(), stringB.begin(), toupper); + + return stringA == stringB; +} + +std::wstring TelldusCore::intToWstring(int value) { +#ifdef _WINDOWS + // no stream used + // TODO(stefan): Make effective and safe... + wchar_t numstr[21]; // enough to hold all numbers up to 64-bits + _itow_s(value, numstr, sizeof(numstr), 10); + std::wstring newstring(numstr); + return newstring; + // return TelldusCore::charToWstring(stdstring.c_str()); + // std::wstring temp = TelldusCore::charToWstring(stdstring.c_str()); + // std::wstring temp(stdstring.length(), L' '); + // std::copy(stdstring.begin(), stdstring.end(), temp.begin()); + // return temp; +#else + std::wstringstream st; + st << value; + return st.str(); +#endif +} + +std::string TelldusCore::intToString(int value) { + // Not sure if this is neecssary (for ordinary stringstream that is) +#ifdef _WINDOWS + char numstr[21]; // enough to hold all numbers up to 64-bits + _itoa_s(value, numstr, sizeof(numstr), 10); + std::string stdstring(numstr); + return stdstring; +#else + std::stringstream st; + st << value; + return st.str(); +#endif +} + +/* +std::wstring TelldusCore::intToWStringSafe(int value){ + #ifdef _WINDOWS + //no stream used + //TODO! Make effective and safe... + char numstr[21]; // enough to hold all numbers up to 64-bits + itoa(value, numstr, 10); + std::string stdstring(numstr); + return TelldusCore::charToWstring(stdstring.c_str()); + //std::wstring temp = TelldusCore::charToWstring(stdstring.c_str()); + //std::wstring temp(stdstring.length(), L' '); + //std::copy(stdstring.begin(), stdstring.end(), temp.begin()); + //return temp; + #else + return TelldusCore::intToWString(value); + #endif +} +*/ + +uint64_t TelldusCore::hexTo64l(const std::string data) { +#ifdef _WINDOWS + return _strtoui64(data.c_str(), NULL, 16); +#elif defined(_MACOSX) + return strtoq(data.c_str(), NULL, 16); +#else + return strtoull(data.c_str(), NULL, 16); +#endif +} + +int TelldusCore::wideToInteger(const std::wstring &input) { + std::wstringstream inputstream; + inputstream << input; + int retval; + inputstream >> retval; + return retval; +} + +std::string TelldusCore::wideToString(const std::wstring &input) { +#ifdef _WINDOWS + // Determine size + int size = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, NULL, 0, NULL, NULL); + if (size == 0) { + return ""; + } + char *buffer; + buffer = new char[size]; + memset(buffer, 0, sizeof(*buffer)*size); + + int bytes = WideCharToMultiByte(CP_UTF8, 0, input.c_str(), -1, buffer, size, NULL, NULL); + std::string retval(buffer); + delete[] buffer; + return retval; + +#else + size_t wideSize = sizeof(wchar_t)*input.length(); + // We cannot know how many wide character there is yet + size_t outbytesLeft = wideSize+sizeof(char); // NOLINT(runtime/sizeof) + + // Copy the instring + char *inString = reinterpret_cast(new wchar_t[input.length()+1]); + memcpy(inString, input.c_str(), wideSize+sizeof(wchar_t)); + + // Create buffer for output + char *outString = new char[outbytesLeft]; + memset(outString, 0, sizeof(*outString)*(outbytesLeft)); + +#ifdef _FREEBSD + const char *inPointer = inString; +#else + char *inPointer = inString; +#endif + char *outPointer = outString; + + iconv_t convDesc = iconv_open("UTF-8", WCHAR_T_ENCODING); + iconv(convDesc, &inPointer, &wideSize, &outPointer, &outbytesLeft); + iconv_close(convDesc); + + std::string retval(outString); + + // Cleanup + delete[] inString; + delete[] outString; + + return retval; +#endif +} + +std::string TelldusCore::formatf(const char *format, ...) { + va_list ap; + va_start(ap, format); + std::string retval = sformatf(format, ap); + va_end(ap); + return retval; +} + +std::string TelldusCore::sformatf(const char *format, va_list ap) { + // This code is based on code from the Linux man-pages project (man vsprintf) + int n; + int size = 100; /* Guess we need no more than 100 bytes. */ + char *p, *np; + + if ((p = reinterpret_cast(malloc(size))) == NULL) { + return ""; + } + + while (1) { + /* Try to print in the allocated space. */ + va_list ap2; + va_copy(ap2, ap); + n = vsnprintf(p, size, format, ap2); + va_end(ap2); + + /* If that worked, return the string. */ + if (n > -1 && n < size) { + std::string retval(p); + free(p); + return retval; + } + + /* Else try again with more space. */ + + if (n > -1) { /* glibc 2.1 */ + size = n+1; /* precisely what is needed */ + } else { /* glibc 2.0 */ + size *= 2; /* twice the old size */ + } + if ((np = reinterpret_cast(realloc (p, size))) == NULL) { + free(p); + return ""; + } else { + p = np; + } + } +} diff --git a/telldus-core/common/Strings.h b/telldus-core/common/Strings.h new file mode 100644 index 0000000..d4b8197 --- /dev/null +++ b/telldus-core/common/Strings.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_STRINGS_H_ +#define TELLDUS_CORE_COMMON_STRINGS_H_ + +#include +#ifdef _MSC_VER +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +#include +#endif +#include + +namespace TelldusCore { + std::wstring charToWstring(const char *value); + int charToInteger(const char *value); + std::wstring charUnsignedToWstring(const unsigned char value); + + bool comparei(std::wstring stringA, std::wstring stringB); + std::wstring intToWstring(int value); + // std::wstring intToWStringSafe(int value); + std::string intToString(int value); + uint64_t hexTo64l(const std::string data); + std::string wideToString(const std::wstring &input); + + int wideToInteger(const std::wstring &input); + + std::string formatf(const char *format, ...); + std::string sformatf(const char *format, va_list ap); +} + +#endif // TELLDUS_CORE_COMMON_STRINGS_H_ diff --git a/telldus-core/common/Thread.cpp b/telldus-core/common/Thread.cpp new file mode 100644 index 0000000..dd5f4b1 --- /dev/null +++ b/telldus-core/common/Thread.cpp @@ -0,0 +1,98 @@ +// +// C++ Implementation: Thread +// +// Description: +// +// +// Author: Micke Prag , (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "common/Thread.h" +#ifdef _WINDOWS +#include +#endif +#include "common/EventHandler.h" + +namespace TelldusCore { + +class ThreadPrivate { +public: + bool running; + EventRef threadStarted; + Mutex *mutex; +#ifdef _WINDOWS + HANDLE thread; + DWORD threadId; +#else + pthread_t thread; +#endif +}; + +Thread::Thread() { + d = new ThreadPrivate; + d->thread = 0; + d->mutex = 0; +} + +Thread::~Thread() { + delete d; +} + +void Thread::start() { +#ifdef _WINDOWS + d->running = true; + d->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Thread::exec, this, 0, &d->threadId); +#else + pthread_create(&d->thread, NULL, &Thread::exec, this ); +#endif +} + +void Thread::startAndLock(Mutex *lock) { + EventHandler handler; + d->threadStarted = handler.addEvent(); + d->mutex = lock; + this->start(); + while (!handler.waitForAny()) { + continue; + } + d->threadStarted.reset(); +} + +bool Thread::wait() { + if (!d->thread) { + return true; + } +#ifdef _WINDOWS + while(d->running) { + WaitForSingleObject(d->thread, 200); + } + CloseHandle(d->thread); +#else + pthread_join(d->thread, 0); +#endif + return true; +} + +void *Thread::exec( void *ptr ) { + Thread *t = reinterpret_cast(ptr); + if (t) { + if (t->d->threadStarted) { + t->d->mutex->lock(); + t->d->threadStarted->signal(); + } + t->run(); + if (t->d->mutex) { + t->d->mutex->unlock(); + } + t->d->running = false; + } +#ifdef _WINDOWS + ExitThread(0); +#endif + return 0; +} + +} // namespace TelldusCore diff --git a/telldus-core/common/Thread.h b/telldus-core/common/Thread.h new file mode 100644 index 0000000..c5724eb --- /dev/null +++ b/telldus-core/common/Thread.h @@ -0,0 +1,37 @@ +// +// C++ Interface: Thread +// +// Description: +// +// +// Author: Micke Prag , (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_COMMON_THREAD_H_ +#define TELLDUS_CORE_COMMON_THREAD_H_ + +#include +#include "common/Mutex.h" + +namespace TelldusCore { + class ThreadPrivate; + class Thread { + public: + Thread(); + virtual ~Thread(); + void start(); + void startAndLock(Mutex *lock); + bool wait(); + + protected: + virtual void run() = 0; + + private: + static void* exec( void *ptr ); + ThreadPrivate *d; + }; +} + +#endif // TELLDUS_CORE_COMMON_THREAD_H_ diff --git a/telldus-core/common/common.h b/telldus-core/common/common.h new file mode 100644 index 0000000..95e8927 --- /dev/null +++ b/telldus-core/common/common.h @@ -0,0 +1,108 @@ +// +// Author: Micke Prag , (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef TELLDUS_CORE_COMMON_COMMON_H_ +#define TELLDUS_CORE_COMMON_COMMON_H_ + +#ifdef _WINDOWS +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#include +#include +#else +#include +#include +#endif +#include +#include +#include +#include +#include + +#ifdef _WINDOWS +#include // NOLINT(readability/streams) +#endif +#include +#include "common/Strings.h" + +inline void msleep( const int msec) { +#ifdef _WINDOWS + Sleep(msec); +#else + usleep(msec*1000); +#endif +} + +inline void dlog(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + fflush(stdout); +} + +inline void debuglogfilename(const int intMessage, const std::string strMessage, const std::string filename) { +#ifdef _WINDOWS + static bool firstRun = true; + std::ofstream file; + + if (firstRun) { + file.open(filename.c_str(), std::ios::out); + firstRun = false; + } else { + file.open(filename.c_str(), std::ios::out | std::ios::app); + } + + __time32_t now = _time32(0); + + // Convert now to tm struct for local timezone + struct tm localtm; + _localtime32_s(&localtm, &now); + char thetime[32]; + errno_t err = asctime_s(thetime, 32, &localtm); + if (!err) { + file << thetime << " [" << GetCurrentThreadId() << "] " << intMessage << " - " << strMessage << "\n"; + file.flush(); + file.close(); + } + +#elif !defined(_MACOSX) && !defined(__FreeBSD__) + pthread_t thread = pthread_self(); + printf("[%i] %i - %s\n", static_cast(thread), intMessage, strMessage.c_str()); + fflush(stdout); +#else + printf("%i - %s\n", intMessage, strMessage.c_str()); +#endif +} + +inline void debuglogservice(const int intMessage, const std::string strMessage) { + std::string filename("C:/telldus_service_debug.txt"); + debuglogfilename(intMessage, strMessage, filename); +} + +inline void debuglog(const int intMessage, const std::string strMessage) { + std::string filename("C:/telldus_client_debug.txt"); + debuglogfilename(intMessage, strMessage, filename); +} + +inline char *wrapStdString( const std::string &string) { +#ifdef _WINDOWS + return reinterpret_cast(SysAllocStringByteLen(string.c_str(), (unsigned int)string.size())); +#else + char *returnVal; + returnVal = reinterpret_cast(malloc(sizeof(*returnVal) * (string.size()+1))); + snprintf(returnVal, string.size()+1, "%s", string.c_str()); + return returnVal; +#endif +} + +inline char *wrapStdWstring( const std::wstring &wstring) { + return wrapStdString(TelldusCore::wideToString(wstring)); +} + +#endif // TELLDUS_CORE_COMMON_COMMON_H_ diff --git a/telldus-core/common/stdlibc_workaround.cpp b/telldus-core/common/stdlibc_workaround.cpp new file mode 100644 index 0000000..3b348c7 --- /dev/null +++ b/telldus-core/common/stdlibc_workaround.cpp @@ -0,0 +1,73 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include // NOLINT(readability/streams) +// Workarounds for symbols that are missing from Leopard stdlibc++.dylib. +_GLIBCXX_BEGIN_NAMESPACE(std) +// From ostream_insert.h +template ostream& __ostream_insert(ostream&, const char*, streamsize); + +#ifdef _GLIBCXX_USE_WCHAR_T + template wostream& __ostream_insert(wostream&, const wchar_t*, streamsize); +#endif + +// From ostream.tcc +template ostream& ostream::_M_insert(long); // NOLINT(runtime/int) +template ostream& ostream::_M_insert(unsigned long); // NOLINT(runtime/int) +template ostream& ostream::_M_insert(bool); // NOLINT(readability/function) +#ifdef _GLIBCXX_USE_LONG_LONG + template ostream& ostream::_M_insert(long long); // NOLINT(runtime/int) + template ostream& ostream::_M_insert(unsigned long long); // NOLINT(runtime/int) +#endif +template ostream& ostream::_M_insert(double); // NOLINT(readability/function) +template ostream& ostream::_M_insert(long double); +template ostream& ostream::_M_insert(const void*); + +#ifdef _GLIBCXX_USE_WCHAR_T + template wostream& wostream::_M_insert(long); // NOLINT(runtime/int) + template wostream& wostream::_M_insert(unsigned long); // NOLINT(runtime/int) + template wostream& wostream::_M_insert(bool); // NOLINT(readability/function) + #ifdef _GLIBCXX_USE_LONG_LONG + template wostream& wostream::_M_insert(long long); // NOLINT(runtime/int) + template wostream& wostream::_M_insert(unsigned long long); // NOLINT(runtime/int) + #endif + template wostream& wostream::_M_insert(double); // NOLINT(readability/function) + template wostream& wostream::_M_insert(long double); + template wostream& wostream::_M_insert(const void*); +#endif + +// From istream.tcc +template istream& istream::_M_extract(unsigned short&); // NOLINT(runtime/int) +template istream& istream::_M_extract(unsigned int&); +template istream& istream::_M_extract(long&); // NOLINT(runtime/int) +template istream& istream::_M_extract(unsigned long&); // NOLINT(runtime/int) +template istream& istream::_M_extract(bool&); +#ifdef _GLIBCXX_USE_LONG_LONG + template istream& istream::_M_extract(long long&); // NOLINT(runtime/int) + template istream& istream::_M_extract(unsigned long long&); // NOLINT(runtime/int) +#endif +template istream& istream::_M_extract(float&); +template istream& istream::_M_extract(double&); +template istream& istream::_M_extract(long double&); +template istream& istream::_M_extract(void*&); + +#ifdef _GLIBCXX_USE_WCHAR_T + template wistream& wistream::_M_extract(unsigned short&); // NOLINT(runtime/int) + template wistream& wistream::_M_extract(unsigned int&); + template wistream& wistream::_M_extract(long&); // NOLINT(runtime/int) + template wistream& wistream::_M_extract(unsigned long&); // NOLINT(runtime/int) + template wistream& wistream::_M_extract(bool&); + #ifdef _GLIBCXX_USE_LONG_LONG + template wistream& wistream::_M_extract(long long&); // NOLINT(runtime/int) + template wistream& wistream::_M_extract(unsigned long long&); // NOLINT(runtime/int) + #endif + template wistream& wistream::_M_extract(float&); + template wistream& wistream::_M_extract(double&); + template wistream& wistream::_M_extract(long double&); + template wistream& wistream::_M_extract(void*&); +#endif + +_GLIBCXX_END_NAMESPACE diff --git a/telldus-core/debian/changelog b/telldus-core/debian/changelog new file mode 100644 index 0000000..9f91680 --- /dev/null +++ b/telldus-core/debian/changelog @@ -0,0 +1,5 @@ +telldus-core (2.1.2-1) UNRELEASED; urgency=low + + * Initial release (Closes: #xxxx) + + -- Ruben Undheim Sat, 25 Oct 2014 19:42:44 +0200 diff --git a/telldus-core/debian/compat b/telldus-core/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/telldus-core/debian/compat @@ -0,0 +1 @@ +9 diff --git a/telldus-core/debian/control b/telldus-core/debian/control new file mode 100644 index 0000000..36e711d --- /dev/null +++ b/telldus-core/debian/control @@ -0,0 +1,40 @@ +Source: telldus-core +Section: utils +Priority: optional +Maintainer: Ruben Undheim +Build-Depends: debhelper (>= 9), libftdi-dev, libconfuse-dev, doxygen +Standards-Version: 3.9.6 +Vcs-Git: git://github.com/rubund/telldus-core.git +Vcs-Browser: https://github.com/rubund/telldus-core/tree/debian +Homepage: http://developer.telldus.com/wiki/TellStick_installation_Linux + + +Package: libtelldus0 +Architecture: any +Section: libs +Depends: ${shlibs:Depends}, + ${misc:Depends} +Pre-Depends: ${misc:Pre-Depends} +Description: Library for tellstick control + +Package: libtelldus-dev +Architecture: any +Section: libdevel +Depends: libtelldus0 (= ${binary:Version}), + libftdi-dev, + libconfuse-dev, + ${misc:Depends} +Description: Library for tellstick control - development files + +Package: libtelldus0-dbg +Architecture: any +Section: debug +Priority: extra +Depends: libtelldus0 (= ${binary:Version}), + ${misc:Depends} +Description: Library for tellstick control - debug symbols + +Package: tdtool +Architecture: any +Depends: libtelldus0 (= ${binary:Version}), ${shlibs:Depends}, ${misc:Depends} +Description: TellStick control software diff --git a/telldus-core/debian/copyright b/telldus-core/debian/copyright new file mode 100644 index 0000000..0dcdb35 --- /dev/null +++ b/telldus-core/debian/copyright @@ -0,0 +1,44 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: telldus-core +Source: http://download.telldus.se/TellStick/Software/telldus-core/ + +Files: * +Copyright: Copyright (C) 2012 Telldus Technologies AB +License: LGPL-2.1 + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + . + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU Lesser General + Public License version 2.1 can be found in + "/usr/share/common-licenses/LGPL-2.1". + + +Files: debian/* +Copyright: 2014 Ruben Undheim +License: GPL-2+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + diff --git a/telldus-core/debian/libtelldus-dev.install b/telldus-core/debian/libtelldus-dev.install new file mode 100644 index 0000000..b341905 --- /dev/null +++ b/telldus-core/debian/libtelldus-dev.install @@ -0,0 +1,2 @@ +usr/include/* +usr/lib/lib*.so diff --git a/telldus-core/debian/libtelldus0.install b/telldus-core/debian/libtelldus0.install new file mode 100644 index 0000000..d0dbfd1 --- /dev/null +++ b/telldus-core/debian/libtelldus0.install @@ -0,0 +1 @@ +usr/lib/lib*.so.* diff --git a/telldus-core/debian/patches/01_add_missing_doxyfile.patch b/telldus-core/debian/patches/01_add_missing_doxyfile.patch new file mode 100644 index 0000000..3b36481 --- /dev/null +++ b/telldus-core/debian/patches/01_add_missing_doxyfile.patch @@ -0,0 +1,1740 @@ +Description: + TODO: Put a short summary on the line above and replace this paragraph + with a longer explanation of this change. Complete the meta-information + with other relevant fields (see below for details). To make it easier, the + information below has been extracted from the changelog. Adjust it or drop + it. + . + telldus-core (2.1.2-1) UNRELEASED; urgency=low + . + * Initial release (Closes: #xxxx) +Author: Ruben Undheim + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: , +Bug: +Bug-Debian: http://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: + +--- /dev/null ++++ telldus-core-2.1.2/Doxyfile.in +@@ -0,0 +1,1712 @@ ++# Doxyfile 1.7.4 ++ ++# This file describes the settings to be used by the documentation system ++# doxygen (www.doxygen.org) for a project ++# ++# All text after a hash (#) is considered a comment and will be ignored ++# The format is: ++# TAG = value [value, ...] ++# For lists items can also be appended using: ++# TAG += value [value, ...] ++# Values that contain spaces should be placed between quotes (" ") ++ ++#--------------------------------------------------------------------------- ++# Project related configuration options ++#--------------------------------------------------------------------------- ++ ++# This tag specifies the encoding used for all characters in the config file ++# that follow. The default is UTF-8 which is also the encoding used for all ++# text before the first occurrence of this tag. Doxygen uses libiconv (or the ++# iconv built into libc) for the transcoding. See ++# http://www.gnu.org/software/libiconv for the list of possible encodings. ++ ++DOXYFILE_ENCODING = UTF-8 ++ ++# The PROJECT_NAME tag is a single word (or a sequence of words surrounded ++# by quotes) that should identify the project. ++ ++PROJECT_NAME = "Telldus Core API" ++ ++# The PROJECT_NUMBER tag can be used to enter a project or revision number. ++# This could be handy for archiving the generated documentation or ++# if some version control system is used. ++ ++PROJECT_NUMBER = @DISPLAYED_VERSION@ ++ ++# Using the PROJECT_BRIEF tag one can provide an optional one line description ++# for a project that appears at the top of each page and should give viewer ++# a quick idea about the purpose of the project. Keep the description short. ++ ++PROJECT_BRIEF = ++ ++# With the PROJECT_LOGO tag one can specify an logo or icon that is ++# included in the documentation. The maximum height of the logo should not ++# exceed 55 pixels and the maximum width should not exceed 200 pixels. ++# Doxygen will copy the logo to the output directory. ++ ++PROJECT_LOGO = ++ ++# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) ++# base path where the generated documentation will be put. ++# If a relative path is entered, it will be relative to the location ++# where doxygen was started. If left blank the current directory will be used. ++ ++OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/docs ++ ++# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create ++# 4096 sub-directories (in 2 levels) under the output directory of each output ++# format and will distribute the generated files over these directories. ++# Enabling this option can be useful when feeding doxygen a huge amount of ++# source files, where putting all generated files in the same directory would ++# otherwise cause performance problems for the file system. ++ ++CREATE_SUBDIRS = NO ++ ++# The OUTPUT_LANGUAGE tag is used to specify the language in which all ++# documentation generated by doxygen is written. Doxygen will use this ++# information to generate all constant output in the proper language. ++# The default language is English, other supported languages are: ++# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, ++# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, ++# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English ++# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, ++# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, ++# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. ++ ++OUTPUT_LANGUAGE = English ++ ++# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will ++# include brief member descriptions after the members that are listed in ++# the file and class documentation (similar to JavaDoc). ++# Set to NO to disable this. ++ ++BRIEF_MEMBER_DESC = YES ++ ++# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend ++# the brief description of a member or function before the detailed description. ++# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the ++# brief descriptions will be completely suppressed. ++ ++REPEAT_BRIEF = YES ++ ++# This tag implements a quasi-intelligent brief description abbreviator ++# that is used to form the text in various listings. Each string ++# in this list, if found as the leading text of the brief description, will be ++# stripped from the text and the result after processing the whole list, is ++# used as the annotated text. Otherwise, the brief description is used as-is. ++# If left blank, the following values are used ("$name" is automatically ++# replaced with the name of the entity): "The $name class" "The $name widget" ++# "The $name file" "is" "provides" "specifies" "contains" ++# "represents" "a" "an" "the" ++ ++ABBREVIATE_BRIEF = ++ ++# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then ++# Doxygen will generate a detailed section even if there is only a brief ++# description. ++ ++ALWAYS_DETAILED_SEC = NO ++ ++# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all ++# inherited members of a class in the documentation of that class as if those ++# members were ordinary class members. Constructors, destructors and assignment ++# operators of the base classes will not be shown. ++ ++INLINE_INHERITED_MEMB = NO ++ ++# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full ++# path before files name in the file list and in the header files. If set ++# to NO the shortest path that makes the file name unique will be used. ++ ++FULL_PATH_NAMES = NO ++ ++# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag ++# can be used to strip a user-defined part of the path. Stripping is ++# only done if one of the specified strings matches the left-hand part of ++# the path. The tag can be used to show relative paths in the file list. ++# If left blank the directory from which doxygen is run is used as the ++# path to strip. ++ ++STRIP_FROM_PATH = ++ ++# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of ++# the path mentioned in the documentation of a class, which tells ++# the reader which header file to include in order to use a class. ++# If left blank only the name of the header file containing the class ++# definition is used. Otherwise one should specify the include paths that ++# are normally passed to the compiler using the -I flag. ++ ++STRIP_FROM_INC_PATH = ++ ++# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter ++# (but less readable) file names. This can be useful if your file system ++# doesn't support long names like on DOS, Mac, or CD-ROM. ++ ++SHORT_NAMES = NO ++ ++# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen ++# will interpret the first line (until the first dot) of a JavaDoc-style ++# comment as the brief description. If set to NO, the JavaDoc ++# comments will behave just like regular Qt-style comments ++# (thus requiring an explicit @brief command for a brief description.) ++ ++JAVADOC_AUTOBRIEF = NO ++ ++# If the QT_AUTOBRIEF tag is set to YES then Doxygen will ++# interpret the first line (until the first dot) of a Qt-style ++# comment as the brief description. If set to NO, the comments ++# will behave just like regular Qt-style comments (thus requiring ++# an explicit \brief command for a brief description.) ++ ++QT_AUTOBRIEF = NO ++ ++# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen ++# treat a multi-line C++ special comment block (i.e. a block of //! or /// ++# comments) as a brief description. This used to be the default behaviour. ++# The new default is to treat a multi-line C++ comment block as a detailed ++# description. Set this tag to YES if you prefer the old behaviour instead. ++ ++MULTILINE_CPP_IS_BRIEF = NO ++ ++# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented ++# member inherits the documentation from any documented member that it ++# re-implements. ++ ++INHERIT_DOCS = YES ++ ++# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce ++# a new page for each member. If set to NO, the documentation of a member will ++# be part of the file/class/namespace that contains it. ++ ++SEPARATE_MEMBER_PAGES = NO ++ ++# The TAB_SIZE tag can be used to set the number of spaces in a tab. ++# Doxygen uses this value to replace tabs by spaces in code fragments. ++ ++TAB_SIZE = 8 ++ ++# This tag can be used to specify a number of aliases that acts ++# as commands in the documentation. An alias has the form "name=value". ++# For example adding "sideeffect=\par Side Effects:\n" will allow you to ++# put the command \sideeffect (or @sideeffect) in the documentation, which ++# will result in a user-defined paragraph with heading "Side Effects:". ++# You can put \n's in the value part of an alias to insert newlines. ++ ++ALIASES = ++ ++# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C ++# sources only. Doxygen will then generate output that is more tailored for C. ++# For instance, some of the names that are used will be different. The list ++# of all members will be omitted, etc. ++ ++OPTIMIZE_OUTPUT_FOR_C = YES ++ ++# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java ++# sources only. Doxygen will then generate output that is more tailored for ++# Java. For instance, namespaces will be presented as packages, qualified ++# scopes will look different, etc. ++ ++OPTIMIZE_OUTPUT_JAVA = NO ++ ++# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran ++# sources only. Doxygen will then generate output that is more tailored for ++# Fortran. ++ ++OPTIMIZE_FOR_FORTRAN = NO ++ ++# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL ++# sources. Doxygen will then generate output that is tailored for ++# VHDL. ++ ++OPTIMIZE_OUTPUT_VHDL = NO ++ ++# Doxygen selects the parser to use depending on the extension of the files it ++# parses. With this tag you can assign which parser to use for a given extension. ++# Doxygen has a built-in mapping, but you can override or extend it using this ++# tag. The format is ext=language, where ext is a file extension, and language ++# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, ++# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make ++# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C ++# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions ++# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. ++ ++EXTENSION_MAPPING = ++ ++# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want ++# to include (a tag file for) the STL sources as input, then you should ++# set this tag to YES in order to let doxygen match functions declarations and ++# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. ++# func(std::string) {}). This also makes the inheritance and collaboration ++# diagrams that involve STL classes more complete and accurate. ++ ++BUILTIN_STL_SUPPORT = NO ++ ++# If you use Microsoft's C++/CLI language, you should set this option to YES to ++# enable parsing support. ++ ++CPP_CLI_SUPPORT = NO ++ ++# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. ++# Doxygen will parse them like normal C++ but will assume all classes use public ++# instead of private inheritance when no explicit protection keyword is present. ++ ++SIP_SUPPORT = NO ++ ++# For Microsoft's IDL there are propget and propput attributes to indicate getter ++# and setter methods for a property. Setting this option to YES (the default) ++# will make doxygen replace the get and set methods by a property in the ++# documentation. This will only work if the methods are indeed getting or ++# setting a simple type. If this is not the case, or you want to show the ++# methods anyway, you should set this option to NO. ++ ++IDL_PROPERTY_SUPPORT = YES ++ ++# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC ++# tag is set to YES, then doxygen will reuse the documentation of the first ++# member in the group (if any) for the other members of the group. By default ++# all members of a group must be documented explicitly. ++ ++DISTRIBUTE_GROUP_DOC = NO ++ ++# Set the SUBGROUPING tag to YES (the default) to allow class member groups of ++# the same type (for instance a group of public functions) to be put as a ++# subgroup of that type (e.g. under the Public Functions section). Set it to ++# NO to prevent subgrouping. Alternatively, this can be done per class using ++# the \nosubgrouping command. ++ ++SUBGROUPING = YES ++ ++# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and ++# unions are shown inside the group in which they are included (e.g. using ++# @ingroup) instead of on a separate page (for HTML and Man pages) or ++# section (for LaTeX and RTF). ++ ++INLINE_GROUPED_CLASSES = NO ++ ++# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum ++# is documented as struct, union, or enum with the name of the typedef. So ++# typedef struct TypeS {} TypeT, will appear in the documentation as a struct ++# with name TypeT. When disabled the typedef will appear as a member of a file, ++# namespace, or class. And the struct will be named TypeS. This can typically ++# be useful for C code in case the coding convention dictates that all compound ++# types are typedef'ed and only the typedef is referenced, never the tag name. ++ ++TYPEDEF_HIDES_STRUCT = NO ++ ++# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to ++# determine which symbols to keep in memory and which to flush to disk. ++# When the cache is full, less often used symbols will be written to disk. ++# For small to medium size projects (<1000 input files) the default value is ++# probably good enough. For larger projects a too small cache size can cause ++# doxygen to be busy swapping symbols to and from disk most of the time ++# causing a significant performance penalty. ++# If the system has enough physical memory increasing the cache will improve the ++# performance by keeping more symbols in memory. Note that the value works on ++# a logarithmic scale so increasing the size by one will roughly double the ++# memory usage. The cache size is given by this formula: ++# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, ++# corresponding to a cache size of 2^16 = 65536 symbols ++ ++SYMBOL_CACHE_SIZE = 0 ++ ++#--------------------------------------------------------------------------- ++# Build related configuration options ++#--------------------------------------------------------------------------- ++ ++# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in ++# documentation are documented, even if no documentation was available. ++# Private class members and static file members will be hidden unless ++# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES ++ ++EXTRACT_ALL = NO ++ ++# If the EXTRACT_PRIVATE tag is set to YES all private members of a class ++# will be included in the documentation. ++ ++EXTRACT_PRIVATE = NO ++ ++# If the EXTRACT_STATIC tag is set to YES all static members of a file ++# will be included in the documentation. ++ ++EXTRACT_STATIC = NO ++ ++# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) ++# defined locally in source files will be included in the documentation. ++# If set to NO only classes defined in header files are included. ++ ++EXTRACT_LOCAL_CLASSES = NO ++ ++# This flag is only useful for Objective-C code. When set to YES local ++# methods, which are defined in the implementation section but not in ++# the interface are included in the documentation. ++# If set to NO (the default) only methods in the interface are included. ++ ++EXTRACT_LOCAL_METHODS = NO ++ ++# If this flag is set to YES, the members of anonymous namespaces will be ++# extracted and appear in the documentation as a namespace called ++# 'anonymous_namespace{file}', where file will be replaced with the base ++# name of the file that contains the anonymous namespace. By default ++# anonymous namespaces are hidden. ++ ++EXTRACT_ANON_NSPACES = NO ++ ++# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all ++# undocumented members of documented classes, files or namespaces. ++# If set to NO (the default) these members will be included in the ++# various overviews, but no documentation section is generated. ++# This option has no effect if EXTRACT_ALL is enabled. ++ ++HIDE_UNDOC_MEMBERS = NO ++ ++# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all ++# undocumented classes that are normally visible in the class hierarchy. ++# If set to NO (the default) these classes will be included in the various ++# overviews. This option has no effect if EXTRACT_ALL is enabled. ++ ++HIDE_UNDOC_CLASSES = NO ++ ++# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all ++# friend (class|struct|union) declarations. ++# If set to NO (the default) these declarations will be included in the ++# documentation. ++ ++HIDE_FRIEND_COMPOUNDS = NO ++ ++# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any ++# documentation blocks found inside the body of a function. ++# If set to NO (the default) these blocks will be appended to the ++# function's detailed documentation block. ++ ++HIDE_IN_BODY_DOCS = NO ++ ++# The INTERNAL_DOCS tag determines if documentation ++# that is typed after a \internal command is included. If the tag is set ++# to NO (the default) then the documentation will be excluded. ++# Set it to YES to include the internal documentation. ++ ++INTERNAL_DOCS = NO ++ ++# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate ++# file names in lower-case letters. If set to YES upper-case letters are also ++# allowed. This is useful if you have classes or files whose names only differ ++# in case and if your file system supports case sensitive file names. Windows ++# and Mac users are advised to set this option to NO. ++ ++CASE_SENSE_NAMES = YES ++ ++# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen ++# will show members with their full class and namespace scopes in the ++# documentation. If set to YES the scope will be hidden. ++ ++HIDE_SCOPE_NAMES = YES ++ ++# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen ++# will put a list of the files that are included by a file in the documentation ++# of that file. ++ ++SHOW_INCLUDE_FILES = YES ++ ++# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen ++# will list include files with double quotes in the documentation ++# rather than with sharp brackets. ++ ++FORCE_LOCAL_INCLUDES = NO ++ ++# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] ++# is inserted in the documentation for inline members. ++ ++INLINE_INFO = YES ++ ++# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen ++# will sort the (detailed) documentation of file and class members ++# alphabetically by member name. If set to NO the members will appear in ++# declaration order. ++ ++SORT_MEMBER_DOCS = YES ++ ++# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the ++# brief documentation of file, namespace and class members alphabetically ++# by member name. If set to NO (the default) the members will appear in ++# declaration order. ++ ++SORT_BRIEF_DOCS = NO ++ ++# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen ++# will sort the (brief and detailed) documentation of class members so that ++# constructors and destructors are listed first. If set to NO (the default) ++# the constructors will appear in the respective orders defined by ++# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. ++# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO ++# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. ++ ++SORT_MEMBERS_CTORS_1ST = NO ++ ++# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the ++# hierarchy of group names into alphabetical order. If set to NO (the default) ++# the group names will appear in their defined order. ++ ++SORT_GROUP_NAMES = NO ++ ++# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be ++# sorted by fully-qualified names, including namespaces. If set to ++# NO (the default), the class list will be sorted only by class name, ++# not including the namespace part. ++# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. ++# Note: This option applies only to the class list, not to the ++# alphabetical list. ++ ++SORT_BY_SCOPE_NAME = NO ++ ++# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to ++# do proper type resolution of all parameters of a function it will reject a ++# match between the prototype and the implementation of a member function even ++# if there is only one candidate or it is obvious which candidate to choose ++# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen ++# will still accept a match between prototype and implementation in such cases. ++ ++STRICT_PROTO_MATCHING = NO ++ ++# The GENERATE_TODOLIST tag can be used to enable (YES) or ++# disable (NO) the todo list. This list is created by putting \todo ++# commands in the documentation. ++ ++GENERATE_TODOLIST = YES ++ ++# The GENERATE_TESTLIST tag can be used to enable (YES) or ++# disable (NO) the test list. This list is created by putting \test ++# commands in the documentation. ++ ++GENERATE_TESTLIST = YES ++ ++# The GENERATE_BUGLIST tag can be used to enable (YES) or ++# disable (NO) the bug list. This list is created by putting \bug ++# commands in the documentation. ++ ++GENERATE_BUGLIST = YES ++ ++# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or ++# disable (NO) the deprecated list. This list is created by putting ++# \deprecated commands in the documentation. ++ ++GENERATE_DEPRECATEDLIST= YES ++ ++# The ENABLED_SECTIONS tag can be used to enable conditional ++# documentation sections, marked by \if sectionname ... \endif. ++ ++ENABLED_SECTIONS = ++ ++# The MAX_INITIALIZER_LINES tag determines the maximum number of lines ++# the initial value of a variable or macro consists of for it to appear in ++# the documentation. If the initializer consists of more lines than specified ++# here it will be hidden. Use a value of 0 to hide initializers completely. ++# The appearance of the initializer of individual variables and macros in the ++# documentation can be controlled using \showinitializer or \hideinitializer ++# command in the documentation regardless of this setting. ++ ++MAX_INITIALIZER_LINES = 30 ++ ++# Set the SHOW_USED_FILES tag to NO to disable the list of files generated ++# at the bottom of the documentation of classes and structs. If set to YES the ++# list will mention the files that were used to generate the documentation. ++ ++SHOW_USED_FILES = YES ++ ++# If the sources in your project are distributed over multiple directories ++# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy ++# in the documentation. The default is NO. ++ ++SHOW_DIRECTORIES = NO ++ ++# Set the SHOW_FILES tag to NO to disable the generation of the Files page. ++# This will remove the Files entry from the Quick Index and from the ++# Folder Tree View (if specified). The default is YES. ++ ++SHOW_FILES = YES ++ ++# Set the SHOW_NAMESPACES tag to NO to disable the generation of the ++# Namespaces page. This will remove the Namespaces entry from the Quick Index ++# and from the Folder Tree View (if specified). The default is YES. ++ ++SHOW_NAMESPACES = YES ++ ++# The FILE_VERSION_FILTER tag can be used to specify a program or script that ++# doxygen should invoke to get the current version for each file (typically from ++# the version control system). Doxygen will invoke the program by executing (via ++# popen()) the command , where is the value of ++# the FILE_VERSION_FILTER tag, and is the name of an input file ++# provided by doxygen. Whatever the program writes to standard output ++# is used as the file version. See the manual for examples. ++ ++FILE_VERSION_FILTER = ++ ++# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed ++# by doxygen. The layout file controls the global structure of the generated ++# output files in an output format independent way. The create the layout file ++# that represents doxygen's defaults, run doxygen with the -l option. ++# You can optionally specify a file name after the option, if omitted ++# DoxygenLayout.xml will be used as the name of the layout file. ++ ++LAYOUT_FILE = ++ ++#--------------------------------------------------------------------------- ++# configuration options related to warning and progress messages ++#--------------------------------------------------------------------------- ++ ++# The QUIET tag can be used to turn on/off the messages that are generated ++# by doxygen. Possible values are YES and NO. If left blank NO is used. ++ ++QUIET = NO ++ ++# The WARNINGS tag can be used to turn on/off the warning messages that are ++# generated by doxygen. Possible values are YES and NO. If left blank ++# NO is used. ++ ++WARNINGS = YES ++ ++# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings ++# for undocumented members. If EXTRACT_ALL is set to YES then this flag will ++# automatically be disabled. ++ ++WARN_IF_UNDOCUMENTED = YES ++ ++# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for ++# potential errors in the documentation, such as not documenting some ++# parameters in a documented function, or documenting parameters that ++# don't exist or using markup commands wrongly. ++ ++WARN_IF_DOC_ERROR = YES ++ ++# The WARN_NO_PARAMDOC option can be enabled to get warnings for ++# functions that are documented, but have no documentation for their parameters ++# or return value. If set to NO (the default) doxygen will only warn about ++# wrong or incomplete parameter documentation, but not about the absence of ++# documentation. ++ ++WARN_NO_PARAMDOC = YES ++ ++# The WARN_FORMAT tag determines the format of the warning messages that ++# doxygen can produce. The string should contain the $file, $line, and $text ++# tags, which will be replaced by the file and line number from which the ++# warning originated and the warning text. Optionally the format may contain ++# $version, which will be replaced by the version of the file (if it could ++# be obtained via FILE_VERSION_FILTER) ++ ++WARN_FORMAT = "$file:$line: $text" ++ ++# The WARN_LOGFILE tag can be used to specify a file to which warning ++# and error messages should be written. If left blank the output is written ++# to stderr. ++ ++WARN_LOGFILE = ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the input files ++#--------------------------------------------------------------------------- ++ ++# The INPUT tag can be used to specify the files and/or directories that contain ++# documented source files. You may enter file names like "myfile.cpp" or ++# directories like "/usr/src/myproject". Separate the files or directories ++# with spaces. ++ ++INPUT = client/telldus-core.h \ ++ client/telldus-core.cpp \ ++ ../docs/01-telldus-core.dox \ ++ ../docs/02-tellstick-protocol.dox \ ++ ../docs/03-tellstick-net-protocol.dox ++ ++# This tag can be used to specify the character encoding of the source files ++# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is ++# also the default input encoding. Doxygen uses libiconv (or the iconv built ++# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for ++# the list of possible encodings. ++ ++INPUT_ENCODING = UTF-8 ++ ++# If the value of the INPUT tag contains directories, you can use the ++# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp ++# and *.h) to filter out the source-files in the directories. If left ++# blank the following patterns are tested: ++# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh ++# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py ++# *.f90 *.f *.for *.vhd *.vhdl ++ ++FILE_PATTERNS = ++ ++# The RECURSIVE tag can be used to turn specify whether or not subdirectories ++# should be searched for input files as well. Possible values are YES and NO. ++# If left blank NO is used. ++ ++RECURSIVE = NO ++ ++# The EXCLUDE tag can be used to specify files and/or directories that should ++# excluded from the INPUT source files. This way you can easily exclude a ++# subdirectory from a directory tree whose root is specified with the INPUT tag. ++ ++EXCLUDE = ++ ++# The EXCLUDE_SYMLINKS tag can be used select whether or not files or ++# directories that are symbolic links (a Unix file system feature) are excluded ++# from the input. ++ ++EXCLUDE_SYMLINKS = NO ++ ++# If the value of the INPUT tag contains directories, you can use the ++# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude ++# certain files from those directories. Note that the wildcards are matched ++# against the file with absolute path, so to exclude all test directories ++# for example use the pattern */test/* ++ ++EXCLUDE_PATTERNS = ++ ++# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names ++# (namespaces, classes, functions, etc.) that should be excluded from the ++# output. The symbol name can be a fully qualified name, a word, or if the ++# wildcard * is used, a substring. Examples: ANamespace, AClass, ++# AClass::ANamespace, ANamespace::*Test ++ ++EXCLUDE_SYMBOLS = ++ ++# The EXAMPLE_PATH tag can be used to specify one or more files or ++# directories that contain example code fragments that are included (see ++# the \include command). ++ ++EXAMPLE_PATH = ++ ++# If the value of the EXAMPLE_PATH tag contains directories, you can use the ++# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp ++# and *.h) to filter out the source-files in the directories. If left ++# blank all files are included. ++ ++EXAMPLE_PATTERNS = ++ ++# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be ++# searched for input files to be used with the \include or \dontinclude ++# commands irrespective of the value of the RECURSIVE tag. ++# Possible values are YES and NO. If left blank NO is used. ++ ++EXAMPLE_RECURSIVE = NO ++ ++# The IMAGE_PATH tag can be used to specify one or more files or ++# directories that contain image that are included in the documentation (see ++# the \image command). ++ ++IMAGE_PATH = ++ ++# The INPUT_FILTER tag can be used to specify a program that doxygen should ++# invoke to filter for each input file. Doxygen will invoke the filter program ++# by executing (via popen()) the command , where ++# is the value of the INPUT_FILTER tag, and is the name of an ++# input file. Doxygen will then use the output that the filter program writes ++# to standard output. If FILTER_PATTERNS is specified, this tag will be ++# ignored. ++ ++INPUT_FILTER = ++ ++# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern ++# basis. Doxygen will compare the file name with each pattern and apply the ++# filter if there is a match. The filters are a list of the form: ++# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further ++# info on how filters are used. If FILTER_PATTERNS is empty or if ++# non of the patterns match the file name, INPUT_FILTER is applied. ++ ++FILTER_PATTERNS = ++ ++# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using ++# INPUT_FILTER) will be used to filter the input files when producing source ++# files to browse (i.e. when SOURCE_BROWSER is set to YES). ++ ++FILTER_SOURCE_FILES = NO ++ ++# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file ++# pattern. A pattern will override the setting for FILTER_PATTERN (if any) ++# and it is also possible to disable source filtering for a specific pattern ++# using *.ext= (so without naming a filter). This option only has effect when ++# FILTER_SOURCE_FILES is enabled. ++ ++FILTER_SOURCE_PATTERNS = ++ ++#--------------------------------------------------------------------------- ++# configuration options related to source browsing ++#--------------------------------------------------------------------------- ++ ++# If the SOURCE_BROWSER tag is set to YES then a list of source files will ++# be generated. Documented entities will be cross-referenced with these sources. ++# Note: To get rid of all source code in the generated output, make sure also ++# VERBATIM_HEADERS is set to NO. ++ ++SOURCE_BROWSER = NO ++ ++# Setting the INLINE_SOURCES tag to YES will include the body ++# of functions and classes directly in the documentation. ++ ++INLINE_SOURCES = NO ++ ++# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct ++# doxygen to hide any special comment blocks from generated source code ++# fragments. Normal C and C++ comments will always remain visible. ++ ++STRIP_CODE_COMMENTS = YES ++ ++# If the REFERENCED_BY_RELATION tag is set to YES ++# then for each documented function all documented ++# functions referencing it will be listed. ++ ++REFERENCED_BY_RELATION = NO ++ ++# If the REFERENCES_RELATION tag is set to YES ++# then for each documented function all documented entities ++# called/used by that function will be listed. ++ ++REFERENCES_RELATION = NO ++ ++# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) ++# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from ++# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will ++# link to the source code. Otherwise they will link to the documentation. ++ ++REFERENCES_LINK_SOURCE = YES ++ ++# If the USE_HTAGS tag is set to YES then the references to source code ++# will point to the HTML generated by the htags(1) tool instead of doxygen ++# built-in source browser. The htags tool is part of GNU's global source ++# tagging system (see http://www.gnu.org/software/global/global.html). You ++# will need version 4.8.6 or higher. ++ ++USE_HTAGS = NO ++ ++# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen ++# will generate a verbatim copy of the header file for each class for ++# which an include is specified. Set to NO to disable this. ++ ++VERBATIM_HEADERS = YES ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the alphabetical class index ++#--------------------------------------------------------------------------- ++ ++# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index ++# of all compounds will be generated. Enable this if the project ++# contains a lot of classes, structs, unions or interfaces. ++ ++ALPHABETICAL_INDEX = YES ++ ++# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then ++# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns ++# in which this list will be split (can be a number in the range [1..20]) ++ ++COLS_IN_ALPHA_INDEX = 5 ++ ++# In case all classes in a project start with a common prefix, all ++# classes will be put under the same header in the alphabetical index. ++# The IGNORE_PREFIX tag can be used to specify one or more prefixes that ++# should be ignored while generating the index headers. ++ ++IGNORE_PREFIX = ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the HTML output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_HTML tag is set to YES (the default) Doxygen will ++# generate HTML output. ++ ++GENERATE_HTML = YES ++ ++# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. ++# If a relative path is entered the value of OUTPUT_DIRECTORY will be ++# put in front of it. If left blank `html' will be used as the default path. ++ ++HTML_OUTPUT = html ++ ++# The HTML_FILE_EXTENSION tag can be used to specify the file extension for ++# each generated HTML page (for example: .htm,.php,.asp). If it is left blank ++# doxygen will generate files with .html extension. ++ ++HTML_FILE_EXTENSION = .html ++ ++# The HTML_HEADER tag can be used to specify a personal HTML header for ++# each generated HTML page. If it is left blank doxygen will generate a ++# standard header. Note that when using a custom header you are responsible ++# for the proper inclusion of any scripts and style sheets that doxygen ++# needs, which is dependent on the configuration options used. ++# It is adviced to generate a default header using "doxygen -w html ++# header.html footer.html stylesheet.css YourConfigFile" and then modify ++# that header. Note that the header is subject to change so you typically ++# have to redo this when upgrading to a newer version of doxygen or when ++# changing the value of configuration settings such as GENERATE_TREEVIEW! ++ ++HTML_HEADER = ++ ++# The HTML_FOOTER tag can be used to specify a personal HTML footer for ++# each generated HTML page. If it is left blank doxygen will generate a ++# standard footer. ++ ++HTML_FOOTER = ++ ++# The HTML_STYLESHEET tag can be used to specify a user-defined cascading ++# style sheet that is used by each HTML page. It can be used to ++# fine-tune the look of the HTML output. If the tag is left blank doxygen ++# will generate a default style sheet. Note that doxygen will try to copy ++# the style sheet file to the HTML output directory, so don't put your own ++# stylesheet in the HTML output directory as well, or it will be erased! ++ ++HTML_STYLESHEET = ++ ++# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or ++# other source files which should be copied to the HTML output directory. Note ++# that these files will be copied to the base HTML output directory. Use the ++# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these ++# files. In the HTML_STYLESHEET file, use the file name only. Also note that ++# the files will be copied as-is; there are no commands or markers available. ++ ++HTML_EXTRA_FILES = ++ ++# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. ++# Doxygen will adjust the colors in the stylesheet and background images ++# according to this color. Hue is specified as an angle on a colorwheel, ++# see http://en.wikipedia.org/wiki/Hue for more information. ++# For instance the value 0 represents red, 60 is yellow, 120 is green, ++# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. ++# The allowed range is 0 to 359. ++ ++HTML_COLORSTYLE_HUE = 220 ++ ++# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of ++# the colors in the HTML output. For a value of 0 the output will use ++# grayscales only. A value of 255 will produce the most vivid colors. ++ ++HTML_COLORSTYLE_SAT = 100 ++ ++# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to ++# the luminance component of the colors in the HTML output. Values below ++# 100 gradually make the output lighter, whereas values above 100 make ++# the output darker. The value divided by 100 is the actual gamma applied, ++# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, ++# and 100 does not change the gamma. ++ ++HTML_COLORSTYLE_GAMMA = 80 ++ ++# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML ++# page will contain the date and time when the page was generated. Setting ++# this to NO can help when comparing the output of multiple runs. ++ ++HTML_TIMESTAMP = YES ++ ++# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, ++# files or namespaces will be aligned in HTML using tables. If set to ++# NO a bullet list will be used. ++ ++HTML_ALIGN_MEMBERS = YES ++ ++# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML ++# documentation will contain sections that can be hidden and shown after the ++# page has loaded. For this to work a browser that supports ++# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox ++# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). ++ ++HTML_DYNAMIC_SECTIONS = NO ++ ++# If the GENERATE_DOCSET tag is set to YES, additional index files ++# will be generated that can be used as input for Apple's Xcode 3 ++# integrated development environment, introduced with OSX 10.5 (Leopard). ++# To create a documentation set, doxygen will generate a Makefile in the ++# HTML output directory. Running make will produce the docset in that ++# directory and running "make install" will install the docset in ++# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find ++# it at startup. ++# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html ++# for more information. ++ ++GENERATE_DOCSET = NO ++ ++# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the ++# feed. A documentation feed provides an umbrella under which multiple ++# documentation sets from a single provider (such as a company or product suite) ++# can be grouped. ++ ++DOCSET_FEEDNAME = "Doxygen generated docs" ++ ++# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that ++# should uniquely identify the documentation set bundle. This should be a ++# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen ++# will append .docset to the name. ++ ++DOCSET_BUNDLE_ID = org.doxygen.Project ++ ++# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify ++# the documentation publisher. This should be a reverse domain-name style ++# string, e.g. com.mycompany.MyDocSet.documentation. ++ ++DOCSET_PUBLISHER_ID = org.doxygen.Publisher ++ ++# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. ++ ++DOCSET_PUBLISHER_NAME = Publisher ++ ++# If the GENERATE_HTMLHELP tag is set to YES, additional index files ++# will be generated that can be used as input for tools like the ++# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) ++# of the generated HTML documentation. ++ ++GENERATE_HTMLHELP = NO ++ ++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can ++# be used to specify the file name of the resulting .chm file. You ++# can add a path in front of the file if the result should not be ++# written to the html output directory. ++ ++CHM_FILE = ++ ++# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can ++# be used to specify the location (absolute path including file name) of ++# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run ++# the HTML help compiler on the generated index.hhp. ++ ++HHC_LOCATION = ++ ++# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag ++# controls if a separate .chi index file is generated (YES) or that ++# it should be included in the master .chm file (NO). ++ ++GENERATE_CHI = NO ++ ++# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING ++# is used to encode HtmlHelp index (hhk), content (hhc) and project file ++# content. ++ ++CHM_INDEX_ENCODING = ++ ++# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag ++# controls whether a binary table of contents is generated (YES) or a ++# normal table of contents (NO) in the .chm file. ++ ++BINARY_TOC = NO ++ ++# The TOC_EXPAND flag can be set to YES to add extra items for group members ++# to the contents of the HTML help documentation and to the tree view. ++ ++TOC_EXPAND = NO ++ ++# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and ++# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated ++# that can be used as input for Qt's qhelpgenerator to generate a ++# Qt Compressed Help (.qch) of the generated HTML documentation. ++ ++GENERATE_QHP = NO ++ ++# If the QHG_LOCATION tag is specified, the QCH_FILE tag can ++# be used to specify the file name of the resulting .qch file. ++# The path specified is relative to the HTML output folder. ++ ++QCH_FILE = ++ ++# The QHP_NAMESPACE tag specifies the namespace to use when generating ++# Qt Help Project output. For more information please see ++# http://doc.trolltech.com/qthelpproject.html#namespace ++ ++QHP_NAMESPACE = org.doxygen.Project ++ ++# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating ++# Qt Help Project output. For more information please see ++# http://doc.trolltech.com/qthelpproject.html#virtual-folders ++ ++QHP_VIRTUAL_FOLDER = doc ++ ++# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to ++# add. For more information please see ++# http://doc.trolltech.com/qthelpproject.html#custom-filters ++ ++QHP_CUST_FILTER_NAME = ++ ++# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the ++# custom filter to add. For more information please see ++# ++# Qt Help Project / Custom Filters. ++ ++QHP_CUST_FILTER_ATTRS = ++ ++# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this ++# project's ++# filter section matches. ++# ++# Qt Help Project / Filter Attributes. ++ ++QHP_SECT_FILTER_ATTRS = ++ ++# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can ++# be used to specify the location of Qt's qhelpgenerator. ++# If non-empty doxygen will try to run qhelpgenerator on the generated ++# .qhp file. ++ ++QHG_LOCATION = ++ ++# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files ++# will be generated, which together with the HTML files, form an Eclipse help ++# plugin. To install this plugin and make it available under the help contents ++# menu in Eclipse, the contents of the directory containing the HTML and XML ++# files needs to be copied into the plugins directory of eclipse. The name of ++# the directory within the plugins directory should be the same as ++# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before ++# the help appears. ++ ++GENERATE_ECLIPSEHELP = NO ++ ++# A unique identifier for the eclipse help plugin. When installing the plugin ++# the directory name containing the HTML and XML files should also have ++# this name. ++ ++ECLIPSE_DOC_ID = org.doxygen.Project ++ ++# The DISABLE_INDEX tag can be used to turn on/off the condensed index at ++# top of each HTML page. The value NO (the default) enables the index and ++# the value YES disables it. ++ ++DISABLE_INDEX = NO ++ ++# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values ++# (range [0,1..20]) that doxygen will group on one line in the generated HTML ++# documentation. Note that a value of 0 will completely suppress the enum ++# values from appearing in the overview section. ++ ++ENUM_VALUES_PER_LINE = 4 ++ ++# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index ++# structure should be generated to display hierarchical information. ++# If the tag value is set to YES, a side panel will be generated ++# containing a tree-like index structure (just like the one that ++# is generated for HTML Help). For this to work a browser that supports ++# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). ++# Windows users are probably better off using the HTML help feature. ++ ++GENERATE_TREEVIEW = NO ++ ++# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, ++# and Class Hierarchy pages using a tree view instead of an ordered list. ++ ++USE_INLINE_TREES = NO ++ ++# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be ++# used to set the initial width (in pixels) of the frame in which the tree ++# is shown. ++ ++TREEVIEW_WIDTH = 250 ++ ++# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open ++# links to external symbols imported via tag files in a separate window. ++ ++EXT_LINKS_IN_WINDOW = NO ++ ++# Use this tag to change the font size of Latex formulas included ++# as images in the HTML documentation. The default is 10. Note that ++# when you change the font size after a successful doxygen run you need ++# to manually remove any form_*.png images from the HTML output directory ++# to force them to be regenerated. ++ ++FORMULA_FONTSIZE = 10 ++ ++# Use the FORMULA_TRANPARENT tag to determine whether or not the images ++# generated for formulas are transparent PNGs. Transparent PNGs are ++# not supported properly for IE 6.0, but are supported on all modern browsers. ++# Note that when changing this option you need to delete any form_*.png files ++# in the HTML output before the changes have effect. ++ ++FORMULA_TRANSPARENT = YES ++ ++# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax ++# (see http://www.mathjax.org) which uses client side Javascript for the ++# rendering instead of using prerendered bitmaps. Use this if you do not ++# have LaTeX installed or if you want to formulas look prettier in the HTML ++# output. When enabled you also need to install MathJax separately and ++# configure the path to it using the MATHJAX_RELPATH option. ++ ++USE_MATHJAX = NO ++ ++# When MathJax is enabled you need to specify the location relative to the ++# HTML output directory using the MATHJAX_RELPATH option. The destination ++# directory should contain the MathJax.js script. For instance, if the mathjax ++# directory is located at the same level as the HTML output directory, then ++# MATHJAX_RELPATH should be ../mathjax. The default value points to the ++# mathjax.org site, so you can quickly see the result without installing ++# MathJax, but it is strongly recommended to install a local copy of MathJax ++# before deployment. ++ ++MATHJAX_RELPATH = http://www.mathjax.org/mathjax ++ ++# When the SEARCHENGINE tag is enabled doxygen will generate a search box ++# for the HTML output. The underlying search engine uses javascript ++# and DHTML and should work on any modern browser. Note that when using ++# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets ++# (GENERATE_DOCSET) there is already a search function so this one should ++# typically be disabled. For large projects the javascript based search engine ++# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. ++ ++SEARCHENGINE = YES ++ ++# When the SERVER_BASED_SEARCH tag is enabled the search engine will be ++# implemented using a PHP enabled web server instead of at the web client ++# using Javascript. Doxygen will generate the search PHP script and index ++# file to put on the web server. The advantage of the server ++# based approach is that it scales better to large projects and allows ++# full text search. The disadvantages are that it is more difficult to setup ++# and does not have live searching capabilities. ++ ++SERVER_BASED_SEARCH = NO ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the LaTeX output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will ++# generate Latex output. ++ ++GENERATE_LATEX = NO ++ ++# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. ++# If a relative path is entered the value of OUTPUT_DIRECTORY will be ++# put in front of it. If left blank `latex' will be used as the default path. ++ ++LATEX_OUTPUT = latex ++ ++# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be ++# invoked. If left blank `latex' will be used as the default command name. ++# Note that when enabling USE_PDFLATEX this option is only used for ++# generating bitmaps for formulas in the HTML output, but not in the ++# Makefile that is written to the output directory. ++ ++LATEX_CMD_NAME = latex ++ ++# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to ++# generate index for LaTeX. If left blank `makeindex' will be used as the ++# default command name. ++ ++MAKEINDEX_CMD_NAME = makeindex ++ ++# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact ++# LaTeX documents. This may be useful for small projects and may help to ++# save some trees in general. ++ ++COMPACT_LATEX = NO ++ ++# The PAPER_TYPE tag can be used to set the paper type that is used ++# by the printer. Possible values are: a4, letter, legal and ++# executive. If left blank a4wide will be used. ++ ++PAPER_TYPE = a4 ++ ++# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX ++# packages that should be included in the LaTeX output. ++ ++EXTRA_PACKAGES = ++ ++# The LATEX_HEADER tag can be used to specify a personal LaTeX header for ++# the generated latex document. The header should contain everything until ++# the first chapter. If it is left blank doxygen will generate a ++# standard header. Notice: only use this tag if you know what you are doing! ++ ++LATEX_HEADER = ++ ++# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for ++# the generated latex document. The footer should contain everything after ++# the last chapter. If it is left blank doxygen will generate a ++# standard footer. Notice: only use this tag if you know what you are doing! ++ ++LATEX_FOOTER = ++ ++# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated ++# is prepared for conversion to pdf (using ps2pdf). The pdf file will ++# contain links (just like the HTML output) instead of page references ++# This makes the output suitable for online browsing using a pdf viewer. ++ ++PDF_HYPERLINKS = YES ++ ++# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of ++# plain latex in the generated Makefile. Set this option to YES to get a ++# higher quality PDF documentation. ++ ++USE_PDFLATEX = YES ++ ++# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. ++# command to the generated LaTeX files. This will instruct LaTeX to keep ++# running if errors occur, instead of asking the user for help. ++# This option is also used when generating formulas in HTML. ++ ++LATEX_BATCHMODE = NO ++ ++# If LATEX_HIDE_INDICES is set to YES then doxygen will not ++# include the index chapters (such as File Index, Compound Index, etc.) ++# in the output. ++ ++LATEX_HIDE_INDICES = NO ++ ++# If LATEX_SOURCE_CODE is set to YES then doxygen will include ++# source code with syntax highlighting in the LaTeX output. ++# Note that which sources are shown also depends on other settings ++# such as SOURCE_BROWSER. ++ ++LATEX_SOURCE_CODE = NO ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the RTF output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output ++# The RTF output is optimized for Word 97 and may not look very pretty with ++# other RTF readers or editors. ++ ++GENERATE_RTF = NO ++ ++# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. ++# If a relative path is entered the value of OUTPUT_DIRECTORY will be ++# put in front of it. If left blank `rtf' will be used as the default path. ++ ++RTF_OUTPUT = rtf ++ ++# If the COMPACT_RTF tag is set to YES Doxygen generates more compact ++# RTF documents. This may be useful for small projects and may help to ++# save some trees in general. ++ ++COMPACT_RTF = NO ++ ++# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated ++# will contain hyperlink fields. The RTF file will ++# contain links (just like the HTML output) instead of page references. ++# This makes the output suitable for online browsing using WORD or other ++# programs which support those fields. ++# Note: wordpad (write) and others do not support links. ++ ++RTF_HYPERLINKS = NO ++ ++# Load stylesheet definitions from file. Syntax is similar to doxygen's ++# config file, i.e. a series of assignments. You only have to provide ++# replacements, missing definitions are set to their default value. ++ ++RTF_STYLESHEET_FILE = ++ ++# Set optional variables used in the generation of an rtf document. ++# Syntax is similar to doxygen's config file. ++ ++RTF_EXTENSIONS_FILE = ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the man page output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_MAN tag is set to YES (the default) Doxygen will ++# generate man pages ++ ++GENERATE_MAN = NO ++ ++# The MAN_OUTPUT tag is used to specify where the man pages will be put. ++# If a relative path is entered the value of OUTPUT_DIRECTORY will be ++# put in front of it. If left blank `man' will be used as the default path. ++ ++MAN_OUTPUT = man ++ ++# The MAN_EXTENSION tag determines the extension that is added to ++# the generated man pages (default is the subroutine's section .3) ++ ++MAN_EXTENSION = .3 ++ ++# If the MAN_LINKS tag is set to YES and Doxygen generates man output, ++# then it will generate one additional man file for each entity ++# documented in the real man page(s). These additional files ++# only source the real man page, but without them the man command ++# would be unable to find the correct page. The default is NO. ++ ++MAN_LINKS = NO ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the XML output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_XML tag is set to YES Doxygen will ++# generate an XML file that captures the structure of ++# the code including all documentation. ++ ++GENERATE_XML = NO ++ ++# The XML_OUTPUT tag is used to specify where the XML pages will be put. ++# If a relative path is entered the value of OUTPUT_DIRECTORY will be ++# put in front of it. If left blank `xml' will be used as the default path. ++ ++XML_OUTPUT = xml ++ ++# The XML_SCHEMA tag can be used to specify an XML schema, ++# which can be used by a validating XML parser to check the ++# syntax of the XML files. ++ ++XML_SCHEMA = ++ ++# The XML_DTD tag can be used to specify an XML DTD, ++# which can be used by a validating XML parser to check the ++# syntax of the XML files. ++ ++XML_DTD = ++ ++# If the XML_PROGRAMLISTING tag is set to YES Doxygen will ++# dump the program listings (including syntax highlighting ++# and cross-referencing information) to the XML output. Note that ++# enabling this will significantly increase the size of the XML output. ++ ++XML_PROGRAMLISTING = YES ++ ++#--------------------------------------------------------------------------- ++# configuration options for the AutoGen Definitions output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will ++# generate an AutoGen Definitions (see autogen.sf.net) file ++# that captures the structure of the code including all ++# documentation. Note that this feature is still experimental ++# and incomplete at the moment. ++ ++GENERATE_AUTOGEN_DEF = NO ++ ++#--------------------------------------------------------------------------- ++# configuration options related to the Perl module output ++#--------------------------------------------------------------------------- ++ ++# If the GENERATE_PERLMOD tag is set to YES Doxygen will ++# generate a Perl module file that captures the structure of ++# the code including all documentation. Note that this ++# feature is still experimental and incomplete at the ++# moment. ++ ++GENERATE_PERLMOD = NO ++ ++# If the PERLMOD_LATEX tag is set to YES Doxygen will generate ++# the necessary Makefile rules, Perl scripts and LaTeX code to be able ++# to generate PDF and DVI output from the Perl module output. ++ ++PERLMOD_LATEX = NO ++ ++# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be ++# nicely formatted so it can be parsed by a human reader. This is useful ++# if you want to understand what is going on. On the other hand, if this ++# tag is set to NO the size of the Perl module output will be much smaller ++# and Perl will parse it just the same. ++ ++PERLMOD_PRETTY = YES ++ ++# The names of the make variables in the generated doxyrules.make file ++# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. ++# This is useful so different doxyrules.make files included by the same ++# Makefile don't overwrite each other's variables. ++ ++PERLMOD_MAKEVAR_PREFIX = ++ ++#--------------------------------------------------------------------------- ++# Configuration options related to the preprocessor ++#--------------------------------------------------------------------------- ++ ++# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will ++# evaluate all C-preprocessor directives found in the sources and include ++# files. ++ ++ENABLE_PREPROCESSING = YES ++ ++# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro ++# names in the source code. If set to NO (the default) only conditional ++# compilation will be performed. Macro expansion can be done in a controlled ++# way by setting EXPAND_ONLY_PREDEF to YES. ++ ++MACRO_EXPANSION = YES ++ ++# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES ++# then the macro expansion is limited to the macros specified with the ++# PREDEFINED and EXPAND_AS_DEFINED tags. ++ ++EXPAND_ONLY_PREDEF = YES ++ ++# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files ++# pointed to by INCLUDE_PATH will be searched when a #include is found. ++ ++SEARCH_INCLUDES = YES ++ ++# The INCLUDE_PATH tag can be used to specify one or more directories that ++# contain include files that are not input files but should be processed by ++# the preprocessor. ++ ++INCLUDE_PATH = ++ ++# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard ++# patterns (like *.h and *.hpp) to filter out the header-files in the ++# directories. If left blank, the patterns specified with FILE_PATTERNS will ++# be used. ++ ++INCLUDE_FILE_PATTERNS = ++ ++# The PREDEFINED tag can be used to specify one or more macro names that ++# are defined before the preprocessor is started (similar to the -D option of ++# gcc). The argument of the tag is a list of macros of the form: name ++# or name=definition (no spaces). If the definition and the = are ++# omitted =1 is assumed. To prevent a macro definition from being ++# undefined via #undef or recursively expanded use the := operator ++# instead of the = operator. ++ ++PREDEFINED = WINAPI= ++ ++# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then ++# this tag can be used to specify a list of macro names that should be expanded. ++# The macro definition that is found in the sources will be used. ++# Use the PREDEFINED tag if you want to use a different macro definition that ++# overrules the definition found in the source code. ++ ++EXPAND_AS_DEFINED = ++ ++# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then ++# doxygen's preprocessor will remove all references to function-like macros ++# that are alone on a line, have an all uppercase name, and do not end with a ++# semicolon, because these will confuse the parser if not removed. ++ ++SKIP_FUNCTION_MACROS = YES ++ ++#--------------------------------------------------------------------------- ++# Configuration::additions related to external references ++#--------------------------------------------------------------------------- ++ ++# The TAGFILES option can be used to specify one or more tagfiles. ++# Optionally an initial location of the external documentation ++# can be added for each tagfile. The format of a tag file without ++# this location is as follows: ++# TAGFILES = file1 file2 ... ++# Adding location for the tag files is done as follows: ++# TAGFILES = file1=loc1 "file2 = loc2" ... ++# where "loc1" and "loc2" can be relative or absolute paths or ++# URLs. If a location is present for each tag, the installdox tool ++# does not have to be run to correct the links. ++# Note that each tag file must have a unique name ++# (where the name does NOT include the path) ++# If a tag file is not located in the directory in which doxygen ++# is run, you must also specify the path to the tagfile here. ++ ++TAGFILES = ++ ++# When a file name is specified after GENERATE_TAGFILE, doxygen will create ++# a tag file that is based on the input files it reads. ++ ++GENERATE_TAGFILE = ++ ++# If the ALLEXTERNALS tag is set to YES all external classes will be listed ++# in the class index. If set to NO only the inherited external classes ++# will be listed. ++ ++ALLEXTERNALS = NO ++ ++# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed ++# in the modules index. If set to NO, only the current project's groups will ++# be listed. ++ ++EXTERNAL_GROUPS = YES ++ ++# The PERL_PATH should be the absolute path and name of the perl script ++# interpreter (i.e. the result of `which perl'). ++ ++PERL_PATH = /usr/bin/perl ++ ++#--------------------------------------------------------------------------- ++# Configuration options related to the dot tool ++#--------------------------------------------------------------------------- ++ ++# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will ++# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base ++# or super classes. Setting the tag to NO turns the diagrams off. Note that ++# this option also works with HAVE_DOT disabled, but it is recommended to ++# install and use dot, since it yields more powerful graphs. ++ ++CLASS_DIAGRAMS = YES ++ ++# You can define message sequence charts within doxygen comments using the \msc ++# command. Doxygen will then run the mscgen tool (see ++# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the ++# documentation. The MSCGEN_PATH tag allows you to specify the directory where ++# the mscgen tool resides. If left empty the tool is assumed to be found in the ++# default search path. ++ ++MSCGEN_PATH = ++ ++# If set to YES, the inheritance and collaboration graphs will hide ++# inheritance and usage relations if the target is undocumented ++# or is not a class. ++ ++HIDE_UNDOC_RELATIONS = YES ++ ++# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is ++# available from the path. This tool is part of Graphviz, a graph visualization ++# toolkit from AT&T and Lucent Bell Labs. The other options in this section ++# have no effect if this option is set to NO (the default) ++ ++HAVE_DOT = NO ++ ++# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is ++# allowed to run in parallel. When set to 0 (the default) doxygen will ++# base this on the number of processors available in the system. You can set it ++# explicitly to a value larger than 0 to get control over the balance ++# between CPU load and processing speed. ++ ++DOT_NUM_THREADS = 0 ++ ++# By default doxygen will write a font called Helvetica to the output ++# directory and reference it in all dot files that doxygen generates. ++# When you want a differently looking font you can specify the font name ++# using DOT_FONTNAME. You need to make sure dot is able to find the font, ++# which can be done by putting it in a standard location or by setting the ++# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory ++# containing the font. ++ ++DOT_FONTNAME = Helvetica ++ ++# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. ++# The default size is 10pt. ++ ++DOT_FONTSIZE = 10 ++ ++# By default doxygen will tell dot to use the output directory to look for the ++# FreeSans.ttf font (which doxygen will put there itself). If you specify a ++# different font using DOT_FONTNAME you can set the path where dot ++# can find it using this tag. ++ ++DOT_FONTPATH = ++ ++# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen ++# will generate a graph for each documented class showing the direct and ++# indirect inheritance relations. Setting this tag to YES will force the ++# the CLASS_DIAGRAMS tag to NO. ++ ++CLASS_GRAPH = YES ++ ++# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen ++# will generate a graph for each documented class showing the direct and ++# indirect implementation dependencies (inheritance, containment, and ++# class references variables) of the class with other documented classes. ++ ++COLLABORATION_GRAPH = YES ++ ++# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen ++# will generate a graph for groups, showing the direct groups dependencies ++ ++GROUP_GRAPHS = YES ++ ++# If the UML_LOOK tag is set to YES doxygen will generate inheritance and ++# collaboration diagrams in a style similar to the OMG's Unified Modeling ++# Language. ++ ++UML_LOOK = NO ++ ++# If set to YES, the inheritance and collaboration graphs will show the ++# relations between templates and their instances. ++ ++TEMPLATE_RELATIONS = NO ++ ++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT ++# tags are set to YES then doxygen will generate a graph for each documented ++# file showing the direct and indirect include dependencies of the file with ++# other documented files. ++ ++INCLUDE_GRAPH = YES ++ ++# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and ++# HAVE_DOT tags are set to YES then doxygen will generate a graph for each ++# documented header file showing the documented files that directly or ++# indirectly include this file. ++ ++INCLUDED_BY_GRAPH = YES ++ ++# If the CALL_GRAPH and HAVE_DOT options are set to YES then ++# doxygen will generate a call dependency graph for every global function ++# or class method. Note that enabling this option will significantly increase ++# the time of a run. So in most cases it will be better to enable call graphs ++# for selected functions only using the \callgraph command. ++ ++CALL_GRAPH = NO ++ ++# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then ++# doxygen will generate a caller dependency graph for every global function ++# or class method. Note that enabling this option will significantly increase ++# the time of a run. So in most cases it will be better to enable caller ++# graphs for selected functions only using the \callergraph command. ++ ++CALLER_GRAPH = NO ++ ++# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen ++# will generate a graphical hierarchy of all classes instead of a textual one. ++ ++GRAPHICAL_HIERARCHY = YES ++ ++# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES ++# then doxygen will show the dependencies a directory has on other directories ++# in a graphical way. The dependency relations are determined by the #include ++# relations between the files in the directories. ++ ++DIRECTORY_GRAPH = YES ++ ++# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images ++# generated by dot. Possible values are svg, png, jpg, or gif. ++# If left blank png will be used. ++ ++DOT_IMAGE_FORMAT = png ++ ++# The tag DOT_PATH can be used to specify the path where the dot tool can be ++# found. If left blank, it is assumed the dot tool can be found in the path. ++ ++DOT_PATH = ++ ++# The DOTFILE_DIRS tag can be used to specify one or more directories that ++# contain dot files that are included in the documentation (see the ++# \dotfile command). ++ ++DOTFILE_DIRS = ++ ++# The MSCFILE_DIRS tag can be used to specify one or more directories that ++# contain msc files that are included in the documentation (see the ++# \mscfile command). ++ ++MSCFILE_DIRS = ++ ++# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of ++# nodes that will be shown in the graph. If the number of nodes in a graph ++# becomes larger than this value, doxygen will truncate the graph, which is ++# visualized by representing a node as a red box. Note that doxygen if the ++# number of direct children of the root node in a graph is already larger than ++# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note ++# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. ++ ++DOT_GRAPH_MAX_NODES = 50 ++ ++# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the ++# graphs generated by dot. A depth value of 3 means that only nodes reachable ++# from the root by following a path via at most 3 edges will be shown. Nodes ++# that lay further from the root node will be omitted. Note that setting this ++# option to 1 or 2 may greatly reduce the computation time needed for large ++# code bases. Also note that the size of a graph can be further restricted by ++# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. ++ ++MAX_DOT_GRAPH_DEPTH = 0 ++ ++# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent ++# background. This is disabled by default, because dot on Windows does not ++# seem to support this out of the box. Warning: Depending on the platform used, ++# enabling this option may lead to badly anti-aliased labels on the edges of ++# a graph (i.e. they become hard to read). ++ ++DOT_TRANSPARENT = NO ++ ++# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output ++# files in one run (i.e. multiple -o and -T options on the command line). This ++# makes dot run faster, but since only newer versions of dot (>1.8.10) ++# support this, this feature is disabled by default. ++ ++DOT_MULTI_TARGETS = YES ++ ++# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will ++# generate a legend page explaining the meaning of the various boxes and ++# arrows in the dot generated graphs. ++ ++GENERATE_LEGEND = YES ++ ++# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will ++# remove the intermediate dot files that are used to generate ++# the various graphs. ++ ++DOT_CLEANUP = YES diff --git a/telldus-core/debian/patches/02_fix_link_ordering_problem.patch b/telldus-core/debian/patches/02_fix_link_ordering_problem.patch new file mode 100644 index 0000000..08c286d --- /dev/null +++ b/telldus-core/debian/patches/02_fix_link_ordering_problem.patch @@ -0,0 +1,46 @@ +Description: + TODO: Put a short summary on the line above and replace this paragraph + with a longer explanation of this change. Complete the meta-information + with other relevant fields (see below for details). To make it easier, the + information below has been extracted from the changelog. Adjust it or drop + it. + . + telldus-core (2.1.2-1) UNRELEASED; urgency=low + . + * Initial release (Closes: #xxxx) +Author: Ruben Undheim + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: , +Bug: +Bug-Debian: http://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: + +--- telldus-core-2.1.2.orig/client/CMakeLists.txt ++++ telldus-core-2.1.2/client/CMakeLists.txt +@@ -21,8 +21,6 @@ SET( telldus-core_PUB_HDRS + telldus-core.h + ) + +-FIND_PACKAGE(Threads) +-LIST(APPEND telldus-core_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) + +@@ -100,6 +98,9 @@ ENDIF () + + ADD_DEPENDENCIES(${telldus-core_TARGET} TelldusCommon) + ++FIND_PACKAGE(Threads) ++LIST(APPEND telldus-core_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) ++ + IF (UNIX) + SET_TARGET_PROPERTIES( ${telldus-core_TARGET} PROPERTIES COMPILE_FLAGS "-fPIC -fvisibility=hidden") + ENDIF (UNIX) diff --git a/telldus-core/debian/patches/series b/telldus-core/debian/patches/series new file mode 100644 index 0000000..65e7795 --- /dev/null +++ b/telldus-core/debian/patches/series @@ -0,0 +1,2 @@ +01_add_missing_doxyfile.patch +02_fix_link_ordering_problem.patch diff --git a/telldus-core/debian/rules b/telldus-core/debian/rules new file mode 100755 index 0000000..f0c03c8 --- /dev/null +++ b/telldus-core/debian/rules @@ -0,0 +1,15 @@ +#!/usr/bin/make -f + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + +export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + +%: + dh $@ + + +override_dh_strip: + dh_strip -plibtelldus0 --keep-debug --dbg-package=libtelldus0-dbg + dh_strip -ptdtool + diff --git a/telldus-core/debian/source/format b/telldus-core/debian/source/format new file mode 100644 index 0000000..163aaf8 --- /dev/null +++ b/telldus-core/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/telldus-core/debian/tdtool.docs b/telldus-core/debian/tdtool.docs new file mode 100644 index 0000000..e845566 --- /dev/null +++ b/telldus-core/debian/tdtool.docs @@ -0,0 +1 @@ +README diff --git a/telldus-core/debian/tdtool.install b/telldus-core/debian/tdtool.install new file mode 100644 index 0000000..d95cc49 --- /dev/null +++ b/telldus-core/debian/tdtool.install @@ -0,0 +1,5 @@ +usr/bin/* +usr/sbin/* +etc/* +var/state/* +usr/share/telldus-core/* diff --git a/telldus-core/service/CMakeLists.txt b/telldus-core/service/CMakeLists.txt new file mode 100644 index 0000000..595d551 --- /dev/null +++ b/telldus-core/service/CMakeLists.txt @@ -0,0 +1,278 @@ +IF(COMMAND cmake_policy) + CMAKE_POLICY(SET CMP0003 NEW) +ENDIF(COMMAND cmake_policy) + +FIND_PACKAGE( SignTool REQUIRED ) + +SET (telldusd_DESCRIPTION + "background service for Telldus TellStick, must be running to control TellStick" +) + +ADD_DEFINITIONS( -DVERSION="${DISPLAYED_VERSION}" ) + +######## Non configurable options ######## +SET( telldus-service_SRCS + ClientCommunicationHandler.cpp + Controller.cpp + ControllerManager.cpp + ControllerMessage.cpp + Device.cpp + DeviceManager.cpp + Log.cpp + Sensor.cpp + Settings.cpp + TelldusMain.cpp + TellStick.cpp + Timer.cpp + EventUpdateManager.cpp +) +SET( telldus-service_protocol_SRCS + Protocol.h + Protocol.cpp + ProtocolBrateck.h + ProtocolBrateck.cpp + ProtocolComen.h + ProtocolComen.cpp + ProtocolEverflourish.h + ProtocolEverflourish.cpp + ProtocolFineoffset.h + ProtocolFineoffset.cpp + ProtocolFuhaote.h + ProtocolFuhaote.cpp + ProtocolGroup.h + ProtocolGroup.cpp + ProtocolHasta.h + ProtocolHasta.cpp + ProtocolIkea.h + ProtocolIkea.cpp + ProtocolMandolyn.h + ProtocolMandolyn.cpp + ProtocolNexa.h + ProtocolNexa.cpp + ProtocolOregon.h + ProtocolOregon.cpp + ProtocolRisingSun.h + ProtocolRisingSun.cpp + ProtocolSartano.h + ProtocolSartano.cpp + ProtocolScene.h + ProtocolScene.cpp + ProtocolSilvanChip.h + ProtocolSilvanChip.cpp + ProtocolUpm.h + ProtocolUpm.cpp + ProtocolWaveman.h + ProtocolWaveman.cpp + ProtocolX10.h + ProtocolX10.cpp + ProtocolYidong.h + ProtocolYidong.cpp +) +SET( telldus-service_HDRS + ClientCommunicationHandler.h + ConnectionListener.h + Controller.h + ControllerListener.h + ControllerManager.h + ControllerMessage.h + Device.h + DeviceManager.h + EventUpdateManager.h + Log.h + Sensor.h + Settings.h + TelldusMain.h + TellStick.h + Timer.h +) +FIND_PACKAGE(Threads REQUIRED) +LIST(APPEND telldus-service_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) + +######## Configurable options for the platform ######## + +IF (UNIX AND NOT APPLE) + SET(SCRIPT_PATH "/usr/local/share/telldus/scripts" CACHE PATH "The paths for scripts to execute on sensor events") + SET(SYSCONF_INSTALL_DIR "/etc" CACHE PATH "The sysconfig install dir (default prefix/etc)" ) + IF (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") + SET(DEFAULT_STATE_INSTALL_DIR "/var/spool") + ELSE () + SET(DEFAULT_STATE_INSTALL_DIR "/var/state") + ENDIF () + SET(STATE_INSTALL_DIR "${DEFAULT_STATE_INSTALL_DIR}" CACHE PATH "The directory to store state information of the devices") + +ENDIF() + +######## Platforms-specific, non configurable ######## + +IF (APPLE) #### Mac OS X #### + SET(DEFAULT_FTDI_ENGINE "ftd2xx") + SET( telldus-service_TARGET TelldusService ) + ADD_DEFINITIONS( -D_MACOSX ) + FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) + FIND_LIBRARY(IOKIT_LIBRARY IOKit) + + SET( telldus-service_LIBRARIES + ${telldus-service_LIBRARIES} + ${COREFOUNDATION_LIBRARY} + ${IOKIT_LIBRARY} + TelldusCommon + ) + LIST(APPEND telldus-service_SRCS + main_mac.cpp + ConnectionListener_unix.cpp + ControllerListener_mac.cpp + SettingsCoreFoundationPreferences.cpp + ) + +ELSEIF (WIN32) #### Windows #### + SET(DEFAULT_FTDI_ENGINE "ftd2xx") + SET( telldus-service_TARGET TelldusService ) + ADD_DEFINITIONS( -DUNICODE ) + ADD_DEFINITIONS( /Zc:wchar_t- ) # Treat wchar_t as Built-in Type' = No + SET(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:CONSOLE" + ) + LIST(APPEND telldus-service_LIBRARIES + TelldusCommon + ) + LIST(APPEND telldus-service_SRCS + ConnectionListener_win.cpp + main_win.cpp + SettingsWinRegistry.cpp + TelldusWinService_win.cpp + Messages.mc + ${CMAKE_CURRENT_BINARY_DIR}/Messages.rc + ${CMAKE_CURRENT_BINARY_DIR}/service/Messages.h + ) + LIST(APPEND telldus-service_HDRS + TelldusWinService_win.h + ) + ADD_CUSTOM_COMMAND( + OUTPUT Messages.rc service/Messages.h + COMMAND mc.exe -u -r \"${CMAKE_CURRENT_BINARY_DIR}\" -h \"${CMAKE_CURRENT_BINARY_DIR}/service\" \"${CMAKE_CURRENT_SOURCE_DIR}/Messages.mc\" + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Messages.mc + DEPENDS Messages.rc + COMMENT "Compiling Messages Resource" + ) + INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} ) + +ELSE (APPLE) #### Linux #### + SET(DEFAULT_FTDI_ENGINE "libftdi") + FIND_LIBRARY(CONFUSE_LIBRARY confuse) + ADD_DEFINITIONS( -D_CONFUSE ) + ADD_DEFINITIONS( -D_LINUX ) + + SET( telldus-service_TARGET telldusd ) + LIST(APPEND telldus-service_SRCS + ConnectionListener_unix.cpp + main_unix.cpp + SettingsConfuse.cpp + ) + + LIST(APPEND telldus-service_LIBRARIES + ${CONFUSE_LIBRARY} + TelldusCommon + ) +ENDIF (APPLE) + +SET(FTDI_ENGINE ${DEFAULT_FTDI_ENGINE} CACHE STRING "Which FTDI engine to use. This could be either 'libftdi' or 'ftd2xx'") +IF (FTDI_ENGINE STREQUAL "ftd2xx") + FIND_LIBRARY(FTD2XX_LIBRARY ftd2xx) + ADD_DEFINITIONS( -DLIBFTD2XX ) + LIST(APPEND telldus-service_SRCS TellStick_ftd2xx.cpp ) + LIST(APPEND telldus-service_LIBRARIES ${FTD2XX_LIBRARY}) +ELSE (FTDI_ENGINE STREQUAL "ftd2xx") + FIND_LIBRARY(FTDI_LIBRARY ftdi) + INCLUDE(FindPkgConfig) + PKG_SEARCH_MODULE(FTDI libftdi) + INCLUDE_DIRECTORIES( ${FTDI_INCLUDEDIR} ) + ADD_DEFINITIONS( -DLIBFTDI ) + LIST(APPEND telldus-service_SRCS TellStick_libftdi.cpp ) + LIST(APPEND telldus-service_LIBRARIES ${FTDI_LIBRARY}) +ENDIF (FTDI_ENGINE STREQUAL "ftd2xx") + +######## Configuring ######## + +SOURCE_GROUP("Protocol Files" FILES ${telldus-service_protocol_SRCS}) + +CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/service/config.h) +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) + +SET(telldus-service_TARGET ${telldus-service_TARGET} PARENT_SCOPE) +ADD_EXECUTABLE(${telldus-service_TARGET} + ${telldus-service_SRCS} + ${telldus-service_protocol_SRCS} + ${telldus-service_HDRS} +) +ADD_DEPENDENCIES(${telldus-service_TARGET} TelldusCommon) +SET_SOURCE_FILES_PROPERTIES(${telldus-service_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + +TARGET_LINK_LIBRARIES( ${telldus-service_TARGET} ${telldus-service_LIBRARIES} ) + +SIGN(${telldus-service_TARGET}) + +SET_TARGET_PROPERTIES(${telldus-service_TARGET} PROPERTIES + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist +) +IF (APPLE) + SET(TELLDUS_SERVICE_TARGET_PATH "/Library/Telldus" CACHE STRING "Path to install TelldusService") + SET_TARGET_PROPERTIES(${telldus-service_TARGET} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) + INSTALL(TARGETS ${telldus-service_TARGET} + RUNTIME DESTINATION ${TELLDUS_SERVICE_TARGET_PATH} + ) + INSTALL(CODE " + INCLUDE(GetPrerequisites) + GET_FILENAME_COMPONENT(DESTDIR \$ENV{DESTDIR} ABSOLUTE) + GET_PREREQUISITES(\"\${DESTDIR}/${TELLDUS_SERVICE_TARGET_PATH}/${telldus-service_TARGET}\" prereqs 1 0 \"\$\" \"\$\") + FOREACH(pr \${prereqs}) + GET_FILENAME_COMPONENT(lib \${pr} NAME) + FILE(INSTALL \${pr} DESTINATION ${TELLDUS_SERVICE_TARGET_PATH}) + EXECUTE_PROCESS(COMMAND install_name_tool + -change \"\${pr}\" \"${TELLDUS_SERVICE_TARGET_PATH}/\${lib}\" \"\${DESTDIR}/${TELLDUS_SERVICE_TARGET_PATH}/${telldus-service_TARGET}\" + ) + ENDFOREACH () + ") + INSTALL(FILES com.telldus.service.plist DESTINATION /Library/LaunchDaemons) +ENDIF (APPLE) + +IF (UNIX) + IF (GENERATE_MAN) + ADD_CUSTOM_COMMAND( + TARGET ${telldus-service_TARGET} + POST_BUILD + COMMAND help2man -n ${telldusd_DESCRIPTION} ./telldusd > telldusd.1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating man file telldusd.1" + ) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/telldusd.1 DESTINATION share/man/man1) + ENDIF (GENERATE_MAN) +ENDIF (UNIX) + +IF (UNIX AND NOT APPLE) + INSTALL(TARGETS ${telldus-service_TARGET} RUNTIME DESTINATION sbin) + INSTALL(FILES tellstick.conf + DESTINATION ${SYSCONF_INSTALL_DIR} + ) + INSTALL(FILES telldus-core.conf + DESTINATION ${STATE_INSTALL_DIR} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ GROUP_WRITE WORLD_READ WORLD_WRITE + ) +ENDIF (UNIX AND NOT APPLE) + +IF(ENABLE_TESTING) + # Build it once more, but as static library so it can be linked by the test-tool + ADD_LIBRARY(TelldusServiceStatic STATIC + ${telldus-service_SRCS} + ${telldus-service_protocol_SRCS} + ${telldus-service_HDRS} + ) + TARGET_LINK_LIBRARIES( TelldusServiceStatic ${telldus-service_LIBRARIES} ) + IF (UNIX) + SET_TARGET_PROPERTIES( TelldusServiceStatic PROPERTIES COMPILE_FLAGS "-fPIC") + ENDIF (UNIX) +ENDIF() diff --git a/telldus-core/service/ClientCommunicationHandler.cpp b/telldus-core/service/ClientCommunicationHandler.cpp new file mode 100644 index 0000000..1670069 --- /dev/null +++ b/telldus-core/service/ClientCommunicationHandler.cpp @@ -0,0 +1,266 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ClientCommunicationHandler.h" + +#include +#include + +#include "common/Message.h" +#include "common/Strings.h" + +class ClientCommunicationHandler::PrivateData { +public: + TelldusCore::Socket *clientSocket; + TelldusCore::EventRef event, deviceUpdateEvent; + bool done; + DeviceManager *deviceManager; + ControllerManager *controllerManager; +}; + +ClientCommunicationHandler::ClientCommunicationHandler() { +} + +ClientCommunicationHandler::ClientCommunicationHandler(TelldusCore::Socket *clientSocket, TelldusCore::EventRef event, DeviceManager *deviceManager, TelldusCore::EventRef deviceUpdateEvent, ControllerManager *controllerManager) + :Thread() { + d = new PrivateData; + d->clientSocket = clientSocket; + d->event = event; + d->done = false; + d->deviceManager = deviceManager; + d->deviceUpdateEvent = deviceUpdateEvent; + d->controllerManager = controllerManager; +} + +ClientCommunicationHandler::~ClientCommunicationHandler(void) { + wait(); + delete(d->clientSocket); + delete d; +} + +void ClientCommunicationHandler::run() { + // run thread + + std::wstring clientMessage = d->clientSocket->read(2000); + + int intReturn; + std::wstring strReturn; + strReturn = L""; + parseMessage(clientMessage, &intReturn, &strReturn); + + TelldusCore::Message msg; + + if(strReturn == L"") { + msg.addArgument(intReturn); + } else { + msg.addArgument(strReturn); + } + msg.append(L"\n"); + d->clientSocket->write(msg); + + // We are done, signal for removal + d->done = true; + d->event->signal(); +} + +bool ClientCommunicationHandler::isDone() { + return d->done; +} + + +void ClientCommunicationHandler::parseMessage(const std::wstring &clientMessage, int *intReturn, std::wstring *wstringReturn) { + (*intReturn) = 0; + (*wstringReturn) = L""; + std::wstring msg(clientMessage); // Copy + std::wstring function(TelldusCore::Message::takeString(&msg)); + + if (function == L"tdTurnOn") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_TURNON, 0); + + } else if (function == L"tdTurnOff") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_TURNOFF, 0); + + } else if (function == L"tdBell") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_BELL, 0); + + } else if (function == L"tdDim") { + int deviceId = TelldusCore::Message::takeInt(&msg); + int level = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_DIM, level); + + } else if (function == L"tdExecute") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_EXECUTE, 0); + + } else if (function == L"tdUp") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_UP, 0); + + } else if (function == L"tdDown") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_DOWN, 0); + + } else if (function == L"tdStop") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_STOP, 0); + + } else if (function == L"tdLearn") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->doAction(deviceId, TELLSTICK_LEARN, 0); + + } else if (function == L"tdLastSentCommand") { + int deviceId = TelldusCore::Message::takeInt(&msg); + int methodsSupported = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->getDeviceLastSentCommand(deviceId, methodsSupported); + + } else if (function == L"tdLastSentValue") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*wstringReturn) = d->deviceManager->getDeviceStateValue(deviceId); + + } else if(function == L"tdGetNumberOfDevices") { + (*intReturn) = d->deviceManager->getNumberOfDevices(); + + } else if (function == L"tdGetDeviceId") { + int deviceIndex = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->getDeviceId(deviceIndex); + + } else if (function == L"tdGetDeviceType") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->getDeviceType(deviceId); + + } else if (function == L"tdGetName") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*wstringReturn) = d->deviceManager->getDeviceName(deviceId); + + } else if (function == L"tdSetName") { + int deviceId = TelldusCore::Message::takeInt(&msg); + std::wstring name = TelldusCore::Message::takeString(&msg); + (*intReturn) = d->deviceManager->setDeviceName(deviceId, name); + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_NAME); + + } else if (function == L"tdGetProtocol") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*wstringReturn) = d->deviceManager->getDeviceProtocol(deviceId); + + } else if (function == L"tdSetProtocol") { + int deviceId = TelldusCore::Message::takeInt(&msg); + std::wstring protocol = TelldusCore::Message::takeString(&msg); + int oldMethods = d->deviceManager->getDeviceMethods(deviceId); + (*intReturn) = d->deviceManager->setDeviceProtocol(deviceId, protocol); + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_PROTOCOL); + if(oldMethods != d->deviceManager->getDeviceMethods(deviceId)) { + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_METHOD); + } + + } else if (function == L"tdGetModel") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*wstringReturn) = d->deviceManager->getDeviceModel(deviceId); + + } else if (function == L"tdSetModel") { + int deviceId = TelldusCore::Message::takeInt(&msg); + std::wstring model = TelldusCore::Message::takeString(&msg); + int oldMethods = d->deviceManager->getDeviceMethods(deviceId); + (*intReturn) = d->deviceManager->setDeviceModel(deviceId, model); + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_MODEL); + if(oldMethods != d->deviceManager->getDeviceMethods(deviceId)) { + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_METHOD); + } + + } else if (function == L"tdGetDeviceParameter") { + int deviceId = TelldusCore::Message::takeInt(&msg); + std::wstring name = TelldusCore::Message::takeString(&msg); + std::wstring defaultValue = TelldusCore::Message::takeString(&msg); + (*wstringReturn) = d->deviceManager->getDeviceParameter(deviceId, name, defaultValue); + + } else if (function == L"tdSetDeviceParameter") { + int deviceId = TelldusCore::Message::takeInt(&msg); + std::wstring name = TelldusCore::Message::takeString(&msg); + std::wstring value = TelldusCore::Message::takeString(&msg); + int oldMethods = d->deviceManager->getDeviceMethods(deviceId); + (*intReturn) = d->deviceManager->setDeviceParameter(deviceId, name, value); + if(oldMethods != d->deviceManager->getDeviceMethods(deviceId)) { + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_METHOD); + } + + } else if (function == L"tdAddDevice") { + (*intReturn) = d->deviceManager->addDevice(); + if((*intReturn) >= 0) { + sendDeviceSignal((*intReturn), TELLSTICK_DEVICE_ADDED, 0); + } + + } else if (function == L"tdRemoveDevice") { + int deviceId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->removeDevice(deviceId); + if((*intReturn) == TELLSTICK_SUCCESS) { + sendDeviceSignal(deviceId, TELLSTICK_DEVICE_REMOVED, 0); + } + + } else if (function == L"tdMethods") { + int deviceId = TelldusCore::Message::takeInt(&msg); + int intMethodsSupported = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->getDeviceMethods(deviceId, intMethodsSupported); + + } else if (function == L"tdSendRawCommand") { + std::wstring command = TelldusCore::Message::takeString(&msg); + int reserved = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->deviceManager->sendRawCommand(command, reserved); + + } else if (function == L"tdConnectTellStickController") { + int vid = TelldusCore::Message::takeInt(&msg); + int pid = TelldusCore::Message::takeInt(&msg); + std::string serial = TelldusCore::wideToString(TelldusCore::Message::takeString(&msg)); + d->deviceManager->connectTellStickController(vid, pid, serial); + + } else if (function == L"tdDisconnectTellStickController") { + int vid = TelldusCore::Message::takeInt(&msg); + int pid = TelldusCore::Message::takeInt(&msg); + std::string serial = TelldusCore::wideToString(TelldusCore::Message::takeString(&msg)); + d->deviceManager->disconnectTellStickController(vid, pid, serial); + + } else if (function == L"tdSensor") { + (*wstringReturn) = d->deviceManager->getSensors(); + + } else if (function == L"tdSensorValue") { + std::wstring protocol = TelldusCore::Message::takeString(&msg); + std::wstring model = TelldusCore::Message::takeString(&msg); + int id = TelldusCore::Message::takeInt(&msg); + int dataType = TelldusCore::Message::takeInt(&msg); + (*wstringReturn) = d->deviceManager->getSensorValue(protocol, model, id, dataType); + + } else if (function == L"tdController") { + (*wstringReturn) = d->controllerManager->getControllers(); + + } else if (function == L"tdControllerValue") { + int id = TelldusCore::Message::takeInt(&msg); + std::wstring name = TelldusCore::Message::takeString(&msg); + (*wstringReturn) = d->controllerManager->getControllerValue(id, name); + + } else if (function == L"tdSetControllerValue") { + int id = TelldusCore::Message::takeInt(&msg); + std::wstring name = TelldusCore::Message::takeString(&msg); + std::wstring value = TelldusCore::Message::takeString(&msg); + (*intReturn) = d->controllerManager->setControllerValue(id, name, value); + + } else if (function == L"tdRemoveController") { + int controllerId = TelldusCore::Message::takeInt(&msg); + (*intReturn) = d->controllerManager->removeController(controllerId); + + } else { + (*intReturn) = TELLSTICK_ERROR_UNKNOWN; + } +} + +void ClientCommunicationHandler::sendDeviceSignal(int deviceId, int eventDeviceChanges, int eventChangeType) { + EventUpdateData *eventData = new EventUpdateData(); + eventData->messageType = L"TDDeviceChangeEvent"; + eventData->deviceId = deviceId; + eventData->eventDeviceChanges = eventDeviceChanges; + eventData->eventChangeType = eventChangeType; + d->deviceUpdateEvent->signal(eventData); +} diff --git a/telldus-core/service/ClientCommunicationHandler.h b/telldus-core/service/ClientCommunicationHandler.h new file mode 100644 index 0000000..e25f172 --- /dev/null +++ b/telldus-core/service/ClientCommunicationHandler.h @@ -0,0 +1,41 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CLIENTCOMMUNICATIONHANDLER_H_ +#define TELLDUS_CORE_SERVICE_CLIENTCOMMUNICATIONHANDLER_H_ + +#include +#include "common/Thread.h" +#include "common/Socket.h" +#include "common/Event.h" +#include "service/DeviceManager.h" +#include "service/ControllerManager.h" + +class ClientCommunicationHandler : public TelldusCore::Thread { +public: + ClientCommunicationHandler(); + ClientCommunicationHandler( + TelldusCore::Socket *clientSocket, + TelldusCore::EventRef event, + DeviceManager *deviceManager, + TelldusCore::EventRef deviceUpdateEvent, + ControllerManager *controllerManager + ); + ~ClientCommunicationHandler(void); + + bool isDone(); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; + void parseMessage(const std::wstring &clientMessage, int *intReturn, std::wstring *wstringReturn); + void sendDeviceSignal(int deviceId, int eventDeviceChanges, int eventChangeType); +}; + +#endif // TELLDUS_CORE_SERVICE_CLIENTCOMMUNICATIONHANDLER_H_ diff --git a/telldus-core/service/ConnectionListener.h b/telldus-core/service/ConnectionListener.h new file mode 100644 index 0000000..08e838e --- /dev/null +++ b/telldus-core/service/ConnectionListener.h @@ -0,0 +1,37 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CONNECTIONLISTENER_H_ +#define TELLDUS_CORE_SERVICE_CONNECTIONLISTENER_H_ + +#include +#include "common/Thread.h" +#include "common/Event.h" + +class Event; +namespace TelldusCore { + class Socket; +}; + +class ConnectionListenerEventData : public TelldusCore::EventDataBase { +public: + TelldusCore::Socket *socket; +}; + +class ConnectionListener : public TelldusCore::Thread { +public: + ConnectionListener(const std::wstring &name, TelldusCore::EventRef waitEvent); + virtual ~ConnectionListener(void); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_CONNECTIONLISTENER_H_ diff --git a/telldus-core/service/ConnectionListener_unix.cpp b/telldus-core/service/ConnectionListener_unix.cpp new file mode 100644 index 0000000..d3b238f --- /dev/null +++ b/telldus-core/service/ConnectionListener_unix.cpp @@ -0,0 +1,100 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "service/ConnectionListener.h" +#include "common/Socket.h" + +#if defined(_MACOSX) && !defined(SOCK_CLOEXEC) +#define SOCK_CLOEXEC 0 +#endif + +class ConnectionListener::PrivateData { +public: + TelldusCore::EventRef waitEvent; + std::string name; + bool running; +}; + +ConnectionListener::ConnectionListener(const std::wstring &name, TelldusCore::EventRef waitEvent) { + d = new PrivateData; + d->waitEvent = waitEvent; + + d->name = "/tmp/" + std::string(name.begin(), name.end()); + d->running = true; + + this->start(); +} + +ConnectionListener::~ConnectionListener(void) { + d->running = false; + this->wait(); + unlink(d->name.c_str()); + delete d; +} + +void ConnectionListener::run() { + struct timeval tv = { 0, 0 }; + + // Timeout for select + + SOCKET_T serverSocket; + struct sockaddr_un name; + serverSocket = socket(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (serverSocket < 0) { + return; + } +#if defined(_MACOSX) + int op = fcntl(serverSocket, F_GETFD); + fcntl(serverSocket, F_SETFD, op | FD_CLOEXEC); // OS X doesn't support SOCK_CLOEXEC yet +#endif + name.sun_family = AF_LOCAL; + memset(name.sun_path, '\0', sizeof(name.sun_path)); + strncpy(name.sun_path, d->name.c_str(), sizeof(name.sun_path)); + unlink(name.sun_path); + int size = SUN_LEN(&name); + bind(serverSocket, (struct sockaddr *)&name, size); + listen(serverSocket, 5); + + // Change permissions to allow everyone + chmod(d->name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + + fd_set infds; + FD_ZERO(&infds); + FD_SET(serverSocket, &infds); + + while(d->running) { + tv.tv_sec = 5; + + int response = select(serverSocket+1, &infds, NULL, NULL, &tv); + if (response == 0) { + FD_SET(serverSocket, &infds); + continue; + } else if (response < 0 ) { + continue; + } + // Make sure it is a new connection + if (!FD_ISSET(serverSocket, &infds)) { + continue; + } + SOCKET_T clientSocket = accept(serverSocket, NULL, NULL); + + ConnectionListenerEventData *data = new ConnectionListenerEventData(); + data->socket = new TelldusCore::Socket(clientSocket); + d->waitEvent->signal(data); + } + close(serverSocket); +} + diff --git a/telldus-core/service/ConnectionListener_win.cpp b/telldus-core/service/ConnectionListener_win.cpp new file mode 100644 index 0000000..6fced4e --- /dev/null +++ b/telldus-core/service/ConnectionListener_win.cpp @@ -0,0 +1,153 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ConnectionListener.h" + +#include +#include +#include + +#include "common/Event.h" +#include "common/Socket.h" + +#define BUFSIZE 512 + +class ConnectionListener::PrivateData { +public: + std::wstring pipename; + SECURITY_ATTRIBUTES sa; + HANDLE hEvent; + bool running; + TelldusCore::EventRef waitEvent; +}; + +ConnectionListener::ConnectionListener(const std::wstring &name, TelldusCore::EventRef waitEvent) { + d = new PrivateData; + d->hEvent = 0; + + d->running = true; + d->waitEvent = waitEvent; + d->pipename = L"\\\\.\\pipe\\" + name; + + PSECURITY_DESCRIPTOR pSD = NULL; + PACL pACL = NULL; + EXPLICIT_ACCESS ea; + PSID pEveryoneSID = NULL; + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL) { + return; + } + + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + LocalFree(pSD); + return; + } + + if(!AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID)) { + LocalFree(pSD); + } + + ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS)); + ea.grfAccessPermissions = STANDARD_RIGHTS_ALL; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance= NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea.Trustee.ptstrName = (LPTSTR) pEveryoneSID; + + // Add the ACL to the security descriptor. + if (!SetSecurityDescriptorDacl(pSD, + TRUE, // bDaclPresent flag + pACL, + FALSE)) { // not a default DACL + LocalFree(pSD); + FreeSid(pEveryoneSID); + } + + + d->sa.nLength = sizeof(SECURITY_ATTRIBUTES); + d->sa.lpSecurityDescriptor = pSD; + d->sa.bInheritHandle = false; + + start(); +} + +ConnectionListener::~ConnectionListener(void) { + d->running = false; + if (d->hEvent) { + SetEvent(d->hEvent); + } + wait(); + delete d; +} + +void ConnectionListener::run() { + HANDLE hPipe; + OVERLAPPED oOverlap; + DWORD cbBytesRead; + + memset(&oOverlap, 0, sizeof(OVERLAPPED)); + + d->hEvent = CreateEvent(NULL, true, false, NULL); + oOverlap.hEvent = d->hEvent; + bool recreate = true; + + while (1) { + BOOL alreadyConnected = false; + if (recreate) { + hPipe = CreateNamedPipe( + (const wchar_t *)d->pipename.c_str(), // pipe name + PIPE_ACCESS_DUPLEX | // read/write access + FILE_FLAG_OVERLAPPED, // Overlapped mode + PIPE_TYPE_MESSAGE | // message type pipe + PIPE_READMODE_MESSAGE | // message-read mode + PIPE_WAIT, // blocking mode + PIPE_UNLIMITED_INSTANCES, // max. instances + BUFSIZE, // output buffer size + BUFSIZE, // input buffer size + 0, // client time-out + &d->sa); // default security attribute + + if (hPipe == INVALID_HANDLE_VALUE) { + return; + } + + ConnectNamedPipe(hPipe, &oOverlap); + alreadyConnected = GetLastError() == ERROR_PIPE_CONNECTED; + recreate = false; + } + if(!alreadyConnected) { + DWORD result = WaitForSingleObject(oOverlap.hEvent, 1000); + if (!d->running) { + CancelIo(hPipe); + WaitForSingleObject(oOverlap.hEvent, INFINITE); + break; + } + + if(result == WAIT_TIMEOUT) { + // CloseHandle(hPipe); + continue; + } + BOOL connected = GetOverlappedResult(hPipe, &oOverlap, &cbBytesRead, false); + + if (!connected) { + CloseHandle(hPipe); + return; + } + } + ConnectionListenerEventData *data = new ConnectionListenerEventData(); + ResetEvent(oOverlap.hEvent); + data->socket = new TelldusCore::Socket(hPipe); + d->waitEvent->signal(data); + + recreate = true; + } + + CloseHandle(d->hEvent); + CloseHandle(hPipe); +} diff --git a/telldus-core/service/Controller.cpp b/telldus-core/service/Controller.cpp new file mode 100644 index 0000000..1a0e43f --- /dev/null +++ b/telldus-core/service/Controller.cpp @@ -0,0 +1,99 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#define _CRT_RAND_S +#include "service/Controller.h" +#include +#include +#include +#include +#include +#include "service/Protocol.h" +#include "service/EventUpdateManager.h" +#include "common/Strings.h" + +inline int random( unsigned int* seed ) { + #ifdef _WINDOWS + unsigned int randomNumber; + rand_s( &randomNumber ); // no seed needed + return randomNumber; + #else + return rand_r( seed ); + #endif +} + +class Controller::PrivateData { +public: + TelldusCore::EventRef event, updateEvent; + int id, firmwareVersion; + unsigned int randSeed; + std::map duplicates; +}; + +Controller::Controller(int id, TelldusCore::EventRef event, TelldusCore::EventRef updateEvent) { + d = new PrivateData; + d->event = event; + d->updateEvent = updateEvent; + d->id = id; + d->firmwareVersion = 0; + d->randSeed = time(NULL); +} + +Controller::~Controller() { + delete d; +} + +void Controller::publishData(const std::string &msg) const { + ControllerEventData *data = new ControllerEventData; + data->msg = msg; + data->controllerId = d->id; + d->event->signal(data); +} + +void Controller::decodePublishData(const std::string &data) const { + // Garbange collect? + if (random(&d->randSeed) % 1000 == 1) { + time_t t = time(NULL); + // Standard associative-container erase idiom + for (std::map::iterator it = d->duplicates.begin(); it != d->duplicates.end(); /* no increment */) { + if ((*it).second != t) { + d->duplicates.erase(it++); + } else { + ++it; + } + } + } + // Duplicate check + if (d->duplicates.count(data) > 0) { + time_t t = d->duplicates[data]; + if (t == time(NULL)) { + // Duplicate message + return; + } + } + d->duplicates[data] = time(NULL); + + std::list msgList = Protocol::decodeData(data); + + for (std::list::iterator msgIt = msgList.begin(); msgIt != msgList.end(); ++msgIt) { + this->publishData(*msgIt); + } +} + +int Controller::firmwareVersion() const { + return d->firmwareVersion; +} + +void Controller::setFirmwareVersion(int version) { + d->firmwareVersion = version; + EventUpdateData *eventData = new EventUpdateData(); + eventData->messageType = L"TDControllerEvent"; + eventData->controllerId = d->id; + eventData->eventState = TELLSTICK_DEVICE_CHANGED; + eventData->eventChangeType = TELLSTICK_CHANGE_FIRMWARE; + eventData->eventValue = TelldusCore::intToWstring(version); + d->updateEvent->signal(eventData); +} diff --git a/telldus-core/service/Controller.h b/telldus-core/service/Controller.h new file mode 100644 index 0000000..3bbb667 --- /dev/null +++ b/telldus-core/service/Controller.h @@ -0,0 +1,38 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CONTROLLER_H_ +#define TELLDUS_CORE_SERVICE_CONTROLLER_H_ + +#include +#include "common/Event.h" + +class ControllerEventData : public TelldusCore::EventDataBase { +public: + std::string msg; + int controllerId; +}; + +class Controller { +public: + virtual ~Controller(); + + virtual int firmwareVersion() const; + virtual int send( const std::string &message ) = 0; + virtual int reset() = 0; + +protected: + Controller(int id, TelldusCore::EventRef event, TelldusCore::EventRef updateEvent); + void publishData(const std::string &data) const; + void decodePublishData(const std::string &data) const; + void setFirmwareVersion(int version); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_CONTROLLER_H_ diff --git a/telldus-core/service/ControllerListener.h b/telldus-core/service/ControllerListener.h new file mode 100644 index 0000000..3730f0f --- /dev/null +++ b/telldus-core/service/ControllerListener.h @@ -0,0 +1,32 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CONTROLLERLISTENER_H_ +#define TELLDUS_CORE_SERVICE_CONTROLLERLISTENER_H_ + +#include "common/Thread.h" +#include "common/Event.h" + +class ControllerChangeEventData : public TelldusCore::EventDataBase { +public: + int vid, pid; + bool inserted; +}; + +class ControllerListener : public TelldusCore::Thread { +public: + explicit ControllerListener(TelldusCore::EventRef event); + virtual ~ControllerListener(); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_CONTROLLERLISTENER_H_ diff --git a/telldus-core/service/ControllerListener_mac.cpp b/telldus-core/service/ControllerListener_mac.cpp new file mode 100644 index 0000000..f45ae10 --- /dev/null +++ b/telldus-core/service/ControllerListener_mac.cpp @@ -0,0 +1,187 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ControllerListener.h" + +#include +#include +#include +#include +#include +#include + +class TellStickData { +public: + io_object_t notification; + CFStringRef serialNumber; + UInt32 vid; + UInt32 pid; + TelldusCore::EventRef event; +}; + +class ControllerListener::PrivateData { +public: + IONotificationPortRef gNotifyPort; + CFRunLoopRef gRunLoop; + io_iterator_t gAddedIter; + TelldusCore::EventRef event; + bool running; + + void addUsbFilter(int vid, int pid); + static void DeviceAdded(void *refCon, io_iterator_t iterator); + static void DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument); +}; + +ControllerListener::ControllerListener(TelldusCore::EventRef event) +:Thread() { + d = new PrivateData; + d->event = event; + d->running = true; + d->gRunLoop = NULL; + this->start(); +} + +ControllerListener::~ControllerListener() { + d->running = false; + if(d->gRunLoop != NULL) + CFRunLoopStop(d->gRunLoop); + + this->wait(); + delete d; +} + +void ControllerListener::run() { + CFRunLoopSourceRef runLoopSource; + + d->gNotifyPort = IONotificationPortCreate(kIOMasterPortDefault); + runLoopSource = IONotificationPortGetRunLoopSource(d->gNotifyPort); + + d->gRunLoop = CFRunLoopGetCurrent(); + CFRunLoopAddSource(d->gRunLoop, runLoopSource, kCFRunLoopDefaultMode); + + d->addUsbFilter(0x1781, 0x0c30); + d->addUsbFilter(0x1781, 0x0c31); + + // Race check, if destructor was called really close to thread init, + // running might have gone false. Make sure we don't get stuck + if (d->running) { + CFRunLoopRun(); + } +} + +void ControllerListener::PrivateData::addUsbFilter(int vid, int pid) { + CFNumberRef numberRef; + CFMutableDictionaryRef matchingDict; + + matchingDict = IOServiceMatching(kIOUSBDeviceClassName); // Interested in instances of class + // IOUSBDevice and its subclasses + if (matchingDict == NULL) { + return; + } + + // Create a CFNumber for the idVendor and set the value in the dictionary + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vid); + CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); + CFRelease(numberRef); + + // Create a CFNumber for the idProduct and set the value in the dictionary + numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &pid); + CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); + CFRelease(numberRef); + + // Now set up a notification to be called when a device is first matched by I/O Kit. + IOServiceAddMatchingNotification(gNotifyPort, // notifyPort + kIOFirstMatchNotification, // notificationType + matchingDict, // matching + PrivateData::DeviceAdded, // callback + this, // refCon + &gAddedIter // notification + ); + // Iterate once to get already-present devices and arm the notification + PrivateData::DeviceAdded(this, gAddedIter); +} + +void ControllerListener::PrivateData::DeviceNotification(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) { + if (messageType != kIOMessageServiceIsTerminated) { + return; + } + + TellStickData *tsd = reinterpret_cast (refCon); + if (!tsd) { + return; + } + + CFIndex size = CFStringGetLength(tsd->serialNumber); + char *s = new char[size+1]; + CFStringGetCString(tsd->serialNumber, s, size+1, kCFStringEncodingASCII); + std::string serial(s); // Copy the string to the stack + delete[] s; + + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = tsd->vid; + data->pid = tsd->pid; + data->inserted = false; + tsd->event->signal(data); + + // Free the data we're no longer using now that the device is going away + CFRelease(tsd->serialNumber); + + IOObjectRelease(tsd->notification); + + delete tsd; +} + +void ControllerListener::PrivateData::DeviceAdded(void *refCon, io_iterator_t iterator) { + io_service_t usbDevice; + + PrivateData *pd = reinterpret_cast (refCon); + + while ((usbDevice = IOIteratorNext(iterator))) { + TellStickData *tsd = new TellStickData; + tsd->event = pd->event; + + // Get the serial number + CFStringRef serialRef = reinterpret_cast(IORegistryEntryCreateCFProperty( usbDevice, CFSTR("USB Serial Number" ), kCFAllocatorDefault, 0 )); + if (serialRef == NULL) { + // No serial number, we cannot continue. Sorry + continue; + } + + CFNumberRef vidRef = reinterpret_cast (IORegistryEntryCreateCFProperty(usbDevice, CFSTR("idVendor"), kCFAllocatorDefault, 0)); + if (vidRef) { + CFNumberGetValue(vidRef, kCFNumberIntType, &(tsd->vid)); + CFRelease(vidRef); + } + + CFNumberRef pidRef = reinterpret_cast (IORegistryEntryCreateCFProperty(usbDevice, CFSTR("idProduct"), kCFAllocatorDefault, 0)); + if (pidRef) { + CFNumberGetValue(pidRef, kCFNumberIntType, &(tsd->pid)); + CFRelease(pidRef); + } + + CFStringRef serialNumberAsCFString = CFStringCreateCopy(kCFAllocatorDefault, serialRef); + tsd->serialNumber = serialNumberAsCFString; + CFRelease(serialRef); + + // Register for an interest notification of this device being removed. Use a reference to our + // private data as the refCon which will be passed to the notification callback. + IOServiceAddInterestNotification(pd->gNotifyPort, usbDevice, kIOGeneralInterest, DeviceNotification, tsd, &(tsd->notification)); + + CFIndex size = CFStringGetLength(serialNumberAsCFString); + char *s = new char[size+1]; + CFStringGetCString(serialNumberAsCFString, s, size+1, kCFStringEncodingASCII); + std::string serial(s); // Copy the string to the stack + delete[] s; + + IOObjectRelease(usbDevice); + + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = tsd->vid; + data->pid = tsd->pid; + data->inserted = true; + tsd->event->signal(data); + } +} diff --git a/telldus-core/service/ControllerManager.cpp b/telldus-core/service/ControllerManager.cpp new file mode 100644 index 0000000..b02d65a --- /dev/null +++ b/telldus-core/service/ControllerManager.cpp @@ -0,0 +1,374 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ControllerManager.h" + +#include +#include +#include +#include + +#include "service/Controller.h" +#include "common/Mutex.h" +#include "service/TellStick.h" +#include "service/Log.h" +#include "common/Message.h" +#include "common/Strings.h" +#include "service/Settings.h" +#include "service/EventUpdateManager.h" +#include "client/telldus-core.h" + +class ControllerDescriptor { +public: + std::wstring name, serial; + int type; + Controller *controller; +}; + +typedef std::map ControllerMap; + +class ControllerManager::PrivateData { +public: + int lastControllerId; + Settings settings; + ControllerMap controllers; + TelldusCore::EventRef event, updateEvent; + TelldusCore::Mutex mutex; +}; + +ControllerManager::ControllerManager(TelldusCore::EventRef event, TelldusCore::EventRef updateEvent) { + d = new PrivateData; + d->lastControllerId = 0; + d->event = event; + d->updateEvent = updateEvent; + this->loadStoredControllers(); + this->loadControllers(); +} + +ControllerManager::~ControllerManager() { + for (ControllerMap::iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (it->second.controller) { + delete( it->second.controller ); + } + } + delete d; +} + +int ControllerManager::count() { + unsigned int count = 0; + { + TelldusCore::MutexLocker locker(&d->mutex); + // Find all available controllers + for(ControllerMap::const_iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (it->second.controller) { + ++count; + } + } + } + if (count == 0) { + this->loadControllers(); + // Try again + TelldusCore::MutexLocker locker(&d->mutex); + // Find all available controllers + for(ControllerMap::const_iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (it->second.controller) { + ++count; + } + } + } + return count; +} + +void ControllerManager::deviceInsertedOrRemoved(int vid, int pid, const std::string &serial, bool inserted) { + if (vid == 0x0 && pid == 0x0) { // All + if (inserted) { + loadControllers(); + } else { + // Disconnect all + TelldusCore::MutexLocker locker(&d->mutex); + while(d->controllers.size()) { + ControllerMap::iterator it = d->controllers.begin(); + delete it->second.controller; + it->second.controller = 0; + signalControllerEvent(it->first, TELLSTICK_DEVICE_STATE_CHANGED, TELLSTICK_CHANGE_AVAILABLE, L"0"); + } + } + return; + } + if (vid != 0x1781) { + return; + } + if (pid != 0x0C30 && pid != 0x0C31) { + return; + } + if (inserted) { + loadControllers(); + } else { + // Autodetect which has been disconnected + TelldusCore::MutexLocker locker(&d->mutex); + for(ControllerMap::iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (!it->second.controller) { + continue; + } + TellStick *tellstick = reinterpret_cast(it->second.controller); + if (!tellstick) { + continue; + } + if (serial.compare("") != 0) { + TellStickDescriptor tsd; + tsd.vid = vid; + tsd.pid = pid; + tsd.serial = serial; + if (!tellstick->isSameAsDescriptor(tsd)) { + continue; + } + } else if (tellstick->stillConnected()) { + continue; + } + + it->second.controller = 0; + delete tellstick; + signalControllerEvent(it->first, TELLSTICK_DEVICE_STATE_CHANGED, TELLSTICK_CHANGE_AVAILABLE, L"0"); + } + } +} + +Controller *ControllerManager::getBestControllerById(int id) { + TelldusCore::MutexLocker locker(&d->mutex); + if (!d->controllers.size()) { + return 0; + } + ControllerMap::const_iterator it = d->controllers.find(id); + if (it != d->controllers.end() && it->second.controller) { + return it->second.controller; + } + // Find first available controller + for(it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (it->second.controller) { + return it->second.controller; + } + } + return 0; +} + +void ControllerManager::loadControllers() { + TelldusCore::MutexLocker locker(&d->mutex); + + std::list list = TellStick::findAll(); + + std::list::iterator it = list.begin(); + for(; it != list.end(); ++it) { + // Most backend only report non-opened devices. + // If they don't make sure we don't open them twice + bool found = false; + ControllerMap::const_iterator cit = d->controllers.begin(); + for(; cit != d->controllers.end(); ++cit) { + if (!cit->second.controller) { + continue; + } + TellStick *tellstick = reinterpret_cast(cit->second.controller); + if (!tellstick) { + continue; + } + if (tellstick->isSameAsDescriptor(*it)) { + found = true; + break; + } + } + if (found) { + continue; + } + + int type = TELLSTICK_CONTROLLER_TELLSTICK; + if ((*it).pid == 0x0c31) { + type = TELLSTICK_CONTROLLER_TELLSTICK_DUO; + } + int controllerId = 0; + // See if the controller matches one of the loaded, non available controllers + std::wstring serial = TelldusCore::charToWstring((*it).serial.c_str()); + for(cit = d->controllers.begin(); cit != d->controllers.end(); ++cit) { + if (cit->second.type == type && cit->second.serial.compare(serial) == 0) { + controllerId = cit->first; + break; + } + } + bool isNew = false; + if (!controllerId) { + controllerId = d->settings.addNode(Settings::Controller); + if(controllerId < 0) { + // TODO(micke): How to handle this? + continue; + } + isNew = true; + d->controllers[controllerId].type = type; + d->settings.setControllerType(controllerId, type); + d->controllers[controllerId].serial = TelldusCore::charToWstring((*it).serial.c_str()); + d->settings.setControllerSerial(controllerId, d->controllers[controllerId].serial); + } + + // int controllerId = d->lastControllerId+1; + TellStick *controller = new TellStick(controllerId, d->event, d->updateEvent, *it); + if (!controller->isOpen()) { + delete controller; + continue; + } + d->controllers[controllerId].controller = controller; + if (isNew) { + signalControllerEvent(controllerId, TELLSTICK_DEVICE_ADDED, type, L""); + } else { + signalControllerEvent(controllerId, TELLSTICK_DEVICE_STATE_CHANGED, TELLSTICK_CHANGE_AVAILABLE, L"1"); + } + } +} + +void ControllerManager::loadStoredControllers() { + int numberOfControllers = d->settings.getNumberOfNodes(Settings::Controller); + TelldusCore::MutexLocker locker(&d->mutex); + + for (int i = 0; i < numberOfControllers; ++i) { + int id = d->settings.getNodeId(Settings::Controller, i); + d->controllers[id].controller = NULL; + d->controllers[id].name = d->settings.getName(Settings::Controller, id); + const int type = d->settings.getControllerType(id); + d->controllers[id].type = type; + d->controllers[id].serial = d->settings.getControllerSerial(id); + signalControllerEvent(id, TELLSTICK_DEVICE_ADDED, type, L""); + } +} + +void ControllerManager::queryControllerStatus() { + std::list tellStickControllers; + + { + TelldusCore::MutexLocker locker(&d->mutex); + for(ControllerMap::iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + if (!it->second.controller) { + continue; + } + TellStick *tellstick = reinterpret_cast(it->second.controller); + if (tellstick) { + tellStickControllers.push_back(tellstick); + } + } + } + + bool reloadControllers = false; + std::string noop = "N+"; + for(std::list::iterator it = tellStickControllers.begin(); it != tellStickControllers.end(); ++it) { + int success = (*it)->send(noop); + if(success == TELLSTICK_ERROR_BROKEN_PIPE) { + Log::warning("TellStick query: Error in communication with TellStick, resetting USB"); + resetController(*it); + } + if(success == TELLSTICK_ERROR_BROKEN_PIPE || success == TELLSTICK_ERROR_NOT_FOUND) { + reloadControllers = true; + } + } + + if(!tellStickControllers.size() || reloadControllers) { + // no tellstick at all found, or controller was reset + Log::debug("TellStick query: Rescanning USB ports"); // only log as debug, since this will happen all the time if no TellStick is connected + loadControllers(); + } +} + +int ControllerManager::resetController(Controller *controller) { + TellStick *tellstick = reinterpret_cast(controller); + if (!tellstick) { + return true; // not tellstick, nothing to reset at the moment, just return true + } + int success = tellstick->reset(); + deviceInsertedOrRemoved(tellstick->vid(), tellstick->pid(), tellstick->serial(), false); // remove from list and delete + return success; +} + +std::wstring ControllerManager::getControllers() const { + TelldusCore::MutexLocker locker(&d->mutex); + + TelldusCore::Message msg; + + msg.addArgument(static_cast(d->controllers.size())); + + for(ControllerMap::iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) { + msg.addArgument(it->first); + msg.addArgument(it->second.type); + msg.addArgument(it->second.name.c_str()); + msg.addArgument(it->second.controller ? 1 : 0); + } + return msg; +} + +std::wstring ControllerManager::getControllerValue(int id, const std::wstring &name) { + TelldusCore::MutexLocker locker(&d->mutex); + + ControllerMap::iterator it = d->controllers.find(id); + if (it == d->controllers.end()) { + return L""; + } + if (name == L"serial") { + return it->second.serial; + } else if (name == L"name") { + return it->second.name; + } else if (name == L"available") { + return it->second.controller ? L"1" : L"0"; + } else if (name == L"firmware") { + if (!it->second.controller) { + return L"-1"; + } + return TelldusCore::intToWstring(it->second.controller->firmwareVersion()); + } + return L""; +} + +int ControllerManager::removeController(int id) { + TelldusCore::MutexLocker locker(&d->mutex); + + ControllerMap::iterator it = d->controllers.find(id); + if (it == d->controllers.end()) { + return TELLSTICK_ERROR_NOT_FOUND; + } + if (it->second.controller) { + // Still connected + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + + int ret = d->settings.removeNode(Settings::Controller, id); + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + + d->controllers.erase(it); + + signalControllerEvent(id, TELLSTICK_DEVICE_REMOVED, 0, L""); + return TELLSTICK_SUCCESS; +} + +int ControllerManager::setControllerValue(int id, const std::wstring &name, const std::wstring &value) { + TelldusCore::MutexLocker locker(&d->mutex); + + ControllerMap::iterator it = d->controllers.find(id); + if (it == d->controllers.end()) { + return TELLSTICK_ERROR_NOT_FOUND; + } + if (name == L"name") { + it->second.name = value; + d->settings.setName(Settings::Controller, id, value); + signalControllerEvent(id, TELLSTICK_DEVICE_CHANGED, TELLSTICK_CHANGE_NAME, value); + } else { + return TELLSTICK_ERROR_SYNTAX; // TODO(micke): Is this the best error? + } + return TELLSTICK_SUCCESS; +} + +void ControllerManager::signalControllerEvent(int controllerId, int changeEvent, int changeType, const std::wstring &newValue) { + EventUpdateData *eventData = new EventUpdateData(); + eventData->messageType = L"TDControllerEvent"; + eventData->controllerId = controllerId; + eventData->eventState = changeEvent; + eventData->eventChangeType = changeType; + eventData->eventValue = newValue; + d->updateEvent->signal(eventData); +} diff --git a/telldus-core/service/ControllerManager.h b/telldus-core/service/ControllerManager.h new file mode 100644 index 0000000..093caf2 --- /dev/null +++ b/telldus-core/service/ControllerManager.h @@ -0,0 +1,40 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CONTROLLERMANAGER_H_ +#define TELLDUS_CORE_SERVICE_CONTROLLERMANAGER_H_ + +#include +#include "common/Event.h" +class Controller; + + +class ControllerManager { +public: + ControllerManager(TelldusCore::EventRef event, TelldusCore::EventRef updateEvent); + ~ControllerManager(void); + + void deviceInsertedOrRemoved(int vid, int pid, const std::string &serial, bool inserted); + + int count(); + Controller *getBestControllerById(int id); + void loadControllers(); + void loadStoredControllers(); + void queryControllerStatus(); + int resetController(Controller *controller); + + std::wstring getControllers() const; + std::wstring getControllerValue(int id, const std::wstring &name); + int removeController(int id); + int setControllerValue(int id, const std::wstring &name, const std::wstring &value); + +private: + void signalControllerEvent(int controllerId, int changeEvent, int changeType, const std::wstring &newValue); + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_CONTROLLERMANAGER_H_ diff --git a/telldus-core/service/ControllerMessage.cpp b/telldus-core/service/ControllerMessage.cpp new file mode 100644 index 0000000..90f54cd --- /dev/null +++ b/telldus-core/service/ControllerMessage.cpp @@ -0,0 +1,95 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ControllerMessage.h" +#include +#include +#include "service/Device.h" +#include "common/Strings.h" +#include "common/common.h" + +class ControllerMessage::PrivateData { +public: + std::map parameters; + std::string protocol, model, msgClass; + int method; +}; + +ControllerMessage::ControllerMessage(const std::string &message) { + d = new PrivateData; + + // Process our message into bits + size_t prevPos = 0; + size_t pos = message.find(";"); + while(pos != std::string::npos) { + std::string param = message.substr(prevPos, pos-prevPos); + prevPos = pos+1; + size_t delim = param.find(":"); + if (delim == std::string::npos) { + break; + } + if (param.substr(0, delim).compare("class") == 0) { + d->msgClass = param.substr(delim+1, param.length()-delim); + } else if (param.substr(0, delim).compare("protocol") == 0) { + d->protocol = param.substr(delim+1, param.length()-delim); + } else if (param.substr(0, delim).compare("model") == 0) { + d->model = param.substr(delim+1, param.length()-delim); + } else if (param.substr(0, delim).compare("method") == 0) { + d->method = Device::methodId(param.substr(delim+1, param.length()-delim)); + } else { + d->parameters[param.substr(0, delim)] = param.substr(delim+1, param.length()-delim); + } + pos = message.find(";", pos+1); + } +} + +ControllerMessage::~ControllerMessage() { + delete d; +} + +std::string ControllerMessage::msgClass() const { + return d->msgClass; +} + +int ControllerMessage::method() const { + return d->method; +} + +std::wstring ControllerMessage::protocol() const { + return TelldusCore::charToWstring(d->protocol.c_str()); +} + +std::wstring ControllerMessage::model() const { + return TelldusCore::charToWstring(d->model.c_str()); +} + +uint64_t ControllerMessage::getInt64Parameter(const std::string &key) const { + std::string strValue = getParameter(key); + if (strValue.compare("") == 0) { + return -1; + } + if (strValue.substr(0, 2).compare("0x") == 0) { + return TelldusCore::hexTo64l(strValue); + } + // TODO(micke): strtol() does not return uint64_t. Create a platform independent version similar to hexTo64l() + return strtol(strValue.c_str(), NULL, 10); +} + +std::string ControllerMessage::getParameter(const std::string &key) const { + std::map::iterator it = d->parameters.find(key); + if (it == d->parameters.end()) { + return ""; + } + return d->parameters[key]; +} + +bool ControllerMessage::hasParameter(const std::string &key) const { + std::map::iterator it = d->parameters.find(key); + if (it == d->parameters.end()) { + return false; + } + return true; +} diff --git a/telldus-core/service/ControllerMessage.h b/telldus-core/service/ControllerMessage.h new file mode 100644 index 0000000..9532ea8 --- /dev/null +++ b/telldus-core/service/ControllerMessage.h @@ -0,0 +1,36 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_CONTROLLERMESSAGE_H_ +#define TELLDUS_CORE_SERVICE_CONTROLLERMESSAGE_H_ + +#ifdef _MSC_VER +typedef unsigned __int64 uint64_t; +#else +#include +#endif +#include + +class ControllerMessage { +public: + explicit ControllerMessage(const std::string &rawMessage); + virtual ~ControllerMessage(); + + std::string msgClass() const; + uint64_t getInt64Parameter(const std::string &key) const; + std::string getParameter(const std::string &key) const; + int method() const; + std::wstring protocol() const; + std::wstring model() const; + + bool hasParameter(const std::string &key) const; + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_CONTROLLERMESSAGE_H_ diff --git a/telldus-core/service/Device.cpp b/telldus-core/service/Device.cpp new file mode 100644 index 0000000..6aa44ac --- /dev/null +++ b/telldus-core/service/Device.cpp @@ -0,0 +1,272 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Device.h" +#include +#include +#include "service/Settings.h" +#include "service/TellStick.h" + +class Device::PrivateData { +public: + std::wstring model; + std::wstring name; + ParameterMap parameterList; + Protocol *protocol; + std::wstring protocolName; + int preferredControllerId; + int state; + std::wstring stateValue; +}; + +Device::Device(int id) + :Mutex() { + d = new PrivateData; + d->protocol = 0; + d->preferredControllerId = 0; + d->state = 0; +} + +Device::~Device(void) { + delete d->protocol; + delete d; +} + +/** +* Get-/Set-methods +*/ + +int Device::getLastSentCommand(int methodsSupported) { + int lastSentCommand = Device::maskUnsupportedMethods(d->state, methodsSupported); + + if (lastSentCommand == TELLSTICK_BELL) { + // Bell is not a state + lastSentCommand = TELLSTICK_TURNOFF; + } + if (lastSentCommand == 0) { + lastSentCommand = TELLSTICK_TURNOFF; + } + return lastSentCommand; +} + +int Device::getMethods() const { + Protocol *p = this->retrieveProtocol(); + if (p) { + return p->methods(); + } + return 0; +} + +void Device::setLastSentCommand(int command, std::wstring value) { + d->state = command; + d->stateValue = value; +} + +std::wstring Device::getModel() { + return d->model; +} + +void Device::setModel(const std::wstring &model) { + if(d->protocol) { + delete(d->protocol); + d->protocol = 0; + } + d->model = model; +} + +std::wstring Device::getName() { + return d->name; +} + +void Device::setName(const std::wstring &name) { + d->name = name; +} + +std::wstring Device::getParameter(const std::wstring &key) { + ParameterMap::iterator it = d->parameterList.find(key); + if (it == d->parameterList.end()) { + return L""; + } + return d->parameterList[key]; +} + +std::list Device::getParametersForProtocol() const { + return Protocol::getParametersForProtocol(getProtocolName()); +} + +void Device::setParameter(const std::wstring &key, const std::wstring &value) { + d->parameterList[key] = value; + if(d->protocol) { + d->protocol->setParameters(d->parameterList); + } +} + +int Device::getPreferredControllerId() { + return d->preferredControllerId; +} + +void Device::setPreferredControllerId(int controllerId) { + d->preferredControllerId = controllerId; +} + +std::wstring Device::getProtocolName() const { + return d->protocolName; +} + +void Device::setProtocolName(const std::wstring &protocolName) { + if(d->protocol) { + delete(d->protocol); + d->protocol = 0; + } + d->protocolName = protocolName; +} + +std::wstring Device::getStateValue() { + return d->stateValue; +} + +int Device::getType() { + if(d->protocolName == L"group") { + return TELLSTICK_TYPE_GROUP; + } else if(d->protocolName == L"scene") { + return TELLSTICK_TYPE_SCENE; + } + return TELLSTICK_TYPE_DEVICE; +} + +/** +* End Get-/Set +*/ + +int Device::doAction(int action, unsigned char data, Controller *controller) { + Protocol *p = this->retrieveProtocol(); + if (!p) { + // Syntax error in configuration, no such protocol + return TELLSTICK_ERROR_CONFIG_SYNTAX; + } + // Try to determine if we need to call another method due to masking + int method = this->isMethodSupported(action); + if (method <= 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + std::string code = p->getStringForMethod(method, data, controller); + if (code == "") { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + if (code[0] != 'S' && code[0] != 'T' && code[0] != 'P' && code[0] != 'R') { + // Try autodetect sendtype + TellStick *tellstick = reinterpret_cast(controller); + if (!tellstick) { + return TELLSTICK_ERROR_UNKNOWN; + } + unsigned int maxlength = 80; + if (tellstick->pid() == 0x0c31) { + maxlength = 512; + } + if (code.length() <= maxlength) { + // S is enough + code.insert(0, 1, 'S'); + code.append(1, '+'); + } else { + code = TellStick::createTPacket(code); + } + } + return controller->send(code); +} + +int Device::isMethodSupported(int method) const { + Protocol *p = this->retrieveProtocol(); + if (!p) { + // Syntax error in configuration, no such protocol + return TELLSTICK_ERROR_CONFIG_SYNTAX; + } + // Try to determine if we need to call another method due to masking + int methods = p->methods(); + if ((method & methods) == 0) { + // Loop all methods an see if any method masks to this one + for(int i = 1; i <= methods; i <<= 1) { + if ((i & methods) == 0) { + continue; + } + if (this->maskUnsupportedMethods(i, method)) { + method = i; + break; + } + } + } + if ((method & methods) == 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + return method; +} + +Protocol* Device::retrieveProtocol() const { + if (d->protocol) { + return d->protocol; + } + + d->protocol = Protocol::getProtocolInstance(d->protocolName); + if(d->protocol) { + d->protocol->setModel(d->model); + d->protocol->setParameters(d->parameterList); + return d->protocol; + } + + return 0; +} + +int Device::maskUnsupportedMethods(int methods, int supportedMethods) { + // Bell -> On + if ((methods & TELLSTICK_BELL) && !(supportedMethods & TELLSTICK_BELL)) { + methods |= TELLSTICK_TURNON; + } + + // Execute -> On + if ((methods & TELLSTICK_EXECUTE) && !(supportedMethods & TELLSTICK_EXECUTE)) { + methods |= TELLSTICK_TURNON; + } + + // Up -> Off + if ((methods & TELLSTICK_UP) && !(supportedMethods & TELLSTICK_UP)) { + methods |= TELLSTICK_TURNOFF; + } + + // Down -> On + if ((methods & TELLSTICK_DOWN) && !(supportedMethods & TELLSTICK_DOWN)) { + methods |= TELLSTICK_TURNON; + } + + // Cut of the rest of the unsupported methods we don't have a fallback for + return methods & supportedMethods; +} + +int Device::methodId( const std::string &methodName ) { + if (methodName.compare("turnon") == 0) { + return TELLSTICK_TURNON; + } + if (methodName.compare("turnoff") == 0) { + return TELLSTICK_TURNOFF; + } + if (methodName.compare("bell") == 0) { + return TELLSTICK_BELL; + } + if (methodName.compare("dim") == 0) { + return TELLSTICK_DIM; + } + if (methodName.compare("execute") == 0) { + return TELLSTICK_EXECUTE; + } + if (methodName.compare("up") == 0) { + return TELLSTICK_UP; + } + if (methodName.compare("down") == 0) { + return TELLSTICK_DOWN; + } + if (methodName.compare("stop") == 0) { + return TELLSTICK_STOP; + } + return 0; +} diff --git a/telldus-core/service/Device.h b/telldus-core/service/Device.h new file mode 100644 index 0000000..35b3668 --- /dev/null +++ b/telldus-core/service/Device.h @@ -0,0 +1,51 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_DEVICE_H_ +#define TELLDUS_CORE_SERVICE_DEVICE_H_ + +#include +#include +#include "service/Controller.h" +#include "common/Mutex.h" +#include "service/Protocol.h" + +class Device : public TelldusCore::Mutex { +public: + explicit Device(int id); + ~Device(void); + + int doAction(int action, unsigned char data, Controller *controller); + int isMethodSupported(int method) const; + std::wstring getStateValue(); + int getLastSentCommand(int methodsSupported); + int getMethods() const; + std::wstring getModel(); + void setModel(const std::wstring &model); + std::wstring getName(); + void setName(const std::wstring &name); + std::wstring getParameter(const std::wstring &key); + std::list getParametersForProtocol() const; + void setParameter(const std::wstring &key, const std::wstring &value); + int getPreferredControllerId(); + void setPreferredControllerId(int controllerId); + std::wstring getProtocolName() const; + void setProtocolName(const std::wstring &name); + void setStateValue(int stateValue); + void setLastSentCommand(int command, std::wstring value); + int getType(); + + static int maskUnsupportedMethods(int methods, int supportedMethods); + static int methodId( const std::string &methodName ); + +private: + Protocol *retrieveProtocol() const; + + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_DEVICE_H_ diff --git a/telldus-core/service/DeviceManager.cpp b/telldus-core/service/DeviceManager.cpp new file mode 100644 index 0000000..82b8cba --- /dev/null +++ b/telldus-core/service/DeviceManager.cpp @@ -0,0 +1,790 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/DeviceManager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "service/ControllerMessage.h" +#include "common/Mutex.h" +#include "service/Sensor.h" +#include "service/Settings.h" +#include "common/Strings.h" +#include "common/Message.h" +#include "service/Log.h" + +typedef std::map DeviceMap; + +class DeviceManager::PrivateData { +public: + DeviceMap devices; + std::list sensorList; + Settings set; + TelldusCore::Mutex lock; + ControllerManager *controllerManager; + TelldusCore::EventRef deviceUpdateEvent, executeActionEvent; +}; + +class ExecuteActionEventData : public TelldusCore::EventDataBase { +public: + int deviceId; + int method; + unsigned char data; +}; + + +DeviceManager::DeviceManager(ControllerManager *controllerManager, TelldusCore::EventRef deviceUpdateEvent) { + d = new PrivateData; + d->controllerManager = controllerManager; + d->deviceUpdateEvent = deviceUpdateEvent; + fillDevices(); +} + +DeviceManager::~DeviceManager(void) { + { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + for (DeviceMap::iterator it = d->devices.begin(); it != d->devices.end(); ++it) { + {TelldusCore::MutexLocker deviceLocker(it->second);} // aquire lock, and release it, just to see that the device it's not in use anywhere + delete(it->second); + } + for (std::list::iterator it = d->sensorList.begin(); it != d->sensorList.end(); ++it) { + {TelldusCore::MutexLocker sensorLocker(*it);} // aquire lock, and release it, just to see that the device it's not in use anywhere + delete(*it); + } + } + delete d; +} + +void DeviceManager::executeActionEvent() { + Device *device = 0; + TelldusCore::EventDataRef eventData = d->executeActionEvent->takeSignal(); + ExecuteActionEventData *data = dynamic_cast(eventData.get()); + if (!data) { + Log::error("Could not cast executeAction data"); + return; + } + Log::notice("Execute a TellStick Action for device %i", data->deviceId); + + std::auto_ptr deviceLocker(0); + { + // devicelist locked + TelldusCore::MutexLocker deviceListLocker(&d->lock); + + DeviceMap::iterator it = d->devices.find(data->deviceId); + if (it == d->devices.end()) { + return; + } + // device locked + deviceLocker = std::auto_ptr(new TelldusCore::MutexLocker(it->second)); + device = it->second; + } // devicelist unlocked + + Controller *controller = d->controllerManager->getBestControllerById(device->getPreferredControllerId()); + if(!controller) { + return; + } + + int retval = device->doAction(data->method, data->data, controller); + if(retval == TELLSTICK_ERROR_BROKEN_PIPE) { + Log::warning("Error in communication with TellStick when executing action. Resetting USB"); + d->controllerManager->resetController(controller); + } + if(retval == TELLSTICK_ERROR_BROKEN_PIPE || retval == TELLSTICK_ERROR_NOT_FOUND) { + Log::warning("Rescanning USB ports"); + d->controllerManager->loadControllers(); + controller = d->controllerManager->getBestControllerById(device->getPreferredControllerId()); + if(!controller) { + Log::error("No contoller (TellStick) found, even after reset. Giving up."); + return; + } + retval = device->doAction(data->method, data->data, controller); // retry one more time + } + + if(retval == TELLSTICK_SUCCESS && device->getMethods() & data->method) { + // if method isn't explicitly supported by device, but used anyway as a fallback (i.e. bell), don't change state + std::wstring datastring = TelldusCore::charUnsignedToWstring(data->data); + if (this->triggerDeviceStateChange(data->deviceId, data->method, datastring)) { + device->setLastSentCommand(data->method, datastring); + d->set.setDeviceState(data->deviceId, data->method, datastring); + } + } +} + +void DeviceManager::setExecuteActionEvent(TelldusCore::EventRef event) { + d->executeActionEvent = event; +} + +void DeviceManager::fillDevices() { + int numberOfDevices = d->set.getNumberOfNodes(Settings::Device); + TelldusCore::MutexLocker deviceListLocker(&d->lock); + + for (int i = 0; i < numberOfDevices; ++i) { + int id = d->set.getNodeId(Settings::Device, i); + d->devices[id] = new Device(id); + d->devices[id]->setName(d->set.getName(Settings::Device, id)); + d->devices[id]->setModel(d->set.getModel(id)); + d->devices[id]->setProtocolName(d->set.getProtocol(id)); + d->devices[id]->setPreferredControllerId(d->set.getPreferredControllerId(id)); + d->devices[id]->setLastSentCommand(d->set.getDeviceState(id), d->set.getDeviceStateValue(id)); + d->devices[id]->setParameter(L"house", d->set.getDeviceParameter(id, L"house")); + d->devices[id]->setParameter(L"unit", d->set.getDeviceParameter(id, L"unit")); + d->devices[id]->setParameter(L"code", d->set.getDeviceParameter(id, L"code")); + d->devices[id]->setParameter(L"units", d->set.getDeviceParameter(id, L"units")); + d->devices[id]->setParameter(L"fade", d->set.getDeviceParameter(id, L"fade")); + d->devices[id]->setParameter(L"system", d->set.getDeviceParameter(id, L"system")); + d->devices[id]->setParameter(L"devices", d->set.getDeviceParameter(id, L"devices")); + } +} + +int DeviceManager::getDeviceLastSentCommand(int deviceId, int methodsSupported) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getLastSentCommand(methodsSupported); + } + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; +} + +int DeviceManager::setDeviceLastSentCommand(int deviceId, int command, const std::wstring &value) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + d->set.setDeviceState(deviceId, command, value); + it->second->setLastSentCommand(command, value); + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + return TELLSTICK_SUCCESS; +} + +std::wstring DeviceManager::getDeviceStateValue(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return L"UNKNOWN"; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getStateValue(); + } + return L"UNKNOWN"; +} + +int DeviceManager::getDeviceMethods(int deviceId, int methodsSupported) { + return Device::maskUnsupportedMethods(DeviceManager::getDeviceMethods(deviceId), methodsSupported); +} + +int DeviceManager::getDeviceMethods(int deviceId) { + std::set duplicateDeviceIds; + return DeviceManager::getDeviceMethods(deviceId, &duplicateDeviceIds); +} + +int DeviceManager::getDeviceMethods(int deviceId, std::set *duplicateDeviceIds) { + int type = 0; + int methods = 0; + std::wstring deviceIds; + std::wstring protocol; + + { + // devices locked + TelldusCore::MutexLocker deviceListLocker(&d->lock); + + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + { + TelldusCore::MutexLocker deviceLocker(it->second); + type = it->second->getType(); + methods = it->second->getMethods(); + deviceIds = it->second->getParameter(L"devices"); + protocol = it->second->getProtocolName(); + } + } + } + if(type == 0) { + return 0; + } + if(type == TELLSTICK_TYPE_GROUP) { + // get all methods that some device in the groups supports + std::wstring deviceIdBuffer; + std::wstringstream devicesstream(deviceIds); + methods = 0; + + duplicateDeviceIds->insert(deviceId); + + while(std::getline(devicesstream, deviceIdBuffer, L',')) { + int deviceIdInGroup = TelldusCore::wideToInteger(deviceIdBuffer); + if(duplicateDeviceIds->count(deviceIdInGroup) == 1) { + // action for device already executed, or will execute, do nothing to avoid infinite loop + continue; + } + + duplicateDeviceIds->insert(deviceIdInGroup); + + int deviceMethods = getDeviceMethods(deviceIdInGroup, duplicateDeviceIds); + if(deviceMethods > 0) { + methods |= deviceMethods; + } + } + } + return methods; +} + +std::wstring DeviceManager::getDeviceModel(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return L"UNKNOWN"; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getModel(); + } + return L"UNKNOWN"; +} + +int DeviceManager::setDeviceModel(int deviceId, const std::wstring &model) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + int ret = d->set.setModel(deviceId, model); + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + it->second->setModel(model); + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + return TELLSTICK_SUCCESS; +} + +std::wstring DeviceManager::getDeviceName(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return L"UNKNOWN"; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getName(); + } + return L"UNKNOWN"; +} + +int DeviceManager::setDeviceName(int deviceId, const std::wstring &name) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + int ret = d->set.setName(Settings::Device, deviceId, name); + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + it->second->setName(name); + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + return TELLSTICK_SUCCESS; +} + +std::wstring DeviceManager::getDeviceParameter(int deviceId, const std::wstring &name, const std::wstring &defaultValue) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return defaultValue; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + std::wstring returnString = it->second->getParameter(name); + if(returnString != L"") { + return returnString; + } + } + return defaultValue; +} + +int DeviceManager::setDeviceParameter(int deviceId, const std::wstring &name, const std::wstring &value) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + int ret = d->set.setDeviceParameter(deviceId, name, value); + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + it->second->setParameter(name, value); + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + return TELLSTICK_SUCCESS; +} + +std::wstring DeviceManager::getDeviceProtocol(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return L"UNKNOWN"; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getProtocolName(); + } + return L"UNKNOWN"; +} + +int DeviceManager::setDeviceProtocol(int deviceId, const std::wstring &protocol) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + int ret = d->set.setProtocol(deviceId, protocol); + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + it->second->setProtocolName(protocol); + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + return TELLSTICK_SUCCESS; +} + +int DeviceManager::getNumberOfDevices() { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + return static_cast(d->devices.size()); +} + +int DeviceManager::addDevice() { + int id = d->set.addNode(Settings::Device); + if(id < 0) { + return id; + } + + TelldusCore::MutexLocker deviceListLocker(&d->lock); + d->devices[id] = new Device(id); + if(!d->devices[id]) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + return id; +} + +int DeviceManager::getDeviceId(int deviceIndex) { + return d->set.getNodeId(Settings::Device, deviceIndex); +} + +int DeviceManager::getDeviceType(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getType(); + } + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; +} + +int DeviceManager::getPreferredControllerId(int deviceId) { + TelldusCore::MutexLocker deviceListLocker(&d->lock); + + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + TelldusCore::MutexLocker deviceLocker(it->second); + return it->second->getPreferredControllerId(); + } + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; +} + +void DeviceManager::connectTellStickController(int vid, int pid, const std::string &serial) { + d->controllerManager->deviceInsertedOrRemoved(vid, pid, serial, true); +} + +void DeviceManager::disconnectTellStickController(int vid, int pid, const std::string &serial) { + d->controllerManager->deviceInsertedOrRemoved(vid, pid, serial, false); +} + +int DeviceManager::doAction(int deviceId, int action, unsigned char data) { + int deviceType = 0; + { + // devicelist locked + TelldusCore::MutexLocker deviceListLocker(&d->lock); + + DeviceMap::iterator it = d->devices.find(deviceId); + if (it == d->devices.end()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; // not found + } + // device locked + TelldusCore::MutexLocker deviceLocker(it->second); + + deviceType = it->second->getType(); + if (it->second->isMethodSupported(action) <= 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + } + + if (d->controllerManager->count() == 0) { + return TELLSTICK_ERROR_NOT_FOUND; + } + + // The device exists and there is at least one connected controller + + if(deviceType == TELLSTICK_TYPE_GROUP || deviceType == TELLSTICK_TYPE_SCENE) { + return this->doGroupSceneAction(deviceId, action, data); + } + + ExecuteActionEventData *eventData = new ExecuteActionEventData(); + eventData->deviceId = deviceId; + eventData->method = action; + eventData->data = data; + d->executeActionEvent->signal(eventData); + return TELLSTICK_SUCCESS; +} + +int DeviceManager::doGroupSceneAction(int deviceId, int action, unsigned char data) { + std::set parsedDevices; + std::queue devicesToParse; + + devicesToParse.push(deviceId); + while (!devicesToParse.empty()) { + int deviceId = devicesToParse.front(); + devicesToParse.pop(); + if (parsedDevices.count(deviceId)) { + continue; + } + parsedDevices.insert(deviceId); + + TelldusCore::MutexLocker deviceListLocker(&d->lock); + DeviceMap::iterator it = d->devices.find(deviceId); + if (it == d->devices.end()) { + // Not found + continue; + } + + if (it->second->isMethodSupported(action) <= 0) { + return TELLSTICK_ERROR_METHOD_NOT_SUPPORTED; + } + + TelldusCore::MutexLocker deviceLocker(it->second); + if (it->second->getType() == TELLSTICK_TYPE_DEVICE) { + ExecuteActionEventData *eventData = new ExecuteActionEventData(); + eventData->deviceId = deviceId; + eventData->method = action; + eventData->data = data; + d->executeActionEvent->signal(eventData); + continue; + } + if (it->second->getType() == TELLSTICK_TYPE_GROUP) { + std::string devices = TelldusCore::wideToString(it->second->getParameter(L"devices")); + std::stringstream devicesstream(devices); + std::string singledevice; + while(std::getline(devicesstream, singledevice, ',')) { + devicesToParse.push(TelldusCore::charToInteger(singledevice.c_str())); + } + // Update state + if(it->second->getMethods() & action) { + // if method isn't explicitly supported by device, but used anyway as a fallback (i.e. bell), don't change state + std::wstring datastring = TelldusCore::charUnsignedToWstring(data); + if (this->triggerDeviceStateChange(deviceId, action, datastring)) { + it->second->setLastSentCommand(action, datastring); + d->set.setDeviceState(deviceId, action, datastring); + } + } + } + if (it->second->getType() == TELLSTICK_TYPE_SCENE) { + // TODO(micke): Not supported yet + Log::warning("Scenes are not supported yet!"); + } + } + + return TELLSTICK_SUCCESS; +} + +int DeviceManager::executeScene(std::wstring singledevice, int groupDeviceId) { + std::wstringstream devicestream(singledevice); + + const int deviceParameterLength = 3; + std::wstring deviceParts[deviceParameterLength] = {L"", L"", L""}; + std::wstring devicePart = L""; + int i = 0; + while(std::getline(devicestream, devicePart, L':') && i < deviceParameterLength) { + deviceParts[i] = devicePart; + i++; + } + + if(deviceParts[0] == L"" || deviceParts[1] == L"") { + return TELLSTICK_ERROR_UNKNOWN; // malformed or missing parameter + } + + int deviceId = TelldusCore::wideToInteger(deviceParts[0]); + if(deviceId == groupDeviceId) { + return TELLSTICK_ERROR_UNKNOWN; // the scene itself has been added to its devices, avoid infinite loop + } + int method = Device::methodId(TelldusCore::wideToString(deviceParts[1])); // support methodparts both in the form of integers (e.g. TELLSTICK_TURNON) or text (e.g. "turnon") + if(method == 0) { + method = TelldusCore::wideToInteger(deviceParts[1]); + } + unsigned char devicedata = 0; + if(deviceParts[2] != L"") { + devicedata = TelldusCore::wideToInteger(deviceParts[2]); + } + + if(deviceId > 0 && method > 0) { // check for format error in parameter "devices" + return doAction(deviceId, method, devicedata); + } + + return TELLSTICK_ERROR_UNKNOWN; +} + +int DeviceManager::removeDevice(int deviceId) { + Device *device = 0; + { + int ret = d->set.removeNode(Settings::Device, deviceId); // remove from register/settings + if (ret != TELLSTICK_SUCCESS) { + return ret; + } + + TelldusCore::MutexLocker deviceListLocker(&d->lock); + if (!d->devices.size()) { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + DeviceMap::iterator it = d->devices.find(deviceId); + if (it != d->devices.end()) { + device = it->second; + d->devices.erase(it); // remove from list, keep reference + } else { + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + } + {TelldusCore::MutexLocker lock(device);} // waiting for device lock, if it's aquired, just unlock again. Device is removed from list, and cannot be accessed from anywhere else + delete device; + + return TELLSTICK_SUCCESS; +} + +std::wstring DeviceManager::getSensors() const { + TelldusCore::MutexLocker sensorListLocker(&d->lock); + + TelldusCore::Message msg; + + msg.addArgument(static_cast(d->sensorList.size())); + + for (std::list::iterator it = d->sensorList.begin(); it != d->sensorList.end(); ++it) { + TelldusCore::MutexLocker sensorLocker(*it); + msg.addArgument((*it)->protocol()); + msg.addArgument((*it)->model()); + msg.addArgument((*it)->id()); + msg.addArgument((*it)->dataTypes()); + } + + return msg; +} + +std::wstring DeviceManager::getSensorValue(const std::wstring &protocol, const std::wstring &model, int id, int dataType) const { + TelldusCore::MutexLocker sensorListLocker(&d->lock); + Sensor *sensor = 0; + for (std::list::iterator it = d->sensorList.begin(); it != d->sensorList.end(); ++it) { + TelldusCore::MutexLocker sensorLocker(*it); + if (!TelldusCore::comparei((*it)->protocol(), protocol)) { + continue; + } + if (!TelldusCore::comparei((*it)->model(), model)) { + continue; + } + if ((*it)->id() != id) { + continue; + } + sensor = *it; + break; + } + + if (!sensor) { + return L""; + } + TelldusCore::MutexLocker sensorLocker(sensor); + TelldusCore::Message msg; + std::string value = sensor->value(dataType); + if (value.length() > 0) { + msg.addArgument(TelldusCore::charToWstring(value.c_str())); + msg.addArgument(static_cast(sensor->timestamp())); + } + return msg; +} + + +void DeviceManager::handleControllerMessage(const ControllerEventData &eventData) { + // Trigger raw-event + EventUpdateData *eventUpdateData = new EventUpdateData(); + eventUpdateData->messageType = L"TDRawDeviceEvent"; + eventUpdateData->controllerId = eventData.controllerId; + eventUpdateData->eventValue = TelldusCore::charToWstring(eventData.msg.c_str()); + d->deviceUpdateEvent->signal(eventUpdateData); + + ControllerMessage msg(eventData.msg); + if (msg.msgClass().compare("sensor") == 0) { + handleSensorMessage(msg); + return; + } + + TelldusCore::MutexLocker deviceListLocker(&d->lock); + for (DeviceMap::iterator it = d->devices.begin(); it != d->devices.end(); ++it) { + TelldusCore::MutexLocker deviceLocker(it->second); + if (!TelldusCore::comparei(it->second->getProtocolName(), msg.protocol())) { + continue; + } + if ( !(it->second->getMethods() & msg.method()) ) { + continue; + } + + std::list parameters = it->second->getParametersForProtocol(); + bool thisDevice = true; + for (std::list::iterator paramIt = parameters.begin(); paramIt != parameters.end(); ++paramIt) { + if(!TelldusCore::comparei(it->second->getParameter(TelldusCore::charToWstring((*paramIt).c_str())), TelldusCore::charToWstring(msg.getParameter(*paramIt).c_str()))) { + thisDevice = false; + break; + } + } + + if(!thisDevice) { + continue; + } + + if (this->triggerDeviceStateChange(it->first, msg.method(), L"")) { + d->set.setDeviceState(it->first, msg.method(), L""); + it->second->setLastSentCommand(msg.method(), L""); + } + } +} + +void DeviceManager::handleSensorMessage(const ControllerMessage &msg) { + TelldusCore::MutexLocker sensorListLocker(&d->lock); + Sensor *sensor = 0; + for (std::list::iterator it = d->sensorList.begin(); it != d->sensorList.end(); ++it) { + TelldusCore::MutexLocker sensorLocker(*it); + if (!TelldusCore::comparei((*it)->protocol(), msg.protocol())) { + continue; + } + if (!TelldusCore::comparei((*it)->model(), msg.model())) { + continue; + } + if ((*it)->id() != msg.getInt64Parameter("id")) { + continue; + } + sensor = *it; + break; + } + + if (!sensor) { + sensor = new Sensor(msg.protocol(), msg.model(), msg.getInt64Parameter("id")); + d->sensorList.push_back(sensor); + } + TelldusCore::MutexLocker sensorLocker(sensor); + + time_t t = time(NULL); + + setSensorValueAndSignal("temp", TELLSTICK_TEMPERATURE, sensor, msg, t); + setSensorValueAndSignal("humidity", TELLSTICK_HUMIDITY, sensor, msg, t); + setSensorValueAndSignal("rainrate", TELLSTICK_RAINRATE, sensor, msg, t); + setSensorValueAndSignal("raintotal", TELLSTICK_RAINTOTAL, sensor, msg, t); + setSensorValueAndSignal("winddirection", TELLSTICK_WINDDIRECTION, sensor, msg, t); + setSensorValueAndSignal("windaverage", TELLSTICK_WINDAVERAGE, sensor, msg, t); + setSensorValueAndSignal("windgust", TELLSTICK_WINDGUST, sensor, msg, t); +} + +void DeviceManager::setSensorValueAndSignal( const std::string &dataType, int dataTypeId, Sensor *sensor, const ControllerMessage &msg, time_t timestamp) const { + if (!msg.hasParameter(dataType)) { + return; + } + sensor->setValue(dataTypeId, msg.getParameter(dataType), timestamp); + + EventUpdateData *eventData = new EventUpdateData(); + eventData->messageType = L"TDSensorEvent"; + eventData->protocol = sensor->protocol(); + eventData->model = sensor->model(); + eventData->sensorId = sensor->id(); + eventData->dataType = dataTypeId; + eventData->value = TelldusCore::charToWstring(sensor->value(dataTypeId).c_str()); + eventData->timestamp = static_cast(timestamp); + d->deviceUpdateEvent->signal(eventData); +} + +int DeviceManager::sendRawCommand(const std::wstring &command, int reserved) { + Controller *controller = d->controllerManager->getBestControllerById(-1); + + if(!controller) { + // no controller found, scan for one, and retry once + d->controllerManager->loadControllers(); + controller = d->controllerManager->getBestControllerById(-1); + } + + int retval = TELLSTICK_ERROR_UNKNOWN; + if(controller) { + retval = controller->send(TelldusCore::wideToString(command)); + if(retval == TELLSTICK_ERROR_BROKEN_PIPE) { + d->controllerManager->resetController(controller); + } + if(retval == TELLSTICK_ERROR_BROKEN_PIPE || retval == TELLSTICK_ERROR_NOT_FOUND) { + d->controllerManager->loadControllers(); + controller = d->controllerManager->getBestControllerById(-1); + if(!controller) { + return TELLSTICK_ERROR_NOT_FOUND; + } + retval = controller->send(TelldusCore::wideToString(command)); // retry one more time + } + return retval; + } else { + return TELLSTICK_ERROR_NOT_FOUND; + } +} + +bool DeviceManager::triggerDeviceStateChange(int deviceId, int intDeviceState, const std::wstring &strDeviceStateValue ) { + if ( intDeviceState == TELLSTICK_BELL || intDeviceState == TELLSTICK_LEARN || intDeviceState == TELLSTICK_EXECUTE) { + return false; + } + + EventUpdateData *eventData = new EventUpdateData(); + eventData->messageType = L"TDDeviceEvent"; + eventData->eventState = intDeviceState; + eventData->deviceId = deviceId; + eventData->eventValue = strDeviceStateValue; + d->deviceUpdateEvent->signal(eventData); + return true; +} diff --git a/telldus-core/service/DeviceManager.h b/telldus-core/service/DeviceManager.h new file mode 100644 index 0000000..6e5a6f2 --- /dev/null +++ b/telldus-core/service/DeviceManager.h @@ -0,0 +1,68 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_DEVICEMANAGER_H_ +#define TELLDUS_CORE_SERVICE_DEVICEMANAGER_H_ + +#include +#include +#include "service/Device.h" +#include "service/ControllerManager.h" +#include "service/ControllerMessage.h" +#include "service/EventUpdateManager.h" + +class Sensor; + +class DeviceManager { +public: + DeviceManager(ControllerManager *controllerManager, TelldusCore::EventRef deviceUpdateEvent); + ~DeviceManager(void); + int getNumberOfDevices(void); + int addDevice(); + void connectTellStickController(int vid, int pid, const std::string &serial); + void disconnectTellStickController(int vid, int pid, const std::string &serial); + void executeActionEvent(); + int getDeviceId(int deviceIndex); + int getDeviceLastSentCommand(int deviceId, int methodsSupported); + int setDeviceLastSentCommand(int deviceId, int command, const std::wstring &value); + int getDeviceMethods(int deviceId); + int getDeviceMethods(int deviceId, int methodsSupported); + std::wstring getDeviceModel(int deviceId); + int setDeviceModel(int deviceId, const std::wstring &model); + std::wstring getDeviceName(int deviceId); + int setDeviceName(int deviceId, const std::wstring &name); + std::wstring getDeviceParameter(int deviceId, const std::wstring &name, const std::wstring &defauleValue); + int setDeviceParameter(int deviceId, const std::wstring &name, const std::wstring &value); + std::wstring getDeviceProtocol(int deviceId); + int setDeviceProtocol(int deviceId, const std::wstring &name); + std::wstring getDeviceStateValue(int deviceId); + int getDeviceType(int deviceId); + int getPreferredControllerId(int deviceId); + int doAction(int deviceId, int action, unsigned char data); + int removeDevice(int deviceId); + int sendRawCommand(const std::wstring &command, int reserved); + + void setExecuteActionEvent(TelldusCore::EventRef event); + + std::wstring getSensors() const; + std::wstring getSensorValue(const std::wstring &protocol, const std::wstring &model, int id, int dataType) const; + + void handleControllerMessage(const ControllerEventData &event); + +private: + void handleSensorMessage(const ControllerMessage &msg); + void setSensorValueAndSignal( const std::string &dataType, int dataTypeId, Sensor *sensor, const ControllerMessage &msg, time_t timestamp) const; + int getDeviceMethods(int deviceId, std::set *duplicateDeviceIds); + int doGroupSceneAction(int deviceId, int action, unsigned char data); + int executeScene(std::wstring singledevice, int groupDeviceId); + bool triggerDeviceStateChange(int deviceId, int intDeviceState, const std::wstring &strDeviceStateValue ); + void fillDevices(void); + + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_DEVICEMANAGER_H_ diff --git a/telldus-core/service/EventUpdateManager.cpp b/telldus-core/service/EventUpdateManager.cpp new file mode 100644 index 0000000..a76d5ca --- /dev/null +++ b/telldus-core/service/EventUpdateManager.cpp @@ -0,0 +1,259 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/EventUpdateManager.h" + +#ifdef _LINUX +#include +#include +#include +#include +#include +#include +#include +#include +#endif // _LINUX + +#include +#include +#include +#ifdef _LINUX +#include +#include +#endif // _LINUX + +#include "common/EventHandler.h" +#include "common/Message.h" +#include "common/Socket.h" +#include "common/Strings.h" +#include "service/config.h" +#include "service/ConnectionListener.h" +#include "service/Log.h" + +typedef std::list SocketList; +typedef std::list StringList; + +class EventUpdateManager::PrivateData { +public: + TelldusCore::EventHandler eventHandler; + TelldusCore::EventRef stopEvent, updateEvent, clientConnectEvent; + SocketList clients; + ConnectionListener *eventUpdateClientListener; +#ifdef _LINUX + std::map fileList; +#endif // _LINUX +}; + +EventUpdateManager::EventUpdateManager() + :Thread() { + d = new PrivateData; + d->stopEvent = d->eventHandler.addEvent(); + d->updateEvent = d->eventHandler.addEvent(); + d->clientConnectEvent = d->eventHandler.addEvent(); + d->eventUpdateClientListener = new ConnectionListener(L"TelldusEvents", d->clientConnectEvent); +#ifdef _LINUX + loadScripts("deviceevent"); + loadScripts("devicechangeevent"); + loadScripts("rawdeviceevent"); + loadScripts("sensorevent"); + loadScripts("controllerevent"); +#endif // _LINUX +} + +EventUpdateManager::~EventUpdateManager(void) { + d->stopEvent->signal(); + wait(); + delete d->eventUpdateClientListener; + + for (SocketList::iterator it = d->clients.begin(); it != d->clients.end(); ++it) { + delete(*it); + } + + delete d; +} + +TelldusCore::EventRef EventUpdateManager::retrieveUpdateEvent() { + return d->updateEvent; +} + +void EventUpdateManager::run() { + while(!d->stopEvent->isSignaled()) { + if (!d->eventHandler.waitForAny()) { + continue; + } + + if(d->clientConnectEvent->isSignaled()) { + // new client added + TelldusCore::EventDataRef eventData = d->clientConnectEvent->takeSignal(); + ConnectionListenerEventData *data = dynamic_cast(eventData.get()); + if(data) { + d->clients.push_back(data->socket); + } + } else if(d->updateEvent->isSignaled()) { + // device event, signal all clients + TelldusCore::EventDataRef eventData = d->updateEvent->takeSignal(); + EventUpdateData *data = reinterpret_cast(eventData.get()); + if(data) { + sendMessageToClients(data); + executeScripts(data); + } + } + } +} + +void EventUpdateManager::loadScripts(const std::string &folder) { +#ifdef _LINUX + std::string path = TelldusCore::formatf("%s/%s", SCRIPT_PATH, folder.c_str()); + struct dirent **namelist; + int count = scandir(path.c_str(), &namelist, NULL, alphasort); + if (count < 0) { + return; + } + + for(int i = 0; i < count; ++i) { + if (strcmp(namelist[i]->d_name, ".") != 0 && strcmp(namelist[i]->d_name, "..") != 0) { + d->fileList[folder].push_back(namelist[i]->d_name); + } + free(namelist[i]); + } + free(namelist); +#endif // _LINUX +} + +void EventUpdateManager::sendMessageToClients(EventUpdateData *data) { + int connected = 0; + for(SocketList::iterator it = d->clients.begin(); it != d->clients.end();) { + if((*it)->isConnected()) { + connected++; + TelldusCore::Message msg; + + if(data->messageType == L"TDDeviceEvent") { + msg.addArgument("TDDeviceEvent"); + msg.addArgument(data->deviceId); + msg.addArgument(data->eventState); + msg.addArgument(data->eventValue); // string + } else if(data->messageType == L"TDDeviceChangeEvent") { + msg.addArgument("TDDeviceChangeEvent"); + msg.addArgument(data->deviceId); + msg.addArgument(data->eventDeviceChanges); + msg.addArgument(data->eventChangeType); + } else if(data->messageType == L"TDRawDeviceEvent") { + msg.addArgument("TDRawDeviceEvent"); + msg.addArgument(data->eventValue); // string + msg.addArgument(data->controllerId); + } else if(data->messageType == L"TDSensorEvent") { + msg.addArgument("TDSensorEvent"); + msg.addArgument(data->protocol); + msg.addArgument(data->model); + msg.addArgument(data->sensorId); + msg.addArgument(data->dataType); + msg.addArgument(data->value); + msg.addArgument(data->timestamp); + } else if(data->messageType == L"TDControllerEvent") { + msg.addArgument("TDControllerEvent"); + msg.addArgument(data->controllerId); + msg.addArgument(data->eventState); + msg.addArgument(data->eventChangeType); + msg.addArgument(data->eventValue); + } + + (*it)->write(msg); + + it++; + } else { + // connection is dead, remove it + delete *it; + it = d->clients.erase(it); + } + } +} + +void EventUpdateManager::executeScripts(EventUpdateData *data) { +#ifdef _LINUX + std::string dir; + std::vector env; + + // Create a copy of the environment + unsigned int size = 0; + for(; ; ++size) { + if (environ[size] == 0) { + break; + } + } + env.reserve(size + 6); // 6 is the most used extra environmental variables any event uses + for(unsigned int i = 0; i < size; ++i) { + env.push_back(environ[i]); + } + + if(data->messageType == L"TDDeviceEvent") { + dir = "deviceevent"; + env.push_back(TelldusCore::formatf("DEVICEID=%i", data->deviceId)); + env.push_back(TelldusCore::formatf("METHOD=%i", data->eventState)); + env.push_back(TelldusCore::formatf("METHODDATA=%s", TelldusCore::wideToString(data->eventValue).c_str())); + } else if(data->messageType == L"TDDeviceChangeEvent") { + dir = "devicechangeevent"; + env.push_back(TelldusCore::formatf("DEVICEID=%i", data->deviceId)); + env.push_back(TelldusCore::formatf("CHANGEEVENT=%i", data->eventDeviceChanges)); + env.push_back(TelldusCore::formatf("CHANGETYPE=%i", data->eventChangeType)); + } else if(data->messageType == L"TDRawDeviceEvent") { + dir = "rawdeviceevent"; + env.push_back(TelldusCore::formatf("RAWDATA=%s", TelldusCore::wideToString(data->eventValue).c_str())); // string + env.push_back(TelldusCore::formatf("CONTROLLERID=%i", data->controllerId)); + } else if (data->messageType == L"TDSensorEvent") { + dir = "sensorevent"; + env.push_back(TelldusCore::formatf("PROTOCOL=%s", TelldusCore::wideToString(data->protocol).c_str())); + env.push_back(TelldusCore::formatf("MODEL=%s", TelldusCore::wideToString(data->model).c_str())); + env.push_back(TelldusCore::formatf("SENSORID=%i", data->sensorId)); + env.push_back(TelldusCore::formatf("DATATYPE=%i", data->dataType)); + env.push_back(TelldusCore::formatf("VALUE=%s", TelldusCore::wideToString(data->value).c_str())); + env.push_back(TelldusCore::formatf("TIMESTAMP=%i", data->timestamp)); + } else if(data->messageType == L"TDControllerEvent") { + dir = "controllerevent"; + env.push_back(TelldusCore::formatf("CONTROLLERID=%i", data->controllerId)); + env.push_back(TelldusCore::formatf("CHANGEEVENT=%i", data->eventState)); + env.push_back(TelldusCore::formatf("CHANGETYPE=%i", data->eventChangeType)); + env.push_back(TelldusCore::formatf("VALUE=%s", TelldusCore::wideToString(data->eventValue).c_str())); + } else { + // Unknown event, should not happen + return; + } + + char *newEnv[env.size()+1]; // +1 for the last stop element + for(int i = 0; i < env.size(); ++i) { + newEnv[i] = new char[env.at(i).length()+1]; + snprintf(newEnv[i], env.at(i).length()+1, "%s", env.at(i).c_str()); + } + newEnv[env.size()] = NULL; // Mark end of array + + for(StringList::iterator it = d->fileList[dir].begin(); it != d->fileList[dir].end(); ++it) { + executeScript(TelldusCore::formatf("%s/%s/%s", SCRIPT_PATH, dir.c_str(), (*it).c_str()), (*it), newEnv); + } + // Cleanup + for(int i = 0; newEnv[i] != 0; ++i) { + delete[] newEnv[i]; + } +#endif // _LINUX +} + +void EventUpdateManager::executeScript(std::string script, const std::string &name, char ** env) { +#ifdef _LINUX + pid_t pid = fork(); + if (pid == -1) { + Log::error("Could not fork() to execute script %s", script.c_str()); + return; + } + + if (pid == 0) { + char *n = new char[name.length()+1]; + snprintf(n, name.length()+1, "%s", name.c_str()); + static char * argv[] = { n, NULL }; + execve(script.c_str(), argv, env); + delete[] n; + Log::error("Could not execute %s (%i): %s", script.c_str(), errno, strerror(errno)); + exit(1); + } +#endif // _LINUX +} diff --git a/telldus-core/service/EventUpdateManager.h b/telldus-core/service/EventUpdateManager.h new file mode 100644 index 0000000..5956072 --- /dev/null +++ b/telldus-core/service/EventUpdateManager.h @@ -0,0 +1,53 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_EVENTUPDATEMANAGER_H_ +#define TELLDUS_CORE_SERVICE_EVENTUPDATEMANAGER_H_ + +#include +#include "common/Thread.h" +#include "common/Event.h" + +class EventUpdateData : public TelldusCore::EventDataBase { +public: + std::wstring messageType; + int controllerId; + int deviceId; + int eventChangeType; + int eventDeviceChanges; + int eventState; + std::wstring eventValue; + + // Sensor event + std::wstring protocol; + std::wstring model; + int sensorId; + int dataType; + std::wstring value; + int timestamp; +}; + +class EventUpdateManager : public TelldusCore::Thread { +public: + EventUpdateManager(void); + ~EventUpdateManager(void); + + TelldusCore::EventRef retrieveUpdateEvent(); + TelldusCore::EventRef retrieveClientConnectEvent(); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; + void loadScripts(const std::string &folder); + void sendMessageToClients(EventUpdateData *data); + void executeScripts(EventUpdateData *data); + static void executeScript(std::string script, const std::string &name, char **env); +}; + +#endif // TELLDUS_CORE_SERVICE_EVENTUPDATEMANAGER_H_ diff --git a/telldus-core/service/Log.cpp b/telldus-core/service/Log.cpp new file mode 100644 index 0000000..64c11a0 --- /dev/null +++ b/telldus-core/service/Log.cpp @@ -0,0 +1,183 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Log.h" +#include + +#if defined(_LINUX) +#include +#elif defined(_WINDOWS) +#include +#include "common/Strings.h" +#include "service/Messages.h" +#endif + +class Log::PrivateData { +public: + PrivateData() : logOutput(Log::System), debug(false) {} + + Log::LogOutput logOutput; + bool debug; + + static Log *instance; +#ifdef _WINDOWS + HANDLE eventSource; +#endif +}; + +Log *Log::PrivateData::instance = 0; + +Log::Log() + :d(new PrivateData) { +#if defined(_LINUX) + setlogmask(LOG_UPTO(LOG_INFO)); + openlog("telldusd", LOG_CONS, LOG_USER); +#elif defined(_MACOSX) + d->logOutput = Log::StdOut; +#elif defined(_WINDOWS) + // Add ourselves to the registy + HKEY hRegKey = NULL; + DWORD dwError = 0; + TCHAR filePath[MAX_PATH]; + + std::wstring path(L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\TelldusService"); + dwError = RegCreateKey( HKEY_LOCAL_MACHINE, path.c_str(), &hRegKey ); + + GetModuleFileName( NULL, filePath, MAX_PATH ); + dwError = RegSetValueEx( hRegKey, L"EventMessageFile", 0, REG_EXPAND_SZ, (PBYTE) filePath, (DWORD)(wcslen(filePath) + 1) * sizeof TCHAR ); + + DWORD dwTypes = LOG_DEBUG | LOG_NOTICE | LOG_WARNING | LOG_ERR; + dwError = RegSetValueEx( hRegKey, L"TypesSupported", 0, REG_DWORD, (LPBYTE) &dwTypes, sizeof dwTypes ); + + RegCloseKey(hRegKey); + + d->eventSource = RegisterEventSource(NULL, L"TelldusService"); +#endif +} + +Log::~Log() { +#if defined(_LINUX) + closelog(); +#elif defined(_WINDOWS) + if (d->eventSource != NULL) { + DeregisterEventSource(d->eventSource); + } +#endif + delete d; +} + +void Log::destroy() { + if (PrivateData::instance == 0) { + return; + } + delete PrivateData::instance; + PrivateData::instance = 0; +} + +void Log::debug(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Debug, fmt, ap); + va_end(ap); +} + +void Log::notice(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Notice, fmt, ap); + va_end(ap); +} + +void Log::warning(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Warning, fmt, ap); + va_end(ap); +} + +void Log::error(const char *fmt, ...) { + Log *log = Log::instance(); + va_list ap; + va_start(ap, fmt); + log->message(Error, fmt, ap); + va_end(ap); +} + +void Log::setDebug() { + Log *log = Log::instance(); + log->d->debug = true; + Log::debug("Debug message output enabled"); +} + +void Log::setLogOutput(LogOutput logOutput) { +#ifdef _MACOSX + // Always stdout + return; +#endif + Log *log = Log::instance(); + log->d->logOutput = logOutput; +} + +void Log::message(Log::LogLevel logLevel, const char *format, va_list ap) const { + if (logLevel == Debug && d->debug == false) { + return; + } + if (d->logOutput == StdOut) { + FILE *stream = stdout; + if (logLevel == Warning || logLevel == Error) { + stream = stderr; + } + vfprintf(stream, format, ap); + fprintf(stream, "\n"); + fflush(stream); + } else { +#if defined(_LINUX) + switch (logLevel) { + case Debug: + vsyslog(LOG_DEBUG, format, ap); + break; + case Notice: + vsyslog(LOG_NOTICE, format, ap); + break; + case Warning: + vsyslog(LOG_WARNING, format, ap); + break; + case Error: + vsyslog(LOG_ERR, format, ap); + break; + } +#elif defined(_WINDOWS) + LPWSTR pInsertStrings[2] = {NULL, NULL}; + std::wstring str = TelldusCore::charToWstring(TelldusCore::sformatf(format, ap).c_str()); + pInsertStrings[0] = (LPWSTR)str.c_str(); + + switch (logLevel) { + case Debug: + ReportEvent(d->eventSource, EVENTLOG_SUCCESS, NULL, LOG_DEBUG, NULL, 1, 0, reinterpret_cast(&pInsertStrings), NULL); + break; + case Notice: + ReportEvent(d->eventSource, EVENTLOG_INFORMATION_TYPE, NULL, LOG_NOTICE, NULL, 1, 0, reinterpret_cast(&pInsertStrings), NULL); + break; + case Warning: + ReportEvent(d->eventSource, EVENTLOG_WARNING_TYPE, NULL, LOG_WARNING, NULL, 1, 0, reinterpret_cast(&pInsertStrings), NULL); + break; + case Error: + ReportEvent(d->eventSource, EVENTLOG_ERROR_TYPE, NULL, LOG_ERR, NULL, 1, 0, reinterpret_cast(&pInsertStrings), NULL); + break; + } +#endif + } +} + +Log *Log::instance() { + if (PrivateData::instance == 0) { + PrivateData::instance = new Log(); + } + return PrivateData::instance; +} diff --git a/telldus-core/service/Log.h b/telldus-core/service/Log.h new file mode 100644 index 0000000..fc2491e --- /dev/null +++ b/telldus-core/service/Log.h @@ -0,0 +1,40 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_LOG_H_ +#define TELLDUS_CORE_SERVICE_LOG_H_ + +#include +#include + +class Log { +public: + enum LogLevel { Debug, Notice, Warning, Error }; + enum LogOutput { StdOut, System }; + virtual ~Log(); + + static void destroy(); + + static void debug(const char *fmt, ...); + static void notice(const char *fmt, ...); + static void warning(const char *fmt, ...); + static void error(const char *fmt, ...); + + static void setDebug(); + static void setLogOutput(LogOutput logOutput); + +protected: + Log(); + void message(LogLevel logLevel, const char *format, va_list ap) const; + static Log *instance(); + +private: + class PrivateData; + PrivateData *d; +}; + + +#endif // TELLDUS_CORE_SERVICE_LOG_H_ diff --git a/telldus-core/service/Messages.mc b/telldus-core/service/Messages.mc new file mode 100644 index 0000000000000000000000000000000000000000..b7f81b41460477b0b3e06193edfb4e8499597ac5 GIT binary patch literal 1158 zcmdT^!D_-l5PfH%{}2$NrJybJq9KR2hBRPPFiMX>tBnDThp6b!Yu`+=!uFyR50+t> z-Oaq2H!r(;*t@7B!#8qFFr{~p(B9!AradFjS%Np+zd;RxC}T6Cre`4pC!>cEF)7yc zDX|g#g1%rDPa_=8#BQyX+4U7YC*uM$#W;Atmg9+|jSiVhwxY^kkhRY$Lo3^zuF76o zxU88@UCryT`i6{htjNj!W>(^D)*k@F;X>eM?T|G32e0N5UmX8-^I literal 0 HcmV?d00001 diff --git a/telldus-core/service/Protocol.cpp b/telldus-core/service/Protocol.cpp new file mode 100644 index 0000000..2a8f00e --- /dev/null +++ b/telldus-core/service/Protocol.cpp @@ -0,0 +1,267 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Protocol.h" +#include +#include +#include + +#include "client/telldus-core.h" +#include "service/ControllerMessage.h" +#include "service/ProtocolBrateck.h" +#include "service/ProtocolComen.h" +#include "service/ProtocolEverflourish.h" +#include "service/ProtocolFineoffset.h" +#include "service/ProtocolFuhaote.h" +#include "service/ProtocolGroup.h" +#include "service/ProtocolHasta.h" +#include "service/ProtocolIkea.h" +#include "service/ProtocolMandolyn.h" +#include "service/ProtocolNexa.h" +#include "service/ProtocolOregon.h" +#include "service/ProtocolRisingSun.h" +#include "service/ProtocolSartano.h" +#include "service/ProtocolScene.h" +#include "service/ProtocolSilvanChip.h" +#include "service/ProtocolUpm.h" +#include "service/ProtocolWaveman.h" +#include "service/ProtocolX10.h" +#include "service/ProtocolYidong.h" +#include "common/Strings.h" + +class Protocol::PrivateData { +public: + ParameterMap parameterList; + std::wstring model; +}; + +Protocol::Protocol() { + d = new PrivateData; +} + +Protocol::~Protocol(void) { + delete d; +} + +std::wstring Protocol::model() const { + std::wstring strModel = d->model; + // Strip anything after : if it is found + size_t pos = strModel.find(L":"); + if (pos != std::wstring::npos) { + strModel = strModel.substr(0, pos); + } + + return strModel; +} + +void Protocol::setModel(const std::wstring &model) { + d->model = model; +} + +void Protocol::setParameters(const ParameterMap ¶meterList) { + d->parameterList = parameterList; +} + +std::wstring Protocol::getStringParameter(const std::wstring &name, const std::wstring &defaultValue) const { + ParameterMap::const_iterator it = d->parameterList.find(name); + if (it == d->parameterList.end()) { + return defaultValue; + } + return it->second; +} + +int Protocol::getIntParameter(const std::wstring &name, int min, int max) const { + std::wstring value = getStringParameter(name, L""); + if (value == L"") { + return min; + } + std::wstringstream st; + st << value; + int intValue = 0; + st >> intValue; + if (intValue < min) { + return min; + } + if (intValue > max) { + return max; + } + return intValue; +} + +bool Protocol::checkBit(int data, int bitno) { + return ((data >> bitno)&0x01); +} + + +Protocol *Protocol::getProtocolInstance(const std::wstring &protocolname) { + if(TelldusCore::comparei(protocolname, L"arctech")) { + return new ProtocolNexa(); + + } else if (TelldusCore::comparei(protocolname, L"brateck")) { + return new ProtocolBrateck(); + + } else if (TelldusCore::comparei(protocolname, L"comen")) { + return new ProtocolComen(); + + } else if (TelldusCore::comparei(protocolname, L"everflourish")) { + return new ProtocolEverflourish(); + + } else if (TelldusCore::comparei(protocolname, L"fuhaote")) { + return new ProtocolFuhaote(); + + } else if (TelldusCore::comparei(protocolname, L"hasta")) { + return new ProtocolHasta(); + + } else if (TelldusCore::comparei(protocolname, L"ikea")) { + return new ProtocolIkea(); + + } else if (TelldusCore::comparei(protocolname, L"risingsun")) { + return new ProtocolRisingSun(); + + } else if (TelldusCore::comparei(protocolname, L"sartano")) { + return new ProtocolSartano(); + + } else if (TelldusCore::comparei(protocolname, L"silvanchip")) { + return new ProtocolSilvanChip(); + + } else if (TelldusCore::comparei(protocolname, L"upm")) { + return new ProtocolUpm(); + + } else if (TelldusCore::comparei(protocolname, L"waveman")) { + return new ProtocolWaveman(); + + } else if (TelldusCore::comparei(protocolname, L"x10")) { + return new ProtocolX10(); + + } else if (TelldusCore::comparei(protocolname, L"yidong")) { + return new ProtocolYidong(); + + } else if (TelldusCore::comparei(protocolname, L"group")) { + return new ProtocolGroup(); + + } else if (TelldusCore::comparei(protocolname, L"scene")) { + return new ProtocolScene(); + } + + return 0; +} + +std::list Protocol::getParametersForProtocol(const std::wstring &protocolName) { + std::list parameters; + if(TelldusCore::comparei(protocolName, L"arctech")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"brateck")) { + parameters.push_back("house"); + + } else if (TelldusCore::comparei(protocolName, L"comen")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"everflourish")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"fuhaote")) { + parameters.push_back("code"); + + } else if (TelldusCore::comparei(protocolName, L"hasta")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"ikea")) { + parameters.push_back("system"); + parameters.push_back("units"); + // parameters.push_back("fade"); + + } else if (TelldusCore::comparei(protocolName, L"risingsun")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"sartano")) { + parameters.push_back("code"); + + } else if (TelldusCore::comparei(protocolName, L"silvanchip")) { + parameters.push_back("house"); + + } else if (TelldusCore::comparei(protocolName, L"upm")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"waveman")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"x10")) { + parameters.push_back("house"); + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"yidong")) { + parameters.push_back("unit"); + + } else if (TelldusCore::comparei(protocolName, L"group")) { + parameters.push_back("devices"); + + } else if (TelldusCore::comparei(protocolName, L"scene")) { + parameters.push_back("devices"); + } + + return parameters; +} + +std::list Protocol::decodeData(const std::string &fullData) { + std::list retval; + std::string decoded = ""; + + ControllerMessage dataMsg(fullData); + if( TelldusCore::comparei(dataMsg.protocol(), L"arctech") ) { + decoded = ProtocolNexa::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + decoded = ProtocolWaveman::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + decoded = ProtocolSartano::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"everflourish") ) { + decoded = ProtocolEverflourish::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"fineoffset") ) { + decoded = ProtocolFineoffset::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"mandolyn") ) { + decoded = ProtocolMandolyn::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"oregon") ) { + decoded = ProtocolOregon::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"x10") ) { + decoded = ProtocolX10::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } else if(TelldusCore::comparei(dataMsg.protocol(), L"hasta") ) { + decoded = ProtocolHasta::decodeData(dataMsg); + if (decoded != "") { + retval.push_back(decoded); + } + } + + return retval; +} diff --git a/telldus-core/service/Protocol.h b/telldus-core/service/Protocol.h new file mode 100644 index 0000000..a4e7e6c --- /dev/null +++ b/telldus-core/service/Protocol.h @@ -0,0 +1,46 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOL_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOL_H_ + +#include +#include +#include +#include "client/telldus-core.h" + +typedef std::map ParameterMap; + +class Controller; + +class Protocol { +public: + Protocol(); + virtual ~Protocol(void); + + static Protocol *getProtocolInstance(const std::wstring &protocolname); + static std::list getParametersForProtocol(const std::wstring &protocolName); + static std::list decodeData(const std::string &fullData); + + virtual int methods() const = 0; + std::wstring model() const; + void setModel(const std::wstring &model); + void setParameters(const ParameterMap ¶meterList); + + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller) = 0; + +protected: + virtual std::wstring getStringParameter(const std::wstring &name, const std::wstring &defaultValue = L"") const; + virtual int getIntParameter(const std::wstring &name, int min, int max) const; + + static bool checkBit(int data, int bit); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOL_H_ diff --git a/telldus-core/service/ProtocolBrateck.cpp b/telldus-core/service/ProtocolBrateck.cpp new file mode 100644 index 0000000..ab03387 --- /dev/null +++ b/telldus-core/service/ProtocolBrateck.cpp @@ -0,0 +1,53 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolBrateck.h" +#include + +int ProtocolBrateck::methods() const { + return TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP; +} + +std::string ProtocolBrateck::getStringForMethod(int method, unsigned char, Controller *) { + const char S = '!'; + const char L = 'V'; + const char B1[] = {L, S, L, S, 0}; + const char BX[] = {S, L, L, S, 0}; + const char B0[] = {S, L, S, L, 0}; + const char BUP[] = {L, S, L, S, S, L, S, L, S, L, S, L, S, L, S, L, S, 0}; + const char BSTOP[] = {S, L, S, L, L, S, L, S, S, L, S, L, S, L, S, L, S, 0}; + const char BDOWN[] = {S, L, S, L, S, L, S, L, S, L, S, L, L, S, L, S, S, 0}; + + std::string strReturn; + std::wstring strHouse = this->getStringParameter(L"house", L""); + if (strHouse == L"") { + return ""; + } + + for( size_t i = 0; i < strHouse.length(); ++i ) { + if (strHouse[i] == '1') { + strReturn.insert(0, B1); + } else if (strHouse[i] == '-') { + strReturn.insert(0, BX); + } else if (strHouse[i] == '0') { + strReturn.insert(0, B0); + } + } + + strReturn.insert(0, "S"); + if (method == TELLSTICK_UP) { + strReturn.append(BUP); + } else if (method == TELLSTICK_DOWN) { + strReturn.append(BDOWN); + } else if (method == TELLSTICK_STOP) { + strReturn.append(BSTOP); + } else { + return ""; + } + strReturn.append("+"); + + return strReturn; +} diff --git a/telldus-core/service/ProtocolBrateck.h b/telldus-core/service/ProtocolBrateck.h new file mode 100644 index 0000000..c533308 --- /dev/null +++ b/telldus-core/service/ProtocolBrateck.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLBRATECK_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLBRATECK_H_ + +#include +#include "service/Protocol.h" + +class ProtocolBrateck : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLBRATECK_H_ diff --git a/telldus-core/service/ProtocolComen.cpp b/telldus-core/service/ProtocolComen.cpp new file mode 100644 index 0000000..dfc7d38 --- /dev/null +++ b/telldus-core/service/ProtocolComen.cpp @@ -0,0 +1,23 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolComen.h" +#include + +int ProtocolComen::methods() const { + return (TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN); +} + +int ProtocolComen::getIntParameter(const std::wstring &name, int min, int max) const { + if (name.compare(L"house") == 0) { + int intHouse = Protocol::getIntParameter(L"house", 1, 16777215); + // The last two bits must be hardcoded + intHouse <<= 2; + intHouse += 2; + return intHouse; + } + return Protocol::getIntParameter(name, min, max); +} diff --git a/telldus-core/service/ProtocolComen.h b/telldus-core/service/ProtocolComen.h new file mode 100644 index 0000000..f43167a --- /dev/null +++ b/telldus-core/service/ProtocolComen.h @@ -0,0 +1,21 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLCOMEN_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLCOMEN_H_ + +#include +#include "service/ProtocolNexa.h" + +class ProtocolComen : public ProtocolNexa { +public: + virtual int methods() const; + +protected: + virtual int getIntParameter(const std::wstring &name, int min, int max) const; +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLCOMEN_H_ diff --git a/telldus-core/service/ProtocolEverflourish.cpp b/telldus-core/service/ProtocolEverflourish.cpp new file mode 100644 index 0000000..cc19393 --- /dev/null +++ b/telldus-core/service/ProtocolEverflourish.cpp @@ -0,0 +1,133 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolEverflourish.h" +#include +#include +#include +#include "service/ControllerMessage.h" + +int ProtocolEverflourish::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN; +} + +std::string ProtocolEverflourish::getStringForMethod(int method, unsigned char, Controller *) { + unsigned int deviceCode = this->getIntParameter(L"house", 0, 16383); + unsigned int intCode = this->getIntParameter(L"unit", 1, 4)-1; + unsigned char action; + + if (method == TELLSTICK_TURNON) { + action = 15; + } else if (method == TELLSTICK_TURNOFF) { + action = 0; + } else if (method == TELLSTICK_LEARN) { + action = 10; + } else { + return ""; + } + + const char ssss = 85; + const char sssl = 84; // 0 + const char slss = 69; // 1 + + const char bits[2] = {sssl, slss}; + int i, check; + + std::string strCode; + + deviceCode = (deviceCode << 2) | intCode; + + check = calculateChecksum(deviceCode); + + char preamble[] = {'R', 5, 'T', 114, 60, 1, 1, 105, ssss, ssss, 0}; + strCode.append(preamble); + + for(i = 15; i >= 0; i--) { + strCode.append(1, bits[(deviceCode >> i)&0x01]); + } + for(i = 3; i >= 0; i--) { + strCode.append(1, bits[(check >> i)&0x01]); + } + for(i = 3; i >= 0; i--) { + strCode.append(1, bits[(action >> i)&0x01]); + } + + strCode.append(1, ssss); + strCode.append(1, '+'); + + return strCode; +} + +// The calculation used in this function is provided by Frank Stevenson +unsigned int ProtocolEverflourish::calculateChecksum(unsigned int x) { + unsigned int bits[16] = { + 0xf, 0xa, 0x7, 0xe, + 0xf, 0xd, 0x9, 0x1, + 0x1, 0x2, 0x4, 0x8, + 0x3, 0x6, 0xc, 0xb + }; + unsigned int bit = 1; + unsigned int res = 0x5; + int i; + unsigned int lo, hi; + + if ((x & 0x3) == 3) { + lo = x & 0x00ff; + hi = x & 0xff00; + lo += 4; + if (lo>0x100) { + lo = 0x12; + } + x = lo | hi; + } + + for(i = 0; i < 16; i++) { + if (x & bit) { + res = res ^ bits[i]; + } + bit = bit << 1; + } + + return res; +} + +std::string ProtocolEverflourish::decodeData(const ControllerMessage &dataMsg) { + uint64_t allData; + unsigned int house = 0; + unsigned int unit = 0; + unsigned int method = 0; + + allData = dataMsg.getInt64Parameter("data"); + + house = allData & 0xFFFC00; + house >>= 10; + + unit = allData & 0x300; + unit >>= 8; + unit++; // unit from 1 to 4 + + method = allData & 0xF; + + if(house > 16383 || unit < 1 || unit > 4) { + // not everflourish + return ""; + } + + std::stringstream retString; + retString << "class:command;protocol:everflourish;model:selflearning;house:" << house << ";unit:" << unit << ";method:"; + if(method == 0) { + retString << "turnoff;"; + } else if(method == 15) { + retString << "turnon;"; + } else if(method == 10) { + retString << "learn;"; + } else { + // not everflourish + return ""; + } + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolEverflourish.h b/telldus-core/service/ProtocolEverflourish.h new file mode 100644 index 0000000..ae4abf7 --- /dev/null +++ b/telldus-core/service/ProtocolEverflourish.h @@ -0,0 +1,24 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLEVERFLOURISH_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLEVERFLOURISH_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolEverflourish : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + static std::string decodeData(const ControllerMessage &dataMsg); + +private: + static unsigned int calculateChecksum(unsigned int x); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLEVERFLOURISH_H_ diff --git a/telldus-core/service/ProtocolFineoffset.cpp b/telldus-core/service/ProtocolFineoffset.cpp new file mode 100644 index 0000000..2fd6f5a --- /dev/null +++ b/telldus-core/service/ProtocolFineoffset.cpp @@ -0,0 +1,52 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolFineoffset.h" +#include +#include +#include +#include +#include "common/Strings.h" + +std::string ProtocolFineoffset::decodeData(const ControllerMessage &dataMsg) { + std::string data = dataMsg.getParameter("data"); + if (data.length() < 8) { + return ""; + } + + // Checksum currently not used + // uint8_t checksum = (uint8_t)TelldusCore::hexTo64l(data.substr(data.length()-2)); + data = data.substr(0, data.length()-2); + + uint8_t humidity = (uint8_t)TelldusCore::hexTo64l(data.substr(data.length()-2)); + data = data.substr(0, data.length()-2); + + uint16_t value = (uint16_t)TelldusCore::hexTo64l(data.substr(data.length()-3)); + double temperature = (value & 0x7FF)/10.0; + + value >>= 11; + if (value & 1) { + temperature = -temperature; + } + data = data.substr(0, data.length()-3); + + uint16_t id = (uint16_t)TelldusCore::hexTo64l(data) & 0xFF; + + std::stringstream retString; + retString << "class:sensor;protocol:fineoffset;id:" << id << ";model:"; + + if (humidity <= 100) { + retString << "temperaturehumidity;humidity:" << static_cast(humidity) << ";"; + } else if (humidity == 0xFF) { + retString << "temperature;"; + } else { + return ""; + } + + retString << "temp:" << std::fixed << std::setprecision(1) << temperature << ";"; + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolFineoffset.h b/telldus-core/service/ProtocolFineoffset.h new file mode 100644 index 0000000..b3bc3ce --- /dev/null +++ b/telldus-core/service/ProtocolFineoffset.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLFINEOFFSET_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLFINEOFFSET_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolFineoffset : public Protocol { +public: + static std::string decodeData(const ControllerMessage &dataMsg); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLFINEOFFSET_H_ diff --git a/telldus-core/service/ProtocolFuhaote.cpp b/telldus-core/service/ProtocolFuhaote.cpp new file mode 100644 index 0000000..7254520 --- /dev/null +++ b/telldus-core/service/ProtocolFuhaote.cpp @@ -0,0 +1,60 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolFuhaote.h" +#include + +int ProtocolFuhaote::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; +} + +std::string ProtocolFuhaote::getStringForMethod(int method, unsigned char, Controller *) { + const char S = 19; + const char L = 58; + const char B0[] = {S, L, L, S, 0}; + const char B1[] = {L, S, L, S, 0}; + const char OFF[] = {S, L, S, L, S, L, L, S, 0}; + const char ON[] = {S, L, L, S, S, L, S, L, 0}; + + std::string strReturn = "S"; + std::wstring strCode = this->getStringParameter(L"code", L""); + if (strCode == L"") { + return ""; + } + + // House code + for(size_t i = 0; i < 5; ++i) { + if (strCode[i] == '0') { + strReturn.append(B0); + } else if (strCode[i] == '1') { + strReturn.append(B1); + } + } + // Unit code + for(size_t i = 5; i < 10; ++i) { + if (strCode[i] == '0') { + strReturn.append(B0); + } else if (strCode[i] == '1') { + strReturn.append(1, S); + strReturn.append(1, L); + strReturn.append(1, S); + strReturn.append(1, L); + } + } + + if (method == TELLSTICK_TURNON) { + strReturn.append(ON); + } else if (method == TELLSTICK_TURNOFF) { + strReturn.append(OFF); + } else { + return ""; + } + + strReturn.append(1, S); + strReturn.append("+"); + return strReturn; +} + diff --git a/telldus-core/service/ProtocolFuhaote.h b/telldus-core/service/ProtocolFuhaote.h new file mode 100644 index 0000000..2618ef8 --- /dev/null +++ b/telldus-core/service/ProtocolFuhaote.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLFUHAOTE_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLFUHAOTE_H_ + +#include +#include "service/Protocol.h" + +class ProtocolFuhaote : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLFUHAOTE_H_ diff --git a/telldus-core/service/ProtocolGroup.cpp b/telldus-core/service/ProtocolGroup.cpp new file mode 100644 index 0000000..4c4d0d1 --- /dev/null +++ b/telldus-core/service/ProtocolGroup.cpp @@ -0,0 +1,16 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolGroup.h" +#include + +int ProtocolGroup::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_DIM | TELLSTICK_BELL | TELLSTICK_LEARN | TELLSTICK_EXECUTE | TELLSTICK_TOGGLE | TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP; +} + +std::string ProtocolGroup::getStringForMethod(int method, unsigned char data, Controller *) { + return ""; +} diff --git a/telldus-core/service/ProtocolGroup.h b/telldus-core/service/ProtocolGroup.h new file mode 100644 index 0000000..509c2a3 --- /dev/null +++ b/telldus-core/service/ProtocolGroup.h @@ -0,0 +1,22 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLGROUP_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLGROUP_H_ + +#include +#include "service/Protocol.h" + +class ProtocolGroup : public Protocol { +public: + virtual int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLGROUP_H_ + + + diff --git a/telldus-core/service/ProtocolHasta.cpp b/telldus-core/service/ProtocolHasta.cpp new file mode 100644 index 0000000..786f068 --- /dev/null +++ b/telldus-core/service/ProtocolHasta.cpp @@ -0,0 +1,201 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolHasta.h" +#include +#include +#include +#include "common/Strings.h" + +int ProtocolHasta::methods() const { + return TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP | TELLSTICK_LEARN; +} + +std::string ProtocolHasta::getStringForMethod(int method, unsigned char, Controller *) { + if (TelldusCore::comparei(model(), L"selflearningv2")) { + return getStringForMethodv2(method); + } + return getStringForMethodv1(method); +} + +std::string ProtocolHasta::getStringForMethodv1(int method) { + int house = this->getIntParameter(L"house", 1, 65536); + int unit = this->getIntParameter(L"unit", 1, 15); + std::string strReturn; + + strReturn.append(1, 164); + strReturn.append(1, 1); + strReturn.append(1, 164); + strReturn.append(1, 1); + strReturn.append(1, 164); + strReturn.append(1, 164); + + strReturn.append(convertByte( (house & 0xFF) )); + strReturn.append(convertByte( (house>>8) & 0xFF )); + + int byte = unit&0x0F; + + if (method == TELLSTICK_UP) { + byte |= 0x00; + + } else if (method == TELLSTICK_DOWN) { + byte |= 0x10; + + } else if (method == TELLSTICK_STOP) { + byte |= 0x50; + + } else if (method == TELLSTICK_LEARN) { + byte |= 0x40; + + } else { + return ""; + } + strReturn.append(convertByte(byte)); + + strReturn.append(convertByte(0x0)); + strReturn.append(convertByte(0x0)); + + // Remove the last pulse + strReturn.erase(strReturn.end()-1, strReturn.end()); + + return strReturn; +} + +std::string ProtocolHasta::convertByte(unsigned char byte) { + std::string retval; + for(int i = 0; i < 8; ++i) { + if (byte & 1) { + retval.append(1, 33); + retval.append(1, 17); + } else { + retval.append(1, 17); + retval.append(1, 33); + } + byte >>= 1; + } + return retval; +} + +std::string ProtocolHasta::getStringForMethodv2(int method) { + int house = this->getIntParameter(L"house", 1, 65536); + int unit = this->getIntParameter(L"unit", 1, 15); + int sum = 0; + std::string strReturn; + strReturn.append(1, 245); + strReturn.append(1, 1); + strReturn.append(1, 245); + strReturn.append(1, 245); + strReturn.append(1, 63); + strReturn.append(1, 1); + strReturn.append(1, 63); + strReturn.append(1, 1); + strReturn.append(1, 35); + strReturn.append(1, 35); + + strReturn.append(convertBytev2( (house>>8) & 0xFF )); + sum = ((house>>8)&0xFF); + strReturn.append(convertBytev2( (house & 0xFF) )); + sum += (house & 0xFF); + + int byte = unit&0x0F; + + if (method == TELLSTICK_UP) { + byte |= 0xC0; + + } else if (method == TELLSTICK_DOWN) { + byte |= 0x10; + + } else if (method == TELLSTICK_STOP) { + byte |= 0x50; + + } else if (method == TELLSTICK_LEARN) { + byte |= 0x40; + + } else { + return ""; + } + strReturn.append(convertBytev2(byte)); + sum += byte; + + strReturn.append(convertBytev2(0x01)); + sum += 0x01; + + int checksum = ((static_cast(sum/256)+1)*256+1) - sum; + strReturn.append(convertBytev2(checksum)); + strReturn.append(1, 63); + strReturn.append(1, 35); + + return strReturn; +} + +std::string ProtocolHasta::convertBytev2(unsigned char byte) { + std::string retval; + for(int i = 0; i < 8; ++i) { + if (byte & 1) { + retval.append(1, 63); + retval.append(1, 35); + } else { + retval.append(1, 35); + retval.append(1, 63); + } + byte >>= 1; + } + return retval; +} + +std::string ProtocolHasta::decodeData(const ControllerMessage& dataMsg) { + uint64_t allData = dataMsg.getInt64Parameter("data"); + + unsigned int house = 0; + unsigned int unit = 0; + unsigned int method = 0; + std::string model; + std::string methodstring; + + allData >>= 8; + unit = allData & 0xF; + allData >>= 4; + method = allData & 0xF; + allData >>= 4; + if(TelldusCore::comparei(dataMsg.model(), L"selflearning")) { + // version1 + house = allData & 0xFFFF; + house = ((house << 8) | (house >> 8)) & 0xFFFF; + model = "selflearning"; + if(method == 0) { + methodstring = "up"; + } else if(method == 1) { + methodstring = "down"; + } else if(method == 5) { + methodstring = "stop"; + } else { + return ""; + } + } else { + // version2 + house = allData & 0xFFFF; + + model = "selflearningv2"; + if(method == 12) { + methodstring = "up"; + } else if(method == 1 || method == 8) { // is method 8 correct? + methodstring = "down"; + } else if(method == 5) { + methodstring = "stop"; + } else { + return ""; + } + } + + if(house < 1 || house > 65535 || unit < 1 || unit > 16) { + // not hasta + return ""; + } + + std::stringstream retString; + retString << "class:command;protocol:hasta;model:" << model << ";house:" << house << ";unit:" << unit << ";method:" << methodstring << ";"; + return retString.str(); +} diff --git a/telldus-core/service/ProtocolHasta.h b/telldus-core/service/ProtocolHasta.h new file mode 100644 index 0000000..7a122bb --- /dev/null +++ b/telldus-core/service/ProtocolHasta.h @@ -0,0 +1,27 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLHASTA_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLHASTA_H_ + +#include +#include "service/ControllerMessage.h" +#include "service/Protocol.h" + +class ProtocolHasta : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + static std::string decodeData(const ControllerMessage &dataMsg); + +protected: + static std::string convertByte(unsigned char byte); + static std::string convertBytev2(unsigned char byte); + std::string getStringForMethodv1(int method); + std::string getStringForMethodv2(int method); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLHASTA_H_ diff --git a/telldus-core/service/ProtocolIkea.cpp b/telldus-core/service/ProtocolIkea.cpp new file mode 100644 index 0000000..07d3ef3 --- /dev/null +++ b/telldus-core/service/ProtocolIkea.cpp @@ -0,0 +1,151 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolIkea.h" +#include +#include +#include +#include +#include "common/Strings.h" +#ifdef _WINDOWS +#define strtok_r(s, d, p) strtok_s(s, d, p) +#endif + +int ProtocolIkea::methods() const { + if (TelldusCore::comparei(model(), L"selflearning-switch")) { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; + } + return TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_DIM; +} + +std::string ProtocolIkea::getStringForMethod(int method, unsigned char level, Controller *) { + const char B1[] = {84, 84, 0}; + const char B0[] = {static_cast(170), 0}; + + int intSystem = this->getIntParameter(L"system", 1, 16)-1; + int intFadeStyle = TelldusCore::comparei(this->getStringParameter(L"fade", L"true"), L"true"); + std::wstring wstrUnits = this->getStringParameter(L"units", L""); + + if (method == TELLSTICK_TURNON) { + level = 255; + } else if (method == TELLSTICK_TURNOFF) { + level = 0; + } else if (method == TELLSTICK_DIM) { + } else { + return ""; + } + + if (wstrUnits == L"") { + return ""; + } + + std::string strUnits(TelldusCore::wideToString(wstrUnits)); + int intUnits = 0; // Start without any units + + char *tempUnits = new char[strUnits.size()+1]; +#ifdef _WINDOWS + strcpy_s(tempUnits, strUnits.size()+1, strUnits.c_str()); +#else + snprintf(tempUnits, strUnits.size()+1, "%s", strUnits.c_str()); +#endif + + char *saveptr; + char *strToken = strtok_r(tempUnits, ",", &saveptr); + do { + int intUnit = atoi(strToken); + if (intUnit == 10) { + intUnit = 0; + } + intUnits = intUnits | ( 1<<(9-intUnit) ); + } while ( (strToken = strtok_r(NULL, ",", &saveptr)) != NULL ); + + delete[] tempUnits; + + std::string strReturn; + strReturn.append(1, 'S'); + strReturn.append(1, 84); + strReturn.append(1, 84); + strReturn.append(1, 84); + strReturn.append(1, 84); + strReturn.append(1, 84); + strReturn.append(1, 84); + strReturn.append(1, static_cast(170)); + + std::string strChannels = ""; + int intCode = (intSystem << 10) | intUnits; + int checksum1 = 0; + int checksum2 = 0; + for (int i = 13; i >= 0; --i) { + if ((intCode >> i) & 1) { + strChannels.append(B1); + if (i % 2 == 0) + checksum2++; + else + checksum1++; + } else { + strChannels.append(B0); + } + } + strReturn.append(strChannels); // System + Units + + strReturn.append(checksum1 %2 == 0 ? B1 : B0); // 1st checksum + strReturn.append(checksum2 %2 == 0 ? B1 : B0); // 2nd checksum + + int intLevel = 0; + if (level <= 12) { + intLevel = 10; // Level 10 is actually off + } else if (level <= 37) { + intLevel = 1; + } else if (level <= 62) { + intLevel = 2; + } else if (level <= 87) { + intLevel = 3; + } else if (level <= 112) { + intLevel = 4; + } else if (level <= 137) { + intLevel = 5; + } else if (level <= 162) { + intLevel = 6; + } else if (level <= 187) { + intLevel = 7; + } else if (level <= 212) { + intLevel = 8; + } else if (level <= 237) { + intLevel = 9; + } else { + intLevel = 0; // Level 0 is actually full on + } + + int intFade = 0; + if (intFadeStyle == 1) { + intFade = 11 << 4; // Smooth + } else { + intFade = 1 << 4; // Instant + } + + intCode = intLevel | intFade; // Concat level and fade + + checksum1 = 0; + checksum2 = 0; + for (int i = 0; i < 6; ++i) { + if ((intCode >> i) & 1) { + strReturn.append(B1); + if (i % 2 == 0) + checksum1++; + else + checksum2++; + } else { + strReturn.append(B0); + } + } + + strReturn.append(checksum1 %2 == 0 ? B1 : B0); // 1st checksum + strReturn.append(checksum2 %2 == 0 ? B1 : B0); // 2nd checksum + + strReturn.append("+"); + + return strReturn; +} diff --git a/telldus-core/service/ProtocolIkea.h b/telldus-core/service/ProtocolIkea.h new file mode 100644 index 0000000..11a18cb --- /dev/null +++ b/telldus-core/service/ProtocolIkea.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLIKEA_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLIKEA_H_ + +#include +#include "service/Protocol.h" + +class ProtocolIkea : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLIKEA_H_ diff --git a/telldus-core/service/ProtocolMandolyn.cpp b/telldus-core/service/ProtocolMandolyn.cpp new file mode 100644 index 0000000..c37555c --- /dev/null +++ b/telldus-core/service/ProtocolMandolyn.cpp @@ -0,0 +1,46 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolMandolyn.h" +#include +#include +#include +#include +#include "common/Strings.h" + +std::string ProtocolMandolyn::decodeData(const ControllerMessage &dataMsg) { + std::string data = dataMsg.getParameter("data"); + uint32_t value = (uint32_t)TelldusCore::hexTo64l(data); + + // parity not used + // bool parity = value & 0x1; + value >>= 1; + + double temp = static_cast(value & 0x7FFF) - static_cast(6400); + temp = temp/128.0; + value >>= 15; + + uint8_t humidity = (value & 0x7F); + value >>= 7; + + // battOk not used + // bool battOk = value & 0x1; + value >>= 3; + + uint8_t channel = (value & 0x3)+1; + value >>= 2; + + uint8_t house = value & 0xF; + + std::stringstream retString; + retString << "class:sensor;protocol:mandolyn;id:" + << house*10+channel + << ";model:temperaturehumidity;" + << "temp:" << std::fixed << std::setprecision(1) << temp + << ";humidity:" << static_cast(humidity) << ";"; + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolMandolyn.h b/telldus-core/service/ProtocolMandolyn.h new file mode 100644 index 0000000..8fcd90a --- /dev/null +++ b/telldus-core/service/ProtocolMandolyn.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLMANDOLYN_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLMANDOLYN_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolMandolyn : public Protocol { +public: + static std::string decodeData(const ControllerMessage &dataMsg); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLMANDOLYN_H_ diff --git a/telldus-core/service/ProtocolNexa.cpp b/telldus-core/service/ProtocolNexa.cpp new file mode 100644 index 0000000..76cc1ef --- /dev/null +++ b/telldus-core/service/ProtocolNexa.cpp @@ -0,0 +1,279 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolNexa.h" +#include +#include +#include +#include "service/TellStick.h" +#include "common/Strings.h" + +int ProtocolNexa::lastArctecCodeSwitchWasTurnOff = 0; // TODO(stefan): always removing first turnon now, make more flexible (waveman too) + +int ProtocolNexa::methods() const { + if (TelldusCore::comparei(model(), L"codeswitch")) { + return (TELLSTICK_TURNON | TELLSTICK_TURNOFF); + + } else if (TelldusCore::comparei(model(), L"selflearning-switch")) { + return (TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN); + + } else if (TelldusCore::comparei(model(), L"selflearning-dimmer")) { + return (TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_DIM | TELLSTICK_LEARN); + + } else if (TelldusCore::comparei(model(), L"bell")) { + return TELLSTICK_BELL; + } + return 0; +} + +std::string ProtocolNexa::getStringForMethod(int method, unsigned char data, Controller *controller) { + if (TelldusCore::comparei(model(), L"codeswitch")) { + return getStringCodeSwitch(method); + } else if (TelldusCore::comparei(model(), L"bell")) { + return getStringBell(); + } + if ((method == TELLSTICK_TURNON) && TelldusCore::comparei(model(), L"selflearning-dimmer")) { + // Workaround for not letting a dimmer do into "dimming mode" + return getStringSelflearning(TELLSTICK_DIM, 255); + } + if (method == TELLSTICK_LEARN) { + std::string str = getStringSelflearning(TELLSTICK_TURNON, data); + + // Check to see if we are an old TellStick (fw <= 2, batch <= 8) + TellStick *ts = reinterpret_cast(controller); + if (!ts) { + return str; + } + if (ts->pid() == 0x0c30 && ts->firmwareVersion() <= 2) { + // Workaround for the bug in early firmwares + // The TellStick have a fixed pause (max) between two packets. + // It is only correct between the first and second packet. + // It seems faster to send two packes at a time and some + // receivers seems picky about this when learning. + // We also return the last packet so Device::doAction() doesn't + // report TELLSTICK_ERROR_METHOD_NOT_SUPPORTED + + str.insert(0, 1, 2); // Repeat two times + str.insert(0, 1, 'R'); + for (int i = 0; i < 5; ++i) { + controller->send(str); + } + } + return str; + } + return getStringSelflearning(method, data); +} + +std::string ProtocolNexa::getStringCodeSwitch(int method) { + std::string strReturn = "S"; + + std::wstring house = getStringParameter(L"house", L"A"); + int intHouse = house[0] - L'A'; + strReturn.append(getCodeSwitchTuple(intHouse)); + strReturn.append(getCodeSwitchTuple(getIntParameter(L"unit", 1, 16)-1)); + + if (method == TELLSTICK_TURNON) { + strReturn.append("$k$k$kk$$kk$$kk$$k+"); + } else if (method == TELLSTICK_TURNOFF) { + strReturn.append(this->getOffCode()); + } else { + return ""; + } + return strReturn; +} + +std::string ProtocolNexa::getStringBell() { + std::string strReturn = "S"; + + std::wstring house = getStringParameter(L"house", L"A"); + int intHouse = house[0] - L'A'; + strReturn.append(getCodeSwitchTuple(intHouse)); + strReturn.append("$kk$$kk$$kk$$k$k"); // Unit 7 + strReturn.append("$kk$$kk$$kk$$kk$$k+"); // Bell + return strReturn; +} + +std::string ProtocolNexa::getStringSelflearning(int method, unsigned char level) { + int intHouse = getIntParameter(L"house", 1, 67108863); + int intCode = getIntParameter(L"unit", 1, 16)-1; + return getStringSelflearningForCode(intHouse, intCode, method, level); +} + +std::string ProtocolNexa::getStringSelflearningForCode(int intHouse, int intCode, int method, unsigned char level) { + const unsigned char START[] = {'T', 127, 255, 24, 1, 0}; + // const char START[] = {'T',130,255,26,24,0}; + + std::string strMessage(reinterpret_cast(START)); + strMessage.append(1, (method == TELLSTICK_DIM ? 147 : 132)); // Number of pulses + + std::string m; + for (int i = 25; i >= 0; --i) { + m.append( intHouse & 1 << i ? "10" : "01" ); + } + m.append("01"); // Group + + // On/off + if (method == TELLSTICK_DIM) { + m.append("00"); + } else if (method == TELLSTICK_TURNOFF) { + m.append("01"); + } else if (method == TELLSTICK_TURNON) { + m.append("10"); + } else { + return ""; + } + + for (int i = 3; i >= 0; --i) { + m.append( intCode & 1 << i ? "10" : "01" ); + } + + if (method == TELLSTICK_DIM) { + unsigned char newLevel = level/16; + for (int i = 3; i >= 0; --i) { + m.append(newLevel & 1 << i ? "10" : "01"); + } + } + + // The number of data is odd. + // Add this to make it even, otherwise the following loop will not work + m.append("0"); + + unsigned char code = 9; // b1001, startcode + for (unsigned int i = 0; i < m.length(); ++i) { + code <<= 4; + if (m[i] == '1') { + code |= 8; // b1000 + } else { + code |= 10; // b1010 + // code |= 11; //b1011 + } + if (i % 2 == 0) { + strMessage.append(1, code); + code = 0; + } + } + strMessage.append("+"); + +// for( int i = 0; i < strMessage.length(); ++i ) { +// printf("%i,", (unsigned char)strMessage[i]); +// } +// printf("\n"); + return strMessage; +} + +std::string ProtocolNexa::decodeData(const ControllerMessage& dataMsg) { + uint64_t allData = dataMsg.getInt64Parameter("data"); + + if(TelldusCore::comparei(dataMsg.model(), L"selflearning")) { + // selflearning + return decodeDataSelfLearning(allData); + } else { + // codeswitch + return decodeDataCodeSwitch(allData); + } +} + +std::string ProtocolNexa::decodeDataSelfLearning(uint64_t allData) { + unsigned int house = 0; + unsigned int unit = 0; + unsigned int group = 0; + unsigned int method = 0; + + house = allData & 0xFFFFFFC0; + house >>= 6; + + group = allData & 0x20; + group >>= 5; + + method = allData & 0x10; + method >>= 4; + + unit = allData & 0xF; + unit++; + + if(house < 1 || house > 67108863 || unit < 1 || unit > 16) { + // not arctech selflearning + return ""; + } + + std::stringstream retString; + retString << "class:command;protocol:arctech;model:selflearning;house:" << house << ";unit:" << unit << ";group:" << group << ";method:"; + if(method == 1) { + retString << "turnon;"; + } else if(method == 0) { + retString << "turnoff;"; + } else { + // not arctech selflearning + return ""; + } + + return retString.str(); +} + +std::string ProtocolNexa::decodeDataCodeSwitch(uint64_t allData) { + unsigned int house = 0; + unsigned int unit = 0; + unsigned int method = 0; + + method = allData & 0xF00; + method >>= 8; + + unit = allData & 0xF0; + unit >>= 4; + unit++; + + house = allData & 0xF; + + if(house > 16 || unit < 1 || unit > 16) { + // not arctech codeswitch + return ""; + } + + house = house + 'A'; // house from A to P + + if(method != 6 && lastArctecCodeSwitchWasTurnOff == 1) { + lastArctecCodeSwitchWasTurnOff = 0; + return ""; // probably a stray turnon or bell (perhaps: only certain time interval since last, check that it's the same house/unit... Will lose + // one turnon/bell, but it's better than the alternative... + } + + if(method == 6) { + lastArctecCodeSwitchWasTurnOff = 1; + } + + std::stringstream retString; + retString << "class:command;protocol:arctech;model:codeswitch;house:" << static_cast(house); + + if(method == 6) { + retString << ";unit:" << unit << ";method:turnoff;"; + } else if(method == 14) { + retString << ";unit:" << unit << ";method:turnon;"; + } else if(method == 15) { + retString << ";method:bell;"; + } else { + // not arctech codeswitch + return ""; + } + + return retString.str(); +} + +std::string ProtocolNexa::getCodeSwitchTuple(int intCode) { + std::string strReturn = ""; + for( int i = 0; i < 4; ++i ) { + if (intCode & 1) { // Convert 1 + strReturn.append("$kk$"); + } else { // Convert 0 + strReturn.append("$k$k"); + } + intCode >>= 1; + } + return strReturn; +} + +std::string ProtocolNexa::getOffCode() const { + return "$k$k$kk$$kk$$k$k$k+"; +} diff --git a/telldus-core/service/ProtocolNexa.h b/telldus-core/service/ProtocolNexa.h new file mode 100644 index 0000000..8280997 --- /dev/null +++ b/telldus-core/service/ProtocolNexa.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLNEXA_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLNEXA_H_ + +#ifdef _MSC_VER +typedef unsigned __int64 uint64_t; +#else +#include +#endif +#include +#include "service/ControllerMessage.h" +#include "service/Device.h" + +class ProtocolNexa : public Protocol { +public: + virtual int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + static std::string decodeData(const ControllerMessage &dataMsg); + +protected: + std::string getStringSelflearning(int method, unsigned char data); + std::string getStringCodeSwitch(int method); + std::string getStringBell(); + virtual std::string getOffCode() const; + static std::string getCodeSwitchTuple(int code); + static std::string getStringSelflearningForCode(int house, int unit, int method, unsigned char data); + +private: + static int lastArctecCodeSwitchWasTurnOff; + static std::string decodeDataCodeSwitch(uint64_t allData); + static std::string decodeDataSelfLearning(uint64_t allData); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLNEXA_H_ diff --git a/telldus-core/service/ProtocolOregon.cpp b/telldus-core/service/ProtocolOregon.cpp new file mode 100644 index 0000000..3fcfe6d --- /dev/null +++ b/telldus-core/service/ProtocolOregon.cpp @@ -0,0 +1,347 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolOregon.h" +#include +#include +#include +#include +#include "common/Strings.h" + +std::string ProtocolOregon::decodeData(const ControllerMessage &dataMsg) { + std::string data = dataMsg.getParameter("data"); + + std::wstring model = dataMsg.model(); + if (model.compare(L"0xEA4C") == 0) { + return decodeEA4C(data); + } else if (model.compare(L"0x1A2D") == 0) { + return decode1A2D(data); + } else if (model.compare(L"0xF824") == 0) { + return decodeF824(data); + } else if (model.compare(L"0x1984") == 0 || model.compare(L"0x1994") == 0) { + return decode1984(data, model); + } else if (model.compare(L"0x2914") == 0) { + return decode2914(data); + } else if (model.compare(L"0xC844") == 0 || model.compare(L"0xEC40") == 0) { + // C844 - pool thermometer + return decodeC844(data, model); + } + + return ""; +} + +std::string ProtocolOregon::decodeEA4C(const std::string &data) { + uint64_t value = TelldusCore::hexTo64l(data); + + uint8_t checksum = 0xE + 0xA + 0x4 + 0xC; + checksum -= (value & 0xF) * 0x10; + checksum -= 0xA; + value >>= 8; + + uint8_t checksumw = (value >> 4) & 0xF; + bool neg = value & (1 << 3); + int hundred = value & 3; + checksum += (value & 0xF); + value >>= 8; + + uint8_t temp2 = value & 0xF; + uint8_t temp1 = (value >> 4) & 0xF; + checksum += temp2 + temp1; + value >>= 8; + + uint8_t temp3 = (value >> 4) & 0xF; + checksum += (value & 0xF) + temp3; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + uint8_t address = value & 0xFF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + // channel not used + // uint8_t channel = (value >> 4) & 0x7; + + if (checksum != checksumw) { + return ""; + } + + double temperature = ((hundred * 1000) + (temp1 * 100) + (temp2 * 10) + temp3)/10.0; + if (neg) { + temperature = -temperature; + } + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:EA4C;id:" << static_cast(address) + << ";temp:" << std::fixed << std::setprecision(1) << temperature << ";"; + + return retString.str(); +} + +std::string ProtocolOregon::decode1984(const std::string &data, const std::wstring &model) { + // wind + uint64_t value = TelldusCore::hexTo64l(data); + + uint8_t crcCheck = value & 0xF; // PROBABLY crc + value >>= 4; + uint8_t messageChecksum1 = value & 0xF; + value >>= 4; + uint8_t messageChecksum2 = value & 0xF; + + value >>= 4; + uint8_t avg1 = value & 0xF; + value >>= 4; + uint8_t avg2 = value & 0xF; + value >>= 4; + uint8_t avg3 = value & 0xF; + value >>= 4; + uint8_t gust1 = value & 0xF; + value >>= 4; + uint8_t gust2 = value & 0xF; + value >>= 4; + uint8_t gust3 = value & 0xF; + value >>= 4; + uint8_t unknown1 = value & 0xF; + value >>= 4; + uint8_t unknown2 = value & 0xF; + value >>= 4; + uint8_t direction = value & 0xF; + + value >>= 4; + uint8_t battery = value & 0xF; // PROBABLY battery + value >>= 4; + uint8_t rollingcode = ((value >> 4) & 0xF) + (value & 0xF); + uint8_t checksum = ((value >> 4) & 0xF) + (value & 0xF); + value >>= 8; + uint8_t channel = value & 0xF; + checksum += unknown1 + unknown2 + avg1 + avg2 + avg3 + gust1 + gust2 + gust3 + direction + battery + channel; + + if (model.compare(L"0x1984") == 0) { + checksum += 0x1 + 0x9 + 0x8 + 0x4; + } else { + checksum += 0x1 + 0x9 + 0x9 + 0x4; + } + + if (((checksum >> 4) & 0xF) != messageChecksum1 || (checksum & 0xF) != messageChecksum2) { + // checksum error + return ""; + } + + + double avg = ((avg1 * 100) + (avg2 * 10) + avg3)/10.0; + double gust = ((gust1 * 100) + (gust2 * 10) + gust3)/10.0; + float directiondegree = 22.5 * direction; + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:1984;id:" << static_cast(rollingcode) + << ";winddirection:" << directiondegree + << ";windaverage:" << std::fixed << std::setprecision(1) << avg + << ";windgust:" << std::fixed << std::setprecision(1) << gust << ";"; + + return retString.str(); +} + +std::string ProtocolOregon::decode1A2D(const std::string &data) { + uint64_t value = TelldusCore::hexTo64l(data); + // checksum2 not used yet + // uint8_t checksum2 = value & 0xFF; + value >>= 8; + uint8_t checksum1 = value & 0xFF; + value >>= 8; + + uint8_t checksum = ((value >> 4) & 0xF) + (value & 0xF); + uint8_t hum1 = value & 0xF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + uint8_t neg = value & (1 << 3); + uint8_t hum2 = (value >> 4) & 0xF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + uint8_t temp2 = value & 0xF; + uint8_t temp1 = (value >> 4) & 0xF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + uint8_t temp3 = (value >> 4) & 0xF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + uint8_t address = value & 0xFF; + value >>= 8; + + checksum += ((value >> 4) & 0xF) + (value & 0xF); + // channel not used + // uint8_t channel = (value >> 4) & 0x7; + + checksum += 0x1 + 0xA + 0x2 + 0xD - 0xA; + + // TODO(micke): Find out how checksum2 works + if (checksum != checksum1) { + return ""; + } + + double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0; + if (neg) { + temperature = -temperature; + } + int humidity = (hum1 * 10.0) + hum2; + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:1A2D;id:" << static_cast(address) + << ";temp:" << std::fixed << std::setprecision(1) << temperature + << ";humidity:" << humidity << ";"; + + return retString.str(); +} + +std::string ProtocolOregon::decode2914(const std::string &data) { + // rain + uint64_t value = TelldusCore::hexTo64l(data); + + uint8_t messageChecksum1 = value & 0xF; + value >>= 4; + uint8_t messageChecksum2 = value & 0xF; + + value >>= 4; + uint8_t totRain1 = value & 0xF; + value >>= 4; + uint8_t totRain2 = value & 0xF; + value >>= 4; + uint8_t totRain3 = value & 0xF; + value >>= 4; + uint8_t totRain4 = value & 0xF; + value >>= 4; + uint8_t totRain5 = value & 0xF; + value >>= 4; + uint8_t totRain6 = value & 0xF; + value >>= 4; + uint8_t rainRate1 = value & 0xF; + value >>= 4; + uint8_t rainRate2 = value & 0xF; + value >>= 4; + uint8_t rainRate3 = value & 0xF; + value >>= 4; + uint8_t rainRate4 = value & 0xF; + + value >>= 4; + uint8_t battery = value & 0xF; // PROBABLY battery + value >>= 4; + uint8_t rollingcode = ((value >> 4) & 0xF) + (value & 0xF); + uint8_t checksum = ((value >> 4) & 0xF) + (value & 0xF); + value >>= 8; + uint8_t channel = value & 0xF; + checksum += totRain1 + totRain2 + totRain3 + totRain4 + totRain5 + totRain6 + rainRate1 + rainRate2 + rainRate3 + rainRate4 + battery + channel + 0x2 + 0x9 + 0x1 + 0x4; + + if (((checksum >> 4) & 0xF) != messageChecksum1 || (checksum & 0xF) != messageChecksum2) { + // checksum error + return ""; + } + + double totRain = ((totRain1 * 100000) + (totRain2 * 10000) + (totRain3 * 1000) + (totRain4 * 100) + (totRain5 * 10) + totRain6)/1000.0*25.4; + double rainRate = ((rainRate1 * 1000) + (rainRate2 * 100) + (rainRate3 * 10) + rainRate4)/100.0*25.4; + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:2914;id:" << static_cast(rollingcode) + << ";raintotal:" << std::fixed << std::setprecision(1) << totRain + << ";rainrate:" << std::fixed << std::setprecision(1) << rainRate << ";"; + return retString.str(); +} + +std::string ProtocolOregon::decodeF824(const std::string &data) { + uint64_t value = TelldusCore::hexTo64l(data); + + uint8_t crcCheck = value & 0xF; // PROBABLY crc + value >>= 4; + uint8_t messageChecksum1 = value & 0xF; + value >>= 4; + uint8_t messageChecksum2 = value & 0xF; + value >>= 4; + uint8_t unknown = value & 0xF; + value >>= 4; + uint8_t hum1 = value & 0xF; + value >>= 4; + uint8_t hum2 = value & 0xF; + value >>= 4; + uint8_t neg = value & 0xF; + value >>= 4; + uint8_t temp1 = value & 0xF; + value >>= 4; + uint8_t temp2 = value & 0xF; + value >>= 4; + uint8_t temp3 = value & 0xF; + value >>= 4; + uint8_t battery = value & 0xF; // PROBABLY battery + value >>= 4; + uint8_t rollingcode = ((value >> 4) & 0xF) + (value & 0xF); + uint8_t checksum = ((value >> 4) & 0xF) + (value & 0xF); + value >>= 8; + uint8_t channel = value & 0xF; + checksum += unknown + hum1 + hum2 + neg + temp1 + temp2 + temp3 + battery + channel + 0xF + 0x8 + 0x2 + 0x4; + + if (((checksum >> 4) & 0xF) != messageChecksum1 || (checksum & 0xF) != messageChecksum2) { + // checksum error + return ""; + } + + double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0; + if (neg) { + temperature = -temperature; + } + int humidity = (hum1 * 10.0) + hum2; + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:F824;id:" << static_cast(rollingcode) + << ";temp:" << std::fixed << std::setprecision(1) << temperature + << ";humidity:" << humidity << ";"; + + return retString.str(); +} + +std::string ProtocolOregon::decodeC844(const std::string &data, const std::wstring &model) { + uint64_t value = TelldusCore::hexTo64l(data); + + uint8_t messageChecksum1 = value & 0xF; + value >>= 4; + uint8_t messageChecksum2 = value & 0xF; + value >>= 4; + uint8_t neg = value & 0xF; + value >>= 4; + uint8_t temp1 = value & 0xF; + value >>= 4; + uint8_t temp2 = value & 0xF; + value >>= 4; + uint8_t temp3 = value & 0xF; + value >>= 4; + uint8_t battery = value & 0xF; // PROBABLY battery + value >>= 4; + uint8_t rollingcode = ((value >> 4) & 0xF) + (value & 0xF); + uint8_t checksum = ((value >> 4) & 0xF) + (value & 0xF); + value >>= 8; + uint8_t channel = value & 0xF; + checksum += neg + temp1 + temp2 + temp3 + battery + channel; + + if (model.compare(L"0xC844") == 0) { + checksum += 0xC + 0x8 + 0x4 + 0x4; + } else { + checksum += 0xE + 0xC + 0x4 + 0x0; + } + + if (((checksum >> 4) & 0xF) != messageChecksum1 || (checksum & 0xF) != messageChecksum2) { + // checksum error + return ""; + } + + double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0; + if (neg) { + temperature = -temperature; + } + + std::stringstream retString; + retString << "class:sensor;protocol:oregon;model:C844;id:" << static_cast(rollingcode) + << ";temp:" << std::fixed << std::setprecision(1) << temperature << ";"; + return retString.str(); +} diff --git a/telldus-core/service/ProtocolOregon.h b/telldus-core/service/ProtocolOregon.h new file mode 100644 index 0000000..cc68fe8 --- /dev/null +++ b/telldus-core/service/ProtocolOregon.h @@ -0,0 +1,27 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLOREGON_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLOREGON_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolOregon : public Protocol { +public: + static std::string decodeData(const ControllerMessage &dataMsg); + +protected: + static std::string decodeEA4C(const std::string &data); + static std::string decode1A2D(const std::string &data); + static std::string decodeF824(const std::string &data); + static std::string decode1984(const std::string &data, const std::wstring &model); + static std::string decode2914(const std::string &data); + static std::string decodeC844(const std::string &data, const std::wstring &model); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLOREGON_H_ diff --git a/telldus-core/service/ProtocolRisingSun.cpp b/telldus-core/service/ProtocolRisingSun.cpp new file mode 100644 index 0000000..c4e9ae0 --- /dev/null +++ b/telldus-core/service/ProtocolRisingSun.cpp @@ -0,0 +1,115 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolRisingSun.h" +#include +#include "common/Strings.h" + +int ProtocolRisingSun::methods() const { + if (TelldusCore::comparei(model(), L"selflearning")) { + return (TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN); + } + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; +} + +std::string ProtocolRisingSun::getStringForMethod(int method, unsigned char data, Controller *controller) { + if (TelldusCore::comparei(model(), L"selflearning")) { + return getStringSelflearning(method); + } + return getStringCodeSwitch(method); +} + +std::string ProtocolRisingSun::getStringSelflearning(int method) { + int intHouse = this->getIntParameter(L"house", 1, 33554432)-1; + int intCode = this->getIntParameter(L"code", 1, 16)-1; + + const char code_on[][7] = { + "110110", "001110", "100110", "010110", + "111001", "000101", "101001", "011001", + "110000", "001000", "100000", "010000", + "111100", "000010", "101100", "011100" + }; + const char code_off[][7] = { + "111110", "000001", "101110", "011110", + "110101", "001101", "100101", "010101", + "111000", "000100", "101000", "011000", + "110010", "001010", "100010", "010010" + }; + const char l = 120; + const char s = 51; + + std::string strCode = "10"; + int code = intCode; + code = (code < 0 ? 0 : code); + code = (code > 15 ? 15 : code); + if (method == TELLSTICK_TURNON) { + strCode.append(code_on[code]); + } else if (method == TELLSTICK_TURNOFF) { + strCode.append(code_off[code]); + } else if (method == TELLSTICK_LEARN) { + strCode.append(code_on[code]); + } else { + return ""; + } + + int house = intHouse; + for(int i = 0; i < 25; ++i) { + if (house & 1) { + strCode.append(1, '1'); + } else { + strCode.append(1, '0'); + } + house >>= 1; + } + + std::string strReturn; + for(unsigned int i = 0; i < strCode.length(); ++i) { + if (strCode[i] == '1') { + strReturn.append(1, l); + strReturn.append(1, s); + } else { + strReturn.append(1, s); + strReturn.append(1, l); + } + } + + std::string prefix = "P"; + prefix.append(1, 5); + if (method == TELLSTICK_LEARN) { + prefix.append("R"); + prefix.append( 1, 50 ); + } + prefix.append("S"); + strReturn.insert(0, prefix); + strReturn.append(1, '+'); + return strReturn; +} + +std::string ProtocolRisingSun::getStringCodeSwitch(int method) { + std::string strReturn = "S.e"; + strReturn.append(getCodeSwitchTuple(this->getIntParameter(L"house", 1, 4)-1)); + strReturn.append(getCodeSwitchTuple(this->getIntParameter(L"unit", 1, 4)-1)); + if (method == TELLSTICK_TURNON) { + strReturn.append("e..ee..ee..ee..e+"); + } else if (method == TELLSTICK_TURNOFF) { + strReturn.append("e..ee..ee..e.e.e+"); + } else { + return ""; + } + return strReturn; +} + +std::string ProtocolRisingSun::getCodeSwitchTuple(int intToConvert) { + std::string strReturn = ""; + for(int i = 0; i < 4; ++i) { + if (i == intToConvert) { + strReturn.append( ".e.e" ); + } else { + strReturn.append( "e..e" ); + } + } + return strReturn; +} diff --git a/telldus-core/service/ProtocolRisingSun.h b/telldus-core/service/ProtocolRisingSun.h new file mode 100644 index 0000000..ef6262e --- /dev/null +++ b/telldus-core/service/ProtocolRisingSun.h @@ -0,0 +1,24 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLRISINGSUN_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLRISINGSUN_H_ + +#include +#include "service/Protocol.h" + +class ProtocolRisingSun : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + +protected: + std::string getStringSelflearning(int method); + std::string getStringCodeSwitch(int method); + static std::string getCodeSwitchTuple(int code); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLRISINGSUN_H_ diff --git a/telldus-core/service/ProtocolSartano.cpp b/telldus-core/service/ProtocolSartano.cpp new file mode 100644 index 0000000..0b33f35 --- /dev/null +++ b/telldus-core/service/ProtocolSartano.cpp @@ -0,0 +1,108 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolSartano.h" +#ifdef _MSC_VER +typedef unsigned __int16 uint16_t; +#else +#include +#endif +#include +#include +#include + +int ProtocolSartano::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; +} + +std::string ProtocolSartano::getStringForMethod(int method, unsigned char, Controller *) { + std::wstring strCode = this->getStringParameter(L"code", L""); + return getStringForCode(strCode, method); +} + +std::string ProtocolSartano::getStringForCode(const std::wstring &strCode, int method) { + std::string strReturn("S"); + + for (size_t i = 0; i < strCode.length(); ++i) { + if (strCode[i] == L'1') { + strReturn.append("$k$k"); + } else { + strReturn.append("$kk$"); + } + } + + if (method == TELLSTICK_TURNON) { + strReturn.append("$k$k$kk$$k+"); + } else if (method == TELLSTICK_TURNOFF) { + strReturn.append("$kk$$k$k$k+"); + } else { + return ""; + } + + return strReturn; +} + +std::string ProtocolSartano::decodeData(const ControllerMessage &dataMsg) { + uint64_t allDataIn; + uint16_t allData = 0; + unsigned int code = 0; + unsigned int method1 = 0; + unsigned int method2 = 0; + unsigned int method = 0; + + allDataIn = dataMsg.getInt64Parameter("data"); + + uint16_t mask = (1<<11); + for(int i = 0; i < 12; ++i) { + allData >>= 1; + if((allDataIn & mask) == 0) { + allData |= (1<<11); + } + mask >>= 1; + } + + code = allData & 0xFFC; + code >>= 2; + + method1 = allData & 0x2; + method1 >>= 1; + + method2 = allData & 0x1; + + if(method1 == 0 && method2 == 1) { + method = 0; // off + } else if(method1 == 1 && method2 == 0) { + method = 1; // on + } else { + return ""; + } + + if(code > 1023) { + // not sartano + return ""; + } + + std::stringstream retString; + retString << "class:command;protocol:sartano;model:codeswitch;code:"; + mask = (1<<9); + for(int i = 0; i < 10; i++) { + if((code & mask) != 0) { + retString << 1; + } else { + retString << 0; + } + mask >>= 1; + } + retString << ";method:"; + + if(method == 0) { + retString << "turnoff;"; + } else { + retString << "turnon;"; + } + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolSartano.h b/telldus-core/service/ProtocolSartano.h new file mode 100644 index 0000000..d644a0e --- /dev/null +++ b/telldus-core/service/ProtocolSartano.h @@ -0,0 +1,24 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLSARTANO_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLSARTANO_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolSartano : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + static std::string decodeData(const ControllerMessage &dataMsg); + +protected: + std::string getStringForCode(const std::wstring &code, int method); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLSARTANO_H_ diff --git a/telldus-core/service/ProtocolScene.cpp b/telldus-core/service/ProtocolScene.cpp new file mode 100644 index 0000000..24ae531 --- /dev/null +++ b/telldus-core/service/ProtocolScene.cpp @@ -0,0 +1,16 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolScene.h" +#include + +int ProtocolScene::methods() const { + return TELLSTICK_EXECUTE; +} + +std::string ProtocolScene::getStringForMethod(int method, unsigned char data, Controller *) { + return ""; +} diff --git a/telldus-core/service/ProtocolScene.h b/telldus-core/service/ProtocolScene.h new file mode 100644 index 0000000..87f80af --- /dev/null +++ b/telldus-core/service/ProtocolScene.h @@ -0,0 +1,22 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLSCENE_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLSCENE_H_ + +#include +#include "service/Protocol.h" + +class ProtocolScene : public Protocol { +public: + virtual int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLSCENE_H_ + + + diff --git a/telldus-core/service/ProtocolSilvanChip.cpp b/telldus-core/service/ProtocolSilvanChip.cpp new file mode 100644 index 0000000..ddc2006 --- /dev/null +++ b/telldus-core/service/ProtocolSilvanChip.cpp @@ -0,0 +1,146 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolSilvanChip.h" +#include +#include "common/Strings.h" + +int ProtocolSilvanChip::methods() const { + if (TelldusCore::comparei(model(), L"kp100")) { + return TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP | TELLSTICK_LEARN; + } else if (TelldusCore::comparei(model(), L"ecosavers")) { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN; + } else if (TelldusCore::comparei(model(), L"displaymatic")) { + return TELLSTICK_UP | TELLSTICK_DOWN | TELLSTICK_STOP; + } + return 0; +} + +std::string ProtocolSilvanChip::getStringForMethod(int method, unsigned char data, Controller *controller) { + if (TelldusCore::comparei(model(), L"kp100")) { + std::string preamble; + preamble.append(1, 100); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 100); + + const std::string one = "\xFF\x1\x2E\x2E"; + const std::string zero = "\x2E\xFF\x1\x2E"; + int button = 0; + if (method == TELLSTICK_UP) { + button = 2; + } else if (method == TELLSTICK_DOWN) { + button = 8; + } else if (method == TELLSTICK_STOP) { + button = 4; + } else if (method == TELLSTICK_LEARN) { + button = 1; + } else { + return ""; + } + return this->getString(preamble, one, zero, button); + } else if (TelldusCore::comparei(model(), L"displaymatic")) { + std::string preamble; + preamble.append(1, 0x25); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 0x25); + const std::string one = "\x69\25"; + const std::string zero = "\x25\x69"; + int button = 0; + if (method == TELLSTICK_UP) { + button = 1; + } else if (method == TELLSTICK_DOWN) { + button = 4; + } else if (method == TELLSTICK_STOP) { + button = 2; + } + return this->getString(preamble, one, zero, button); + } else if (TelldusCore::comparei(model(), L"ecosavers")) { + std::string preamble; + preamble.append(1, 0x25); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 255); + preamble.append(1, 1); + preamble.append(1, 0x25); + const std::string one = "\x69\25"; + const std::string zero = "\x25\x69"; + int intUnit = this->getIntParameter(L"unit", 1, 4); + int button = 0; + if (intUnit == 1) { + button = 7; + } else if (intUnit == 2) { + button = 3; + } else if (intUnit == 3) { + button = 5; + } else if (intUnit == 4) { + button = 6; + } + + if (method == TELLSTICK_TURNON || method == TELLSTICK_LEARN) { + button |= 8; + } + return this->getString(preamble, one, zero, button); + } + return ""; +} + +std::string ProtocolSilvanChip::getString(const std::string &preamble, const std::string &one, const std::string &zero, int button) { + int intHouse = this->getIntParameter(L"house", 1, 1048575); + std::string strReturn = preamble; + + for( int i = 19; i >= 0; --i ) { + if (intHouse & (1 << i)) { + strReturn.append(one); + } else { + strReturn.append(zero); + } + } + + for( int i = 3; i >= 0; --i) { + if (button & (1 << i)) { + strReturn.append(one); + } else { + strReturn.append(zero); + } + } + + strReturn.append(zero); + return strReturn; +} diff --git a/telldus-core/service/ProtocolSilvanChip.h b/telldus-core/service/ProtocolSilvanChip.h new file mode 100644 index 0000000..188a2d4 --- /dev/null +++ b/telldus-core/service/ProtocolSilvanChip.h @@ -0,0 +1,22 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLSILVANCHIP_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLSILVANCHIP_H_ + +#include +#include "service/Protocol.h" + +class ProtocolSilvanChip : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + +protected: + virtual std::string getString(const std::string &preamble, const std::string &one, const std::string &zero, int button); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLSILVANCHIP_H_ diff --git a/telldus-core/service/ProtocolUpm.cpp b/telldus-core/service/ProtocolUpm.cpp new file mode 100644 index 0000000..4e70b79 --- /dev/null +++ b/telldus-core/service/ProtocolUpm.cpp @@ -0,0 +1,78 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolUpm.h" +#include + +int ProtocolUpm::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF | TELLSTICK_LEARN; +} + +std::string ProtocolUpm::getStringForMethod(int method, unsigned char, Controller *) { + const char S = ';'; + const char L = '~'; + const char START[] = {S, 0}; + const char B1[] = {L, S, 0}; + const char B0[] = {S, L, 0}; + // const char BON[] = {S,L,L,S,0}; + // const char BOFF[] = {S,L,S,L,0}; + + int intUnit = this->getIntParameter(L"unit", 1, 4)-1; + std::string strReturn; + + int code = this->getIntParameter(L"house", 0, 4095); + for( size_t i = 0; i < 12; ++i ) { + if (code & 1) { + strReturn.insert(0, B1); + } else { + strReturn.insert(0, B0); + } + code >>= 1; + } + strReturn.insert(0, START); // Startcode, first + + code = 0; + if (method == TELLSTICK_TURNON || method == TELLSTICK_LEARN) { + code += 2; + } else if (method != TELLSTICK_TURNOFF) { + return ""; + } + code <<= 2; + code += intUnit; + + int check1 = 0, check2 = 0; + for( size_t i = 0; i < 6; ++i ) { + if (code & 1) { + if (i % 2 == 0) { + check1++; + } else { + check2++; + } + } + if (code & 1) { + strReturn.append(B1); + } else { + strReturn.append(B0); + } + code >>= 1; + } + + if (check1 % 2 == 0) { + strReturn.append(B0); + } else { + strReturn.append(B1); + } + if (check2 % 2 == 0) { + strReturn.append(B0); + } else { + strReturn.append(B1); + } + + strReturn.insert(0, "S"); + strReturn.append("+"); + return strReturn; +} + diff --git a/telldus-core/service/ProtocolUpm.h b/telldus-core/service/ProtocolUpm.h new file mode 100644 index 0000000..9283a40 --- /dev/null +++ b/telldus-core/service/ProtocolUpm.h @@ -0,0 +1,19 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLUPM_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLUPM_H_ + +#include +#include "service/Protocol.h" + +class ProtocolUpm : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLUPM_H_ diff --git a/telldus-core/service/ProtocolWaveman.cpp b/telldus-core/service/ProtocolWaveman.cpp new file mode 100644 index 0000000..9684934 --- /dev/null +++ b/telldus-core/service/ProtocolWaveman.cpp @@ -0,0 +1,77 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolWaveman.h" +#ifdef _MSC_VER +#else +#include +#endif +#include +#include +#include + +int ProtocolWaveman::lastArctecCodeSwitchWasTurnOff = 0; + +int ProtocolWaveman::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; +} + +std::string ProtocolWaveman::getStringForMethod(int method, unsigned char, Controller *) { + return getStringCodeSwitch(method); +} + +std::string ProtocolWaveman::getOffCode() const { + return "$k$k$k$k$k$k$k$k$k+"; +} + +std::string ProtocolWaveman::decodeData(const ControllerMessage& dataMsg) { + uint64_t allData = 0; + unsigned int house = 0; + unsigned int unit = 0; + unsigned int method = 0; + + allData = dataMsg.getInt64Parameter("data"); + + method = allData & 0xF00; + method >>= 8; + + unit = allData & 0xF0; + unit >>= 4; + unit++; + + house = allData & 0xF; + + if(house > 16 || unit < 1 || unit > 16) { + // not waveman + return ""; + } + + house = house + 'A'; // house from A to P + + if(method != 6 && lastArctecCodeSwitchWasTurnOff == 1) { + lastArctecCodeSwitchWasTurnOff = 0; + return ""; // probably a stray turnon or bell (perhaps: only certain time interval since last, check that it's the same house/unit... Will lose + // one turnon/bell, but it's better than the alternative... + } + + if(method == 6) { + lastArctecCodeSwitchWasTurnOff = 1; + } + + std::stringstream retString; + retString << "class:command;protocol:waveman;model:codeswitch;house:" << static_cast(house); + + if(method == 0) { + retString << ";unit:" << unit << ";method:turnoff;"; + } else if(method == 14) { + retString << ";unit:" << unit << ";method:turnon;"; + } else { + // not waveman + return ""; + } + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolWaveman.h b/telldus-core/service/ProtocolWaveman.h new file mode 100644 index 0000000..a6feb39 --- /dev/null +++ b/telldus-core/service/ProtocolWaveman.h @@ -0,0 +1,26 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLWAVEMAN_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLWAVEMAN_H_ + +#include +#include "service/ProtocolNexa.h" + +class ProtocolWaveman : public ProtocolNexa { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + static std::string decodeData(const ControllerMessage &dataMsg); + +protected: + virtual std::string getOffCode() const; + +private: + static int lastArctecCodeSwitchWasTurnOff; +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLWAVEMAN_H_ diff --git a/telldus-core/service/ProtocolX10.cpp b/telldus-core/service/ProtocolX10.cpp new file mode 100644 index 0000000..f988211 --- /dev/null +++ b/telldus-core/service/ProtocolX10.cpp @@ -0,0 +1,185 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/ProtocolX10.h" +#ifdef _MSC_VER +#else +#include +#endif +#include +#include +#include + +const unsigned char HOUSES[] = {6, 0xE, 2, 0xA, 1, 9, 5, 0xD, 7, 0xF, 3, 0xB, 0, 8, 4, 0xC}; + +int ProtocolX10::methods() const { + return TELLSTICK_TURNON | TELLSTICK_TURNOFF; +} + +std::string ProtocolX10::getStringForMethod(int method, unsigned char data, Controller *controller) { + const unsigned char S = 59, L = 169; + const char B0[] = {static_cast(S), static_cast(S), 0}; + const char B1[] = {static_cast(S), static_cast(L), 0}; + const unsigned char START_CODE[] = {'S', 255, 1, 255, 1, 255, 1, 100, 255, 1, 180, 0}; + const unsigned char STOP_CODE[] = {S, 0}; + + std::string strReturn = reinterpret_cast(START_CODE); + std::string strComplement = ""; + + std::wstring strHouse = getStringParameter(L"house", L"A"); + int intHouse = strHouse[0] - L'A'; + if (intHouse < 0) { + intHouse = 0; + } else if (intHouse > 15) { + intHouse = 15; + } + // Translate it + intHouse = HOUSES[intHouse]; + int intCode = getIntParameter(L"unit", 1, 16)-1; + + for( int i = 0; i < 4; ++i ) { + if (intHouse & 1) { + strReturn.append(B1); + strComplement.append(B0); + } else { + strReturn.append(B0); + strComplement.append(B1); + } + intHouse >>= 1; + } + strReturn.append( B0 ); + strComplement.append( B1 ); + + if (intCode >= 8) { + strReturn.append(B1); + strComplement.append(B0); + } else { + strReturn.append(B0); + strComplement.append(B1); + } + + strReturn.append( B0 ); + strComplement.append( B1 ); + strReturn.append( B0 ); + strComplement.append( B1 ); + + strReturn.append( strComplement ); + strComplement = ""; + + strReturn.append( B0 ); + strComplement.append( B1 ); + + if (intCode >> 2 & 1) { // Bit 2 of intCode + strReturn.append(B1); + strComplement.append(B0); + } else { + strReturn.append(B0); + strComplement.append(B1); + } + + if (method == TELLSTICK_TURNON) { + strReturn.append(B0); + strComplement.append(B1); + } else if (method == TELLSTICK_TURNOFF) { + strReturn.append(B1); + strComplement.append(B0); + } else { + return ""; + } + + if (intCode & 1) { // Bit 0 of intCode + strReturn.append(B1); + strComplement.append(B0); + } else { + strReturn.append(B0); + strComplement.append(B1); + } + + if (intCode >> 1 & 1) { // Bit 1 of intCode + strReturn.append(B1); + strComplement.append(B0); + } else { + strReturn.append(B0); + strComplement.append(B1); + } + + for( int i = 0; i < 3; ++i ) { + strReturn.append( B0 ); + strComplement.append( B1 ); + } + + strReturn.append( strComplement ); + strReturn.append( reinterpret_cast(STOP_CODE) ); + strReturn.append("+"); + return strReturn; +} + +std::string ProtocolX10::decodeData(const ControllerMessage& dataMsg) { + uint64_t intData = 0, currentBit = 31; + bool method = 0; + + intData = dataMsg.getInt64Parameter("data"); + + int unit = 0; + int rawHouse = 0; + for(int i = 0; i < 4; ++i) { + rawHouse >>= 1; + if (checkBit(intData, currentBit--)) { + rawHouse |= 0x8; + } + } + + if (checkBit(intData, currentBit--) != 0) { + return ""; + } + + if (checkBit(intData, currentBit--)) { + unit |= (1<<3); + } + + if (checkBit(intData, currentBit--)) { + return ""; + } + if (checkBit(intData, currentBit--)) { + return ""; + } + + currentBit = 14; + + if (checkBit(intData, currentBit--)) { + unit |= (1<<2); + } + if (checkBit(intData, currentBit--)) { + method = 1; + } + if (checkBit(intData, currentBit--)) { + unit |= (1<<0); + } + if (checkBit(intData, currentBit--)) { + unit |= (1<<1); + } + + int intHouse = 0; + for(int i = 0; i < 16; ++i) { + if (HOUSES[i] == rawHouse) { + intHouse = i; + break; + } + } + + std::stringstream retString; + retString << "class:command;protocol:x10;model:codeswitch;"; + retString << "house:" << static_cast('A' + intHouse); + retString << ";unit:" << unit+1; + retString << ";method:"; + if(method == 0) { + retString << "turnon;"; + } else { + retString << "turnoff;"; + } + + return retString.str(); +} diff --git a/telldus-core/service/ProtocolX10.h b/telldus-core/service/ProtocolX10.h new file mode 100644 index 0000000..c8cf842 --- /dev/null +++ b/telldus-core/service/ProtocolX10.h @@ -0,0 +1,22 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLX10_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLX10_H_ + +#include +#include "service/Protocol.h" +#include "service/ControllerMessage.h" + +class ProtocolX10 : public Protocol { +public: + int methods() const; + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); + + static std::string decodeData(const ControllerMessage &dataMsg); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLX10_H_ diff --git a/telldus-core/service/ProtocolYidong.cpp b/telldus-core/service/ProtocolYidong.cpp new file mode 100644 index 0000000..61b9f33 --- /dev/null +++ b/telldus-core/service/ProtocolYidong.cpp @@ -0,0 +1,31 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include "service/ProtocolYidong.h" + +std::string ProtocolYidong::getStringForMethod(int method, unsigned char, Controller *) { + int intCode = this->getIntParameter(L"unit", 1, 4); + std::wstring strCode = L"111"; + + switch(intCode) { + case 1: + strCode.append(L"0010"); + break; + case 2: + strCode.append(L"0001"); + break; + case 3: + strCode.append(L"0100"); + break; + case 4: + strCode.append(L"1000"); + break; + } + + strCode.append(L"110"); + return getStringForCode(strCode, method); +} diff --git a/telldus-core/service/ProtocolYidong.h b/telldus-core/service/ProtocolYidong.h new file mode 100644 index 0000000..c6b628c --- /dev/null +++ b/telldus-core/service/ProtocolYidong.h @@ -0,0 +1,18 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_PROTOCOLYIDONG_H_ +#define TELLDUS_CORE_SERVICE_PROTOCOLYIDONG_H_ + +#include +#include "service/ProtocolSartano.h" + +class ProtocolYidong : public ProtocolSartano { +public: + virtual std::string getStringForMethod(int method, unsigned char data, Controller *controller); +}; + +#endif // TELLDUS_CORE_SERVICE_PROTOCOLYIDONG_H_ diff --git a/telldus-core/service/Sensor.cpp b/telldus-core/service/Sensor.cpp new file mode 100644 index 0000000..d500265 --- /dev/null +++ b/telldus-core/service/Sensor.cpp @@ -0,0 +1,73 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Sensor.h" +#include +#include +#include "common/common.h" +#include "client/telldus-core.h" + +class Sensor::PrivateData { +public: + std::wstring protocol, model; + int id; + std::map values; + time_t timestamp; +}; + +Sensor::Sensor(const std::wstring &protocol, const std::wstring &model, int id) + :Mutex() { + d = new PrivateData; + d->protocol = protocol; + d->model = model; + d->id = id; +} + +Sensor::~Sensor() { + delete d; +} + +std::wstring Sensor::protocol() const { + return d->protocol; +} + +std::wstring Sensor::model() const { + return d->model; +} + +int Sensor::id() const { + return d->id; +} + +time_t Sensor::timestamp() const { + return d->timestamp; +} + +int Sensor::dataTypes() const { + int retval = 0; + for (std::map::iterator it = d->values.begin(); it != d->values.end(); ++it) { + retval |= (*it).first; + } + return retval; +} + +void Sensor::setValue(int type, const std::string &value, time_t timestamp) { + if (value.substr(0, 2).compare("0x") == 0) { + int intval = strtol(value.c_str(), NULL, 16); + d->values[type] = TelldusCore::intToString(intval); + } else { + d->values[type] = value; + } + d->timestamp = timestamp; +} + +std::string Sensor::value(int type) const { + std::map::const_iterator it = d->values.find(type); + if (it == d->values.end()) { + return ""; + } + return (*it).second; +} diff --git a/telldus-core/service/Sensor.h b/telldus-core/service/Sensor.h new file mode 100644 index 0000000..0ebd633 --- /dev/null +++ b/telldus-core/service/Sensor.h @@ -0,0 +1,33 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_SENSOR_H_ +#define TELLDUS_CORE_SERVICE_SENSOR_H_ + +#include +#include "common/Mutex.h" + +class Sensor : public TelldusCore::Mutex { +public: + Sensor(const std::wstring &protocol, const std::wstring &model, int id); + ~Sensor(); + + std::wstring protocol() const; + std::wstring model() const; + int id() const; + time_t timestamp() const; + + int dataTypes() const; + + void setValue(int type, const std::string &value, time_t timestamp); + std::string value(int type) const; + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_SENSOR_H_ diff --git a/telldus-core/service/Settings.cpp b/telldus-core/service/Settings.cpp new file mode 100644 index 0000000..296c49d --- /dev/null +++ b/telldus-core/service/Settings.cpp @@ -0,0 +1,140 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Settings.h" +#include + +TelldusCore::Mutex Settings::mutex; + +/* +* Get the name of the device +*/ +std::wstring Settings::getName(Node type, int intNodeId) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting(type, intNodeId, L"name", false); +} + +/* +* Set the name of the device +*/ +int Settings::setName(Node type, int intDeviceId, const std::wstring &strNewName) { + TelldusCore::MutexLocker locker(&mutex); + return setStringSetting(type, intDeviceId, L"name", strNewName, false); +} + +/* +* Get the device vendor +*/ +std::wstring Settings::getProtocol(int intDeviceId) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting(Device, intDeviceId, L"protocol", false); +} + +/* +* Set the device vendor +*/ +int Settings::setProtocol(int intDeviceId, const std::wstring &strVendor) { + TelldusCore::MutexLocker locker(&mutex); + return setStringSetting(Device, intDeviceId, L"protocol", strVendor, false); +} + +/* +* Get the device model +*/ +std::wstring Settings::getModel(int intDeviceId) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting(Device, intDeviceId, L"model", false); +} + +/* +* Set the device model +*/ +int Settings::setModel(int intDeviceId, const std::wstring &strModel) { + TelldusCore::MutexLocker locker(&mutex); + return setStringSetting(Device, intDeviceId, L"model", strModel, false); +} + +/* +* Set device argument +*/ +int Settings::setDeviceParameter(int intDeviceId, const std::wstring &strName, const std::wstring &strValue) { + TelldusCore::MutexLocker locker(&mutex); + return setStringSetting(Device, intDeviceId, strName, strValue, true); +} + +/* +* Get device argument +*/ +std::wstring Settings::getDeviceParameter(int intDeviceId, const std::wstring &strName) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting(Device, intDeviceId, strName, true); +} + +/* +* Set preferred controller id +*/ +int Settings::setPreferredControllerId(int intDeviceId, int value) { + TelldusCore::MutexLocker locker(&mutex); + return setIntSetting(Device, intDeviceId, L"controller", value, false); +} + +/* +* Get preferred controller id +*/ +int Settings::getPreferredControllerId(int intDeviceId) { + TelldusCore::MutexLocker locker(&mutex); + return getIntSetting(Device, intDeviceId, L"controller", false); +} + +std::wstring Settings::getControllerSerial(int intControllerId) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting(Controller, intControllerId, L"serial", false); +} + +int Settings::setControllerSerial(int intControllerId, const std::wstring &serial) { + TelldusCore::MutexLocker locker(&mutex); + return setStringSetting(Controller, intControllerId, L"serial", serial, false); +} + +int Settings::getControllerType(int intControllerId) const { + TelldusCore::MutexLocker locker(&mutex); + return getIntSetting(Controller, intControllerId, L"type", false); +} + +int Settings::setControllerType(int intControllerId, int type) { + TelldusCore::MutexLocker locker(&mutex); + return setIntSetting(Controller, intControllerId, L"type", type, false); +} + +std::string Settings::getNodeString(Settings::Node type) const { + if (type == Device) { + return "device"; + } else if (type == Controller) { + return "controller"; + } + return ""; +} + +#ifndef _CONFUSE + +bool Settings::setDeviceState( int intDeviceId, int intDeviceState, const std::wstring &strDeviceStateValue ) { + TelldusCore::MutexLocker locker(&mutex); + bool retval = setIntSetting( Settings::Device, intDeviceId, L"state", intDeviceState, true ); + setStringSetting( Settings::Device, intDeviceId, L"stateValue", strDeviceStateValue, true ); + return retval; +} + +int Settings::getDeviceState( int intDeviceId ) const { + TelldusCore::MutexLocker locker(&mutex); + return getIntSetting( Settings::Device, intDeviceId, L"state", true ); +} + +std::wstring Settings::getDeviceStateValue( int intDeviceId ) const { + TelldusCore::MutexLocker locker(&mutex); + return getStringSetting( Settings::Device, intDeviceId, L"stateValue", true ); +} + +#endif diff --git a/telldus-core/service/Settings.h b/telldus-core/service/Settings.h new file mode 100644 index 0000000..4b26d84 --- /dev/null +++ b/telldus-core/service/Settings.h @@ -0,0 +1,60 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_SETTINGS_H_ +#define TELLDUS_CORE_SERVICE_SETTINGS_H_ + +#include +#include "common/Mutex.h" + +class Settings { +public: + enum Node { Device, Controller }; + + Settings(void); + virtual ~Settings(void); + + std::wstring getSetting(const std::wstring &strName) const; + int getNumberOfNodes(Node type) const; + std::wstring getName(Node type, int intNodeId) const; + int setName(Node type, int intDeviceId, const std::wstring &strNewName); + std::wstring getProtocol(int intDeviceId) const; + int setProtocol(int intDeviceId, const std::wstring &strVendor); + std::wstring getModel(int intDeviceId) const; + int setModel(int intDeviceId, const std::wstring &strModel); + std::wstring getDeviceParameter(int intDeviceId, const std::wstring &strName) const; + int setDeviceParameter(int intDeviceId, const std::wstring &strName, const std::wstring &strValue); + bool setDeviceState( int intDeviceId, int intDeviceState, const std::wstring &strDeviceStateValue ); + int getDeviceState( int intDeviceId ) const; + std::wstring getDeviceStateValue( int intDeviceId ) const; + int getPreferredControllerId(int intDeviceId); + int setPreferredControllerId(int intDeviceId, int value); + + int addNode(Node type); + int getNodeId(Node type, int intDeviceIndex) const; + int removeNode(Node type, int intNodeId); + + std::wstring getControllerSerial(int intControllerId) const; + int setControllerSerial(int intControllerId, const std::wstring &serial); + int getControllerType(int intControllerId) const; + int setControllerType(int intControllerId, int type); + +protected: + std::wstring getStringSetting(Node type, int intNodeId, const std::wstring &name, bool parameter) const; + int setStringSetting(Node type, int intDeviceId, const std::wstring &name, const std::wstring &value, bool parameter); + int getIntSetting(Node type, int intDeviceId, const std::wstring &name, bool parameter) const; + int setIntSetting(Node type, int intDeviceId, const std::wstring &name, int value, bool parameter); + +private: + int getNextNodeId(Node type) const; + std::string getNodeString(Node type) const; + + class PrivateData; + PrivateData *d; + static TelldusCore::Mutex mutex; +}; + +#endif // TELLDUS_CORE_SERVICE_SETTINGS_H_ diff --git a/telldus-core/service/SettingsConfuse.cpp b/telldus-core/service/SettingsConfuse.cpp new file mode 100644 index 0000000..b2e0b32 --- /dev/null +++ b/telldus-core/service/SettingsConfuse.cpp @@ -0,0 +1,451 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include +#include "service/Settings.h" +#include "service/config.h" +#include "client/telldus-core.h" +#include "common/Strings.h" +#include "service/Log.h" + +class Settings::PrivateData { +public: + cfg_t *cfg; + cfg_t *var_cfg; +}; + +bool readConfig(cfg_t **cfg); +bool readVarConfig(cfg_t **cfg); + +const char* CONFIG_FILE = CONFIG_PATH "/tellstick.conf"; +const char* VAR_CONFIG_FILE = VAR_CONFIG_PATH "/telldus-core.conf"; + +/* +* Constructor +*/ +Settings::Settings(void) { + TelldusCore::MutexLocker locker(&mutex); + d = new PrivateData; + readConfig(&d->cfg); + readVarConfig(&d->var_cfg); +} + +/* +* Destructor +*/ +Settings::~Settings(void) { + TelldusCore::MutexLocker locker(&mutex); + if (d->cfg != NULL) { + cfg_free(d->cfg); + } + if (d->var_cfg != NULL) { + cfg_free(d->var_cfg); + } + delete d; +} + +/* +* Return a setting +*/ +std::wstring Settings::getSetting(const std::wstring &strName) const { + TelldusCore::MutexLocker locker(&mutex); + if (d->cfg != NULL) { + std::string setting(cfg_getstr(d->cfg, TelldusCore::wideToString(strName).c_str())); + return TelldusCore::charToWstring(setting.c_str()); + } + return L""; +} + +/* +* Return the number of stored devices +*/ +int Settings::getNumberOfNodes(Node node) const { + TelldusCore::MutexLocker locker(&mutex); + if (d->cfg != NULL) { + if (node == Device) { + return cfg_size(d->cfg, "device"); + } else if (node == Controller) { + return cfg_size(d->cfg, "controller"); + } + } + return 0; +} + +int Settings::getNodeId(Node type, int intDeviceIndex) const { + if (intDeviceIndex >= getNumberOfNodes(type)) { // Out of bounds + return -1; + } + TelldusCore::MutexLocker locker(&mutex); + cfg_t *cfg_node; + if (type == Device) { + cfg_node = cfg_getnsec(d->cfg, "device", intDeviceIndex); + } else if (type == Controller) { + cfg_node = cfg_getnsec(d->cfg, "controller", intDeviceIndex); + } + int id = cfg_getint(cfg_node, "id"); + return id; +} + +/* +* Add a new node +*/ +int Settings::addNode(Node type) { + TelldusCore::MutexLocker locker(&mutex); + int intNodeId = getNextNodeId(type); + + FILE *fp = fopen(CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + cfg_print(d->cfg, fp); // Print the config-file + if (type == Device) { + fprintf(fp, "device {\n id=%d\n}\n", intNodeId); // Print the new device + } else if (type == Controller) { + fprintf(fp, "controller {\n id=%d\n}\n", intNodeId); // Print the new controller + } + fclose(fp); + + // Re-read config-file + cfg_free(d->cfg); + readConfig(&d->cfg); + return intNodeId; +} + +/* +* Get next available node id +*/ +int Settings::getNextNodeId(Node type) const { + // Private, no locks needed + int intNodeId = 0; + cfg_t *cfg_node; + std::string strType; + if (type == Device) { + strType = "device"; + } else if (type == Controller) { + strType = "controller"; + } + for (int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_node = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_node, "id") >= intNodeId) { + intNodeId = cfg_getint(cfg_node, "id"); + } + } + intNodeId++; + return intNodeId; +} + +/* +* Remove a device +*/ +int Settings::removeNode(Node type, int intNodeId) { + TelldusCore::MutexLocker locker(&mutex); + FILE *fp = fopen(CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + + std::string strType = getNodeString(type); + + // Print all opts + for(int i = 0; d->cfg->opts[i].name; i++) { + // Check if it isn't a device section + if (strcmp(d->cfg->opts[i].name, strType.c_str()) != 0) { + cfg_opt_print(&d->cfg->opts[i], fp); + } else { + // Print all sections except the one to remove + cfg_t *cfg_node; + for (int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_node = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_node, "id") != intNodeId) { // This isn't the one to skip + fprintf(fp, "%s {\n", strType.c_str()); + cfg_print_indent(cfg_node, fp, 1); + fprintf(fp, "}\n"); + } + } + } + } + fclose(fp); + + // Re-read config-file + cfg_free(d->cfg); + readConfig(&d->cfg); + + return TELLSTICK_SUCCESS; +} + +bool Settings::setDeviceState( int intDeviceId, int intDeviceState, const std::wstring &strDeviceStateValue ) { + TelldusCore::MutexLocker locker(&mutex); + if (d->var_cfg == 0) { + return false; + } + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->var_cfg, "device"); ++i) { + cfg_device = cfg_getnsec(d->var_cfg, "device", i); + int deviceId = atoi(cfg_title(cfg_device)); + if (deviceId == intDeviceId) { + cfg_setint(cfg_device, "state", intDeviceState); + cfg_setstr(cfg_device, "stateValue", TelldusCore::wideToString(strDeviceStateValue).c_str()); + + FILE *fp = fopen(VAR_CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if(fp == 0) { + return false; + } + cfg_print(d->var_cfg, fp); + fclose(fp); + return true; + } + } + // The device is not found in the file, we must create it manualy... + FILE *fp = fopen(VAR_CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if(!fp) { + fprintf(stderr, "Failed to write state to %s: %s\n", + VAR_CONFIG_FILE, strerror(errno)); + return false; + } + + cfg_print(d->var_cfg, fp); // Print the config-file + fprintf(fp, "device %d {\n}\n", intDeviceId); // Print the new device + fclose(fp); + + // Re-read config-file + cfg_free(d->var_cfg); + readVarConfig(&d->var_cfg); + + return false; +} + +int Settings::getDeviceState( int intDeviceId ) const { + TelldusCore::MutexLocker locker(&mutex); + if (d->var_cfg == 0) { + return false; + } + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->var_cfg, "device"); ++i) { + cfg_device = cfg_getnsec(d->var_cfg, "device", i); + int deviceId = atoi(cfg_title(cfg_device)); + if (deviceId == intDeviceId) { + return cfg_getint(cfg_device, "state"); + } + } + return TELLSTICK_TURNOFF; +} + +std::wstring Settings::getDeviceStateValue( int intDeviceId ) const { + TelldusCore::MutexLocker locker(&mutex); + if (d->var_cfg == 0) { + return L""; + } + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->var_cfg, "device"); ++i) { + cfg_device = cfg_getnsec(d->var_cfg, "device", i); + int deviceId = atoi(cfg_title(cfg_device)); + if (deviceId == intDeviceId) { + std::string value(cfg_getstr(cfg_device, "stateValue")); + return TelldusCore::charToWstring(value.c_str()); + } + } + return L""; +} + +std::wstring Settings::getStringSetting(Node type, int intNodeId, const std::wstring &name, bool parameter) const { + // already locked + if (d->cfg == 0) { + return L""; + } + std::string strType = getNodeString(type); + + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_device = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_device, "id") == intNodeId) { + if (parameter) { + cfg_device = cfg_getsec(cfg_device, "parameters"); + } + std::wstring setting; + char *cSetting = cfg_getstr(cfg_device, TelldusCore::wideToString(name).c_str()); + if (cSetting) { + setting = TelldusCore::charToWstring(cSetting); + } + return setting; + } + } + return L""; +} + +int Settings::setStringSetting(Node type, int intDeviceId, const std::wstring &name, const std::wstring &value, bool parameter) { + // already locked + if (d->cfg == 0) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + std::string strType = getNodeString(type); + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_device = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_device, "id") == intDeviceId) { + std::string newValue = TelldusCore::wideToString(value); + cfg_t *p = cfg_device; + if (parameter) { + p = cfg_getsec(cfg_device, "parameters"); + } + cfg_opt_t *opt = cfg_getopt(p, TelldusCore::wideToString(name).c_str()); + if (!opt) { + return TELLSTICK_ERROR_CONFIG_SYNTAX; + } + cfg_setstr(p, TelldusCore::wideToString(name).c_str(), newValue.c_str()); + FILE *fp = fopen(CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + cfg_print(d->cfg, fp); + fclose(fp); + return TELLSTICK_SUCCESS; + } + } + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; +} + +int Settings::getIntSetting(Node type, int intDeviceId, const std::wstring &name, bool parameter) const { + // already locked + if (d->cfg == 0) { + return 0; + } + std::string strType = getNodeString(type); + cfg_t *cfg_node; + for(int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_node = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_node, "id") == intDeviceId) { + if (parameter) { + cfg_node = cfg_getsec(cfg_node, "parameters"); + } + return cfg_getint(cfg_node, TelldusCore::wideToString(name).c_str()); + } + } + return 0; +} + +int Settings::setIntSetting(Node type, int intDeviceId, const std::wstring &name, int value, bool parameter) { + // already locked + if (d->cfg == 0) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + std::string strType = getNodeString(type); + cfg_t *cfg_device; + for (int i = 0; i < cfg_size(d->cfg, strType.c_str()); ++i) { + cfg_device = cfg_getnsec(d->cfg, strType.c_str(), i); + if (cfg_getint(cfg_device, "id") == intDeviceId) { + if (parameter) { + cfg_t *cfg_parameters = cfg_getsec(cfg_device, "parameters"); + cfg_setint(cfg_parameters, TelldusCore::wideToString(name).c_str(), value); + } else { + cfg_setint(cfg_device, TelldusCore::wideToString(name).c_str(), value); + } + FILE *fp = fopen(CONFIG_FILE, "we"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + return TELLSTICK_ERROR_PERMISSION_DENIED; + } + cfg_print(d->cfg, fp); + fclose(fp); + return TELLSTICK_SUCCESS; + } + } + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; +} + + +bool readConfig(cfg_t **cfg) { + // All the const_cast keywords is to remove the compiler warnings generated by the C++-compiler. + cfg_opt_t controller_opts[] = { + CFG_INT(const_cast("id"), -1, CFGF_NONE), + CFG_STR(const_cast("name"), const_cast(""), CFGF_NONE), + CFG_INT(const_cast("type"), 0, CFGF_NONE), + CFG_STR(const_cast("serial"), const_cast(""), CFGF_NONE), + + CFG_END() + }; + + cfg_opt_t device_parameter_opts[] = { + // Groups + CFG_STR(const_cast("devices"), 0, CFGF_NONE), + + CFG_STR(const_cast("house"), 0, CFGF_NONE), + CFG_STR(const_cast("unit"), 0, CFGF_NONE), + CFG_STR(const_cast("code"), 0, CFGF_NONE), + CFG_STR(const_cast("system"), 0, CFGF_NONE), + CFG_STR(const_cast("units"), 0, CFGF_NONE), + CFG_STR(const_cast("fade"), 0, CFGF_NONE), + + CFG_END() + }; + + cfg_opt_t device_opts[] = { + CFG_INT(const_cast("id"), -1, CFGF_NONE), + CFG_STR(const_cast("name"), const_cast("Unnamed"), CFGF_NONE), + CFG_INT(const_cast("controller"), 0, CFGF_NONE), + CFG_STR(const_cast("protocol"), const_cast("arctech"), CFGF_NONE), + CFG_STR(const_cast("model"), const_cast(""), CFGF_NONE), + CFG_SEC(const_cast("parameters"), device_parameter_opts, CFGF_NONE), + CFG_END() + }; + + cfg_opt_t opts[] = { + CFG_STR(const_cast("user"), const_cast("nobody"), CFGF_NONE), + CFG_STR(const_cast("group"), const_cast("plugdev"), CFGF_NONE), + CFG_STR(const_cast("deviceNode"), const_cast("/dev/tellstick"), CFGF_NONE), + CFG_STR(const_cast("ignoreControllerConfirmation"), const_cast("false"), CFGF_NONE), + CFG_SEC(const_cast("device"), device_opts, CFGF_MULTI), + CFG_SEC(const_cast("controller"), controller_opts, CFGF_MULTI), + CFG_END() + }; + + FILE *fp = fopen(CONFIG_FILE, "re"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + Log::warning("Unable to open config file, %s", CONFIG_FILE); + return false; + } + (*cfg) = cfg_init(opts, CFGF_NOCASE); + if (cfg_parse_fp((*cfg), fp) == CFG_PARSE_ERROR) { + (*cfg) = 0; + fclose(fp); + Log::warning("Unable to parse config file, %s", CONFIG_FILE); + return false; + } + + fclose(fp); + return true; +} + +bool readVarConfig(cfg_t **cfg) { + cfg_opt_t device_opts[] = { + CFG_INT(const_cast("state"), 0, CFGF_NONE), + CFG_STR(const_cast("stateValue"), const_cast(""), CFGF_NONE), + CFG_END() + }; + + cfg_opt_t opts[] = { + CFG_SEC(const_cast("device"), device_opts, CFGF_MULTI | CFGF_TITLE), + CFG_END() + }; + + FILE *fp = fopen(VAR_CONFIG_FILE, "re"); // e for setting O_CLOEXEC on the file handle + if (!fp) { + Log::warning("Unable to open var config file, %s", VAR_CONFIG_FILE); + return false; + } + (*cfg) = cfg_init(opts, CFGF_NOCASE); + if (cfg_parse_fp((*cfg), fp) == CFG_PARSE_ERROR) { + (*cfg) = 0; + fclose(fp); + Log::warning("Unable to parse var config file, %s", VAR_CONFIG_FILE); + return false; + } + + fclose(fp); + return true; +} diff --git a/telldus-core/service/SettingsCoreFoundationPreferences.cpp b/telldus-core/service/SettingsCoreFoundationPreferences.cpp new file mode 100644 index 0000000..207ae92 --- /dev/null +++ b/telldus-core/service/SettingsCoreFoundationPreferences.cpp @@ -0,0 +1,310 @@ +// +// C++ Implementation: telldussettingsconfuse +// +// Description: +// +// +// Author: Micke Prag , (C) 2008 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include "common/Strings.h" +#include "service/Settings.h" +#include "client/telldus-core.h" + +class privateVars { +public: + CFStringRef app_ID; + CFStringRef userName; + CFStringRef hostName; +}; + +class Settings::PrivateData { +public: + CFStringRef app_ID; + CFStringRef userName; + CFStringRef hostName; +}; + +/* +* Constructor +*/ +Settings::Settings(void) { + d = new PrivateData(); + d->app_ID = CFSTR( "com.telldus.core" ); + d->userName = kCFPreferencesAnyUser; + d->hostName = kCFPreferencesCurrentHost; +} + +/* +* Destructor +*/ +Settings::~Settings(void) { + delete d; +} + +/* +* Return a setting +*/ +std::wstring Settings::getSetting(const std::wstring &strName) const { + return L""; +} + +/* +* Return the number of stored devices +*/ +int Settings::getNumberOfNodes(Node type) const { + CFArrayRef cfarray = CFPreferencesCopyKeyList( d->app_ID, d->userName, d->hostName ); + if (!cfarray) { + return 0; + } + CFIndex size = CFArrayGetCount( cfarray ); + int nodes = 0; + for (CFIndex k = 0; k < size; ++k) { + CFStringRef key = (CFStringRef) CFArrayGetValueAtIndex(cfarray, k); + if (!CFStringHasSuffix( key, CFSTR(".name") )) { + continue; + } + if (type == Device && CFStringHasPrefix( key, CFSTR("devices.") )) { + ++nodes; + } else if (type == Controller && CFStringHasPrefix( key, CFSTR("controllers.") )) { + ++nodes; + } + } + CFRelease(cfarray); + return nodes; +} + +int Settings::getNodeId(Node type, int intNodeIndex) const { + CFArrayRef cfarray = CFPreferencesCopyKeyList( d->app_ID, d->userName, d->hostName ); + if (!cfarray) { + return 0; + } + CFIndex size = CFArrayGetCount( cfarray ); + int index = 0; + int id = 0; + for (CFIndex k = 0; k < size; ++k) { + CFStringRef key = (CFStringRef) CFArrayGetValueAtIndex(cfarray, k); + if (!CFStringHasSuffix( key, CFSTR(".name") )) { + continue; + } + if ( type == Device && !CFStringHasPrefix(key, CFSTR("devices.")) ) { + continue; + } + if ( type == Controller && !CFStringHasPrefix(key, CFSTR("controllers.")) ) { + continue; + } + if (index == intNodeIndex) { + CFArrayRef split = CFStringCreateArrayBySeparatingStrings( 0, key, CFSTR(".") ); + if ( !split ) { + continue; + } + if (CFArrayGetCount( split ) != 3 ) { + CFRelease( split ); + continue; + } + + // This code crashes! + // CFNumberRef cfid = (CFNumberRef) CFArrayGetValueAtIndex( split, 1 ); + // if (cfid) + // CFNumberGetValue( cfid, kCFNumberIntType, &id); + + CFStringRef cfid = (CFStringRef) CFArrayGetValueAtIndex( split, 1 ); + char *cp = NULL; + CFIndex size = CFStringGetMaximumSizeForEncoding( CFStringGetLength( cfid ), kCFStringEncodingUTF8) + 1; + cp = reinterpret_cast(malloc(size)); + CFStringGetCString( cfid, cp, size, kCFStringEncodingUTF8 ); + char *newcp = reinterpret_cast(realloc( cp, strlen(cp) + 1)); + if (newcp != NULL) { + cp = newcp; + id = atoi(cp); + } else { + // Should not happen + id = 0; + } + free(cp); + + CFRelease(split); + break; + } + index++; + } + CFRelease(cfarray); + return id; +} + +/* +* Add a new node +*/ +int Settings::addNode(Node type) { + int id = getNextNodeId(type); + setStringSetting( type, id, L"name", L"", false ); // Create a empty name so the node has an entry + if (type == Device) { + // Is there a reason we do this? + setStringSetting( type, id, L"model", L"", false ); + } + return id; +} + +/* +* Get next available node id +*/ +int Settings::getNextNodeId(Node type) const { + int id = 0, max = 0; + int numberOfNodes = getNumberOfNodes(type); + for( int i = 0; i < numberOfNodes; i++) { + id = getNodeId( type, i ); + if (id > max) { + max = id; + } + } + max++; + return max; +} + +/* +* Remove a device +*/ +int Settings::removeNode(Node type, int intNodeId) { + int ret = TELLSTICK_ERROR_DEVICE_NOT_FOUND; + CFStringRef filterKey = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d."), getNodeString(type).c_str(), intNodeId); // The key to search for + + CFArrayRef cfarray = CFPreferencesCopyKeyList( d->app_ID, d->userName, d->hostName ); + if (!cfarray) { + CFRelease(filterKey); + return TELLSTICK_ERROR_UNKNOWN; + } + CFIndex size = CFArrayGetCount( cfarray ); + for (CFIndex k = 0; k < size; ++k) { + CFStringRef key = (CFStringRef) CFArrayGetValueAtIndex(cfarray, k); + if (CFStringHasPrefix( key, filterKey ) ) { + CFPreferencesSetValue( key, NULL, d->app_ID, d->userName, d->hostName ); // Remove the key + ret = TELLSTICK_SUCCESS; + } + } + + CFPreferencesSynchronize( d->app_ID, d->userName, d->hostName ); + CFRelease(cfarray); + CFRelease(filterKey); + return ret; +} + +std::wstring Settings::getStringSetting(Node type, int intNodeId, const std::wstring &wname, bool parameter) const { + std::string name(TelldusCore::wideToString(wname)); + CFStringRef cfname = CFStringCreateWithCString( 0, name.c_str(), kCFStringEncodingUTF8 ); + + CFStringRef key; + if (parameter) { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.parameters.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } else { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } + + CFStringRef value; + + value = (CFStringRef)CFPreferencesCopyValue(key, d->app_ID, d->userName, d->hostName); + if (!value) { + CFRelease(key); + CFRelease(cfname); + return L""; + } + + std::wstring retval; + char *cp = NULL; + CFIndex size = CFStringGetMaximumSizeForEncoding( CFStringGetLength( value ), kCFStringEncodingUTF8) + 1; + cp = reinterpret_cast(malloc(size)); + CFStringGetCString( value, cp, size, kCFStringEncodingUTF8 ); + char *newcp = reinterpret_cast(realloc( cp, strlen(cp) + 1)); + if (newcp != NULL) { + cp = newcp; + retval = TelldusCore::charToWstring(cp); + } else { + // Should not happen + retval = L""; + } + free(cp); + + CFRelease(value); + CFRelease(key); + CFRelease(cfname); + return retval; +} + +int Settings::setStringSetting(Node type, int intNodeId, const std::wstring &wname, const std::wstring &wvalue, bool parameter) { + std::string name(TelldusCore::wideToString(wname)); + std::string value(TelldusCore::wideToString(wvalue)); + CFStringRef cfname = CFStringCreateWithCString( 0, name.c_str(), kCFStringEncodingUTF8 ); + CFStringRef cfvalue = CFStringCreateWithCString( 0, value.c_str(), kCFStringEncodingUTF8 ); + + CFStringRef key; + if (parameter) { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.parameters.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } else { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } + + CFPreferencesSetValue( key, cfvalue, d->app_ID, d->userName, d->hostName ); + CFPreferencesSynchronize( d->app_ID, d->userName, d->hostName ); + CFRelease(key); + CFRelease(cfvalue); + CFRelease(cfname); + return TELLSTICK_SUCCESS; +} + +int Settings::getIntSetting(Node type, int intNodeId, const std::wstring &wname, bool parameter) const { + int retval = 0; + std::string name(TelldusCore::wideToString(wname)); + CFStringRef cfname = CFStringCreateWithCString( 0, name.c_str(), kCFStringEncodingUTF8 ); + CFNumberRef cfvalue; + + CFStringRef key; + if (parameter) { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.parameters.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } else { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } + + cfvalue = (CFNumberRef)CFPreferencesCopyValue(key, d->app_ID, d->userName, d->hostName); + + // If the preference exists, use it. + if (cfvalue) { + try { + // Numbers come out of preferences as CFNumber objects. + if (!CFNumberGetValue(cfvalue, kCFNumberIntType, &retval)) { + retval = 0; + } + CFRelease(cfvalue); + } catch (std::exception e) { + retval = 0; + } + } + + CFRelease(key); + CFRelease(cfname); + return retval; +} + +int Settings::setIntSetting(Node type, int intNodeId, const std::wstring &wname, int value, bool parameter) { + std::string name(TelldusCore::wideToString(wname)); + CFStringRef cfname = CFStringCreateWithCString( 0, name.c_str(), kCFStringEncodingUTF8 ); + CFNumberRef cfvalue = CFNumberCreate(NULL, kCFNumberIntType, &value); + + CFStringRef key; + if (parameter) { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.parameters.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } else { + key = CFStringCreateWithFormat(0, NULL, CFSTR("%ss.%d.%@"), getNodeString(type).c_str(), intNodeId, cfname); + } + + CFPreferencesSetValue( key, cfvalue, d->app_ID, d->userName, d->hostName ); + CFPreferencesSynchronize( d->app_ID, d->userName, d->hostName ); + CFRelease(key); + CFRelease(cfvalue); + CFRelease(cfname); + return TELLSTICK_SUCCESS; +} diff --git a/telldus-core/service/SettingsWinRegistry.cpp b/telldus-core/service/SettingsWinRegistry.cpp new file mode 100644 index 0000000..c27eaaf --- /dev/null +++ b/telldus-core/service/SettingsWinRegistry.cpp @@ -0,0 +1,270 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include + +#include "../client/telldus-core.h" +#include "common/common.h" +#include "common/Strings.h" +#include "service/Settings.h" + +const int intMaxRegValueLength = 1000; + +class Settings::PrivateData { +public: + HKEY rootKey; + std::wstring strRegPath; + std::wstring getNodePath(Settings::Node type); +}; + +std::wstring Settings::PrivateData::getNodePath(Settings::Node type) { + if (type == Settings::Device) { + return L"SOFTWARE\\Telldus\\Devices\\"; + } else if (type == Settings::Controller) { + return L"SOFTWARE\\Telldus\\Controllers\\"; + } + return L""; +} + +/* +* Constructor +*/ +Settings::Settings(void) { + d = new PrivateData(); + d->strRegPath = L"SOFTWARE\\Telldus\\"; + d->rootKey = HKEY_LOCAL_MACHINE; +} + +/* +* Destructor +*/ +Settings::~Settings(void) { + delete d; +} + +/* +* Return the number of stored devices +*/ +int Settings::getNumberOfNodes(Node type) const { + TelldusCore::MutexLocker locker(&mutex); + + int intNumberOfNodes = 0; + HKEY hk; + + LONG lnExists = RegOpenKeyEx(d->rootKey, d->getNodePath(type).c_str(), 0, KEY_QUERY_VALUE, &hk); + + if(lnExists == ERROR_SUCCESS) { + std::wstring strNumSubKeys; + DWORD dNumSubKeys; + RegQueryInfoKey(hk, NULL, NULL, NULL, &dNumSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + + intNumberOfNodes = static_cast(dNumSubKeys); + + RegCloseKey(hk); + } + return intNumberOfNodes; +} + + +int Settings::getNodeId(Node type, int intNodeIndex) const { + TelldusCore::MutexLocker locker(&mutex); + + int intReturn = -1; + HKEY hk; + + LONG lnExists = RegOpenKeyEx(d->rootKey, d->getNodePath(type).c_str(), 0, KEY_READ, &hk); + + if(lnExists == ERROR_SUCCESS) { + wchar_t* Buff = new wchar_t[intMaxRegValueLength]; + DWORD size = intMaxRegValueLength; + if (RegEnumKeyEx(hk, intNodeIndex, (LPWSTR)Buff, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { + intReturn = _wtoi(Buff); + } + + delete[] Buff; + RegCloseKey(hk); + } + return intReturn; +} + +/* +* Add a new node +*/ +int Settings::addNode(Node type) { + TelldusCore::MutexLocker locker(&mutex); + + int intNodeId = -1; + HKEY hk; + + DWORD dwDisp; + intNodeId = getNextNodeId(type); + + std::wstring strCompleteRegPath = d->getNodePath(type); + strCompleteRegPath.append(TelldusCore::intToWstring(intNodeId)); + + if (RegCreateKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, &dwDisp)) { + // fail + intNodeId = -1; + } + + RegCloseKey(hk); + return intNodeId; +} + +/* +* Get next available device id +*/ +int Settings::getNextNodeId(Node type) const { + // Private, no locks needed + int intReturn = -1; + HKEY hk; + DWORD dwDisp; + + LONG lnExists = RegCreateKeyEx(d->rootKey, d->getNodePath(type).c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, &dwDisp); // create or open if already created + + if(lnExists == ERROR_SUCCESS) { + DWORD dwLength = sizeof(DWORD); + DWORD nResult(0); + + LONG lngStatus = RegQueryValueEx(hk, L"LastUsedId", NULL, NULL, reinterpret_cast(&nResult), &dwLength); + + if(lngStatus == ERROR_SUCCESS) { + intReturn = nResult + 1; + } else { + intReturn = 1; + } + DWORD dwVal = intReturn; + RegSetValueEx (hk, L"LastUsedId", 0L, REG_DWORD, (CONST BYTE*) &dwVal, sizeof(DWORD)); + } + RegCloseKey(hk); + return intReturn; +} + +/* +* Remove a device +*/ +int Settings::removeNode(Node type, int intNodeId) { + TelldusCore::MutexLocker locker(&mutex); + + std::wstring strCompleteRegPath = d->getNodePath(type); + strCompleteRegPath.append(TelldusCore::intToWstring(intNodeId)); + + LONG lngSuccess = RegDeleteKey(d->rootKey, strCompleteRegPath.c_str()); + + if(lngSuccess == ERROR_SUCCESS) { + // one of the deletions succeeded + return TELLSTICK_SUCCESS; + } + + return TELLSTICK_ERROR_UNKNOWN; +} + +std::wstring Settings::getSetting(const std::wstring &strName) const { + std::wstring strReturn; + HKEY hk; + + std::wstring strCompleteRegPath = d->strRegPath; + LONG lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_QUERY_VALUE, &hk); + + if(lnExists == ERROR_SUCCESS) { + wchar_t* Buff = new wchar_t[intMaxRegValueLength]; + DWORD dwLength = sizeof(wchar_t)*intMaxRegValueLength; + LONG lngStatus = RegQueryValueEx(hk, strName.c_str(), NULL, NULL, (LPBYTE)Buff, &dwLength); + + if(lngStatus == ERROR_MORE_DATA) { + // The buffer is to small, recreate it + delete[] Buff; + Buff = new wchar_t[dwLength]; + lngStatus = RegQueryValueEx(hk, strName.c_str(), NULL, NULL, (LPBYTE)Buff, &dwLength); + } + if (lngStatus == ERROR_SUCCESS) { + strReturn = Buff; + } + delete[] Buff; + } + RegCloseKey(hk); + return strReturn; +} + +std::wstring Settings::getStringSetting(Node type, int intNodeId, const std::wstring &name, bool parameter) const { + std::wstring strReturn; + HKEY hk; + + std::wstring strCompleteRegPath = d->getNodePath(type); + strCompleteRegPath.append(TelldusCore::intToWstring(intNodeId)); + LONG lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_QUERY_VALUE, &hk); + + if(lnExists == ERROR_SUCCESS) { + wchar_t* Buff = new wchar_t[intMaxRegValueLength]; + DWORD dwLength = sizeof(wchar_t)*intMaxRegValueLength; + LONG lngStatus = RegQueryValueEx(hk, name.c_str(), NULL, NULL, (LPBYTE)Buff, &dwLength); + + if(lngStatus == ERROR_MORE_DATA) { + // The buffer is to small, recreate it + delete[] Buff; + Buff = new wchar_t[dwLength]; + lngStatus = RegQueryValueEx(hk, name.c_str(), NULL, NULL, (LPBYTE)Buff, &dwLength); + } + if (lngStatus == ERROR_SUCCESS) { + strReturn = Buff; + } + delete[] Buff; + } + RegCloseKey(hk); + return strReturn; +} + +int Settings::setStringSetting(Node type, int intNodeId, const std::wstring &name, const std::wstring &value, bool parameter) { + HKEY hk; + int ret = TELLSTICK_SUCCESS; + + std::wstring strNodeId = TelldusCore::intToWstring(intNodeId); + std::wstring strCompleteRegPath = d->getNodePath(type); + strCompleteRegPath.append(strNodeId); + LONG lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_WRITE, &hk); + + if (lnExists == ERROR_SUCCESS) { + int length = static_cast(value.length()) * sizeof(wchar_t); + RegSetValueEx(hk, name.c_str(), 0, REG_SZ, (LPBYTE)value.c_str(), length+1); + } else { + ret = TELLSTICK_ERROR_UNKNOWN; + } + RegCloseKey(hk); + + return ret; +} + +int Settings::getIntSetting(Node type, int intNodeId, const std::wstring &name, bool parameter) const { + int intReturn = 0; + + std::wstring strSetting = getStringSetting(type, intNodeId, name, parameter); + if (strSetting.length()) { + intReturn = static_cast(strSetting[0]); // TODO(micke): do real conversion instead + } + + return intReturn; +} + +int Settings::setIntSetting(Node type, int intNodeId, const std::wstring &name, int value, bool parameter) { + int intReturn = TELLSTICK_ERROR_UNKNOWN; + HKEY hk; + + std::wstring strCompleteRegPath = d->getNodePath(type); + strCompleteRegPath.append(TelldusCore::intToWstring(intNodeId)); + LONG lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_WRITE, &hk); + if (lnExists == ERROR_SUCCESS) { + DWORD dwVal = value; + lnExists = RegSetValueEx (hk, name.c_str(), 0L, REG_DWORD, (CONST BYTE*) &dwVal, sizeof(DWORD)); + if (lnExists == ERROR_SUCCESS) { + intReturn = TELLSTICK_SUCCESS; + } + } + RegCloseKey(hk); + return intReturn; +} diff --git a/telldus-core/service/TellStick.cpp b/telldus-core/service/TellStick.cpp new file mode 100644 index 0000000..3fa0205 --- /dev/null +++ b/telldus-core/service/TellStick.cpp @@ -0,0 +1,79 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/TellStick.h" + +#include +#include +#include + +std::string TellStick::createTPacket( const std::string &msg ) { + std::map times; + std::string data; + int index = 0; + for(size_t i = 0; i < msg.length(); ++i) { + // Search to se if it already exists and get the index + if (times.find(msg.at(i)) == times.end()) { + times[msg.at(i)] = index++; + if (times.size() > 4) { + return ""; + } + } + data.append(1, times[msg.at(i)]); + } + // Reorder the times + unsigned char t0 = 1, t1 = 1, t2 = 1, t3 = 1; + for(std::map::const_iterator it = times.begin(); it != times.end(); ++it) { + if ((*it).second == 0) { + t0 = (*it).first; + } else if ((*it).second == 1) { + t1 = (*it).first; + } else if ((*it).second == 2) { + t2 = (*it).first; + } else if ((*it).second == 3) { + t3 = (*it).first; + } + } + + return TellStick::convertSToT(t0, t1, t2, t3, data); +} + +std::string TellStick::convertSToT( unsigned char t0, unsigned char t1, unsigned char t2, unsigned char t3, const std::string &data ) { + unsigned char dataByte = 0; + std::string retString = "T"; + retString.append(1, t0); + retString.append(1, t1); + retString.append(1, t2); + retString.append(1, t3); + + if (data.length() > 255) { + return ""; + } + unsigned char length = (unsigned char)data.length(); + retString.append(1, length); + + for (size_t i = 0; i < data.length(); ++i) { + dataByte <<= 2; + if (data.at(i) == 1) { + dataByte |= 1; + } else if (data.at(i) == 2) { + dataByte |= 2; + } else if (data.at(i) == 3) { + dataByte |= 3; + } + if ( (i+1) % 4 == 0) { + retString.append(1, dataByte); + dataByte = 0; + } + } + if (data.length() % 4 != 0) { + dataByte <<= (data.length() % 4)*2; + retString.append(1, dataByte); + } + + retString.append("+"); + return retString; +} diff --git a/telldus-core/service/TellStick.h b/telldus-core/service/TellStick.h new file mode 100644 index 0000000..61d844b --- /dev/null +++ b/telldus-core/service/TellStick.h @@ -0,0 +1,54 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_TELLSTICK_H_ +#define TELLDUS_CORE_SERVICE_TELLSTICK_H_ + +#include +#include +#include "service/Controller.h" +#include "common/Thread.h" + +class TellStickDescriptor { +public: + std::string serial; + int vid, pid; +}; + +class TellStick : public Controller, public TelldusCore::Thread { +public: + TellStick(int controllerId, TelldusCore::EventRef event, TelldusCore::EventRef updateEvent, const TellStickDescriptor &d); + virtual ~TellStick(); + + virtual int pid() const; + virtual int vid() const; + virtual std::string serial() const; + + bool isOpen() const; + bool isSameAsDescriptor(const TellStickDescriptor &d) const; + virtual int reset(); + virtual int send( const std::string &message ); + bool stillConnected() const; + + static std::list findAll(); + + static std::string createTPacket( const std::string & ); + static std::string convertSToT( unsigned char t0, unsigned char t1, unsigned char t2, unsigned char t3, const std::string &data ); + +protected: + void processData( const std::string &data ); + void run(); + void setBaud( int baud ); + void stop(); + +private: + static std::list findAllByVIDPID( int vid, int pid ); + + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_TELLSTICK_H_ diff --git a/telldus-core/service/TellStick_ftd2xx.cpp b/telldus-core/service/TellStick_ftd2xx.cpp new file mode 100644 index 0000000..23c5677 --- /dev/null +++ b/telldus-core/service/TellStick_ftd2xx.cpp @@ -0,0 +1,343 @@ +// +// C++ Implementation: TellStick +// +// Description: +// +// +// Author: Micke Prag , (C) 2009 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include "common/common.h" +#include "common/Mutex.h" +#include "common/Strings.h" +#include "service/Log.h" +#include "service/Settings.h" +#include "service/TellStick.h" +#include "../client/telldus-core.h" + +#include "service/ftd2xx.h" + +class TellStick::PrivateData { +public: + bool open, running, ignoreControllerConfirmation; + int vid, pid; + std::string serial, message; + FT_HANDLE ftHandle; + TelldusCore::Mutex mutex; + +#ifdef _WINDOWS + HANDLE eh; +#else +// #include + struct { + pthread_cond_t eCondVar; + pthread_mutex_t eMutex; + } eh; +#endif +}; + +TellStick::TellStick(int controllerId, TelldusCore::EventRef event, TelldusCore::EventRef updateEvent, const TellStickDescriptor &td ) + :Controller(controllerId, event, updateEvent) { + d = new PrivateData; +#ifdef _WINDOWS + d->eh = CreateEvent( NULL, false, false, NULL ); +#else + pthread_mutex_init(&d->eh.eMutex, NULL); + pthread_cond_init(&d->eh.eCondVar, NULL); +#endif + d->open = false; + d->running = false; + d->vid = td.vid; + d->pid = td.pid; + d->serial = td.serial; + Settings set; + d->ignoreControllerConfirmation = set.getSetting(L"ignoreControllerConfirmation") == L"true"; + + char *tempSerial = new char[td.serial.size()+1]; +#ifdef _WINDOWS + strcpy_s(tempSerial, td.serial.size()+1, td.serial.c_str()); +#else + snprintf(tempSerial, td.serial.size()+1, "%s", td.serial.c_str()); + FT_SetVIDPID(td.vid, td.pid); +#endif + Log::notice("Connecting to TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); + FT_STATUS ftStatus = FT_OpenEx(tempSerial, FT_OPEN_BY_SERIAL_NUMBER, &d->ftHandle); + delete[] tempSerial; + if (ftStatus == FT_OK) { + d->open = true; + FT_SetFlowControl(d->ftHandle, FT_FLOW_NONE, 0, 0); + FT_SetTimeouts(d->ftHandle, 5000, 0); + } + + if (d->open) { + if (td.pid == 0x0C31) { + setBaud(9600); + } else { + setBaud(4800); + } + this->start(); + } else { + Log::warning("Failed to open TellStick"); + } +} + +TellStick::~TellStick() { + Log::warning("Disconnected TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); + if (d->running) { + TelldusCore::MutexLocker locker(&d->mutex); + d->running = false; +#ifdef _WINDOWS + SetEvent(d->eh); +#else + pthread_cond_broadcast(&d->eh.eCondVar); +#endif + } + this->wait(); + if (d->open) { + FT_Close(d->ftHandle); + } + delete d; +} + +void TellStick::setBaud( int baud ) { + FT_SetBaudRate(d->ftHandle, baud); +} + +int TellStick::pid() const { + return d->pid; +} + +int TellStick::vid() const { + return d->vid; +} + +std::string TellStick::serial() const { + return d->serial; +} + +bool TellStick::isOpen() const { + return d->open; +} + +bool TellStick::isSameAsDescriptor(const TellStickDescriptor &td) const { + if (td.vid != d->vid) { + return false; + } + if (td.pid != d->pid) { + return false; + } + if (td.serial != d->serial) { + return false; + } + return true; +} + +void TellStick::processData( const std::string &data ) { + for (unsigned int i = 0; i < data.length(); ++i) { + if (data[i] == 13) { // Skip \r + continue; + } else if (data[i] == 10) { // \n found + if (d->message.substr(0, 2).compare("+V") == 0) { + setFirmwareVersion(TelldusCore::charToInteger(d->message.substr(2).c_str())); + } else if (d->message.substr(0, 2).compare("+R") == 0) { + this->publishData(d->message.substr(2)); + } else if(d->message.substr(0, 2).compare("+W") == 0) { + this->decodePublishData(d->message.substr(2)); + } + d->message.clear(); + } else { // Append the character + d->message.append( 1, data[i] ); + } + } +} + +int TellStick::reset() { +#ifndef _WINDOWS + return TELLSTICK_SUCCESS; // nothing to be done on other platforms +#else + int success = FT_CyclePort( d->ftHandle ); + if(success == FT_OK) { + return TELLSTICK_SUCCESS; + } + return TELLSTICK_ERROR_UNKNOWN; +#endif +} + +void TellStick::run() { + d->running = true; + DWORD dwBytesInQueue = 0; + DWORD dwBytesRead = 0; + char *buf = 0; + + // Send a firmware version request + char msg[] = "V+"; + FT_Write(d->ftHandle, msg, (DWORD)strlen(msg), &dwBytesRead); + + while(1) { +#ifdef _WINDOWS + FT_SetEventNotification(d->ftHandle, FT_EVENT_RXCHAR, d->eh); + WaitForSingleObject(d->eh, INFINITE); +#else + FT_SetEventNotification(d->ftHandle, FT_EVENT_RXCHAR, (PVOID)&d->eh); + pthread_mutex_lock(&d->eh.eMutex); + pthread_cond_wait(&d->eh.eCondVar, &d->eh.eMutex); + pthread_mutex_unlock(&d->eh.eMutex); +#endif + + TelldusCore::MutexLocker locker(&d->mutex); + if (!d->running) { + break; + } + FT_GetQueueStatus(d->ftHandle, &dwBytesInQueue); + if (dwBytesInQueue < 1) { + continue; + } + buf = reinterpret_cast(malloc(sizeof(buf) * (dwBytesInQueue+1))); + memset(buf, 0, dwBytesInQueue+1); + FT_Read(d->ftHandle, buf, dwBytesInQueue, &dwBytesRead); + processData( buf ); + free(buf); + } +} + +int TellStick::send( const std::string &strMessage ) { + if (!d->open) { + return TELLSTICK_ERROR_NOT_FOUND; + } + + // This lock does two things + // 1 Prevents two calls from different threads to this function + // 2 Prevents our running thread from receiving the data we are interested in here + TelldusCore::MutexLocker locker(&d->mutex); + + char *tempMessage = reinterpret_cast(malloc(sizeof(std::string::value_type) * (strMessage.size()+1))); +#ifdef _WINDOWS + strcpy_s(tempMessage, strMessage.size()+1, strMessage.c_str()); +#else + snprintf(tempMessage, strMessage.size()+1, "%s", strMessage.c_str()); +#endif + + ULONG bytesWritten, bytesRead; + char in; + FT_STATUS ftStatus; + ftStatus = FT_Write(d->ftHandle, tempMessage, (DWORD)strMessage.length(), &bytesWritten); + free(tempMessage); + + if(ftStatus != FT_OK) { + Log::debug("Broken pipe on send"); + return TELLSTICK_ERROR_BROKEN_PIPE; + } + + if(strMessage.compare("N+") == 0 && ((pid() == 0x0C31 && firmwareVersion() < 5) || (pid() == 0x0C30 && firmwareVersion() < 6))) { + // these firmware versions doesn't implement ack to noop, just check that the noop can be sent correctly + return TELLSTICK_SUCCESS; + } + if(d->ignoreControllerConfirmation) { + // wait for TellStick to finish its air-sending + msleep(1000); + return TELLSTICK_SUCCESS; + } + + while(1) { + ftStatus = FT_Read(d->ftHandle, &in, 1, &bytesRead); + if (ftStatus == FT_OK) { + if (bytesRead == 1) { + if (in == '\n') { + return TELLSTICK_SUCCESS; + } else { + continue; + } + } else { // Timeout + return TELLSTICK_ERROR_COMMUNICATION; + } + } else { // Error + Log::debug("Broken pipe on read"); + return TELLSTICK_ERROR_BROKEN_PIPE; + } + } +} + +bool TellStick::stillConnected() const { + FT_STATUS ftStatus; + DWORD numDevs; + // create the device information list + ftStatus = FT_CreateDeviceInfoList(&numDevs); + if (ftStatus != FT_OK) { + return false; + } + if (numDevs <= 0) { + return false; + } + for (int i = 0; i < static_cast(numDevs); i++) { + FT_HANDLE ftHandleTemp; + DWORD flags; + DWORD id; + DWORD type; + DWORD locId; + char serialNumber[16]; + char description[64]; + // get information for device i + ftStatus = FT_GetDeviceInfoDetail(i, &flags, &type, &id, &locId, serialNumber, description, &ftHandleTemp); + if (ftStatus != FT_OK) { + continue; + } + if (d->serial.compare(serialNumber) == 0) { + return true; + } + } + return false; +} + +std::list TellStick::findAll() { + std::list tellstick = findAllByVIDPID(0x1781, 0x0C30); + + std::list duo = findAllByVIDPID(0x1781, 0x0C31); + for(std::list::const_iterator it = duo.begin(); it != duo.end(); ++it) { + tellstick.push_back(*it); + } + + return tellstick; +} + +std::list TellStick::findAllByVIDPID( int vid, int pid ) { + std::list retval; + + FT_STATUS ftStatus = FT_OK; + DWORD dwNumberOfDevices = 0; + +#ifndef _WINDOWS + FT_SetVIDPID(vid, pid); +#endif + + ftStatus = FT_CreateDeviceInfoList(&dwNumberOfDevices); + if (ftStatus != FT_OK) { + return retval; + } + if (dwNumberOfDevices > 0) { + FT_DEVICE_LIST_INFO_NODE *devInfo; + // allocate storage for list based on dwNumberOfDevices + devInfo = reinterpret_cast(malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*dwNumberOfDevices)); // get the device information list + ftStatus = FT_GetDeviceInfoList(devInfo, &dwNumberOfDevices); + if (ftStatus == FT_OK) { + unsigned int id = (vid << 16) | pid; + for (unsigned int i = 0; i < dwNumberOfDevices; i++) { + if (devInfo[i].ID != id) { + continue; + } + TellStickDescriptor td; + td.vid = vid; + td.pid = pid; + td.serial = devInfo[i].SerialNumber; + retval.push_back(td); + } + } + free(devInfo); + } + return retval; +} diff --git a/telldus-core/service/TellStick_libftdi.cpp b/telldus-core/service/TellStick_libftdi.cpp new file mode 100644 index 0000000..8ce3670 --- /dev/null +++ b/telldus-core/service/TellStick_libftdi.cpp @@ -0,0 +1,327 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include +#include +#include + +#include "service/TellStick.h" +#include "service/Log.h" +#include "service/Settings.h" +#include "client/telldus-core.h" +#include "common/Thread.h" +#include "common/Mutex.h" +#include "common/Strings.h" +#include "common/common.h" + +typedef struct _EVENT_HANDLE { + pthread_cond_t eCondVar; + pthread_mutex_t eMutex; +} EVENT_HANDLE; +typedef int DWORD; + +class TellStick::PrivateData { +public: + bool open, ignoreControllerConfirmation; + int vid, pid; + std::string serial, message; + ftdi_context ftHandle; + EVENT_HANDLE eh; + bool running; + TelldusCore::Mutex mutex; +}; + +TellStick::TellStick(int controllerId, TelldusCore::EventRef event, TelldusCore::EventRef updateEvent, const TellStickDescriptor &td ) + :Controller(controllerId, event, updateEvent) { + d = new PrivateData; + d->open = false; + d->vid = td.vid; + d->pid = td.pid; + d->serial = td.serial; + d->running = false; + + Settings set; + d->ignoreControllerConfirmation = set.getSetting(L"ignoreControllerConfirmation") == L"true"; + + ftdi_init(&d->ftHandle); + ftdi_set_interface(&d->ftHandle, INTERFACE_ANY); + + Log::notice("Connecting to TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); + int ret = ftdi_usb_open_desc(&d->ftHandle, td.vid, td.pid, NULL, td.serial.c_str()); + if (ret < 0) { + ftdi_deinit(&d->ftHandle); + return; + } + d->open = true; + ftdi_usb_reset( &d->ftHandle ); + ftdi_disable_bitbang( &d->ftHandle ); + ftdi_set_latency_timer(&d->ftHandle, 16); + + if (d->open) { + if (td.pid == 0x0C31) { + this->setBaud(9600); + } else { + this->setBaud(4800); + } + this->start(); + } else { + Log::warning("Failed to open TellStick"); + } +} + +TellStick::~TellStick() { + Log::warning("Disconnected TellStick (%X/%X) with serial %s", d->vid, d->pid, d->serial.c_str()); + if (d->running) { + stop(); + } + + if (d->open) { + ftdi_usb_close(&d->ftHandle); + ftdi_deinit(&d->ftHandle); + } + delete d; +} + +int TellStick::pid() const { + return d->pid; +} + +int TellStick::vid() const { + return d->vid; +} + +std::string TellStick::serial() const { + return d->serial; +} + +bool TellStick::isOpen() const { + return d->open; +} + +bool TellStick::isSameAsDescriptor(const TellStickDescriptor &td) const { + if (td.vid != d->vid) { + return false; + } + if (td.pid != d->pid) { + return false; + } + if (td.serial != d->serial) { + return false; + } + return true; +} + +void TellStick::processData( const std::string &data ) { + for (unsigned int i = 0; i < data.length(); ++i) { + if (data[i] == 13) { // Skip \r + continue; + } else if (data[i] == 10) { // \n found + if (d->message.substr(0, 2).compare("+V") == 0) { + setFirmwareVersion(TelldusCore::charToInteger(d->message.substr(2).c_str())); + } else if (d->message.substr(0, 2).compare("+R") == 0) { + this->publishData(d->message.substr(2)); + } else if(d->message.substr(0, 2).compare("+W") == 0) { + this->decodePublishData(d->message.substr(2)); + } + d->message.clear(); + } else { // Append the character + d->message.append( 1, data[i] ); + } + } +} + +int TellStick::reset() { + int success = ftdi_usb_reset( &d->ftHandle ); + if(success < 0) { + return TELLSTICK_ERROR_UNKNOWN; // -1 = FTDI reset failed, -2 = USB device unavailable + } + return TELLSTICK_SUCCESS; +} + +void TellStick::run() { + int dwBytesRead = 0; + unsigned char buf[1024]; // = 0; + + pthread_mutex_init(&d->eh.eMutex, NULL); + pthread_cond_init(&d->eh.eCondVar, NULL); + + { + TelldusCore::MutexLocker locker(&d->mutex); + d->running = true; + } + + // Send a firmware version request + unsigned char msg[] = "V+"; + ftdi_write_data( &d->ftHandle, msg, 2 ); + + while(1) { + // Is there any better way then sleeping between reads? + msleep(100); + TelldusCore::MutexLocker locker(&d->mutex); + if (!d->running) { + break; + } + memset(buf, 0, sizeof(buf)); + dwBytesRead = ftdi_read_data(&d->ftHandle, buf, sizeof(buf)); + if (dwBytesRead < 0) { + // An error occured, avoid flooding by sleeping longer + // Hopefully if will start working again + msleep(1000); // 1s + } + if (dwBytesRead < 1) { + continue; + } + processData( reinterpret_cast(&buf) ); + } +} + +int TellStick::send( const std::string &strMessage ) { + if (!d->open) { + return TELLSTICK_ERROR_NOT_FOUND; + } + + bool c = true; + unsigned char *tempMessage = new unsigned char[strMessage.size()]; + memcpy(tempMessage, strMessage.c_str(), strMessage.size()); + + // This lock does two things + // 1 Prevents two calls from different threads to this function + // 2 Prevents our running thread from receiving the data we are interested in here + TelldusCore::MutexLocker locker(&d->mutex); + + int ret; + ret = ftdi_write_data( &d->ftHandle, tempMessage, strMessage.length() ); + if(ret < 0) { + c = false; + } else if(ret != strMessage.length()) { + Log::debug("Weird send length? retval %i instead of %d\n", ret, static_cast(strMessage.length())); + } + + delete[] tempMessage; + + if(!c) { + Log::debug("Broken pipe on send"); + return TELLSTICK_ERROR_BROKEN_PIPE; + } + + if(strMessage.compare("N+") == 0 && ((pid() == 0x0C31 && firmwareVersion() < 5) || (pid() == 0x0C30 && firmwareVersion() < 6))) { + // these firmware versions doesn't implement ack to noop, just check that the noop can be sent correctly + return TELLSTICK_SUCCESS; + } + + if(d->ignoreControllerConfirmation) { + // allow TellStick to finish its air-sending + msleep(1000); + return TELLSTICK_SUCCESS; + } + + int retrycnt = 250; + unsigned char in; + while(--retrycnt) { + ret = ftdi_read_data( &d->ftHandle, &in, 1); + if (ret > 0) { + if (in == '\n') { + return TELLSTICK_SUCCESS; + } + } else if(ret == 0) { // No data available + usleep(100); + } else { // Error + Log::debug("Broken pipe on read"); + return TELLSTICK_ERROR_BROKEN_PIPE; + } + } + + return TELLSTICK_ERROR_COMMUNICATION; +} + +void TellStick::setBaud(int baud) { + int ret = ftdi_set_baudrate(&d->ftHandle, baud); + if(ret != 0) { + fprintf(stderr, "set Baud failed, retval %i\n", ret); + } +} + +std::list TellStick::findAll() { + std::list tellstick = findAllByVIDPID(0x1781, 0x0C30); + + std::list duo = findAllByVIDPID(0x1781, 0x0C31); + for(std::list::const_iterator it = duo.begin(); it != duo.end(); ++it) { + tellstick.push_back(*it); + } + + return tellstick; +} + +bool TellStick::stillConnected() const { + ftdi_context ftdic; + struct ftdi_device_list *devlist, *curdev; + char serialBuffer[10]; + ftdi_init(&ftdic); + bool found = false; + + int ret = ftdi_usb_find_all(&ftdic, &devlist, d->vid, d->pid); + if (ret > 0) { + for (curdev = devlist; curdev != NULL; curdev = curdev->next) { + ret = ftdi_usb_get_strings(&ftdic, curdev->dev, NULL, 0, NULL, 0, serialBuffer, 10); + if (ret != 0) { + continue; + } + if (d->serial.compare(serialBuffer) == 0) { + found = true; + break; + } + } + } + + ftdi_list_free(&devlist); + ftdi_deinit(&ftdic); + return found; +} + +std::list TellStick::findAllByVIDPID( int vid, int pid ) { + std::list retval; + + ftdi_context ftdic; + struct ftdi_device_list *devlist, *curdev; + char serialBuffer[10]; + ftdi_init(&ftdic); + + int ret = ftdi_usb_find_all(&ftdic, &devlist, vid, pid); + if (ret > 0) { + for (curdev = devlist; curdev != NULL; curdev = curdev->next) { + ret = ftdi_usb_get_strings(&ftdic, curdev->dev, NULL, 0, NULL, 0, serialBuffer, 10); + if (ret != 0) { + continue; + } + TellStickDescriptor td; + td.vid = vid; + td.pid = pid; + td.serial = serialBuffer; + retval.push_back(td); + } + } + ftdi_list_free(&devlist); + ftdi_deinit(&ftdic); + + return retval; +} + +void TellStick::stop() { + if (d->running) { + { + TelldusCore::MutexLocker locker(&d->mutex); + d->running = false; + } + // Unlock the wait-condition + + pthread_cond_broadcast(&d->eh.eCondVar); + } + this->wait(); +} diff --git a/telldus-core/service/TelldusMain.cpp b/telldus-core/service/TelldusMain.cpp new file mode 100644 index 0000000..5d9aa64 --- /dev/null +++ b/telldus-core/service/TelldusMain.cpp @@ -0,0 +1,154 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/TelldusMain.h" +#include +#include +#include + +#include "common/EventHandler.h" +#include "service/ClientCommunicationHandler.h" +#include "service/ConnectionListener.h" +#include "service/ControllerListener.h" +#include "service/ControllerManager.h" +#include "service/DeviceManager.h" +#include "service/EventUpdateManager.h" +#include "service/Log.h" +#include "service/Timer.h" + +class TelldusMain::PrivateData { +public: + TelldusCore::EventHandler eventHandler; + TelldusCore::EventRef stopEvent, controllerChangeEvent; +}; + +TelldusMain::TelldusMain(void) { + d = new PrivateData; + d->stopEvent = d->eventHandler.addEvent(); + d->controllerChangeEvent = d->eventHandler.addEvent(); +} + +TelldusMain::~TelldusMain(void) { + delete d; +} + +void TelldusMain::deviceInsertedOrRemoved(int vid, int pid, bool inserted) { + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = vid; + data->pid = pid; + data->inserted = inserted; + d->controllerChangeEvent->signal(data); +} + +void TelldusMain::resume() { + Log::notice("Came back from suspend"); + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = 0x0; + data->pid = 0x0; + data->inserted = true; + d->controllerChangeEvent->signal(data); +} + +void TelldusMain::suspend() { + Log::notice("Preparing for suspend"); + ControllerChangeEventData *data = new ControllerChangeEventData; + data->vid = 0x0; + data->pid = 0x0; + data->inserted = false; + d->controllerChangeEvent->signal(data); +} + +void TelldusMain::start(void) { + TelldusCore::EventRef clientEvent = d->eventHandler.addEvent(); + TelldusCore::EventRef dataEvent = d->eventHandler.addEvent(); + TelldusCore::EventRef executeActionEvent = d->eventHandler.addEvent(); + TelldusCore::EventRef janitor = d->eventHandler.addEvent(); // Used for regular cleanups + Timer supervisor(janitor); // Tells the janitor to go back to work + supervisor.setInterval(60); // Once every minute + supervisor.start(); + + EventUpdateManager eventUpdateManager; + TelldusCore::EventRef deviceUpdateEvent = eventUpdateManager.retrieveUpdateEvent(); + eventUpdateManager.start(); + ControllerManager controllerManager(dataEvent, deviceUpdateEvent); + DeviceManager deviceManager(&controllerManager, deviceUpdateEvent); + deviceManager.setExecuteActionEvent(executeActionEvent); + + ConnectionListener clientListener(L"TelldusClient", clientEvent); + + std::list clientCommunicationHandlerList; + + TelldusCore::EventRef handlerEvent = d->eventHandler.addEvent(); + +#ifdef _MACOSX + // This is only needed on OS X + ControllerListener controllerListener(d->controllerChangeEvent); +#endif + + + while(!d->stopEvent->isSignaled()) { + if (!d->eventHandler.waitForAny()) { + continue; + } + if (clientEvent->isSignaled()) { + // New client connection + TelldusCore::EventDataRef eventDataRef = clientEvent->takeSignal(); + ConnectionListenerEventData *data = dynamic_cast(eventDataRef.get()); + if (data) { + ClientCommunicationHandler *clientCommunication = new ClientCommunicationHandler(data->socket, handlerEvent, &deviceManager, deviceUpdateEvent, &controllerManager); + clientCommunication->start(); + clientCommunicationHandlerList.push_back(clientCommunication); + } + } + + if (d->controllerChangeEvent->isSignaled()) { + TelldusCore::EventDataRef eventDataRef = d->controllerChangeEvent->takeSignal(); + ControllerChangeEventData *data = dynamic_cast(eventDataRef.get()); + if (data) { + controllerManager.deviceInsertedOrRemoved(data->vid, data->pid, "", data->inserted); + } + } + + if (dataEvent->isSignaled()) { + TelldusCore::EventDataRef eventData = dataEvent->takeSignal(); + ControllerEventData *data = dynamic_cast(eventData.get()); + if (data) { + deviceManager.handleControllerMessage(*data); + } + } + + if (handlerEvent->isSignaled()) { + handlerEvent->popSignal(); + for ( std::list::iterator it = clientCommunicationHandlerList.begin(); it != clientCommunicationHandlerList.end(); ) { + if ((*it)->isDone()) { + delete *it; + it = clientCommunicationHandlerList.erase(it); + + } else { + ++it; + } + } + } + if (executeActionEvent->isSignaled()) { + deviceManager.executeActionEvent(); + } + if (janitor->isSignaled()) { + // Clear all of them if there is more than one + while(janitor->isSignaled()) { + janitor->popSignal(); + } +#ifndef _MACOSX + controllerManager.queryControllerStatus(); +#endif + } + } + + supervisor.stop(); +} + +void TelldusMain::stop(void) { + d->stopEvent->signal(); +} diff --git a/telldus-core/service/TelldusMain.h b/telldus-core/service/TelldusMain.h new file mode 100644 index 0000000..388f933 --- /dev/null +++ b/telldus-core/service/TelldusMain.h @@ -0,0 +1,28 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_TELLDUSMAIN_H_ +#define TELLDUS_CORE_SERVICE_TELLDUSMAIN_H_ + +class TelldusMain { +public: + TelldusMain(void); + ~TelldusMain(void); + + void start(); + void stop(); + + // Thread safe! + void deviceInsertedOrRemoved(int vid, int pid, bool inserted); + void resume(); + void suspend(); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // TELLDUS_CORE_SERVICE_TELLDUSMAIN_H_ diff --git a/telldus-core/service/TelldusWinService_win.cpp b/telldus-core/service/TelldusWinService_win.cpp new file mode 100644 index 0000000..dd405d9 --- /dev/null +++ b/telldus-core/service/TelldusWinService_win.cpp @@ -0,0 +1,181 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/TelldusWinService_win.h" + +#include +#include +#include + +#include "service/Log.h" +#include "service/TelldusMain.h" + +int g_argc; +char **g_argv; + + +static const GUID GUID_DEVINTERFACE_USBRAW = { 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }; + +TelldusWinService::TelldusWinService() + :tm(0) { + tm = new TelldusMain(); +} + +TelldusWinService::~TelldusWinService() { + delete tm; +} + +void TelldusWinService::stop() { + tm->stop(); +} + +DWORD WINAPI TelldusWinService::serviceControlHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData ) { + switch ( controlCode ) { + case SERVICE_CONTROL_INTERROGATE: + SetServiceStatus( serviceStatusHandle, &serviceStatus ); + return NO_ERROR; + + case SERVICE_CONTROL_SHUTDOWN: + case SERVICE_CONTROL_STOP: + stop(); + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus( serviceStatusHandle, &serviceStatus ); + + return NO_ERROR; + case SERVICE_CONTROL_POWEREVENT: + if (dwEventType == PBT_APMSUSPEND) { + tm->suspend(); + } else if (dwEventType == PBT_APMRESUMEAUTOMATIC) { + tm->resume(); + } + return NO_ERROR; + } + return ERROR_CALL_NOT_IMPLEMENTED; +} + +DWORD WINAPI TelldusWinService::deviceNotificationHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData ) { + if (controlCode != SERVICE_CONTROL_DEVICEEVENT) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + + if (dwEventType != DBT_DEVICEARRIVAL && dwEventType != DBT_DEVICEREMOVECOMPLETE) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + + PDEV_BROADCAST_DEVICEINTERFACE pDevInf = reinterpret_cast(lpEventData); + if (!pDevInf) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + + std::wstring name(pDevInf->dbcc_name); + transform(name.begin(), name.end(), name.begin(), toupper); + + // Parse VID + size_t posStart = name.find(L"VID_"); + if (posStart == std::wstring::npos) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + posStart += 4; + size_t posEnd = name.find(L'&', posStart); + if (posEnd == std::wstring::npos) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + std::wstring strVID = name.substr(posStart, posEnd-posStart); + + // Parse PID + posStart = name.find(L"PID_"); + if (posStart == std::wstring::npos) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + posStart += 4; + posEnd = name.find(L'#', posStart); + if (posEnd == std::wstring::npos) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + std::wstring strPID = name.substr(posStart, posEnd-posStart); + + int vid = static_cast(strtol(std::string(strVID.begin(), strVID.end()).c_str(), NULL, 16)); + int pid = static_cast(strtol(std::string(strPID.begin(), strPID.end()).c_str(), NULL, 16)); + + if (dwEventType == DBT_DEVICEARRIVAL) { + tm->deviceInsertedOrRemoved(vid, pid, true); + } else { + tm->deviceInsertedOrRemoved(vid, pid, false); + } + + return NO_ERROR; +} + +DWORD WINAPI TelldusWinService::serviceControlHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext ) { + TelldusWinService *instance = reinterpret_cast(lpContext); + if (!instance) { + return ERROR_CALL_NOT_IMPLEMENTED; + } + if (controlCode == SERVICE_CONTROL_DEVICEEVENT) { + return instance->deviceNotificationHandler(controlCode, dwEventType, lpEventData); + } + return instance->serviceControlHandler(controlCode, dwEventType, lpEventData); +} + +void WINAPI TelldusWinService::serviceMain( DWORD argc, TCHAR* argv[] ) { + TelldusWinService instance; + + // Enable debug if we hade this supplied + for(unsigned int i = 1; i < argc; ++i) { + if (wcscmp(argv[i], L"--debug") == 0) { + Log::setDebug(); + } + } + + // initialise service status + instance.serviceStatus.dwServiceType = SERVICE_WIN32; + instance.serviceStatus.dwCurrentState = SERVICE_STOPPED; + instance.serviceStatus.dwControlsAccepted = 0; + instance.serviceStatus.dwWin32ExitCode = NO_ERROR; + instance.serviceStatus.dwServiceSpecificExitCode = NO_ERROR; + instance.serviceStatus.dwCheckPoint = 0; + instance.serviceStatus.dwWaitHint = 0; + + instance.serviceStatusHandle = RegisterServiceCtrlHandlerEx( serviceName, TelldusWinService::serviceControlHandler, &instance ); + + if ( instance.serviceStatusHandle ) { + // service is starting + instance.serviceStatus.dwCurrentState = SERVICE_START_PENDING; + SetServiceStatus( instance.serviceStatusHandle, &instance.serviceStatus ); + + // running + instance.serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + // Register for power management notification + instance.serviceStatus.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT; + instance.serviceStatus.dwCurrentState = SERVICE_RUNNING; + SetServiceStatus( instance.serviceStatusHandle, &instance.serviceStatus ); + + // Register for device notification + DEV_BROADCAST_DEVICEINTERFACE devInterface; + ZeroMemory( &devInterface, sizeof(devInterface) ); + devInterface.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + devInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + devInterface.dbcc_classguid = GUID_DEVINTERFACE_USBRAW; + HDEVNOTIFY deviceNotificationHandle = RegisterDeviceNotificationW(instance.serviceStatusHandle, &devInterface, DEVICE_NOTIFY_SERVICE_HANDLE); + + Log::notice("TelldusService started"); + + // Start our main-loop + instance.tm->start(); + + Log::notice("TelldusService stopping"); + Log::destroy(); + + // service was stopped + instance.serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + SetServiceStatus( instance.serviceStatusHandle, &instance.serviceStatus ); + + // service is now stopped + instance.serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN); + instance.serviceStatus.dwCurrentState = SERVICE_STOPPED; + SetServiceStatus( instance.serviceStatusHandle, &instance.serviceStatus ); + } +} diff --git a/telldus-core/service/TelldusWinService_win.h b/telldus-core/service/TelldusWinService_win.h new file mode 100644 index 0000000..213cc1f --- /dev/null +++ b/telldus-core/service/TelldusWinService_win.h @@ -0,0 +1,39 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_TELLDUSWINSERVICE_WIN_H_ +#define TELLDUS_CORE_SERVICE_TELLDUSWINSERVICE_WIN_H_ + +#include + +extern int g_argc; +extern char **g_argv; + +class TelldusMain; + +#define serviceName TEXT("TelldusCore") + +class TelldusWinService { +public: + TelldusWinService(); + ~TelldusWinService(); + + static void WINAPI serviceMain( DWORD /*argc*/, TCHAR* /*argv*/[] ); + +protected: + void stop(); + + DWORD WINAPI serviceControlHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData ); + DWORD WINAPI deviceNotificationHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData ); + +private: + TelldusMain *tm; + SERVICE_STATUS serviceStatus; + SERVICE_STATUS_HANDLE serviceStatusHandle; + + static DWORD WINAPI serviceControlHandler( DWORD controlCode, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext ); +}; +#endif // TELLDUS_CORE_SERVICE_TELLDUSWINSERVICE_WIN_H_ diff --git a/telldus-core/service/Timer.cpp b/telldus-core/service/Timer.cpp new file mode 100644 index 0000000..bcce9b1 --- /dev/null +++ b/telldus-core/service/Timer.cpp @@ -0,0 +1,120 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include "service/Timer.h" +#ifdef _WINDOWS +#else +#include +#include +#endif +#include "common/Mutex.h" + +class Timer::PrivateData { +public: + PrivateData() : interval(0), running(false) {} + TelldusCore::EventRef event; + int interval; + bool running; +#ifdef _WINDOWS + HANDLE cond; + TelldusCore::Mutex mutex; +#else + pthread_mutex_t waitMutex; + pthread_cond_t cond; +#endif +}; + +Timer::Timer(TelldusCore::EventRef event) + :TelldusCore::Thread(), d(new PrivateData) { + d->event = event; +#ifdef _WINDOWS + d->cond = CreateEventW(NULL, false, false, NULL); +#else + pthread_cond_init(&d->cond, NULL); + pthread_mutex_init(&d->waitMutex, NULL); +#endif +} + +Timer::~Timer() { + this->stop(); + this->wait(); + +#ifdef _WINDOWS +#else + pthread_mutex_destroy(&d->waitMutex); + pthread_cond_destroy(&d->cond); + delete d; +#endif +} + +void Timer::setInterval(int sec) { + d->interval = sec; +} + +void Timer::stop() { +#ifdef _WINDOWS + TelldusCore::MutexLocker(&d->mutex); + d->running = false; + SetEvent(d->cond); +#else + // Signal event + pthread_mutex_lock(&d->waitMutex); + if (d->running) { + d->running = false; + pthread_cond_signal(&d->cond); + } + pthread_mutex_unlock(&d->waitMutex); +#endif +} + +void Timer::run() { +#ifdef _WINDOWS + int interval = 0; + { + TelldusCore::MutexLocker(&d->mutex); + d->running = true; + interval = d->interval*1000; + } + while(1) { + DWORD retval = WaitForSingleObject(d->cond, interval); + if (retval == WAIT_TIMEOUT) { + d->event->signal(); + } + TelldusCore::MutexLocker(&d->mutex); + if (!d->running) { + break; + } + } +#else + struct timespec ts; + struct timeval tp; + + pthread_mutex_lock(&d->waitMutex); + d->running = true; + pthread_mutex_unlock(&d->waitMutex); + + while(1) { + int rc = 0; + gettimeofday(&tp, NULL); + + ts.tv_sec = tp.tv_sec; + ts.tv_nsec = tp.tv_usec * 1000; + ts.tv_sec += d->interval; + + pthread_mutex_lock( &d->waitMutex ); + if (d->running) { + rc = pthread_cond_timedwait(&d->cond, &d->waitMutex, &ts); + } else { + pthread_mutex_unlock( &d->waitMutex ); + break; + } + pthread_mutex_unlock( &d->waitMutex ); + if (rc == ETIMEDOUT) { + d->event->signal(); + } + } +#endif +} diff --git a/telldus-core/service/Timer.h b/telldus-core/service/Timer.h new file mode 100644 index 0000000..f5f5194 --- /dev/null +++ b/telldus-core/service/Timer.h @@ -0,0 +1,30 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef TELLDUS_CORE_SERVICE_TIMER_H_ +#define TELLDUS_CORE_SERVICE_TIMER_H_ + +#include "common/Event.h" +#include "common/Thread.h" + +class Timer : public TelldusCore::Thread { +public: + explicit Timer(TelldusCore::EventRef event); + virtual ~Timer(); + + void setInterval(int sec); + void stop(); + +protected: + void run(); + +private: + class PrivateData; + PrivateData *d; +}; + + +#endif // TELLDUS_CORE_SERVICE_TIMER_H_ diff --git a/telldus-core/service/com.telldus.service.plist b/telldus-core/service/com.telldus.service.plist new file mode 100644 index 0000000..eb07a78 --- /dev/null +++ b/telldus-core/service/com.telldus.service.plist @@ -0,0 +1,14 @@ + + + + + Label + com.telldus.service + OnDemand + + ProgramArguments + + /Library/Telldus/TelldusService + + + diff --git a/telldus-core/service/config.h.in b/telldus-core/service/config.h.in new file mode 100644 index 0000000..211c884 --- /dev/null +++ b/telldus-core/service/config.h.in @@ -0,0 +1,11 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#define CONFIG_PATH "@SYSCONF_INSTALL_DIR@" +#define VAR_CONFIG_PATH "@STATE_INSTALL_DIR@" + +#ifdef _LINUX +#define SCRIPT_PATH "@SCRIPT_PATH@" +#endif // _LINUX + +#endif // CONFIG_H diff --git a/telldus-core/service/ftd2xx.h b/telldus-core/service/ftd2xx.h new file mode 100644 index 0000000..020aff9 --- /dev/null +++ b/telldus-core/service/ftd2xx.h @@ -0,0 +1,15 @@ + + +#ifdef LIBFTD2XX + #ifdef _WINDOWS + #include + #include "win\ftd2xx.h" + #else + #include "osx/WinTypes.h" + #include "osx/ftd2xx.h" + #endif +#endif + +#ifdef LIBFTDI + #include +#endif diff --git a/telldus-core/service/main_mac.cpp b/telldus-core/service/main_mac.cpp new file mode 100644 index 0000000..1976891 --- /dev/null +++ b/telldus-core/service/main_mac.cpp @@ -0,0 +1,34 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include "service/TelldusMain.h" +#include "service/Log.h" + +TelldusMain tm; + +void shutdownHandler(int onSignal) { + Log::notice("Shutting down"); + tm.stop(); +} + +void sigpipeHandler(int onSignal) { + Log::notice("SIGPIPE received"); +} + +int main(int argc, char **argv) { + /* Install signal traps for proper shutdown */ + signal(SIGTERM, shutdownHandler); + signal(SIGINT, shutdownHandler); + signal(SIGPIPE, sigpipeHandler); + + Log::notice("telldusd started"); + tm.start(); + Log::notice("telldusd stopped gracefully"); + + Log::destroy(); + return 0; +} diff --git a/telldus-core/service/main_unix.cpp b/telldus-core/service/main_unix.cpp new file mode 100644 index 0000000..67d64f7 --- /dev/null +++ b/telldus-core/service/main_unix.cpp @@ -0,0 +1,166 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "service/TelldusMain.h" +#include "service/Settings.h" +#include "common/Strings.h" +#include "service/Log.h" + +#define DAEMON_NAME "telldusd" +#define PID_FILE "/var/run/" DAEMON_NAME ".pid" + +TelldusMain tm; + +void signalHandler(int sig) { + switch(sig) { + case SIGHUP: + Log::warning("Received SIGHUP signal."); + break; + case SIGTERM: + case SIGINT: + Log::warning("Received SIGTERM or SIGINT signal."); + Log::warning("Shutting down"); + tm.stop(); + break; + case SIGPIPE: + break; + default: + Log::warning("Unhandled signal (%d) %s", sig, strsignal(sig)); + break; + } +} + +int main(int argc, char **argv) { + pid_t pid, sid; + FILE *fd; + bool deamonize = true; + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--nodaemon") == 0) { + deamonize = false; + Log::setLogOutput(Log::StdOut); + } else if (strcmp(argv[i], "--debug") == 0) { + Log::setDebug(); + } else if (strcmp(argv[i], "--help") == 0) { + printf("Telldus TellStick background service\n\nStart with --nodaemon to not run as daemon\n\n"); + printf("Report bugs to \n"); + exit(EXIT_SUCCESS); + } else if (strcmp(argv[i], "--version") == 0) { + printf("telldusd " VERSION "\n\n"); + printf("Copyright (C) 2011 Telldus Technologies AB\n\n"); + printf("Written by Micke Prag \n"); + exit(EXIT_SUCCESS); + } else { + printf("Unknown option %s\n", argv[i]); + exit(EXIT_FAILURE); + } + } + + if (deamonize) { + pid = fork(); + if (pid < 0) { + exit(EXIT_FAILURE); + } + if (pid > 0) { + // We are the parent + // Let the parent store the clients pid, + // This way anyone starting the daemon can read the pidfile immediately + + // Record the pid + fd = fopen(PID_FILE, "w"); + if (fd) { + fprintf(fd, "%d\n", pid); + fclose(fd); + } else { + Log::error("Could not open pid file %s: %s", PID_FILE, strerror(errno)); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + } + + Log::notice("%s daemon starting up", DAEMON_NAME); + + if (deamonize) { + /* Change the file mode mask */ + umask(0); + + sid = setsid(); + + if (sid < 0) { + // Something went wrong + printf("Could not set sid\n"); + exit(EXIT_FAILURE); + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + } + + /* Reduce our permissions (change user and group) */ + if (getuid() == 0 || geteuid() == 0) { + Settings settings; + std::string user = TelldusCore::wideToString(settings.getSetting(L"user")); + std::string group = TelldusCore::wideToString(settings.getSetting(L"group")); + + // We use the non threadsafe function getgrnam() here. Since this is startup code + // and no other threads have been started yet. + struct group *grp = getgrnam(group.c_str()); // NOLINT(runtime/threadsafe_fn) + if (grp) { + setgid(grp->gr_gid); + } else { + Log::warning("Group %s could not be found", group.c_str()); + exit(EXIT_FAILURE); + } + // We use the non threadsafe function getpwnam() here. Since this is startup code + // and no other threads have been started yet. + struct passwd *pw = getpwnam(user.c_str()); // NOLINT(runtime/threadsafe_fn) + if (pw) { + setuid( pw->pw_uid ); + } else { + Log::warning("User %s could not be found", user.c_str()); + exit(EXIT_FAILURE); + } + } + + /* Change the current working directory */ + if ((chdir("/")) < 0) { + exit(EXIT_FAILURE); + } + + /* Install signal traps for proper shutdown */ + signal(SIGTERM, signalHandler); + signal(SIGINT, signalHandler); + signal(SIGPIPE, signalHandler); + + // Change so we do not need to call wait() for children processes + struct sigaction sa; + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; + sigemptyset(&sa.sa_mask); + if (sigaction(SIGCHLD, &sa, NULL) == -1) { + Log::error("Could not set the SA_NOCLDWAIT flag. We will be creating zombie processes!"); + } + + tm.start(); + + Log::notice("%s daemon exited", DAEMON_NAME); + // Cleanup + Log::destroy(); + exit(EXIT_SUCCESS); +} diff --git a/telldus-core/service/main_win.cpp b/telldus-core/service/main_win.cpp new file mode 100644 index 0000000..eac33a4 --- /dev/null +++ b/telldus-core/service/main_win.cpp @@ -0,0 +1,25 @@ +// +// Copyright (C) 2012 Telldus Technologies AB. All rights reserved. +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include +#include + +#include "service/TelldusWinService_win.h" +// #include + +int main(int argc, char **argv) { + g_argc = argc; + g_argv = argv; + + SERVICE_TABLE_ENTRY serviceTable[] = { + {serviceName, TelldusWinService::serviceMain }, + { 0, 0 } + }; + + StartServiceCtrlDispatcher( serviceTable ); + + return 0; +} diff --git a/telldus-core/service/osx/WinTypes.h b/telldus-core/service/osx/WinTypes.h new file mode 100755 index 0000000..e8b5b09 --- /dev/null +++ b/telldus-core/service/osx/WinTypes.h @@ -0,0 +1,87 @@ +#ifndef __WINDOWS_TYPES__ +#define __WINDOWS_TYPES__ + +#define MAX_NUM_DEVICES 50 +#include + +typedef unsigned int DWORD; +typedef unsigned int ULONG; +typedef unsigned short USHORT; +typedef unsigned short SHORT; +typedef unsigned char UCHAR; +typedef unsigned short WORD; +typedef unsigned char BYTE; +typedef BYTE *LPBYTE; +typedef unsigned int BOOL; +typedef unsigned char BOOLEAN; +typedef unsigned char CHAR; +typedef BOOL *LPBOOL; +typedef UCHAR *PUCHAR; +typedef const char *LPCSTR; +typedef char *PCHAR; +typedef void *PVOID; +typedef void *HANDLE; +typedef unsigned int LONG; +typedef int INT; +typedef unsigned int UINT; +typedef char *LPSTR; +typedef char *LPTSTR; +typedef DWORD *LPDWORD; +typedef WORD *LPWORD; +typedef ULONG *PULONG; +typedef PVOID LPVOID; +typedef void VOID; +typedef unsigned long long int ULONGLONG; + +typedef struct _OVERLAPPED { + DWORD Internal; + DWORD InternalHigh; + DWORD Offset; + DWORD OffsetHigh; + HANDLE hEvent; +} OVERLAPPED, *LPOVERLAPPED; + +typedef struct _SECURITY_ATTRIBUTES { + DWORD nLength; + LPVOID lpSecurityDescriptor; + BOOL bInheritHandle; +} SECURITY_ATTRIBUTES , *LPSECURITY_ATTRIBUTES; + +typedef struct timeval SYSTEMTIME; +typedef struct timeval FILETIME; +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +// +// Modem Status Flags +// +#define MS_CTS_ON ((DWORD)0x0010) +#define MS_DSR_ON ((DWORD)0x0020) +#define MS_RING_ON ((DWORD)0x0040) +#define MS_RLSD_ON ((DWORD)0x0080) + +// +// Error Flags +// + +#define CE_RXOVER 0x0001 // Receive Queue overflow +#define CE_OVERRUN 0x0002 // Receive Overrun Error +#define CE_RXPARITY 0x0004 // Receive Parity Error +#define CE_FRAME 0x0008 // Receive Framing error +#define CE_BREAK 0x0010 // Break Detected +#define CE_TXFULL 0x0100 // TX Queue is full +#define CE_PTO 0x0200 // LPTx Timeout +#define CE_IOE 0x0400 // LPTx I/O Error +#define CE_DNS 0x0800 // LPTx Device not selected +#define CE_OOP 0x1000 // LPTx Out-Of-Paper +#define CE_MODE 0x8000 // Requested mode unsupported + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE 0xFFFFFFFF +#endif + +#endif diff --git a/telldus-core/service/osx/ftd2xx.h b/telldus-core/service/osx/ftd2xx.h new file mode 100755 index 0000000..04b6527 --- /dev/null +++ b/telldus-core/service/osx/ftd2xx.h @@ -0,0 +1,1008 @@ +/*++ + + Copyright (c) 2001-2011 Future Technology Devices International Limited + + THIS SOFTWARE IS PROVIDED BY FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + OF SUBSTITUTE GOODS OR SERVICES LOSS OF USE, DATA, OR PROFITS OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + FTDI DRIVERS MAY BE USED ONLY IN CONJUNCTION WITH PRODUCTS BASED ON FTDI PARTS. + + FTDI DRIVERS MAY BE DISTRIBUTED IN ANY FORM AS LONG AS LICENSE INFORMATION IS NOT MODIFIED. + + IF A CUSTOM VENDOR ID AND/OR PRODUCT ID OR DESCRIPTION STRING ARE USED, IT IS THE + RESPONSIBILITY OF THE PRODUCT MANUFACTURER TO MAINTAIN ANY CHANGES AND SUBSEQUENT WHQL + RE-CERTIFICATION AS A RESULT OF MAKING THESE CHANGES. + + + Module Name: + + ftd2xx.h + + Abstract: + + Native USB device driver for FTDI FT232x, FT245x, FT2232x and FT4232x devices + FTD2XX library definitions + + Environment: + + user mode + + + --*/ + + +#ifndef FTD2XX_H +#define FTD2XX_H + +#ifndef _WINDOWS +#include +#define WINAPI +#endif + +// The following ifdef block is the standard way of creating macros +// which make exporting from a DLL simpler. All files within this DLL +// are compiled with the FTD2XX_EXPORTS symbol defined on the command line. +// This symbol should not be defined on any project that uses this DLL. +// This way any other project whose source files include this file see +// FTD2XX_API functions as being imported from a DLL, whereas this DLL +// sees symbols defined with this macro as being exported. + +#ifdef FTD2XX_EXPORTS +#define FTD2XX_API __declspec(dllexport) +#else +#define FTD2XX_API __declspec(dllimport) +#endif + +#ifndef _WINDOWS +#include "WinTypes.h" + +#ifdef FTD2XX_API +#undef FTD2XX_API +#define FTD2XX_API +#endif +#endif +typedef struct _EVENT_HANDLE{ + pthread_cond_t eCondVar; + pthread_mutex_t eMutex; + int iVar; +} EVENT_HANDLE; + +typedef PVOID FT_HANDLE; +typedef ULONG FT_STATUS; + +// +// Device status +// +enum { + FT_OK, + FT_INVALID_HANDLE, + FT_DEVICE_NOT_FOUND, + FT_DEVICE_NOT_OPENED, + FT_IO_ERROR, + FT_INSUFFICIENT_RESOURCES, + FT_INVALID_PARAMETER, + FT_INVALID_BAUD_RATE, //7 + + FT_DEVICE_NOT_OPENED_FOR_ERASE, + FT_DEVICE_NOT_OPENED_FOR_WRITE, + FT_FAILED_TO_WRITE_DEVICE, + FT_EEPROM_READ_FAILED, + FT_EEPROM_WRITE_FAILED, + FT_EEPROM_ERASE_FAILED, + FT_EEPROM_NOT_PRESENT, + FT_EEPROM_NOT_PROGRAMMED, + FT_INVALID_ARGS, + FT_NOT_SUPPORTED, + FT_OTHER_ERROR +}; + + +#define FT_SUCCESS(status) ((status) == FT_OK) + +// +// FT_OpenEx Flags +// + +#define FT_OPEN_BY_SERIAL_NUMBER 1 +#define FT_OPEN_BY_DESCRIPTION 2 +#define FT_OPEN_BY_LOCATION 4 + +// +// FT_ListDevices Flags (used in conjunction with FT_OpenEx Flags +// + +#define FT_LIST_NUMBER_ONLY 0x80000000 +#define FT_LIST_BY_INDEX 0x40000000 +#define FT_LIST_ALL 0x20000000 + +#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL) + +// +// Baud Rates +// + +#define FT_BAUD_300 300 +#define FT_BAUD_600 600 +#define FT_BAUD_1200 1200 +#define FT_BAUD_2400 2400 +#define FT_BAUD_4800 4800 +#define FT_BAUD_9600 9600 +#define FT_BAUD_14400 14400 +#define FT_BAUD_19200 19200 +#define FT_BAUD_38400 38400 +#define FT_BAUD_57600 57600 +#define FT_BAUD_115200 115200 +#define FT_BAUD_230400 230400 +#define FT_BAUD_460800 460800 +#define FT_BAUD_921600 921600 + +// +// Word Lengths +// + +#define FT_BITS_8 (UCHAR) 8 +#define FT_BITS_7 (UCHAR) 7 + +// +// Stop Bits +// + +#define FT_STOP_BITS_1 (UCHAR) 0 +#define FT_STOP_BITS_2 (UCHAR) 2 + +// +// Parity +// + +#define FT_PARITY_NONE (UCHAR) 0 +#define FT_PARITY_ODD (UCHAR) 1 +#define FT_PARITY_EVEN (UCHAR) 2 +#define FT_PARITY_MARK (UCHAR) 3 +#define FT_PARITY_SPACE (UCHAR) 4 + +// +// Flow Control +// + +#define FT_FLOW_NONE 0x0000 +#define FT_FLOW_RTS_CTS 0x0100 +#define FT_FLOW_DTR_DSR 0x0200 +#define FT_FLOW_XON_XOFF 0x0400 + +// +// Purge rx and tx buffers +// +#define FT_PURGE_RX 1 +#define FT_PURGE_TX 2 + +// +// Events +// + +typedef void (*PFT_EVENT_HANDLER)(DWORD,DWORD); + +#define FT_EVENT_RXCHAR 1 +#define FT_EVENT_MODEM_STATUS 2 +#define FT_EVENT_LINE_STATUS 4 + +// +// Timeouts +// + +#define FT_DEFAULT_RX_TIMEOUT 300 +#define FT_DEFAULT_TX_TIMEOUT 300 + +// +// Device types +// + +typedef ULONG FT_DEVICE; + +enum { + FT_DEVICE_BM, + FT_DEVICE_AM, + FT_DEVICE_100AX, + FT_DEVICE_UNKNOWN, + FT_DEVICE_2232C, + FT_DEVICE_232R, + FT_DEVICE_2232H, + FT_DEVICE_4232H + }; + +// +// Bit Modes +// + +#define FT_BITMODE_RESET 0x00 +#define FT_BITMODE_ASYNC_BITBANG 0x01 +#define FT_BITMODE_MPSSE 0x02 +#define FT_BITMODE_SYNC_BITBANG 0x04 +#define FT_BITMODE_MCU_HOST 0x08 +#define FT_BITMODE_FAST_SERIAL 0x10 +#define FT_BITMODE_CBUS_BITBANG 0x20 +#define FT_BITMODE_SYNC_FIFO 0x40 + + +#ifdef __cplusplus +extern "C" { +#endif + +FTD2XX_API +FT_STATUS WINAPI FT_Open( + int deviceNumber, + FT_HANDLE *pHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_OpenEx( + PVOID pArg1, + DWORD Flags, + FT_HANDLE *pHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ListDevices( + PVOID pArg1, + PVOID pArg2, + DWORD Flags + ); + +FTD2XX_API +FT_STATUS FT_SetVIDPID( + DWORD dwVID, + DWORD dwPID + ); + +FTD2XX_API +FT_STATUS FT_GetVIDPID( + DWORD * pdwVID, + DWORD * pdwPID + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Close( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Read( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesReturned + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Write( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesWritten + ); + +FTD2XX_API +FT_STATUS WINAPI FT_IoCtl( + FT_HANDLE ftHandle, + DWORD dwIoControlCode, + LPVOID lpInBuf, + DWORD nInBufSize, + LPVOID lpOutBuf, + DWORD nOutBufSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBaudRate( + FT_HANDLE ftHandle, + ULONG BaudRate + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDivisor( + FT_HANDLE ftHandle, + USHORT Divisor + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDataCharacteristics( + FT_HANDLE ftHandle, + UCHAR WordLength, + UCHAR StopBits, + UCHAR Parity + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetFlowControl( + FT_HANDLE ftHandle, + USHORT FlowControl, + UCHAR XonChar, + UCHAR XoffChar + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ResetDevice( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDtr( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ClrDtr( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetRts( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ClrRts( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetModemStatus( + FT_HANDLE ftHandle, + ULONG *pModemStatus + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetChars( + FT_HANDLE ftHandle, + UCHAR EventChar, + UCHAR EventCharEnabled, + UCHAR ErrorChar, + UCHAR ErrorCharEnabled + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Purge( + FT_HANDLE ftHandle, + ULONG Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetTimeouts( + FT_HANDLE ftHandle, + ULONG ReadTimeout, + ULONG WriteTimeout + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetQueueStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetEventNotification( + FT_HANDLE ftHandle, + DWORD Mask, + PVOID Param + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes, + DWORD *dwTxBytes, + DWORD *dwEventDWord + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBreakOn( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBreakOff( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetWaitMask( + FT_HANDLE ftHandle, + DWORD Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_WaitOnMask( + FT_HANDLE ftHandle, + DWORD *Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetEventStatus( + FT_HANDLE ftHandle, + DWORD *dwEventDWord + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ReadEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + LPWORD lpwValue + ); + +FTD2XX_API +FT_STATUS WINAPI FT_WriteEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + WORD wValue + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EraseEE( + FT_HANDLE ftHandle + ); + +// +// structure to hold program data for FT_Program function +// +typedef struct ft_program_data { + + DWORD Signature1; // Header - must be 0x00000000 + DWORD Signature2; // Header - must be 0xffffffff + DWORD Version; // Header - FT_PROGRAM_DATA version + // 0 = original + // 1 = FT2232C extensions + // 2 = FT232R extensions + // 3 = FT2232H extensions + // 4 = FT4232H extensions + WORD VendorId; // 0x0403 + WORD ProductId; // 0x6001 + char *Manufacturer; // "FTDI" + char *ManufacturerId; // "FT" + char *Description; // "USB HS Serial Converter" + char *SerialNumber; // "FT000001" if fixed, or NULL + WORD MaxPower; // 0 < MaxPower <= 500 + WORD PnP; // 0 = disabled, 1 = enabled + WORD SelfPowered; // 0 = bus powered, 1 = self powered + WORD RemoteWakeup; // 0 = not capable, 1 = capable + // + // Rev4 (FT232B) extensions + // + UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise + UCHAR IsoIn; // non-zero if in endpoint is isochronous + UCHAR IsoOut; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable; // non-zero if pull down enabled + UCHAR SerNumEnable; // non-zero if serial number to be used + UCHAR USBVersionEnable; // non-zero if chip uses USBVersion + WORD USBVersion; // BCD (0x0200 => USB2) + // + // Rev 5 (FT2232) extensions + // + UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise + UCHAR IsoInA; // non-zero if in endpoint is isochronous + UCHAR IsoInB; // non-zero if in endpoint is isochronous + UCHAR IsoOutA; // non-zero if out endpoint is isochronous + UCHAR IsoOutB; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable5; // non-zero if pull down enabled + UCHAR SerNumEnable5; // non-zero if serial number to be used + UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion + WORD USBVersion5; // BCD (0x0200 => USB2) + UCHAR AIsHighCurrent; // non-zero if interface is high current + UCHAR BIsHighCurrent; // non-zero if interface is high current + UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer; // non-zero if interface is Fast serial + UCHAR AIsVCP; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer; // non-zero if interface is Fast serial + UCHAR BIsVCP; // non-zero if interface is to use VCP drivers + // + // Rev 6 (FT232R) extensions + // + UCHAR UseExtOsc; // Use External Oscillator + UCHAR HighDriveIOs; // High Drive I/Os + UCHAR EndpointSize; // Endpoint size + UCHAR PullDownEnableR; // non-zero if pull down enabled + UCHAR SerNumEnableR; // non-zero if serial number to be used + UCHAR InvertTXD; // non-zero if invert TXD + UCHAR InvertRXD; // non-zero if invert RXD + UCHAR InvertRTS; // non-zero if invert RTS + UCHAR InvertCTS; // non-zero if invert CTS + UCHAR InvertDTR; // non-zero if invert DTR + UCHAR InvertDSR; // non-zero if invert DSR + UCHAR InvertDCD; // non-zero if invert DCD + UCHAR InvertRI; // non-zero if invert RI + UCHAR Cbus0; // Cbus Mux control + UCHAR Cbus1; // Cbus Mux control + UCHAR Cbus2; // Cbus Mux control + UCHAR Cbus3; // Cbus Mux control + UCHAR Cbus4; // Cbus Mux control + UCHAR RIsD2XX; // non-zero if using D2XX drivers + // + // Rev 7 (FT2232H) Extensions + // + UCHAR PullDownEnable7; // non-zero if pull down enabled + UCHAR SerNumEnable7; // non-zero if serial number to be used + UCHAR ALSlowSlew; // non-zero if AL pins have slow slew + UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR AHSlowSlew; // non-zero if AH pins have slow slew + UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BLSlowSlew; // non-zero if BL pins have slow slew + UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BHSlowSlew; // non-zero if BH pins have slow slew + UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial + UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial + UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs + // + // Rev 8 (FT4232H) Extensions + // + UCHAR PullDownEnable8; // non-zero if pull down enabled + UCHAR SerNumEnable8; // non-zero if serial number to be used + UCHAR ASlowSlew; // non-zero if AL pins have slow slew + UCHAR ASchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BSlowSlew; // non-zero if AH pins have slow slew + UCHAR BSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR CSlowSlew; // non-zero if BL pins have slow slew + UCHAR CSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR DSlowSlew; // non-zero if BH pins have slow slew + UCHAR DSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN + UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN + UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN + UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN + UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers + +} FT_PROGRAM_DATA, *PFT_PROGRAM_DATA; + + +FTD2XX_API +FT_STATUS WINAPI FT_EE_Program( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_ProgramEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA lpData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_Read( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_ReadEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA lpData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UASize( + FT_HANDLE ftHandle, + LPDWORD lpdwSize + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UAWrite( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UARead( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen, + LPDWORD lpdwBytesRead + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetLatencyTimer( + FT_HANDLE ftHandle, + UCHAR ucLatency + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetLatencyTimer( + FT_HANDLE ftHandle, + PUCHAR pucLatency + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBitMode( + FT_HANDLE ftHandle, + UCHAR ucMask, + UCHAR ucEnable + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetBitMode( + FT_HANDLE ftHandle, + PUCHAR pucMode + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetUSBParameters( + FT_HANDLE ftHandle, + ULONG ulInTransferSize, + ULONG ulOutTransferSize + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfo( + FT_HANDLE ftHandle, + FT_DEVICE *lpftDevice, + LPDWORD lpdwID, + PCHAR SerialNumber, + PCHAR Description, + LPVOID Dummy + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceLocId( + FT_HANDLE ftHandle, + LPDWORD lpdwLocId + ); + +FTD2XX_API +FT_STATUS WINAPI FT_StopInTask( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_RestartInTask( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetResetPipeRetryCount( + FT_HANDLE ftHandle, + DWORD dwCount + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ResetPort( + FT_HANDLE ftHandle + ); + + +// +// Win32-type functions +// + +FTD2XX_API +FT_HANDLE WINAPI FT_W32_CreateFile( + LPCSTR lpszName, + DWORD dwAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreate, + DWORD dwAttrsAndFlags, + HANDLE hTemplate + ); + +FTD2XX_API +BOOL WINAPI FT_W32_CloseHandle( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_ReadFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +BOOL WINAPI FT_W32_WriteFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesWritten, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +DWORD WINAPI FT_W32_GetLastError( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetOverlappedResult( + FT_HANDLE ftHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpdwBytesTransferred, + BOOL bWait + ); + +FTD2XX_API +BOOL WINAPI FT_W32_CancelIo( + FT_HANDLE ftHandle + ); + + +// +// Win32 COMM API type functions +// +typedef struct _FTCOMSTAT { + DWORD fCtsHold : 1; + DWORD fDsrHold : 1; + DWORD fRlsdHold : 1; + DWORD fXoffHold : 1; + DWORD fXoffSent : 1; + DWORD fEof : 1; + DWORD fTxim : 1; + DWORD fReserved : 25; + DWORD cbInQue; + DWORD cbOutQue; +} FTCOMSTAT, *LPFTCOMSTAT; + +typedef struct _FTDCB { + DWORD DCBlength; /* sizeof(FTDCB) */ + DWORD BaudRate; /* Baudrate at which running */ + DWORD fBinary: 1; /* Binary Mode (skip EOF check) */ + DWORD fParity: 1; /* Enable parity checking */ + DWORD fOutxCtsFlow:1; /* CTS handshaking on output */ + DWORD fOutxDsrFlow:1; /* DSR handshaking on output */ + DWORD fDtrControl:2; /* DTR Flow control */ + DWORD fDsrSensitivity:1; /* DSR Sensitivity */ + DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */ + DWORD fOutX: 1; /* Enable output X-ON/X-OFF */ + DWORD fInX: 1; /* Enable input X-ON/X-OFF */ + DWORD fErrorChar: 1; /* Enable Err Replacement */ + DWORD fNull: 1; /* Enable Null stripping */ + DWORD fRtsControl:2; /* Rts Flow control */ + DWORD fAbortOnError:1; /* Abort all reads and writes on Error */ + DWORD fDummy2:17; /* Reserved */ + WORD wReserved; /* Not currently used */ + WORD XonLim; /* Transmit X-ON threshold */ + WORD XoffLim; /* Transmit X-OFF threshold */ + BYTE ByteSize; /* Number of bits/byte, 4-8 */ + BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ + BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ + char XonChar; /* Tx and Rx X-ON character */ + char XoffChar; /* Tx and Rx X-OFF character */ + char ErrorChar; /* Error replacement char */ + char EofChar; /* End of Input character */ + char EvtChar; /* Received Event character */ + WORD wReserved1; /* Fill for now. */ +} FTDCB, *LPFTDCB; + +typedef struct _FTTIMEOUTS { + DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ + DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ + DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ +} FTTIMEOUTS,*LPFTTIMEOUTS; + + +FTD2XX_API +BOOL WINAPI FT_W32_ClearCommBreak( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_ClearCommError( + FT_HANDLE ftHandle, + LPDWORD lpdwErrors, + LPFTCOMSTAT lpftComstat + ); + +FTD2XX_API +BOOL WINAPI FT_W32_EscapeCommFunction( + FT_HANDLE ftHandle, + DWORD dwFunc + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommModemStatus( + FT_HANDLE ftHandle, + LPDWORD lpdwModemStatus + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + +FTD2XX_API +BOOL WINAPI FT_W32_PurgeComm( + FT_HANDLE ftHandle, + DWORD dwMask + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommBreak( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommMask( + FT_HANDLE ftHandle, + ULONG ulEventMask + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetupComm( + FT_HANDLE ftHandle, + DWORD dwReadBufferSize, + DWORD dwWriteBufferSize + ); + +FTD2XX_API +BOOL WINAPI FT_W32_WaitCommEvent( + FT_HANDLE ftHandle, + PULONG pulEvent, + LPOVERLAPPED lpOverlapped + ); + +// +// Device information +// + +typedef struct _ft_device_list_info_node { + ULONG Flags; + ULONG Type; + ULONG ID; + DWORD LocId; + char SerialNumber[16]; + char Description[64]; + FT_HANDLE ftHandle; +} FT_DEVICE_LIST_INFO_NODE; + +// Device information flags +enum { + FT_FLAGS_OPENED = 1, + FT_FLAGS_HISPEED = 2 +}; + +FTD2XX_API +FT_STATUS WINAPI FT_CreateDeviceInfoList( + LPDWORD lpdwNumDevs + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfoList( + FT_DEVICE_LIST_INFO_NODE *pDest, + LPDWORD lpdwNumDevs + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfoDetail( + DWORD dwIndex, + LPDWORD lpdwFlags, + LPDWORD lpdwType, + LPDWORD lpdwID, + LPDWORD lpdwLocId, + LPVOID lpSerialNumber, + LPVOID lpDescription, + FT_HANDLE *pftHandle + ); + + +// +// Version information +// + +FTD2XX_API +FT_STATUS WINAPI FT_GetDriverVersion( + FT_HANDLE ftHandle, + LPDWORD lpdwVersion + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetLibraryVersion( + LPDWORD lpdwVersion + ); + +// +// Events +// + +#define EV_RXCHAR 0x0001 // Any Character received +#define EV_RXFLAG 0x0002 // Received certain character +#define EV_TXEMPTY 0x0004 // Transmitt Queue Empty +#define EV_CTS 0x0008 // CTS changed state +#define EV_DSR 0x0010 // DSR changed state +#define EV_RLSD 0x0020 // RLSD changed state +#define EV_BREAK 0x0040 // BREAK received +#define EV_ERR 0x0080 // Line status error occurred +#define EV_RING 0x0100 // Ring signal detected +#define EV_PERR 0x0200 // Printer error occured +#define EV_RX80FULL 0x0400 // Receive buffer is 80 percent full +#define EV_EVENT1 0x0800 // Provider specific event 1 +#define EV_EVENT2 0x1000 // Provider specific event 2 + +// +// Escape Functions +// + +#define SETXOFF 1 // Simulate XOFF received +#define SETXON 2 // Simulate XON received +#define SETRTS 3 // Set RTS high +#define CLRRTS 4 // Set RTS low +#define SETDTR 5 // Set DTR high +#define CLRDTR 6 // Set DTR low +#define RESETDEV 7 // Reset device if possible +#define SETBREAK 8 // Set the device break line. +#define CLRBREAK 9 // Clear the device break line. + +// +// PURGE function flags. +// +#define PURGE_TXABORT 0x0001 // Kill the pending/current writes to the comm port. +#define PURGE_RXABORT 0x0002 // Kill the pending/current reads to the comm port. +#define PURGE_TXCLEAR 0x0004 // Kill the transmit queue if there. +#define PURGE_RXCLEAR 0x0008 // Kill the typeahead buffer if there. + +#ifdef __cplusplus +} +#endif + + +#endif /* FTD2XX_H */ + + + + + + diff --git a/telldus-core/service/telldus-core.conf b/telldus-core/service/telldus-core.conf new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/telldus-core/service/telldus-core.conf @@ -0,0 +1 @@ + diff --git a/telldus-core/service/tellstick.conf b/telldus-core/service/tellstick.conf new file mode 100644 index 0000000..fed9c66 --- /dev/null +++ b/telldus-core/service/tellstick.conf @@ -0,0 +1,13 @@ +user = "nobody" +group = "plugdev" +ignoreControllerConfirmation = "false" +device { + id = 1 + name = "Example device" + protocol = "arctech" + model = "codeswitch" + parameters { + house = "A" + unit = "1" + } +} diff --git a/telldus-core/service/win/ftd2xx.h b/telldus-core/service/win/ftd2xx.h new file mode 100644 index 0000000..d5bc3bf --- /dev/null +++ b/telldus-core/service/win/ftd2xx.h @@ -0,0 +1,963 @@ +/*++ + +Copyright (c) 2001-2007 Future Technology Devices International Ltd. + +Module Name: + + ftd2xx.h + +Abstract: + + Native USB device driver for FTDI FT8U232/245 + FTD2XX library definitions + +Environment: + + kernel & user mode + +Revision History: + + 13/03/01 awm Created. + 13/01/03 awm Added device information support. + 19/03/03 awm Added FT_W32_CancelIo. + 12/06/03 awm Added FT_StopInTask and FT_RestartInTask. + 18/09/03 awm Added FT_SetResetPipeRetryCount. + 10/10/03 awm Added FT_ResetPort. + 23/01/04 awm Added support for open-by-location. + 16/03/04 awm Added support for FT2232C. + 23/09/04 awm Added support for FT232R. + 20/10/04 awm Added FT_CyclePort. + 18/01/05 awm Added FT_DEVICE_LIST_INFO_NODE type. + 11/02/05 awm Added LocId to FT_DEVICE_LIST_INFO_NODE. + 25/08/05 awm Added FT_SetDeadmanTimeout. + 02/12/05 awm Removed obsolete references. + 05/12/05 awm Added FT_GetVersion, FT_GetVersionEx. + 08/09/06 awm Added FT_W32_GetCommMask. + 11/09/06 awm Added FT_Rescan. + 11/07/07 awm Added support for FT2232H and FT4232H. + 10/08/07 awm Added flags definitions. + 21/11/07 mja Added FT_GetComPortNumber. + 05/06/08 mja Added EEPROM extensions for FT2232H. + + +--*/ + + +#ifndef FTD2XX_H +#define FTD2XX_H + +// The following ifdef block is the standard way of creating macros +// which make exporting from a DLL simpler. All files within this DLL +// are compiled with the FTD2XX_EXPORTS symbol defined on the command line. +// This symbol should not be defined on any project that uses this DLL. +// This way any other project whose source files include this file see +// FTD2XX_API functions as being imported from a DLL, whereas this DLL +// sees symbols defined with this macro as being exported. + +#ifdef FTD2XX_EXPORTS +#define FTD2XX_API __declspec(dllexport) +#else +#define FTD2XX_API __declspec(dllimport) +#endif + + +typedef PVOID FT_HANDLE; +typedef ULONG FT_STATUS; + +// +// Device status +// +enum { + FT_OK, + FT_INVALID_HANDLE, + FT_DEVICE_NOT_FOUND, + FT_DEVICE_NOT_OPENED, + FT_IO_ERROR, + FT_INSUFFICIENT_RESOURCES, + FT_INVALID_PARAMETER, + FT_INVALID_BAUD_RATE, + + FT_DEVICE_NOT_OPENED_FOR_ERASE, + FT_DEVICE_NOT_OPENED_FOR_WRITE, + FT_FAILED_TO_WRITE_DEVICE, + FT_EEPROM_READ_FAILED, + FT_EEPROM_WRITE_FAILED, + FT_EEPROM_ERASE_FAILED, + FT_EEPROM_NOT_PRESENT, + FT_EEPROM_NOT_PROGRAMMED, + FT_INVALID_ARGS, + FT_NOT_SUPPORTED, + FT_OTHER_ERROR, + FT_DEVICE_LIST_NOT_READY, +}; + + +#define FT_SUCCESS(status) ((status) == FT_OK) + +// +// FT_OpenEx Flags +// + +#define FT_OPEN_BY_SERIAL_NUMBER 1 +#define FT_OPEN_BY_DESCRIPTION 2 +#define FT_OPEN_BY_LOCATION 4 + +// +// FT_ListDevices Flags (used in conjunction with FT_OpenEx Flags +// + +#define FT_LIST_NUMBER_ONLY 0x80000000 +#define FT_LIST_BY_INDEX 0x40000000 +#define FT_LIST_ALL 0x20000000 + +#define FT_LIST_MASK (FT_LIST_NUMBER_ONLY|FT_LIST_BY_INDEX|FT_LIST_ALL) + +// +// Baud Rates +// + +#define FT_BAUD_300 300 +#define FT_BAUD_600 600 +#define FT_BAUD_1200 1200 +#define FT_BAUD_2400 2400 +#define FT_BAUD_4800 4800 +#define FT_BAUD_9600 9600 +#define FT_BAUD_14400 14400 +#define FT_BAUD_19200 19200 +#define FT_BAUD_38400 38400 +#define FT_BAUD_57600 57600 +#define FT_BAUD_115200 115200 +#define FT_BAUD_230400 230400 +#define FT_BAUD_460800 460800 +#define FT_BAUD_921600 921600 + +// +// Word Lengths +// + +#define FT_BITS_8 (UCHAR) 8 +#define FT_BITS_7 (UCHAR) 7 +#define FT_BITS_6 (UCHAR) 6 +#define FT_BITS_5 (UCHAR) 5 + +// +// Stop Bits +// + +#define FT_STOP_BITS_1 (UCHAR) 0 +#define FT_STOP_BITS_1_5 (UCHAR) 1 +#define FT_STOP_BITS_2 (UCHAR) 2 + +// +// Parity +// + +#define FT_PARITY_NONE (UCHAR) 0 +#define FT_PARITY_ODD (UCHAR) 1 +#define FT_PARITY_EVEN (UCHAR) 2 +#define FT_PARITY_MARK (UCHAR) 3 +#define FT_PARITY_SPACE (UCHAR) 4 + +// +// Flow Control +// + +#define FT_FLOW_NONE 0x0000 +#define FT_FLOW_RTS_CTS 0x0100 +#define FT_FLOW_DTR_DSR 0x0200 +#define FT_FLOW_XON_XOFF 0x0400 + +// +// Purge rx and tx buffers +// +#define FT_PURGE_RX 1 +#define FT_PURGE_TX 2 + +// +// Events +// + +typedef void (*PFT_EVENT_HANDLER)(DWORD,DWORD); + +#define FT_EVENT_RXCHAR 1 +#define FT_EVENT_MODEM_STATUS 2 +#define FT_EVENT_LINE_STATUS 4 + +// +// Timeouts +// + +#define FT_DEFAULT_RX_TIMEOUT 300 +#define FT_DEFAULT_TX_TIMEOUT 300 + +// +// Device types +// + +typedef ULONG FT_DEVICE; + +enum { + FT_DEVICE_BM, + FT_DEVICE_AM, + FT_DEVICE_100AX, + FT_DEVICE_UNKNOWN, + FT_DEVICE_2232C, + FT_DEVICE_232R, + FT_DEVICE_2232H, + FT_DEVICE_4232H +}; + + +#ifdef __cplusplus +extern "C" { +#endif + + +FTD2XX_API +FT_STATUS WINAPI FT_Open( + int deviceNumber, + FT_HANDLE *pHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_OpenEx( + PVOID pArg1, + DWORD Flags, + FT_HANDLE *pHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ListDevices( + PVOID pArg1, + PVOID pArg2, + DWORD Flags + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Close( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Read( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD dwBytesToRead, + LPDWORD lpBytesReturned + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Write( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD dwBytesToWrite, + LPDWORD lpBytesWritten + ); + +FTD2XX_API +FT_STATUS WINAPI FT_IoCtl( + FT_HANDLE ftHandle, + DWORD dwIoControlCode, + LPVOID lpInBuf, + DWORD nInBufSize, + LPVOID lpOutBuf, + DWORD nOutBufSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBaudRate( + FT_HANDLE ftHandle, + ULONG BaudRate + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDivisor( + FT_HANDLE ftHandle, + USHORT Divisor + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDataCharacteristics( + FT_HANDLE ftHandle, + UCHAR WordLength, + UCHAR StopBits, + UCHAR Parity + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetFlowControl( + FT_HANDLE ftHandle, + USHORT FlowControl, + UCHAR XonChar, + UCHAR XoffChar + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ResetDevice( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDtr( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ClrDtr( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetRts( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ClrRts( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetModemStatus( + FT_HANDLE ftHandle, + ULONG *pModemStatus + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetChars( + FT_HANDLE ftHandle, + UCHAR EventChar, + UCHAR EventCharEnabled, + UCHAR ErrorChar, + UCHAR ErrorCharEnabled + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Purge( + FT_HANDLE ftHandle, + ULONG Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetTimeouts( + FT_HANDLE ftHandle, + ULONG ReadTimeout, + ULONG WriteTimeout + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetQueueStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetEventNotification( + FT_HANDLE ftHandle, + DWORD Mask, + PVOID Param + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetStatus( + FT_HANDLE ftHandle, + DWORD *dwRxBytes, + DWORD *dwTxBytes, + DWORD *dwEventDWord + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBreakOn( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBreakOff( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetWaitMask( + FT_HANDLE ftHandle, + DWORD Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_WaitOnMask( + FT_HANDLE ftHandle, + DWORD *Mask + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetEventStatus( + FT_HANDLE ftHandle, + DWORD *dwEventDWord + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ReadEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + LPWORD lpwValue + ); + +FTD2XX_API +FT_STATUS WINAPI FT_WriteEE( + FT_HANDLE ftHandle, + DWORD dwWordOffset, + WORD wValue + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EraseEE( + FT_HANDLE ftHandle + ); + +// +// structure to hold program data for FT_Program function +// +typedef struct ft_program_data { + + DWORD Signature1; // Header - must be 0x00000000 + DWORD Signature2; // Header - must be 0xffffffff + DWORD Version; // Header - FT_PROGRAM_DATA version + // 0 = original + // 1 = FT2232C extensions + // 2 = FT232R extensions + // 3 = FT2232H extensions + // 4 = FT4232H extensions + + WORD VendorId; // 0x0403 + WORD ProductId; // 0x6001 + char *Manufacturer; // "FTDI" + char *ManufacturerId; // "FT" + char *Description; // "USB HS Serial Converter" + char *SerialNumber; // "FT000001" if fixed, or NULL + WORD MaxPower; // 0 < MaxPower <= 500 + WORD PnP; // 0 = disabled, 1 = enabled + WORD SelfPowered; // 0 = bus powered, 1 = self powered + WORD RemoteWakeup; // 0 = not capable, 1 = capable + // + // Rev4 (FT232B) extensions + // + UCHAR Rev4; // non-zero if Rev4 chip, zero otherwise + UCHAR IsoIn; // non-zero if in endpoint is isochronous + UCHAR IsoOut; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable; // non-zero if pull down enabled + UCHAR SerNumEnable; // non-zero if serial number to be used + UCHAR USBVersionEnable; // non-zero if chip uses USBVersion + WORD USBVersion; // BCD (0x0200 => USB2) + // + // Rev 5 (FT2232) extensions + // + UCHAR Rev5; // non-zero if Rev5 chip, zero otherwise + UCHAR IsoInA; // non-zero if in endpoint is isochronous + UCHAR IsoInB; // non-zero if in endpoint is isochronous + UCHAR IsoOutA; // non-zero if out endpoint is isochronous + UCHAR IsoOutB; // non-zero if out endpoint is isochronous + UCHAR PullDownEnable5; // non-zero if pull down enabled + UCHAR SerNumEnable5; // non-zero if serial number to be used + UCHAR USBVersionEnable5; // non-zero if chip uses USBVersion + WORD USBVersion5; // BCD (0x0200 => USB2) + UCHAR AIsHighCurrent; // non-zero if interface is high current + UCHAR BIsHighCurrent; // non-zero if interface is high current + UCHAR IFAIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer; // non-zero if interface is Fast serial + UCHAR AIsVCP; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer; // non-zero if interface is Fast serial + UCHAR BIsVCP; // non-zero if interface is to use VCP drivers + // + // Rev 6 (FT232R) extensions + // + UCHAR UseExtOsc; // Use External Oscillator + UCHAR HighDriveIOs; // High Drive I/Os + UCHAR EndpointSize; // Endpoint size + UCHAR PullDownEnableR; // non-zero if pull down enabled + UCHAR SerNumEnableR; // non-zero if serial number to be used + UCHAR InvertTXD; // non-zero if invert TXD + UCHAR InvertRXD; // non-zero if invert RXD + UCHAR InvertRTS; // non-zero if invert RTS + UCHAR InvertCTS; // non-zero if invert CTS + UCHAR InvertDTR; // non-zero if invert DTR + UCHAR InvertDSR; // non-zero if invert DSR + UCHAR InvertDCD; // non-zero if invert DCD + UCHAR InvertRI; // non-zero if invert RI + UCHAR Cbus0; // Cbus Mux control + UCHAR Cbus1; // Cbus Mux control + UCHAR Cbus2; // Cbus Mux control + UCHAR Cbus3; // Cbus Mux control + UCHAR Cbus4; // Cbus Mux control + UCHAR RIsD2XX; // non-zero if using D2XX driver + // + // Rev 7 (FT2232H) Extensions + // + UCHAR PullDownEnable7; // non-zero if pull down enabled + UCHAR SerNumEnable7; // non-zero if serial number to be used + UCHAR ALSlowSlew; // non-zero if AL pins have slow slew + UCHAR ALSchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ALDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR AHSlowSlew; // non-zero if AH pins have slow slew + UCHAR AHSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR AHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BLSlowSlew; // non-zero if BL pins have slow slew + UCHAR BLSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR BLDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BHSlowSlew; // non-zero if BH pins have slow slew + UCHAR BHSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR BHDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR IFAIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFAIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFAIsFastSer7; // non-zero if interface is Fast serial + UCHAR AIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR IFBIsFifo7; // non-zero if interface is 245 FIFO + UCHAR IFBIsFifoTar7; // non-zero if interface is 245 FIFO CPU target + UCHAR IFBIsFastSer7; // non-zero if interface is Fast serial + UCHAR BIsVCP7; // non-zero if interface is to use VCP drivers + UCHAR PowerSaveEnable; // non-zero if using BCBUS7 to save power for self-powered designs + // + // Rev 8 (FT4232H) Extensions + // + UCHAR PullDownEnable8; // non-zero if pull down enabled + UCHAR SerNumEnable8; // non-zero if serial number to be used + UCHAR ASlowSlew; // non-zero if AL pins have slow slew + UCHAR ASchmittInput; // non-zero if AL pins are Schmitt input + UCHAR ADriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR BSlowSlew; // non-zero if AH pins have slow slew + UCHAR BSchmittInput; // non-zero if AH pins are Schmitt input + UCHAR BDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR CSlowSlew; // non-zero if BL pins have slow slew + UCHAR CSchmittInput; // non-zero if BL pins are Schmitt input + UCHAR CDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR DSlowSlew; // non-zero if BH pins have slow slew + UCHAR DSchmittInput; // non-zero if BH pins are Schmitt input + UCHAR DDriveCurrent; // valid values are 4mA, 8mA, 12mA, 16mA + UCHAR ARIIsTXDEN; // non-zero if port A uses RI as RS485 TXDEN + UCHAR BRIIsTXDEN; // non-zero if port B uses RI as RS485 TXDEN + UCHAR CRIIsTXDEN; // non-zero if port C uses RI as RS485 TXDEN + UCHAR DRIIsTXDEN; // non-zero if port D uses RI as RS485 TXDEN + UCHAR AIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR BIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR CIsVCP8; // non-zero if interface is to use VCP drivers + UCHAR DIsVCP8; // non-zero if interface is to use VCP drivers + +} FT_PROGRAM_DATA, *PFT_PROGRAM_DATA; + +FTD2XX_API +FT_STATUS WINAPI FT_EE_Program( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_ProgramEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_Read( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_ReadEx( + FT_HANDLE ftHandle, + PFT_PROGRAM_DATA pData, + char *Manufacturer, + char *ManufacturerId, + char *Description, + char *SerialNumber + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UASize( + FT_HANDLE ftHandle, + LPDWORD lpdwSize + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UAWrite( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen + ); + +FTD2XX_API +FT_STATUS WINAPI FT_EE_UARead( + FT_HANDLE ftHandle, + PUCHAR pucData, + DWORD dwDataLen, + LPDWORD lpdwBytesRead + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetLatencyTimer( + FT_HANDLE ftHandle, + UCHAR ucLatency + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetLatencyTimer( + FT_HANDLE ftHandle, + PUCHAR pucLatency + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetBitMode( + FT_HANDLE ftHandle, + UCHAR ucMask, + UCHAR ucEnable + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetBitMode( + FT_HANDLE ftHandle, + PUCHAR pucMode + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetUSBParameters( + FT_HANDLE ftHandle, + ULONG ulInTransferSize, + ULONG ulOutTransferSize + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetDeadmanTimeout( + FT_HANDLE ftHandle, + ULONG ulDeadmanTimeout + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfo( + FT_HANDLE ftHandle, + FT_DEVICE *lpftDevice, + LPDWORD lpdwID, + PCHAR SerialNumber, + PCHAR Description, + LPVOID Dummy + ); + +FTD2XX_API +FT_STATUS WINAPI FT_StopInTask( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_RestartInTask( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_SetResetPipeRetryCount( + FT_HANDLE ftHandle, + DWORD dwCount + ); + +FTD2XX_API +FT_STATUS WINAPI FT_ResetPort( + FT_HANDLE ftHandle + ); + +FTD2XX_API +FT_STATUS WINAPI FT_CyclePort( + FT_HANDLE ftHandle + ); + + +// +// Win32-type functions +// + +FTD2XX_API +FT_HANDLE WINAPI FT_W32_CreateFile( + LPCTSTR lpszName, + DWORD dwAccess, + DWORD dwShareMode, + LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreate, + DWORD dwAttrsAndFlags, + HANDLE hTemplate + ); + +FTD2XX_API +BOOL WINAPI FT_W32_CloseHandle( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_ReadFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesReturned, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +BOOL WINAPI FT_W32_WriteFile( + FT_HANDLE ftHandle, + LPVOID lpBuffer, + DWORD nBufferSize, + LPDWORD lpBytesWritten, + LPOVERLAPPED lpOverlapped + ); + +FTD2XX_API +DWORD WINAPI FT_W32_GetLastError( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetOverlappedResult( + FT_HANDLE ftHandle, + LPOVERLAPPED lpOverlapped, + LPDWORD lpdwBytesTransferred, + BOOL bWait + ); + +FTD2XX_API +BOOL WINAPI FT_W32_CancelIo( + FT_HANDLE ftHandle + ); + + +// +// Win32 COMM API type functions +// +typedef struct _FTCOMSTAT { + DWORD fCtsHold : 1; + DWORD fDsrHold : 1; + DWORD fRlsdHold : 1; + DWORD fXoffHold : 1; + DWORD fXoffSent : 1; + DWORD fEof : 1; + DWORD fTxim : 1; + DWORD fReserved : 25; + DWORD cbInQue; + DWORD cbOutQue; +} FTCOMSTAT, *LPFTCOMSTAT; + +typedef struct _FTDCB { + DWORD DCBlength; /* sizeof(FTDCB) */ + DWORD BaudRate; /* Baudrate at which running */ + DWORD fBinary: 1; /* Binary Mode (skip EOF check) */ + DWORD fParity: 1; /* Enable parity checking */ + DWORD fOutxCtsFlow:1; /* CTS handshaking on output */ + DWORD fOutxDsrFlow:1; /* DSR handshaking on output */ + DWORD fDtrControl:2; /* DTR Flow control */ + DWORD fDsrSensitivity:1; /* DSR Sensitivity */ + DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */ + DWORD fOutX: 1; /* Enable output X-ON/X-OFF */ + DWORD fInX: 1; /* Enable input X-ON/X-OFF */ + DWORD fErrorChar: 1; /* Enable Err Replacement */ + DWORD fNull: 1; /* Enable Null stripping */ + DWORD fRtsControl:2; /* Rts Flow control */ + DWORD fAbortOnError:1; /* Abort all reads and writes on Error */ + DWORD fDummy2:17; /* Reserved */ + WORD wReserved; /* Not currently used */ + WORD XonLim; /* Transmit X-ON threshold */ + WORD XoffLim; /* Transmit X-OFF threshold */ + BYTE ByteSize; /* Number of bits/byte, 4-8 */ + BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ + BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ + char XonChar; /* Tx and Rx X-ON character */ + char XoffChar; /* Tx and Rx X-OFF character */ + char ErrorChar; /* Error replacement char */ + char EofChar; /* End of Input character */ + char EvtChar; /* Received Event character */ + WORD wReserved1; /* Fill for now. */ +} FTDCB, *LPFTDCB; + +typedef struct _FTTIMEOUTS { + DWORD ReadIntervalTimeout; /* Maximum time between read chars. */ + DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */ + DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */ + DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */ +} FTTIMEOUTS,*LPFTTIMEOUTS; + + +FTD2XX_API +BOOL WINAPI FT_W32_ClearCommBreak( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_ClearCommError( + FT_HANDLE ftHandle, + LPDWORD lpdwErrors, + LPFTCOMSTAT lpftComstat + ); + +FTD2XX_API +BOOL WINAPI FT_W32_EscapeCommFunction( + FT_HANDLE ftHandle, + DWORD dwFunc + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommModemStatus( + FT_HANDLE ftHandle, + LPDWORD lpdwModemStatus + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + +FTD2XX_API +BOOL WINAPI FT_W32_PurgeComm( + FT_HANDLE ftHandle, + DWORD dwMask + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommBreak( + FT_HANDLE ftHandle + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommMask( + FT_HANDLE ftHandle, + ULONG ulEventMask + ); + +FTD2XX_API +BOOL WINAPI FT_W32_GetCommMask( + FT_HANDLE ftHandle, + LPDWORD lpdwEventMask + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommState( + FT_HANDLE ftHandle, + LPFTDCB lpftDcb + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetCommTimeouts( + FT_HANDLE ftHandle, + FTTIMEOUTS *pTimeouts + ); + +FTD2XX_API +BOOL WINAPI FT_W32_SetupComm( + FT_HANDLE ftHandle, + DWORD dwReadBufferSize, + DWORD dwWriteBufferSize + ); + +FTD2XX_API +BOOL WINAPI FT_W32_WaitCommEvent( + FT_HANDLE ftHandle, + PULONG pulEvent, + LPOVERLAPPED lpOverlapped + ); + + +// +// Device information +// + +typedef struct _ft_device_list_info_node { + ULONG Flags; + ULONG Type; + ULONG ID; + DWORD LocId; + char SerialNumber[16]; + char Description[64]; + FT_HANDLE ftHandle; +} FT_DEVICE_LIST_INFO_NODE; + +// Device information flags +enum { + FT_FLAGS_OPENED = 1, + FT_FLAGS_HISPEED = 2 +}; + + +FTD2XX_API +FT_STATUS WINAPI FT_CreateDeviceInfoList( + LPDWORD lpdwNumDevs + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfoList( + FT_DEVICE_LIST_INFO_NODE *pDest, + LPDWORD lpdwNumDevs + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetDeviceInfoDetail( + DWORD dwIndex, + LPDWORD lpdwFlags, + LPDWORD lpdwType, + LPDWORD lpdwID, + LPDWORD lpdwLocId, + LPVOID lpSerialNumber, + LPVOID lpDescription, + FT_HANDLE *pftHandle + ); + + +// +// Version information +// + +FTD2XX_API +FT_STATUS WINAPI FT_GetDriverVersion( + FT_HANDLE ftHandle, + LPDWORD lpdwVersion + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetLibraryVersion( + LPDWORD lpdwVersion + ); + + +FTD2XX_API +FT_STATUS WINAPI FT_Rescan( + void + ); + +FTD2XX_API +FT_STATUS WINAPI FT_Reload( + WORD wVid, + WORD wPid + ); + +FTD2XX_API +FT_STATUS WINAPI FT_GetComPortNumber( + FT_HANDLE ftHandle, + LPLONG lpdwComPortNumber + ); + + + +#ifdef __cplusplus +} +#endif + + +#endif /* FTD2XX_H */ + diff --git a/telldus-core/tdadmin/05-tellstick.rules b/telldus-core/tdadmin/05-tellstick.rules new file mode 100644 index 0000000..b450b35 --- /dev/null +++ b/telldus-core/tdadmin/05-tellstick.rules @@ -0,0 +1,3 @@ +ATTRS{idVendor}=="1781", SUBSYSTEM=="usb", ACTION=="add", MODE="664", GROUP="plugdev", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}", ENV{ID_SERIAL_SHORT}="$attr{serial}", RUN+="@CMAKE_INSTALL_PREFIX@/share/telldus-core/helpers/udev.sh" +ENV{ID_VENDOR_ID}=="1781", SUBSYSTEM=="usb", ACTION=="remove", ENV{ID_VENDOR_ID}="$attr{idVendor}", ENV{ID_MODEL_ID}="$attr{idProduct}", ENV{ID_SERIAL_SHORT}="$attr{serial}", RUN+="@CMAKE_INSTALL_PREFIX@/share/telldus-core/helpers/udev.sh" + diff --git a/telldus-core/tdadmin/CMakeLists.txt b/telldus-core/tdadmin/CMakeLists.txt new file mode 100644 index 0000000..7c72b26 --- /dev/null +++ b/telldus-core/tdadmin/CMakeLists.txt @@ -0,0 +1,79 @@ +PROJECT(tdadmin) + +cmake_policy(SET CMP0005 NEW) + +SET (tdadmin_DESCRIPTION + "a command line utility to edit devices and controllers for Telldus TellStick" +) + +SET(tdadmin_SRCS + main.cpp +) + +ADD_EXECUTABLE(tdadmin + ${tdadmin_SRCS} +) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/driver +) + +ADD_DEFINITIONS( -DVERSION="${DISPLAYED_VERSION}" ) + +IF (WIN32) + FIND_LIBRARY(TELLDUSCORE_LIBRARY TelldusCore) + TARGET_LINK_LIBRARIES(tdadmin + ${TELLDUSCORE_LIBRARY} + ) +ELSEIF (APPLE) + TARGET_LINK_LIBRARIES(tdadmin + TelldusCore + ) +ELSEIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + # FreeBSD does not have argp in base libc; port devel/argp-standalone is required. + FIND_LIBRARY(ARGP_LIBRARY argp) + TARGET_LINK_LIBRARIES(tdadmin + ${CMAKE_BINARY_DIR}/client/libtelldus-core.so + ${ARGP_LIBRARY} + ) +ELSE (WIN32) + TARGET_LINK_LIBRARIES(tdadmin + ${CMAKE_BINARY_DIR}/client/libtelldus-core.so + ) +ENDIF (WIN32) + +IF (UNIX) + IF (GENERATE_MAN) + ADD_CUSTOM_COMMAND( + TARGET tdadmin + POST_BUILD + COMMAND help2man -n ${tdadmin_DESCRIPTION} ./tdadmin > tdadmin.1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating man file tdadmin.1" + ) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/tdadmin.1 DESTINATION share/man/man1) + ENDIF (GENERATE_MAN) +ENDIF (UNIX) + +INSTALL(TARGETS tdadmin RUNTIME DESTINATION sbin) + +IF (UNIX AND NOT APPLE) + SET(UDEV_RULES_DIR "/etc/udev/rules.d" CACHE PATH "The directory where udev store its rules" ) + CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/05-tellstick.rules + ${CMAKE_BINARY_DIR}/parsed/05-tellstick.rules + @ONLY + ) + CONFIGURE_FILE( + ${CMAKE_CURRENT_SOURCE_DIR}/udev.sh + ${CMAKE_BINARY_DIR}/parsed/udev.sh + @ONLY + ) + INSTALL(FILES ${CMAKE_BINARY_DIR}/parsed/05-tellstick.rules + DESTINATION ${UDEV_RULES_DIR} + ) + INSTALL(PROGRAMS ${CMAKE_BINARY_DIR}/parsed/udev.sh + DESTINATION share/telldus-core/helpers/ + ) +ENDIF (UNIX AND NOT APPLE) diff --git a/telldus-core/tdadmin/main.cpp b/telldus-core/tdadmin/main.cpp new file mode 100644 index 0000000..66f7fcb --- /dev/null +++ b/telldus-core/tdadmin/main.cpp @@ -0,0 +1,88 @@ +#include "../client/telldus-core.h" +#include +#include +#include +#include + +const char *argp_program_version = "tdadmin " VERSION ; +const char *argp_program_bug_address = ""; + +static char args_doc[] = "COMMAND ACTION"; + +static char doc[] = "TellStick admin tool -- a command line utility to edit devices and controllers for Telldus TellStick"; + +const int VID = 1; +const int PID = 2; +const int SERIAL = 3; + +static struct argp_option options[] = { + {0,0,0,0, + "COMMAND: controller, ACTION: connect/disconnect\n" + "Tells the daemon to add or remove a TellStick (duo)" + }, + {"vid",VID,"VID",0, "The vendor id (1781)" }, + {"pid",PID,"PID",0,"The product id (0c30 or 0c31)" }, + {"serial",SERIAL,"SERIAL",0,"The usb serial number" }, + { 0 } +}; + +static std::string command, action; + +int vid, pid; +static std::string serial; + +static error_t parse_opt (int key, char *arg, struct argp_state *state) { + switch (key) { + case PID: + pid = strtol(arg, NULL, 16); + break; + case SERIAL: + serial = arg; + break; + case VID: + vid = strtol(arg, NULL, 16); + break; + + case ARGP_KEY_NO_ARGS: + argp_usage (state); + + case ARGP_KEY_ARG: + if (state->next == state->argc) { + argp_usage (state); + } + command = arg; + action = state->argv[state->next]; + state->next = state->argc; + + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { options, parse_opt, args_doc, doc }; + +void handle_controller(void) { + if (vid == 0 || pid == 0) { + fprintf(stderr, "Missing parameter vid or pid\n"); + } + if (action.compare("connect") == 0) { + tdConnectTellStickController(vid,pid,serial.c_str()); + + } else if (action.compare("disconnect") == 0) { + tdDisconnectTellStickController(vid,pid,serial.c_str()); + } +} + +int main(int argc, char **argv) { + + argp_parse (&argp, argc, argv, 0, 0, 0); + + if (command.compare("controller") == 0) { + handle_controller(); + } + + return 0; +} diff --git a/telldus-core/tdadmin/udev.sh b/telldus-core/tdadmin/udev.sh new file mode 100755 index 0000000..3b935d7 --- /dev/null +++ b/telldus-core/tdadmin/udev.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +if [ "${ID_VENDOR_ID}" = "1781" ]; then + if [ "${ACTION}" = "add" ]; then + @CMAKE_INSTALL_PREFIX@/sbin/tdadmin controller connect --pid=${ID_MODEL_ID} --vid=${ID_VENDOR_ID} --serial=${ID_SERIAL_SHORT} + elif [ "${ACTION}" = "remove" ]; then + @CMAKE_INSTALL_PREFIX@/sbin/tdadmin controller disconnect --pid=${ID_MODEL_ID} --vid=${ID_VENDOR_ID} --serial=${ID_SERIAL_SHORT} + fi +fi diff --git a/telldus-core/tdtool/CMakeLists.txt b/telldus-core/tdtool/CMakeLists.txt new file mode 100644 index 0000000..c9aeb07 --- /dev/null +++ b/telldus-core/tdtool/CMakeLists.txt @@ -0,0 +1,56 @@ +PROJECT(tdtool) + +cmake_policy(SET CMP0005 NEW) + +SET (tdtool_DESCRIPTION + "a command line utility to send commands to a Telldus TellStick" +) + +SET(tdtool_SRCS + main.cpp +) + +ADD_EXECUTABLE(tdtool + ${tdtool_SRCS} +) +SIGN(tdtool) + +INCLUDE_DIRECTORIES( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/driver +) + +ADD_DEFINITIONS( -DVERSION="${DISPLAYED_VERSION}" ) + +IF (WIN32) + TARGET_LINK_LIBRARIES(tdtool + TelldusCore + openbsd-getopt + ) + INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/3rdparty/openbsd-getopt + ) +ELSEIF (APPLE) + TARGET_LINK_LIBRARIES(tdtool + TelldusCore + ) +ELSE (WIN32) + TARGET_LINK_LIBRARIES(tdtool + ${CMAKE_BINARY_DIR}/client/libtelldus-core.so + ) +ENDIF (WIN32) + +IF (UNIX) + IF (GENERATE_MAN) + ADD_CUSTOM_COMMAND( + TARGET tdtool + POST_BUILD + COMMAND help2man -n ${tdtool_DESCRIPTION} ./tdtool > tdtool.1 + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating man file tdtool.1" + ) + INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/tdtool.1 DESTINATION share/man/man1) + ENDIF (GENERATE_MAN) +ENDIF (UNIX) + +INSTALL(TARGETS tdtool RUNTIME DESTINATION bin) diff --git a/telldus-core/tdtool/main.cpp b/telldus-core/tdtool/main.cpp new file mode 100644 index 0000000..b75d281 --- /dev/null +++ b/telldus-core/tdtool/main.cpp @@ -0,0 +1,623 @@ +#include +#include +#include +#include +#include +#include +#include "../client/telldus-core.h" + +#ifdef _WINDOWS +#define strcasecmp _stricmp +#define DEGREE " " +#else +#define DEGREE "°" +#endif + +const int SUPPORTED_METHODS = + TELLSTICK_TURNON | + TELLSTICK_TURNOFF | + TELLSTICK_BELL | + TELLSTICK_DIM; + +const int DATA_LENGTH = 20; + +void print_usage( char *name ) { + printf("Usage: %s [ options ]\n", name); + printf("\n"); + printf("Options:\n"); + printf(" -[bdefhlnrv] [ --list ] [ --help ]\n"); + printf(" [ --list-sensors ] [ --list-devices ]\n"); + printf(" [ --on device ] [ --off device ] [ --bell device ]\n"); + printf(" [ --learn device ]\n"); + printf(" [ --dimlevel level --dim device ]\n"); + printf(" [ --raw input ]\n"); + printf("\n"); + printf(" --list (-l short option)\n"); + printf(" List currently configured devices and all discovered sensors.\n"); + printf("\n"); + printf(" --list-sensors\n"); + printf(" --list-devices\n"); + printf(" Alternative devices/sensors listing:\n"); + printf(" Shows devices and/or sensors using key=value format (with tabs as\n"); + printf(" separators, one device/sensor per line, no header lines.)\n"); + printf("\n"); + printf(" --help (-h short option)\n"); + printf(" Shows this screen.\n"); + printf("\n"); + printf(" --on device (-n short option)\n"); + printf(" Turns on device. 'device' could either be an integer of the\n"); + printf(" device-id, or the name of the device.\n"); + printf(" Both device-id and name is outputed with the --list option\n"); + printf("\n"); + printf(" --off device (-f short option)\n"); + printf(" Turns off device. 'device' could either be an integer of the\n"); + printf(" device-id, or the name of the device.\n"); + printf(" Both device-id and name is outputed with the --list option\n"); + printf("\n"); + printf(" --dim device (-d short option)\n"); + printf(" Dims device. 'device' could either be an integer of the device-id,\n"); + printf(" or the name of the device.\n"); + printf(" Both device-id and name is outputed with the --list option\n"); + printf(" Note: The dimlevel parameter must be set before using this option.\n"); + printf("\n"); + printf(" --dimlevel level (-v short option)\n"); + printf(" Set dim level. 'level' should an integer, 0-255.\n"); + printf(" Note: This parameter must be set before using dim.\n"); + printf("\n"); + printf(" --bell device (-b short option)\n"); + printf(" Sends bell command to devices supporting this. 'device' could\n"); + printf(" either be an integer of the device-id, or the name of the device.\n"); + printf(" Both device-id and name is outputed with the --list option\n"); + printf("\n"); + printf(" --learn device (-e short option)\n"); + printf(" Sends a special learn command to devices supporting this. This is normaly\n"); + printf(" devices of 'selflearning' type. 'device' could either be an integer\n"); + printf(" of the device-id, or the name of the device.\n"); + printf(" Both device-id and name is outputed with the --list option\n"); + printf("\n"); + printf(" --raw input (-r short option)\n"); + printf(" This command sends a raw command to TellStick.\n"); + printf(" input can be either - or a filename. If input is - the data is\n"); + printf(" taken from stdin, otherwise the data is taken from the supplied filename.\n"); + printf("\n"); + printf(" Example to turn on an ArcTech codeswitch A1:\n"); + printf(" echo 'S$k$k$k$k$k$k$k$k$k$k$k$k$k$k$k$k$k$k$kk$$kk$$kk$$}+' | tdtool --raw -\n"); + printf("\n"); + printf("Report bugs to \n"); +} + +void print_version() { + printf("tdtool " VERSION "\n"); + printf("\n"); + printf("Copyright (C) 2011 Telldus Technologies AB\n"); + printf("\n"); + printf("Written by Micke Prag \n"); +} + +void print_device( int index ) { + tdInit(); + int intId = tdGetDeviceId(index); + char *name = tdGetName(intId); + printf("%i\t%s\t", intId, name); + tdReleaseString(name); + int lastSentCommand = tdLastSentCommand(intId, SUPPORTED_METHODS); + char *level = 0; + switch(lastSentCommand) { + case TELLSTICK_TURNON: + printf("ON"); + break; + case TELLSTICK_TURNOFF: + printf("OFF"); + break; + case TELLSTICK_DIM: + level = tdLastSentValue(intId); + printf("DIMMED:%s", level); + tdReleaseString(level); + break; + default: + printf("Unknown state"); + } + printf("\n"); +} + +int list_devices() { + tdInit(); + int intNum = tdGetNumberOfDevices(); + if (intNum < 0) { + char *errorString = tdGetErrorString(intNum); + fprintf(stderr, "Error fetching devices: %s\n", errorString); + tdReleaseString(errorString); + return intNum; + } + printf("Number of devices: %i\n", intNum); + int i = 0; + while (i < intNum) { + print_device( i ); + i++; + } + + char protocol[DATA_LENGTH], model[DATA_LENGTH]; + int sensorId = 0, dataTypes = 0; + + int sensorStatus = tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes); + if(sensorStatus == 0){ + printf("\n\nSENSORS:\n\n%-20s\t%-20s\t%-5s\t%-5s\t%-8s\t%-20s\t%-20s\t%-20s\n", "PROTOCOL", "MODEL", "ID", "TEMP", "HUMIDITY", "RAIN", "WIND", "LAST UPDATED"); + } + while(sensorStatus == 0){ + char tempvalue[DATA_LENGTH]; + tempvalue[0] = 0; + char humidityvalue[DATA_LENGTH]; + humidityvalue[0] = 0; + char windvalue[DATA_LENGTH]; + windvalue[0] = 0; + char winddirectionvalue[DATA_LENGTH]; + winddirectionvalue[0] = 0; + char windaveragevalue[DATA_LENGTH]; + windaveragevalue[0] = 0; + char windgustvalue[DATA_LENGTH]; + windgustvalue[0] = 0; + char rainvalue[DATA_LENGTH]; + rainvalue[0] = 0; + char raintotvalue[DATA_LENGTH]; + raintotvalue[0] = 0; + char rainratevalue[DATA_LENGTH]; + rainratevalue[0] = 0; + char timeBuf[80]; + timeBuf[0] = 0; + time_t timestamp = 0; + + if (dataTypes & TELLSTICK_TEMPERATURE) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_TEMPERATURE, tempvalue, DATA_LENGTH, (int *)×tamp); + strcat(tempvalue, DEGREE); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_HUMIDITY) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_HUMIDITY, humidityvalue, DATA_LENGTH, (int *)×tamp); + strcat(humidityvalue, "%"); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_RAINRATE) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_RAINRATE, rainratevalue, DATA_LENGTH, (int *)×tamp); + strcat(rainratevalue, " mm/h, "); + strcat(rainvalue, rainratevalue); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_RAINTOTAL) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_RAINTOTAL, raintotvalue, DATA_LENGTH, (int *)×tamp); + //TODO detta blir väl fel, kan väl hamna i andra ordningar, eller hur? + strcat(raintotvalue, " mm"); + strcat(rainvalue, raintotvalue); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_WINDDIRECTION) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDDIRECTION, winddirectionvalue, DATA_LENGTH, (int *)×tamp); + //TODO or use charToInteger in common? + std::stringstream inputstream; + inputstream << winddirectionvalue; + int direction; + inputstream >> direction; + direction = direction / 22.5; + std::string directionabbrev = "N"; + switch (direction) { + case 1: + directionabbrev = "NNE"; + break; + case 2: + directionabbrev = "NE"; + break; + case 3: + directionabbrev = "ENE"; + break; + case 4: + directionabbrev = "E"; + break; + case 5: + directionabbrev = "ESE"; + break; + case 6: + directionabbrev = "SE"; + break; + case 7: + directionabbrev = "SSE"; + break; + case 8: + directionabbrev = "S"; + break; + case 9: + directionabbrev = "SSW"; + break; + case 10: + directionabbrev = "SW"; + break; + case 11: + directionabbrev = "WSW"; + break; + case 12: + directionabbrev = "W"; + break; + case 13: + directionabbrev = "WNW"; + break; + case 14: + directionabbrev = "NW"; + break; + case 15: + directionabbrev = "NNW"; + break; + } + strcat(windvalue, directionabbrev.c_str()); + strcat(windvalue, ", "); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_WINDAVERAGE) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDAVERAGE, windaveragevalue, DATA_LENGTH, (int *)×tamp); + strcat(windaveragevalue, " m/s ("); + strcat(windvalue, windaveragevalue); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + if (dataTypes & TELLSTICK_WINDGUST) { + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDGUST, windgustvalue, DATA_LENGTH, (int *)×tamp); + strcat(windgustvalue, " m/s) "); + strcat(windvalue, windgustvalue); + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + } + + printf("%-20s\t%-20s\t%-5i\t%-5s\t%-8s\t%-20s\t%-20s\t%-20s\n", protocol, model, sensorId, tempvalue, humidityvalue, rainvalue, windvalue, timeBuf); + + sensorStatus = tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes); + } + printf("\n"); + if(sensorStatus != TELLSTICK_ERROR_DEVICE_NOT_FOUND){ + char *errorString = tdGetErrorString(sensorStatus); + fprintf(stderr, "Error fetching sensors: %s\n", errorString); + tdReleaseString(errorString); + return sensorStatus; + } + return TELLSTICK_SUCCESS; +} + +/* list sensors using key=value format, one sensor/line, no header lines + * and no degree or percent signs attached to the numbers - just + * plain values. */ +int list_kv_sensors() { + char protocol[DATA_LENGTH], model[DATA_LENGTH]; + + tdInit(); + int sensorId = 0, dataTypes = 0; + time_t now = 0; + int sensorStatus; + + time(&now); + while(1) { + sensorStatus = tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes); + if (sensorStatus != 0) break; + + printf("type=sensor\tprotocol=%s\tmodel=%s\tid=%d", + protocol, model, sensorId); + + time_t timestamp = 0; + + if (dataTypes & TELLSTICK_TEMPERATURE) { + char tempvalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_TEMPERATURE, tempvalue, DATA_LENGTH, (int *)×tamp); + printf("\ttemperature=%s", tempvalue); + } + + if (dataTypes & TELLSTICK_HUMIDITY) { + char humidityvalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_HUMIDITY, humidityvalue, DATA_LENGTH, (int *)×tamp); + printf("\thumidity=%s", humidityvalue); + } + + if (dataTypes & TELLSTICK_WINDDIRECTION) { + char winddirectionvalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDDIRECTION, winddirectionvalue, DATA_LENGTH, (int *)×tamp); + printf("\twinddirection=%s", winddirectionvalue); + } + + if (dataTypes & TELLSTICK_WINDAVERAGE) { + char windaveragevalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDAVERAGE, windaveragevalue, DATA_LENGTH, (int *)×tamp); + printf("\twindaverage=%s", windaveragevalue); + } + + if (dataTypes & TELLSTICK_WINDGUST) { + char windgustvalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_WINDGUST, windgustvalue, DATA_LENGTH, (int *)×tamp); + printf("\twindgust=%s", windgustvalue); + } + + if (dataTypes & TELLSTICK_RAINRATE) { + char rainratevalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_RAINRATE, rainratevalue, DATA_LENGTH, (int *)×tamp); + printf("\trainrate=%s", rainratevalue); + } + + if (dataTypes & TELLSTICK_RAINTOTAL) { + char raintotalvalue[DATA_LENGTH]; + tdSensorValue(protocol, model, sensorId, TELLSTICK_RAINTOTAL, raintotalvalue, DATA_LENGTH, (int *)×tamp); + printf("\traintotal=%s", raintotalvalue); + } + + if (dataTypes & (TELLSTICK_TEMPERATURE | TELLSTICK_HUMIDITY)) { + /* timestamp has been set, print time & age */ + /* (age is more useful on e.g. embedded systems + * which may not have real-time clock chips => + * time is useful only as a relative value) */ + char timeBuf[80]; + strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(×tamp)); + printf("\ttime=%s\tage=%d", timeBuf, (int)(now - timestamp)); + } + printf("\n"); + + } + if(sensorStatus != TELLSTICK_ERROR_DEVICE_NOT_FOUND){ + char *errorString = tdGetErrorString(sensorStatus); + fprintf(stderr, "Error fetching sensors: %s\n", errorString); + tdReleaseString(errorString); + return sensorStatus; + } + return TELLSTICK_SUCCESS; +} + +/* list devices using key=value format, one device/line, no header lines */ +int list_kv_devices() { + tdInit(); + int intNum = tdGetNumberOfDevices(); + if (intNum < 0) { + char *errorString = tdGetErrorString(intNum); + fprintf(stderr, "Error fetching devices: %s\n", errorString); + tdReleaseString(errorString); + return intNum; + } + int index = 0; + while (index < intNum) { + tdInit(); + int intId = tdGetDeviceId(index); + char *name = tdGetName(intId); + printf("type=device\tid=%i\tname=%s", intId, name); + tdReleaseString(name); + + int lastSentCommand = tdLastSentCommand(intId, SUPPORTED_METHODS); + char *level = 0; + switch(lastSentCommand) { + case TELLSTICK_TURNON: + printf("\tlastsentcommand=ON"); + break; + case TELLSTICK_TURNOFF: + printf("\tlastsentcommand=OFF"); + break; + case TELLSTICK_DIM: + level = tdLastSentValue(intId); + printf("\tlastsentcommand=DIMMED\tdimlevel=%s", level); + tdReleaseString(level); + break; + /* default: state is unknown, print nothing. */ + } + printf("\n"); + index++; + } + return TELLSTICK_SUCCESS; +} + + +int find_device( char *device ) { + tdInit(); + int deviceId = atoi(device); + if (deviceId == 0) { //Try to find the id from the name + int intNum = tdGetNumberOfDevices(); + int index = 0; + while (index < intNum) { + int id = tdGetDeviceId(index); + char *name = tdGetName( id ); + if (strcasecmp(name, device) == 0) { + deviceId = id; + tdReleaseString(name); + break; + } + tdReleaseString(name); + index++; + } + } + return deviceId; +} + +int switch_device( bool turnOn, char *device ) { + tdInit(); + int deviceId = find_device( device ); + if (deviceId == 0) { + printf("Device '%s', not found!\n", device); + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + char *name = tdGetName( deviceId ); + int deviceType = tdGetDeviceType( deviceId ); + printf("Turning %s %s %i, %s", + (turnOn ? "on" : "off"), + (deviceType == TELLSTICK_TYPE_DEVICE ? "device" : "group"), + deviceId, + name); + tdReleaseString(name); + + int retval = (turnOn ? tdTurnOn( deviceId ) : tdTurnOff( deviceId )); + char *errorString = tdGetErrorString(retval); + + printf(" - %s\n", errorString); + tdReleaseString(errorString); + return retval; +} + +int dim_device( char *device, int level ) { + tdInit(); + int deviceId = find_device( device ); + if (deviceId == 0) { + printf("Device '%s', not found!\n", device); + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + if (level < 0 || level > 255) { + printf("Level %i out of range!\n", level); + return TELLSTICK_ERROR_SYNTAX; + } + + char *name = tdGetName( deviceId ); + int retval = tdDim( deviceId, (unsigned char)level ); + char *errorString = tdGetErrorString(retval); + printf("Dimming device: %i %s to %i - %s\n", deviceId, name, level, errorString); + tdReleaseString(name); + tdReleaseString(errorString); + return retval; +} + +int bell_device( char *device ) { + tdInit(); + int deviceId = find_device( device ); + if (deviceId == 0) { + printf("Device '%s', not found!\n", device); + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + char *name = tdGetName( deviceId ); + int retval = tdBell( deviceId ); + char *errorString = tdGetErrorString(retval); + printf("Sending bell to: %i %s - %s\n", deviceId, name, errorString); + tdReleaseString(name); + tdReleaseString(errorString); + return retval; +} + +int learn_device( char *device ) { + tdInit(); + int deviceId = find_device( device ); + if (deviceId == 0) { + printf("Device '%s', not found!\n", device); + return TELLSTICK_ERROR_DEVICE_NOT_FOUND; + } + + char *name = tdGetName( deviceId ); + int retval = tdLearn( deviceId ); + char *errorString = tdGetErrorString(retval); + printf("Learning device: %i %s - %s\n", deviceId, name, errorString); + tdReleaseString(name); + tdReleaseString(errorString); + return retval; +} + +int send_raw_command( char *command ) { + tdInit(); + const int MAX_LENGTH = 100; + char msg[MAX_LENGTH]; + + if (strcmp(command, "-") == 0) { + fgets(msg, MAX_LENGTH, stdin); + } else { + FILE *fd; + + fd = fopen(command, "r"); + if (fd == NULL) { + printf("Error opening file %s\n", command); + return TELLSTICK_ERROR_UNKNOWN; + } + fgets(msg, MAX_LENGTH, fd); + fclose(fd); + } + + int retval = tdSendRawCommand( msg, 0 ); + char *errorString = tdGetErrorString(retval); + printf("Sending raw command: %s\n", errorString); + tdReleaseString(errorString); + return retval; +} + +#define LIST_KV_SENSORS 1 +#define LIST_KV_DEVICES 2 + +int main(int argc, char **argv) +{ + int optch, longindex; + static char optstring[] = "ln:f:d:b:v:e:r:hi"; + static struct option long_opts[] = { + { "list", 0, 0, 'l' }, + { "list-sensors", 0, 0, LIST_KV_SENSORS }, + { "list-devices", 0, 0, LIST_KV_DEVICES }, + { "on", 1, 0, 'n' }, + { "off", 1, 0, 'f' }, + { "dim", 1, 0, 'd' }, + { "bell", 1, 0, 'b' }, + { "dimlevel", 1, 0, 'v' }, + { "learn", 1, 0, 'e' }, + { "raw", 1, 0, 'r' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'i'}, + { 0, 0, 0, 0} + }; + int level = -1; + + if (argc < 2) { + print_usage( argv[0] ); + return -TELLSTICK_ERROR_SYNTAX; + } + + int returnSuccess = 0; + while ( (optch = getopt_long(argc,argv,optstring,long_opts,&longindex)) != -1 ){ + int success = 0; + switch (optch) { + case 'b' : + success = bell_device( &optarg[0] ); + break; + case 'd' : + if (level >= 0) { + success = dim_device( &optarg[0], level ); + break; + } + printf("Dim level missing or incorrect value.\n"); + success = TELLSTICK_ERROR_SYNTAX; + break; + case 'f' : + success = switch_device(false, &optarg[0]); + break; + case 'h' : + print_usage( argv[0] ); + success = TELLSTICK_SUCCESS; + break; + case 'i' : + print_version( ); + success = TELLSTICK_SUCCESS; + break; + case 'l' : + success = list_devices(); + break; + case LIST_KV_SENSORS: + success = list_kv_sensors(); + break; + case LIST_KV_DEVICES: + success = list_kv_devices(); + break; + case 'n' : + success = switch_device(true, &optarg[0]); + break; + case 'e' : + success = learn_device(&optarg[0]); + break; + case 'r' : + success = send_raw_command(&optarg[0]); + break; + case 'v' : + level = atoi( &optarg[0] ); + break; + default : + print_usage( argv[0] ); + success = TELLSTICK_ERROR_SYNTAX; + } + if(success != TELLSTICK_SUCCESS){ + returnSuccess = success; //return last error message + } + } + tdClose(); //Cleaning up + return -returnSuccess; +} diff --git a/telldus-core/tests/CMakeLists.txt b/telldus-core/tests/CMakeLists.txt new file mode 100644 index 0000000..8cdc4cf --- /dev/null +++ b/telldus-core/tests/CMakeLists.txt @@ -0,0 +1,51 @@ +SET(ENABLE_TESTING FALSE CACHE BOOL "Enable unit tests") + +#We have disabled some of the default tests in the google style guidelines +# whitespace/tab +# We are using tabs for indentation, not spaces, in our code +# +# whitespace/parens +# We believe grouping of parameters with spaces in some functions could ease the readability +# +# whitespace/line_length +# Although you should try to keep the lines short it should not be a requirement (at least for now) +# +# whitespace/labels +# Since we use tabs instead of spaces for indentation, this test makes no sense +# +# runtime/rtti +# We are using dynamic_cast for the events. We use this to be able to send arbitrary data +# trought the events. +# +SET(cpplint_filters + +whitespace/use_tab_for_indentation,-whitespace/tab,-whitespace/parens,-whitespace/line_length,-whitespace/labels,-runtime/rtti +) + +FUNCTION(ADD_SOURCES TARGET PATH) + GET_TARGET_PROPERTY(SOURCES ${TARGET} SOURCES) + FOREACH(SOURCE ${SOURCES}) + LIST(APPEND L ${PATH}/${SOURCE}) + ENDFOREACH() + ADD_TEST(StyleGuidelines-${TARGET} ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cpplint.py --filter=${cpplint_filters} ${L}) +ENDFUNCTION() + +IF(ENABLE_TESTING) + INCLUDE(FindPythonInterp) + FIND_LIBRARY(CPPUNIT cppunit) + ADD_SUBDIRECTORY(common) + ADD_SUBDIRECTORY(service) + + ADD_EXECUTABLE(TestRunner cppunit.cpp) + TARGET_LINK_LIBRARIES(TestRunner cppunit TelldusCommonTests TelldusServiceTests) + ADD_DEPENDENCIES(TestRunner TelldusCommonTests TelldusServiceTests) + + ADD_SOURCES(TelldusCommon ${CMAKE_SOURCE_DIR}/common) + ADD_SOURCES(${telldus-core_TARGET} ${CMAKE_SOURCE_DIR}/client) + ADD_SOURCES(${telldus-service_TARGET} ${CMAKE_SOURCE_DIR}/service) + + ADD_TEST(cppunit ${CMAKE_CURRENT_BINARY_DIR}/TestRunner) + IF (UNIX) + ADD_TEST(cppcheck cppcheck --quiet --error-exitcode=2 ${CMAKE_SOURCE_DIR}) + ENDIF() +ENDIF() + diff --git a/telldus-core/tests/common/CMakeLists.txt b/telldus-core/tests/common/CMakeLists.txt new file mode 100644 index 0000000..eec397f --- /dev/null +++ b/telldus-core/tests/common/CMakeLists.txt @@ -0,0 +1,9 @@ +FILE(GLOB SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*Test.cpp" ) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/common) + +ADD_LIBRARY(TelldusCommonTests STATIC ${SRCS} ) + +TARGET_LINK_LIBRARIES( TelldusCommonTests TelldusCommon ${CPPUNIT} ) +ADD_DEPENDENCIES( TelldusCommonTests TelldusCommon ) + diff --git a/telldus-core/tests/common/CommonTests.h b/telldus-core/tests/common/CommonTests.h new file mode 100644 index 0000000..805b51e --- /dev/null +++ b/telldus-core/tests/common/CommonTests.h @@ -0,0 +1,11 @@ +#ifndef COMMONTESTS_H +#define COMMONTESTS_H + +#include "StringsTest.h" + +namespace CommonTests { + inline void setup() { + CPPUNIT_TEST_SUITE_REGISTRATION (StringsTest); + } +} +#endif // COMMONTESTS_H diff --git a/telldus-core/tests/common/StringsTest.cpp b/telldus-core/tests/common/StringsTest.cpp new file mode 100644 index 0000000..251d4a3 --- /dev/null +++ b/telldus-core/tests/common/StringsTest.cpp @@ -0,0 +1,16 @@ +#include "StringsTest.h" +#include "Strings.h" + +void StringsTest :: setUp (void) +{ +} + +void StringsTest :: tearDown (void) +{ +} + +void StringsTest :: formatfTest (void) { + CPPUNIT_ASSERT_EQUAL(std::string("42"), TelldusCore::formatf("%u", 42)); + CPPUNIT_ASSERT_EQUAL(std::string("2A"), TelldusCore::formatf("%X", 42)); + CPPUNIT_ASSERT_EQUAL(std::string("42"), TelldusCore::formatf("%s", "42")); +} diff --git a/telldus-core/tests/common/StringsTest.h b/telldus-core/tests/common/StringsTest.h new file mode 100644 index 0000000..4c5642c --- /dev/null +++ b/telldus-core/tests/common/StringsTest.h @@ -0,0 +1,21 @@ +#ifndef STRINGSTEST_H +#define STRINGSTEST_H + +#include +#include + +class StringsTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (StringsTest); + CPPUNIT_TEST (formatfTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void formatfTest(void); +}; + +#endif //STRINGSTEST_H diff --git a/telldus-core/tests/cpplint.py b/telldus-core/tests/cpplint.py new file mode 100755 index 0000000..5c88df4 --- /dev/null +++ b/telldus-core/tests/cpplint.py @@ -0,0 +1,3369 @@ +#!/usr/bin/python +# +# Copyright (c) 2009 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Here are some issues that I've had people identify in my code during reviews, +# that I think are possible to flag automatically in a lint tool. If these were +# caught by lint, it would save time both for myself and that of my reviewers. +# Most likely, some of these are beyond the scope of the current lint framework, +# but I think it is valuable to retain these wish-list items even if they cannot +# be immediately implemented. +# +# Suggestions +# ----------- +# - Check for no 'explicit' for multi-arg ctor +# - Check for boolean assign RHS in parens +# - Check for ctor initializer-list colon position and spacing +# - Check that if there's a ctor, there should be a dtor +# - Check accessors that return non-pointer member variables are +# declared const +# - Check accessors that return non-const pointer member vars are +# *not* declared const +# - Check for using public includes for testing +# - Check for spaces between brackets in one-line inline method +# - Check for no assert() +# - Check for spaces surrounding operators +# - Check for 0 in pointer context (should be NULL) +# - Check for 0 in char context (should be '\0') +# - Check for camel-case method name conventions for methods +# that are not simple inline getters and setters +# - Check that base classes have virtual destructors +# put " // namespace" after } that closes a namespace, with +# namespace's name after 'namespace' if it is named. +# - Do not indent namespace contents +# - Avoid inlining non-trivial constructors in header files +# include base/basictypes.h if DISALLOW_EVIL_CONSTRUCTORS is used +# - Check for old-school (void) cast for call-sites of functions +# ignored return value +# - Check gUnit usage of anonymous namespace +# - Check for class declaration order (typedefs, consts, enums, +# ctor(s?), dtor, friend declarations, methods, member vars) +# + +"""Does google-lint on c++ files. + +The goal of this script is to identify places in the code that *may* +be in non-compliance with google style. It does not attempt to fix +up these problems -- the point is to educate. It does also not +attempt to find all problems, or to ensure that everything it does +find is legitimately a problem. + +In particular, we can get very confused by /* and // inside strings! +We do a small hack, which is to ignore //'s with "'s after them on the +same line, but it is far from perfect (in either direction). +""" + +import codecs +import getopt +import math # for log +import os +import re +import sre_compile +import string +import sys +import unicodedata + + +_USAGE = """ +Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] + [--counting=total|toplevel|detailed] + [file] ... + + The style guidelines this tries to follow are those in + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml + + Every problem is given a confidence score from 1-5, with 5 meaning we are + certain of the problem, and 1 meaning it could be a legitimate construct. + This will miss some errors, and is not a substitute for a code review. + + To suppress false-positive errors of a certain category, add a + 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*) + suppresses errors of all categories on that line. + + The files passed in will be linted; at least one file must be provided. + Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. + + Flags: + + output=vs7 + By default, the output is formatted to ease emacs parsing. Visual Studio + compatible output (vs7) may also be used. Other formats are unsupported. + + verbose=# + Specify a number 0-5 to restrict errors to certain verbosity levels. + + filter=-x,+y,... + Specify a comma-separated list of category-filters to apply: only + error messages whose category names pass the filters will be printed. + (Category names are printed with the message and look like + "[whitespace/indent]".) Filters are evaluated left to right. + "-FOO" and "FOO" means "do not print categories that start with FOO". + "+FOO" means "do print categories that start with FOO". + + Examples: --filter=-whitespace,+whitespace/braces + --filter=whitespace,runtime/printf,+runtime/printf_format + --filter=-,+build/include_what_you_use + + To see a list of all the categories used in cpplint, pass no arg: + --filter= + + counting=total|toplevel|detailed + The total number of errors found is always printed. If + 'toplevel' is provided, then the count of errors in each of + the top-level categories like 'build' and 'whitespace' will + also be printed. If 'detailed' is provided, then a count + is provided for each category like 'build/class'. +""" + +# We categorize each error message we print. Here are the categories. +# We want an explicit list so we can list them all in cpplint --filter=. +# If you add a new error message with a new category, add it to the list +# here! cpplint_unittest.py should tell you if you forget to do this. +# \ used for clearer layout -- pylint: disable-msg=C6013 +_ERROR_CATEGORIES = [ + 'build/class', + 'build/deprecated', + 'build/endif_comment', + 'build/explicit_make_pair', + 'build/forward_decl', + 'build/header_guard', + 'build/include', + 'build/include_alpha', + 'build/include_order', + 'build/include_what_you_use', + 'build/namespaces', + 'build/printf_format', + 'build/storage_class', + 'legal/copyright', + 'readability/braces', + 'readability/casting', + 'readability/check', + 'readability/constructors', + 'readability/fn_size', + 'readability/function', + 'readability/multiline_comment', + 'readability/multiline_string', + 'readability/nolint', + 'readability/streams', + 'readability/todo', + 'readability/utf8', + 'runtime/arrays', + 'runtime/casting', + 'runtime/explicit', + 'runtime/int', + 'runtime/init', + 'runtime/invalid_increment', + 'runtime/member_string_references', + 'runtime/memset', + 'runtime/operator', + 'runtime/printf', + 'runtime/printf_format', + 'runtime/references', + 'runtime/rtti', + 'runtime/sizeof', + 'runtime/string', + 'runtime/threadsafe_fn', + 'runtime/virtual', + 'whitespace/blank_line', + 'whitespace/braces', + 'whitespace/comma', + 'whitespace/comments', + 'whitespace/end_of_line', + 'whitespace/ending_newline', + 'whitespace/indent', + 'whitespace/labels', + 'whitespace/line_length', + 'whitespace/newline', + 'whitespace/operators', + 'whitespace/parens', + 'whitespace/semicolon', + 'whitespace/tab', + 'whitespace/todo', + 'whitespace/use_tab_for_indentation' + ] + +# The default state of the category filter. This is overrided by the --filter= +# flag. By default all errors are on, so only add here categories that should be +# off by default (i.e., categories that must be enabled by the --filter= flags). +# All entries here should start with a '-' or '+', as in the --filter= flag. +_DEFAULT_FILTERS = [ + '-build/include_alpha', + '-whitespace/use_tab_for_indentation' +] + +# We used to check for high-bit characters, but after much discussion we +# decided those were OK, as long as they were in UTF-8 and didn't represent +# hard-coded international strings, which belong in a separate i18n file. + +# Headers that we consider STL headers. +_STL_HEADERS = frozenset([ + 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', + 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', + 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new', + 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', + 'stl_alloc.h', 'stl_relops.h', 'type_traits.h', + 'utility', 'vector', 'vector.h', + ]) + + +# Non-STL C++ system headers. +_CPP_HEADERS = frozenset([ + 'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', + 'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', + 'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', + 'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', + 'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', + 'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', + 'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', + 'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', + 'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', + 'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', + 'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', + 'stdiostream.h', 'streambuf.h', 'stream.h', 'strfile.h', 'string', + 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'valarray', + ]) + + +# Assertion macros. These are defined in base/logging.h and +# testing/base/gunit.h. Note that the _M versions need to come first +# for substring matching to work. +_CHECK_MACROS = [ + 'DCHECK', 'CHECK', + 'EXPECT_TRUE_M', 'EXPECT_TRUE', + 'ASSERT_TRUE_M', 'ASSERT_TRUE', + 'EXPECT_FALSE_M', 'EXPECT_FALSE', + 'ASSERT_FALSE_M', 'ASSERT_FALSE', + ] + +# Replacement macros for CHECK/DCHECK/EXPECT_TRUE/EXPECT_FALSE +_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS]) + +for op, replacement in [('==', 'EQ'), ('!=', 'NE'), + ('>=', 'GE'), ('>', 'GT'), + ('<=', 'LE'), ('<', 'LT')]: + _CHECK_REPLACEMENT['DCHECK'][op] = 'DCHECK_%s' % replacement + _CHECK_REPLACEMENT['CHECK'][op] = 'CHECK_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE'][op] = 'EXPECT_%s' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE'][op] = 'ASSERT_%s' % replacement + _CHECK_REPLACEMENT['EXPECT_TRUE_M'][op] = 'EXPECT_%s_M' % replacement + _CHECK_REPLACEMENT['ASSERT_TRUE_M'][op] = 'ASSERT_%s_M' % replacement + +for op, inv_replacement in [('==', 'NE'), ('!=', 'EQ'), + ('>=', 'LT'), ('>', 'LE'), + ('<=', 'GT'), ('<', 'GE')]: + _CHECK_REPLACEMENT['EXPECT_FALSE'][op] = 'EXPECT_%s' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE'][op] = 'ASSERT_%s' % inv_replacement + _CHECK_REPLACEMENT['EXPECT_FALSE_M'][op] = 'EXPECT_%s_M' % inv_replacement + _CHECK_REPLACEMENT['ASSERT_FALSE_M'][op] = 'ASSERT_%s_M' % inv_replacement + + +# These constants define types of headers for use with +# _IncludeState.CheckNextIncludeOrder(). +_C_SYS_HEADER = 1 +_CPP_SYS_HEADER = 2 +_LIKELY_MY_HEADER = 3 +_POSSIBLE_MY_HEADER = 4 +_OTHER_HEADER = 5 + + +_regexp_compile_cache = {} + +# Finds occurrences of NOLINT or NOLINT(...). +_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?') + +# {str, set(int)}: a map from error categories to sets of linenumbers +# on which those errors are expected and should be suppressed. +_error_suppressions = {} + +def ParseNolintSuppressions(filename, raw_line, linenum, error): + """Updates the global list of error-suppressions. + + Parses any NOLINT comments on the current line, updating the global + error_suppressions store. Reports an error if the NOLINT comment + was malformed. + + Args: + filename: str, the name of the input file. + raw_line: str, the line of input text, with comments. + linenum: int, the number of the current line. + error: function, an error handler. + """ + # FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*). + matched = _RE_SUPPRESSION.search(raw_line) + if matched: + category = matched.group(1) + if category in (None, '(*)'): # => "suppress all" + _error_suppressions.setdefault(None, set()).add(linenum) + else: + if category.startswith('(') and category.endswith(')'): + category = category[1:-1] + if category in _ERROR_CATEGORIES: + _error_suppressions.setdefault(category, set()).add(linenum) + else: + error(filename, linenum, 'readability/nolint', 5, + 'Unknown NOLINT error category: %s' % category) + + +def ResetNolintSuppressions(): + "Resets the set of NOLINT suppressions to empty." + _error_suppressions.clear() + + +def IsErrorSuppressedByNolint(category, linenum): + """Returns true if the specified error category is suppressed on this line. + + Consults the global error_suppressions map populated by + ParseNolintSuppressions/ResetNolintSuppressions. + + Args: + category: str, the category of the error. + linenum: int, the current line number. + Returns: + bool, True iff the error should be suppressed due to a NOLINT comment. + """ + return (linenum in _error_suppressions.get(category, set()) or + linenum in _error_suppressions.get(None, set())) + +def Match(pattern, s): + """Matches the string with the pattern, caching the compiled regexp.""" + # The regexp compilation caching is inlined in both Match and Search for + # performance reasons; factoring it out into a separate function turns out + # to be noticeably expensive. + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].match(s) + + +def Search(pattern, s): + """Searches the string for the pattern, caching the compiled regexp.""" + if not pattern in _regexp_compile_cache: + _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + return _regexp_compile_cache[pattern].search(s) + + +class _IncludeState(dict): + """Tracks line numbers for includes, and the order in which includes appear. + + As a dict, an _IncludeState object serves as a mapping between include + filename and line number on which that file was included. + + Call CheckNextIncludeOrder() once for each header in the file, passing + in the type constants defined above. Calls in an illegal order will + raise an _IncludeError with an appropriate error message. + + """ + # self._section will move monotonically through this set. If it ever + # needs to move backwards, CheckNextIncludeOrder will raise an error. + _INITIAL_SECTION = 0 + _MY_H_SECTION = 1 + _C_SECTION = 2 + _CPP_SECTION = 3 + _OTHER_H_SECTION = 4 + + _TYPE_NAMES = { + _C_SYS_HEADER: 'C system header', + _CPP_SYS_HEADER: 'C++ system header', + _LIKELY_MY_HEADER: 'header this file implements', + _POSSIBLE_MY_HEADER: 'header this file may implement', + _OTHER_HEADER: 'other header', + } + _SECTION_NAMES = { + _INITIAL_SECTION: "... nothing. (This can't be an error.)", + _MY_H_SECTION: 'a header this file implements', + _C_SECTION: 'C system header', + _CPP_SECTION: 'C++ system header', + _OTHER_H_SECTION: 'other header', + } + + def __init__(self): + dict.__init__(self) + # The name of the current section. + self._section = self._INITIAL_SECTION + # The path of last found header. + self._last_header = '' + + def CanonicalizeAlphabeticalOrder(self, header_path): + """Returns a path canonicalized for alphabetical comparison. + + - replaces "-" with "_" so they both cmp the same. + - removes '-inl' since we don't require them to be after the main header. + - lowercase everything, just in case. + + Args: + header_path: Path to be canonicalized. + + Returns: + Canonicalized path. + """ + return header_path.replace('-inl.h', '.h').replace('-', '_').lower() + + def IsInAlphabeticalOrder(self, header_path): + """Check if a header is in alphabetical order with the previous header. + + Args: + header_path: Header to be checked. + + Returns: + Returns true if the header is in alphabetical order. + """ + canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) + if self._last_header > canonical_header: + return False + self._last_header = canonical_header + return True + + def CheckNextIncludeOrder(self, header_type): + """Returns a non-empty error message if the next header is out of order. + + This function also updates the internal state to be ready to check + the next include. + + Args: + header_type: One of the _XXX_HEADER constants defined above. + + Returns: + The empty string if the header is in the right order, or an + error message describing what's wrong. + + """ + error_message = ('Found %s after %s' % + (self._TYPE_NAMES[header_type], + self._SECTION_NAMES[self._section])) + + last_section = self._section + + if header_type == _C_SYS_HEADER: + if self._section <= self._C_SECTION: + self._section = self._C_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _CPP_SYS_HEADER: + if self._section <= self._CPP_SECTION: + self._section = self._CPP_SECTION + else: + self._last_header = '' + return error_message + elif header_type == _LIKELY_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + self._section = self._OTHER_H_SECTION + elif header_type == _POSSIBLE_MY_HEADER: + if self._section <= self._MY_H_SECTION: + self._section = self._MY_H_SECTION + else: + # This will always be the fallback because we're not sure + # enough that the header is associated with this file. + self._section = self._OTHER_H_SECTION + else: + assert header_type == _OTHER_HEADER + self._section = self._OTHER_H_SECTION + + if last_section != self._section: + self._last_header = '' + + return '' + + +class _CppLintState(object): + """Maintains module-wide state..""" + + def __init__(self): + self.verbose_level = 1 # global setting. + self.error_count = 0 # global count of reported errors + # filters to apply when emitting error messages + self.filters = _DEFAULT_FILTERS[:] + self.counting = 'total' # In what way are we counting errors? + self.errors_by_category = {} # string to int dict storing error counts + + # output format: + # "emacs" - format that emacs can parse (default) + # "vs7" - format that Microsoft Visual Studio 7 can parse + self.output_format = 'emacs' + + def SetOutputFormat(self, output_format): + """Sets the output format for errors.""" + self.output_format = output_format + + def SetVerboseLevel(self, level): + """Sets the module's verbosity, and returns the previous setting.""" + last_verbose_level = self.verbose_level + self.verbose_level = level + return last_verbose_level + + def SetCountingStyle(self, counting_style): + """Sets the module's counting options.""" + self.counting = counting_style + + def SetFilters(self, filters): + """Sets the error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "+whitespace/indent"). + Each filter should start with + or -; else we die. + + Raises: + ValueError: The comma-separated filters did not all start with '+' or '-'. + E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter" + """ + # Default filters always have less priority than the flag ones. + self.filters = _DEFAULT_FILTERS[:] + for filt in filters.split(','): + clean_filt = filt.strip() + if clean_filt: + self.filters.append(clean_filt) + for filt in self.filters: + if not (filt.startswith('+') or filt.startswith('-')): + raise ValueError('Every filter in --filters must start with + or -' + ' (%s does not)' % filt) + + def ResetErrorCounts(self): + """Sets the module's error statistic back to zero.""" + self.error_count = 0 + self.errors_by_category = {} + + def IncrementErrorCount(self, category): + """Bumps the module's error statistic.""" + self.error_count += 1 + if self.counting in ('toplevel', 'detailed'): + if self.counting != 'detailed': + category = category.split('/')[0] + if category not in self.errors_by_category: + self.errors_by_category[category] = 0 + self.errors_by_category[category] += 1 + + def PrintErrorCounts(self): + """Print a summary of errors by category, and the total.""" + for category, count in self.errors_by_category.iteritems(): + sys.stderr.write('Category \'%s\' errors found: %d\n' % + (category, count)) + sys.stderr.write('Total errors found: %d\n' % self.error_count) + +_cpplint_state = _CppLintState() + + +def _OutputFormat(): + """Gets the module's output format.""" + return _cpplint_state.output_format + + +def _SetOutputFormat(output_format): + """Sets the module's output format.""" + _cpplint_state.SetOutputFormat(output_format) + + +def _VerboseLevel(): + """Returns the module's verbosity setting.""" + return _cpplint_state.verbose_level + + +def _SetVerboseLevel(level): + """Sets the module's verbosity, and returns the previous setting.""" + return _cpplint_state.SetVerboseLevel(level) + + +def _SetCountingStyle(level): + """Sets the module's counting options.""" + _cpplint_state.SetCountingStyle(level) + + +def _Filters(): + """Returns the module's list of output filters, as a list.""" + return _cpplint_state.filters + + +def _SetFilters(filters): + """Sets the module's error-message filters. + + These filters are applied when deciding whether to emit a given + error message. + + Args: + filters: A string of comma-separated filters (eg "whitespace/indent"). + Each filter should start with + or -; else we die. + """ + _cpplint_state.SetFilters(filters) + + +class _FunctionState(object): + """Tracks current function name and the number of lines in its body.""" + + _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. + _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. + + def __init__(self): + self.in_a_function = False + self.lines_in_function = 0 + self.current_function = '' + + def Begin(self, function_name): + """Start analyzing function body. + + Args: + function_name: The name of the function being tracked. + """ + self.in_a_function = True + self.lines_in_function = 0 + self.current_function = function_name + + def Count(self): + """Count line in current function body.""" + if self.in_a_function: + self.lines_in_function += 1 + + def Check(self, error, filename, linenum): + """Report if too many lines in function body. + + Args: + error: The function to call with any errors found. + filename: The name of the current file. + linenum: The number of the line to check. + """ + if Match(r'T(EST|est)', self.current_function): + base_trigger = self._TEST_TRIGGER + else: + base_trigger = self._NORMAL_TRIGGER + trigger = base_trigger * 2**_VerboseLevel() + + if self.lines_in_function > trigger: + error_level = int(math.log(self.lines_in_function / base_trigger, 2)) + # 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ... + if error_level > 5: + error_level = 5 + error(filename, linenum, 'readability/fn_size', error_level, + 'Small and focused functions are preferred:' + ' %s has %d non-comment lines' + ' (error triggered by exceeding %d lines).' % ( + self.current_function, self.lines_in_function, trigger)) + + def End(self): + """Stop analyzing function body.""" + self.in_a_function = False + + +class _IncludeError(Exception): + """Indicates a problem with the include order in a file.""" + pass + + +class FileInfo: + """Provides utility functions for filenames. + + FileInfo provides easy access to the components of a file's path + relative to the project root. + """ + + def __init__(self, filename): + self._filename = filename + + def FullName(self): + """Make Windows paths like Unix.""" + return os.path.abspath(self._filename).replace('\\', '/') + + def RepositoryName(self): + """FullName after removing the local path to the repository. + + If we have a real absolute path name here we can try to do something smart: + detecting the root of the checkout and truncating /path/to/checkout from + the name so that we get header guards that don't include things like + "C:\Documents and Settings\..." or "/home/username/..." in them and thus + people on different computers who have checked the source out to different + locations won't see bogus errors. + """ + fullname = self.FullName() + + if os.path.exists(fullname): + project_dir = os.path.dirname(fullname) + + if os.path.exists(os.path.join(project_dir, ".svn")): + # If there's a .svn file in the current directory, we recursively look + # up the directory tree for the top of the SVN checkout + root_dir = project_dir + one_up_dir = os.path.dirname(root_dir) + while os.path.exists(os.path.join(one_up_dir, ".svn")): + root_dir = os.path.dirname(root_dir) + one_up_dir = os.path.dirname(one_up_dir) + + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by + # searching up from the current path. + root_dir = os.path.dirname(fullname) + while (root_dir != os.path.dirname(root_dir) and + not os.path.exists(os.path.join(root_dir, ".git")) and + not os.path.exists(os.path.join(root_dir, ".hg")) and + not os.path.exists(os.path.join(root_dir, ".svn"))): + root_dir = os.path.dirname(root_dir) + + if (os.path.exists(os.path.join(root_dir, ".git")) or + os.path.exists(os.path.join(root_dir, ".hg")) or + os.path.exists(os.path.join(root_dir, ".svn"))): + prefix = os.path.commonprefix([root_dir, project_dir]) + return fullname[len(prefix) + 1:] + + # Don't know what to do; header guard warnings may be wrong... + return fullname + + def Split(self): + """Splits the file into the directory, basename, and extension. + + For 'chrome/browser/browser.cc', Split() would + return ('chrome/browser', 'browser', '.cc') + + Returns: + A tuple of (directory, basename, extension). + """ + + googlename = self.RepositoryName() + project, rest = os.path.split(googlename) + return (project,) + os.path.splitext(rest) + + def BaseName(self): + """File base name - text after the final slash, before the final period.""" + return self.Split()[1] + + def Extension(self): + """File extension - text following the final period.""" + return self.Split()[2] + + def NoExtension(self): + """File has no source file extension.""" + return '/'.join(self.Split()[0:2]) + + def IsSource(self): + """File has a source file extension.""" + return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') + + +def _ShouldPrintError(category, confidence, linenum): + """If confidence >= verbose, category passes filter and is not suppressed.""" + + # There are three ways we might decide not to print an error message: + # a "NOLINT(category)" comment appears in the source, + # the verbosity level isn't high enough, or the filters filter it out. + if IsErrorSuppressedByNolint(category, linenum): + return False + if confidence < _cpplint_state.verbose_level: + return False + + is_filtered = False + for one_filter in _Filters(): + if one_filter.startswith('-'): + if category.startswith(one_filter[1:]): + is_filtered = True + elif one_filter.startswith('+'): + if category.startswith(one_filter[1:]): + is_filtered = False + else: + assert False # should have been checked for in SetFilter. + if is_filtered: + return False + + return True + + +def Error(filename, linenum, category, confidence, message): + """Logs the fact we've found a lint error. + + We log where the error was found, and also our confidence in the error, + that is, how certain we are this is a legitimate style regression, and + not a misidentification or a use that's sometimes justified. + + False positives can be suppressed by the use of + "cpplint(category)" comments on the offending line. These are + parsed into _error_suppressions. + + Args: + filename: The name of the file containing the error. + linenum: The number of the line containing the error. + category: A string used to describe the "category" this bug + falls under: "whitespace", say, or "runtime". Categories + may have a hierarchy separated by slashes: "whitespace/indent". + confidence: A number from 1-5 representing a confidence score for + the error, with 5 meaning that we are certain of the problem, + and 1 meaning that it could be a legitimate construct. + message: The error message. + """ + if _ShouldPrintError(category, confidence, linenum): + _cpplint_state.IncrementErrorCount(category) + if _cpplint_state.output_format == 'vs7': + sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + else: + sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + filename, linenum, message, category, confidence)) + + +# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard. +_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile( + r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)') +# Matches strings. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES = re.compile(r'"[^"]*"') +# Matches characters. Escape codes should already be removed by ESCAPES. +_RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES = re.compile(r"'.'") +# Matches multi-line C++ comments. +# This RE is a little bit more complicated than one might expect, because we +# have to take care of space removals tools so we can handle comments inside +# statements better. +# The current rule is: We only clear spaces from both sides when we're at the +# end of the line. Otherwise, we try to remove spaces from the right side, +# if this doesn't work we try on left side but only if there's a non-character +# on the right. +_RE_PATTERN_CLEANSE_LINE_C_COMMENTS = re.compile( + r"""(\s*/\*.*\*/\s*$| + /\*.*\*/\s+| + \s+/\*.*\*/(?=\W)| + /\*.*\*/)""", re.VERBOSE) + + +def IsCppString(line): + """Does line terminate so, that the next symbol is in string constant. + + This function does not consider single-line nor multi-line comments. + + Args: + line: is a partial line of code starting from the 0..n. + + Returns: + True, if next character appended to 'line' is inside a + string constant. + """ + + line = line.replace(r'\\', 'XX') # after this, \\" does not match to \" + return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1 + + +def FindNextMultiLineCommentStart(lines, lineix): + """Find the beginning marker for a multiline comment.""" + while lineix < len(lines): + if lines[lineix].strip().startswith('/*'): + # Only return this marker if the comment goes beyond this line + if lines[lineix].strip().find('*/', 2) < 0: + return lineix + lineix += 1 + return len(lines) + + +def FindNextMultiLineCommentEnd(lines, lineix): + """We are inside a comment, find the end marker.""" + while lineix < len(lines): + if lines[lineix].strip().endswith('*/'): + return lineix + lineix += 1 + return len(lines) + + +def RemoveMultiLineCommentsFromRange(lines, begin, end): + """Clears a range of lines for multi-line comments.""" + # Having // dummy comments makes the lines non-empty, so we will not get + # unnecessary blank line warnings later in the code. + for i in range(begin, end): + lines[i] = '// dummy' + + +def RemoveMultiLineComments(filename, lines, error): + """Removes multiline (c-style) comments from lines.""" + lineix = 0 + while lineix < len(lines): + lineix_begin = FindNextMultiLineCommentStart(lines, lineix) + if lineix_begin >= len(lines): + return + lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin) + if lineix_end >= len(lines): + error(filename, lineix_begin + 1, 'readability/multiline_comment', 5, + 'Could not find end of multi-line comment') + return + RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1) + lineix = lineix_end + 1 + + +def CleanseComments(line): + """Removes //-comments and single-line C-style /* */ comments. + + Args: + line: A line of C++ source. + + Returns: + The line with single-line comments removed. + """ + commentpos = line.find('//') + if commentpos != -1 and not IsCppString(line[:commentpos]): + line = line[:commentpos].rstrip() + # get rid of /* ... */ + return _RE_PATTERN_CLEANSE_LINE_C_COMMENTS.sub('', line) + + +class CleansedLines(object): + """Holds 3 copies of all lines with different preprocessing applied to them. + + 1) elided member contains lines without strings and comments, + 2) lines member contains lines without comments, and + 3) raw member contains all the lines without processing. + All these three members are of , and of the same length. + """ + + def __init__(self, lines): + self.elided = [] + self.lines = [] + self.raw_lines = lines + self.num_lines = len(lines) + for linenum in range(len(lines)): + self.lines.append(CleanseComments(lines[linenum])) + elided = self._CollapseStrings(lines[linenum]) + self.elided.append(CleanseComments(elided)) + + def NumLines(self): + """Returns the number of lines represented.""" + return self.num_lines + + @staticmethod + def _CollapseStrings(elided): + """Collapses strings and chars on a line to simple "" or '' blocks. + + We nix strings first so we're not fooled by text like '"http://"' + + Args: + elided: The line being processed. + + Returns: + The line with collapsed strings. + """ + if not _RE_PATTERN_INCLUDE.match(elided): + # Remove escaped characters first to make quote/single quote collapsing + # basic. Things that look like escaped characters shouldn't occur + # outside of strings and chars. + elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided) + elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided) + elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided) + return elided + + +def CloseExpression(clean_lines, linenum, pos): + """If input points to ( or { or [, finds the position that closes it. + + If lines[linenum][pos] points to a '(' or '{' or '[', finds the + linenum/pos that correspond to the closing of the expression. + + Args: + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + pos: A position on the line. + + Returns: + A tuple (line, linenum, pos) pointer *past* the closing brace, or + (line, len(lines), -1) if we never find a close. Note we ignore + strings and comments when matching; and the line we return is the + 'cleansed' line at linenum. + """ + + line = clean_lines.elided[linenum] + startchar = line[pos] + if startchar not in '({[': + return (line, clean_lines.NumLines(), -1) + if startchar == '(': endchar = ')' + if startchar == '[': endchar = ']' + if startchar == '{': endchar = '}' + + num_open = line.count(startchar) - line.count(endchar) + while linenum < clean_lines.NumLines() and num_open > 0: + linenum += 1 + line = clean_lines.elided[linenum] + num_open += line.count(startchar) - line.count(endchar) + # OK, now find the endchar that actually got us back to even + endpos = len(line) + while num_open >= 0: + endpos = line.rfind(')', 0, endpos) + num_open -= 1 # chopped off another ) + return (line, linenum, endpos + 1) + + +def CheckForCopyright(filename, lines, error): + """Logs an error if no Copyright message appears at the top of the file.""" + + # We'll say it should occur by line 10. Don't forget there's a + # dummy line at the front. + for line in xrange(1, min(len(lines), 11)): + if re.search(r'Copyright', lines[line], re.I): break + else: # means no copyright line was found + error(filename, 0, 'legal/copyright', 5, + 'No copyright message found. ' + 'You should have a line: "Copyright [year] "') + + +def GetHeaderGuardCPPVariable(filename): + """Returns the CPP variable that should be used as a header guard. + + Args: + filename: The name of a C++ header file. + + Returns: + The CPP variable that should be used as a header guard in the + named file. + + """ + + # Restores original filename in case that cpplint is invoked from Emacs's + # flymake. + filename = re.sub(r'_flymake\.h$', '.h', filename) + + fileinfo = FileInfo(filename) + return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_' + + +def CheckForHeaderGuard(filename, lines, error): + """Checks that the file contains a header guard. + + Logs an error if no #ifndef header guard is present. For other + headers, checks that the full pathname is used. + + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + cppvar = GetHeaderGuardCPPVariable(filename) + + ifndef = None + ifndef_linenum = 0 + define = None + endif = None + endif_linenum = 0 + for linenum, line in enumerate(lines): + linesplit = line.split() + if len(linesplit) >= 2: + # find the first occurrence of #ifndef and #define, save arg + if not ifndef and linesplit[0] == '#ifndef': + # set ifndef to the header guard presented on the #ifndef line. + ifndef = linesplit[1] + ifndef_linenum = linenum + if not define and linesplit[0] == '#define': + define = linesplit[1] + # find the last occurrence of #endif, save entire line + if line.startswith('#endif'): + endif = line + endif_linenum = linenum + + if not ifndef: + error(filename, 0, 'build/header_guard', 5, + 'No #ifndef header guard found, suggested CPP variable is: %s' % + cppvar) + return + + if not define: + error(filename, 0, 'build/header_guard', 5, + 'No #define header guard found, suggested CPP variable is: %s' % + cppvar) + return + + # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ + # for backward compatibility. + if ifndef != cppvar: + error_level = 0 + if ifndef != cppvar + '_': + error_level = 5 + + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum, + error) + error(filename, ifndef_linenum, 'build/header_guard', error_level, + '#ifndef header guard has wrong style, please use: %s' % cppvar) + + if define != ifndef: + error(filename, 0, 'build/header_guard', 5, + '#ifndef and #define don\'t match, suggested CPP variable is: %s' % + cppvar) + return + + if endif != ('#endif // %s' % cppvar): + error_level = 0 + if endif != ('#endif // %s' % (cppvar + '_')): + error_level = 5 + + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum, + error) + error(filename, endif_linenum, 'build/header_guard', error_level, + '#endif line should be "#endif // %s"' % cppvar) + + +def CheckForUnicodeReplacementCharacters(filename, lines, error): + """Logs an error for each line containing Unicode replacement characters. + + These indicate that either the file contained invalid UTF-8 (likely) + or Unicode replacement characters (which it shouldn't). Note that + it's possible for this to throw off line numbering if the invalid + UTF-8 occurred adjacent to a newline. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + for linenum, line in enumerate(lines): + if u'\ufffd' in line: + error(filename, linenum, 'readability/utf8', 5, + 'Line contains invalid UTF-8 (or Unicode replacement character).') + + +def CheckForNewlineAtEOF(filename, lines, error): + """Logs an error if there is no newline char at the end of the file. + + Args: + filename: The name of the current file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + + # The array lines() was created by adding two newlines to the + # original file (go figure), then splitting on \n. + # To verify that the file ends in \n, we just have to make sure the + # last-but-two element of lines() exists and is empty. + if len(lines) < 3 or lines[-2]: + error(filename, len(lines) - 2, 'whitespace/ending_newline', 5, + 'Could not find a newline character at the end of the file.') + + +def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error): + """Logs an error if we see /* ... */ or "..." that extend past one line. + + /* ... */ comments are legit inside macros, for one line. + Otherwise, we prefer // comments, so it's ok to warn about the + other. Likewise, it's ok for strings to extend across multiple + lines, as long as a line continuation character (backslash) + terminates each line. Although not currently prohibited by the C++ + style guide, it's ugly and unnecessary. We don't do well with either + in this lint program, so we warn about both. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + + # Remove all \\ (escaped backslashes) from the line. They are OK, and the + # second (escaped) slash may trigger later \" detection erroneously. + line = line.replace('\\\\', '') + + if line.count('/*') > line.count('*/'): + error(filename, linenum, 'readability/multiline_comment', 5, + 'Complex multi-line /*...*/-style comment found. ' + 'Lint may give bogus warnings. ' + 'Consider replacing these with //-style comments, ' + 'with #if 0...#endif, ' + 'or with more clearly structured multi-line comments.') + + if (line.count('"') - line.count('\\"')) % 2: + error(filename, linenum, 'readability/multiline_string', 5, + 'Multi-line string ("...") found. This lint script doesn\'t ' + 'do well with such strings, and may give bogus warnings. They\'re ' + 'ugly and unnecessary, and you should use concatenation instead".') + + +threading_list = ( + ('asctime(', 'asctime_r('), + ('ctime(', 'ctime_r('), + ('getgrgid(', 'getgrgid_r('), + ('getgrnam(', 'getgrnam_r('), + ('getlogin(', 'getlogin_r('), + ('getpwnam(', 'getpwnam_r('), + ('getpwuid(', 'getpwuid_r('), + ('gmtime(', 'gmtime_r('), + ('localtime(', 'localtime_r('), + ('rand(', 'rand_r('), + ('readdir(', 'readdir_r('), + ('strtok(', 'strtok_r('), + ('ttyname(', 'ttyname_r('), + ) + + +def CheckPosixThreading(filename, clean_lines, linenum, error): + """Checks for calls to thread-unsafe functions. + + Much code has been originally written without consideration of + multi-threading. Also, engineers are relying on their old experience; + they have learned posix before threading extensions were added. These + tests guide the engineers to use thread-safe functions (when using + posix directly). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + for single_thread_function, multithread_safe_function in threading_list: + ix = line.find(single_thread_function) + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and + line[ix - 1] not in ('_', '.', '>'))): + error(filename, linenum, 'runtime/threadsafe_fn', 2, + 'Consider using ' + multithread_safe_function + + '...) instead of ' + single_thread_function + + '...) for improved thread safety.') + + +# Matches invalid increment: *count++, which moves pointer instead of +# incrementing a value. +_RE_PATTERN_INVALID_INCREMENT = re.compile( + r'^\s*\*\w+(\+\+|--);') + + +def CheckInvalidIncrement(filename, clean_lines, linenum, error): + """Checks for invalid increment *count++. + + For example following function: + void increment_counter(int* count) { + *count++; + } + is invalid, because it effectively does count++, moving pointer, and should + be replaced with ++*count, (*count)++ or *count += 1. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + line = clean_lines.elided[linenum] + if _RE_PATTERN_INVALID_INCREMENT.match(line): + error(filename, linenum, 'runtime/invalid_increment', 5, + 'Changing pointer instead of value (or unused value of operator*).') + + +class _ClassInfo(object): + """Stores information about a class.""" + + def __init__(self, name, clean_lines, linenum): + self.name = name + self.linenum = linenum + self.seen_open_brace = False + self.is_derived = False + self.virtual_method_linenumber = None + self.has_virtual_destructor = False + self.brace_depth = 0 + + # Try to find the end of the class. This will be confused by things like: + # class A { + # } *x = { ... + # + # But it's still good enough for CheckSectionSpacing. + self.last_line = 0 + depth = 0 + for i in range(linenum, clean_lines.NumLines()): + line = clean_lines.lines[i] + depth += line.count('{') - line.count('}') + if not depth: + self.last_line = i + break + + +class _ClassState(object): + """Holds the current state of the parse relating to class declarations. + + It maintains a stack of _ClassInfos representing the parser's guess + as to the current nesting of class declarations. The innermost class + is at the top (back) of the stack. Typically, the stack will either + be empty or have exactly one entry. + """ + + def __init__(self): + self.classinfo_stack = [] + + def CheckFinished(self, filename, error): + """Checks that all classes have been completely parsed. + + Call this when all lines in a file have been processed. + Args: + filename: The name of the current file. + error: The function to call with any errors found. + """ + if self.classinfo_stack: + # Note: This test can result in false positives if #ifdef constructs + # get in the way of brace matching. See the testBuildClass test in + # cpplint_unittest.py for an example of this. + error(filename, self.classinfo_stack[0].linenum, 'build/class', 5, + 'Failed to find complete declaration of class %s' % + self.classinfo_stack[0].name) + + +def CheckForNonStandardConstructs(filename, clean_lines, linenum, + class_state, error): + """Logs an error if we see certain non-ANSI constructs ignored by gcc-2. + + Complain about several constructs which gcc-2 accepts, but which are + not standard C++. Warning about these in lint is one way to ease the + transition to new compilers. + - put storage class first (e.g. "static const" instead of "const static"). + - "%lld" instead of %qd" in printf-type functions. + - "%1$d" is non-standard in printf-type functions. + - "\%" is an undefined character escape sequence. + - text after #endif is not allowed. + - invalid inner-style forward declaration. + - >? and ?= and )\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', + line): + error(filename, linenum, 'build/deprecated', 3, + '>? and ))?' + # r'\s*const\s*' + type_name + '\s*&\s*\w+\s*;' + error(filename, linenum, 'runtime/member_string_references', 2, + 'const string& members are dangerous. It is much better to use ' + 'alternatives, such as pointers or simple constants.') + + # Track class entry and exit, and attempt to find cases within the + # class declaration that don't meet the C++ style + # guidelines. Tracking is very dependent on the code matching Google + # style guidelines, but it seems to perform well enough in testing + # to be a worthwhile addition to the checks. + classinfo_stack = class_state.classinfo_stack + # Look for a class declaration. The regexp accounts for decorated classes + # such as in: + # class LOCKABLE API Object { + # }; + class_decl_match = Match( + r'\s*(template\s*<[\w\s<>,:]*>\s*)?' + '(class|struct)\s+([A-Z_]+\s+)*(\w+(::\w+)*)', line) + if class_decl_match: + classinfo_stack.append(_ClassInfo( + class_decl_match.group(4), clean_lines, linenum)) + + # Everything else in this function uses the top of the stack if it's + # not empty. + if not classinfo_stack: + return + + classinfo = classinfo_stack[-1] + + # If the opening brace hasn't been seen look for it and also + # parent class declarations. + if not classinfo.seen_open_brace: + # If the line has a ';' in it, assume it's a forward declaration or + # a single-line class declaration, which we won't process. + if line.find(';') != -1: + classinfo_stack.pop() + return + classinfo.seen_open_brace = (line.find('{') != -1) + # Look for a bare ':' + if Search('(^|[^:]):($|[^:])', line): + classinfo.is_derived = True + if not classinfo.seen_open_brace: + return # Everything else in this function is for after open brace + + # The class may have been declared with namespace or classname qualifiers. + # The constructor and destructor will not have those qualifiers. + base_classname = classinfo.name.split('::')[-1] + + # Look for single-argument constructors that aren't marked explicit. + # Technically a valid construct, but against style. + args = Match(r'\s+(?:inline\s+)?%s\s*\(([^,()]+)\)' + % re.escape(base_classname), + line) + if (args and + args.group(1) != 'void' and + not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), + args.group(1).strip())): + error(filename, linenum, 'runtime/explicit', 5, + 'Single-argument constructors should be marked explicit.') + + # Look for methods declared virtual. + if Search(r'\bvirtual\b', line): + classinfo.virtual_method_linenumber = linenum + # Only look for a destructor declaration on the same line. It would + # be extremely unlikely for the destructor declaration to occupy + # more than one line. + if Search(r'~%s\s*\(' % base_classname, line): + classinfo.has_virtual_destructor = True + + # Look for class end. + brace_depth = classinfo.brace_depth + brace_depth = brace_depth + line.count('{') - line.count('}') + if brace_depth <= 0: + classinfo = classinfo_stack.pop() + # Try to detect missing virtual destructor declarations. + # For now, only warn if a non-derived class with virtual methods lacks + # a virtual destructor. This is to make it less likely that people will + # declare derived virtual destructors without declaring the base + # destructor virtual. + if ((classinfo.virtual_method_linenumber is not None) and + (not classinfo.has_virtual_destructor) and + (not classinfo.is_derived)): # Only warn for base classes + error(filename, classinfo.linenum, 'runtime/virtual', 4, + 'The class %s probably needs a virtual destructor due to ' + 'having virtual method(s), one declared at line %d.' + % (classinfo.name, classinfo.virtual_method_linenumber)) + else: + classinfo.brace_depth = brace_depth + + +def CheckSpacingForFunctionCall(filename, line, linenum, error): + """Checks for the correctness of various spacing around function calls. + + Args: + filename: The name of the current file. + line: The text of the line to check. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Since function calls often occur inside if/for/while/switch + # expressions - which have their own, more liberal conventions - we + # first see if we should be looking inside such an expression for a + # function call, to which we can apply more strict standards. + fncall = line # if there's no control flow construct, look at whole line + for pattern in (r'\bif\s*\((.*)\)\s*{', + r'\bfor\s*\((.*)\)\s*{', + r'\bwhile\s*\((.*)\)\s*[{;]', + r'\bswitch\s*\((.*)\)\s*{'): + match = Search(pattern, line) + if match: + fncall = match.group(1) # look inside the parens for function calls + break + + # Except in if/for/while/switch, there should never be space + # immediately inside parens (eg "f( 3, 4 )"). We make an exception + # for nested parens ( (a+b) + c ). Likewise, there should never be + # a space before a ( when it's a function argument. I assume it's a + # function argument when the char before the whitespace is legal in + # a function name (alnum + _) and we're not starting a macro. Also ignore + # pointers and references to arrays and functions coz they're too tricky: + # we use a very simple way to recognize these: + # " (something)(maybe-something)" or + # " (something)(maybe-something," or + # " (something)[something]" + # Note that we assume the contents of [] to be short enough that + # they'll never need to wrap. + if ( # Ignore control structures. + not Search(r'\b(if|for|while|switch|return|delete)\b', fncall) and + # Ignore pointers/references to functions. + not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and + # Ignore pointers/references to arrays. + not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): + if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space after ( in function call') + elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space after (') + if (Search(r'\w\s+\(', fncall) and + not Search(r'#\s*define|typedef', fncall)): + error(filename, linenum, 'whitespace/parens', 4, + 'Extra space before ( in function call') + # If the ) is followed only by a newline or a { + newline, assume it's + # part of a control statement (if/while/etc), and don't complain + if Search(r'[^)]\s+\)\s*[^{\s]', fncall): + # If the closing parenthesis is preceded by only whitespaces, + # try to give a more descriptive error message. + if Search(r'^\s+\)', fncall): + error(filename, linenum, 'whitespace/parens', 2, + 'Closing ) should be moved to the previous line') + else: + error(filename, linenum, 'whitespace/parens', 2, + 'Extra space before )') + + +def IsBlankLine(line): + """Returns true if the given line is blank. + + We consider a line to be blank if the line is empty or consists of + only white spaces. + + Args: + line: A line of a string. + + Returns: + True, if the given line is blank. + """ + return not line or line.isspace() + + +def CheckForFunctionLengths(filename, clean_lines, linenum, + function_state, error): + """Reports for long function bodies. + + For an overview why this is done, see: + http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions + + Uses a simplistic algorithm assuming other style guidelines + (especially spacing) are followed. + Only checks unindented functions, so class members are unchecked. + Trivial bodies are unchecked, so constructors with huge initializer lists + may be missed. + Blank/comment lines are not counted so as to avoid encouraging the removal + of vertical space and comments just to get through a lint check. + NOLINT *on the last line of a function* disables this check. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + function_state: Current function name and lines in body so far. + error: The function to call with any errors found. + """ + lines = clean_lines.lines + line = lines[linenum] + raw = clean_lines.raw_lines + raw_line = raw[linenum] + joined_line = '' + + starting_func = False + regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... + match_result = Match(regexp, line) + if match_result: + # If the name is all caps and underscores, figure it's a macro and + # ignore it, unless it's TEST or TEST_F. + function_name = match_result.group(1).split()[-1] + if function_name == 'TEST' or function_name == 'TEST_F' or ( + not Match(r'[A-Z_]+$', function_name)): + starting_func = True + + if starting_func: + body_found = False + for start_linenum in xrange(linenum, clean_lines.NumLines()): + start_line = lines[start_linenum] + joined_line += ' ' + start_line.lstrip() + if Search(r'(;|})', start_line): # Declarations and trivial functions + body_found = True + break # ... ignore + elif Search(r'{', start_line): + body_found = True + function = Search(r'((\w|:)*)\(', line).group(1) + if Match(r'TEST', function): # Handle TEST... macros + parameter_regexp = Search(r'(\(.*\))', joined_line) + if parameter_regexp: # Ignore bad syntax + function += parameter_regexp.group(1) + else: + function += '()' + function_state.Begin(function) + break + if not body_found: + # No body for the function (or evidence of a non-function) was found. + error(filename, linenum, 'readability/fn_size', 5, + 'Lint failed to find start of function body.') + elif Match(r'^\}\s*$', line): # function end + function_state.Check(error, filename, linenum) + function_state.End() + elif not Match(r'^\s*$', line): + function_state.Count() # Count non-blank/non-comment lines. + + +_RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?') + + +def CheckComment(comment, filename, linenum, error): + """Checks for common mistakes in TODO comments. + + Args: + comment: The text of the comment from the line in question. + filename: The name of the current file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + match = _RE_PATTERN_TODO.match(comment) + if match: + # One whitespace is correct; zero whitespace is handled elsewhere. + leading_whitespace = match.group(1) + if len(leading_whitespace) > 1: + error(filename, linenum, 'whitespace/todo', 2, + 'Too many spaces before TODO') + + username = match.group(2) + if not username: + error(filename, linenum, 'readability/todo', 2, + 'Missing username in TODO; it should look like ' + '"// TODO(my_username): Stuff."') + + middle_whitespace = match.group(3) + # Comparisons made explicit for correctness -- pylint: disable-msg=C6403 + if middle_whitespace != ' ' and middle_whitespace != '': + error(filename, linenum, 'whitespace/todo', 2, + 'TODO(my_username) should be followed by a space') + + +def CheckSpacing(filename, clean_lines, linenum, error): + """Checks for the correctness of various spacing issues in the code. + + Things we check for: spaces around operators, spaces after + if/for/while/switch, no spaces around parens in function calls, two + spaces between code and comment, don't start a block with a blank + line, don't end a function with a blank line, don't add a blank line + after public/protected/private, don't have too many blank lines in a row. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + raw = clean_lines.raw_lines + line = raw[linenum] + + # Before nixing comments, check if the line is blank for no good + # reason. This includes the first line after a block is opened, and + # blank lines at the end of a function (ie, right before a line like '}' + if IsBlankLine(line): + elided = clean_lines.elided + prev_line = elided[linenum - 1] + prevbrace = prev_line.rfind('{') + # TODO(unknown): Don't complain if line before blank line, and line after, + # both start with alnums and are indented the same amount. + # This ignores whitespace at the start of a namespace block + # because those are not usually indented. + if (prevbrace != -1 and prev_line[prevbrace:].find('}') == -1 + and prev_line[:prevbrace].find('namespace') == -1): + # OK, we have a blank line at the start of a code block. Before we + # complain, we check if it is an exception to the rule: The previous + # non-empty line has the parameters of a function header that are indented + # 4 spaces (because they did not fit in a 80 column line when placed on + # the same line as the function name). We also check for the case where + # the previous line is indented 6 spaces, which may happen when the + # initializers of a constructor do not fit into a 80 column line. + exception = False + if Match(r' {6}\w', prev_line): # Initializer list? + # We are looking for the opening column of initializer list, which + # should be indented 4 spaces to cause 6 space indentation afterwards. + search_position = linenum-2 + while (search_position >= 0 + and Match(r' {6}\w', elided[search_position])): + search_position -= 1 + exception = (search_position >= 0 + and elided[search_position][:5] == ' :') + else: + # Search for the function arguments or an initializer list. We use a + # simple heuristic here: If the line is indented 4 spaces; and we have a + # closing paren, without the opening paren, followed by an opening brace + # or colon (for initializer lists) we assume that it is the last line of + # a function header. If we have a colon indented 4 spaces, it is an + # initializer list. + exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)', + prev_line) + or Match(r' {4}:', prev_line)) + + if not exception: + error(filename, linenum, 'whitespace/blank_line', 2, + 'Blank line at the start of a code block. Is this needed?') + # This doesn't ignore whitespace at the end of a namespace block + # because that is too hard without pairing open/close braces; + # however, a special exception is made for namespace closing + # brackets which have a comment containing "namespace". + # + # Also, ignore blank lines at the end of a block in a long if-else + # chain, like this: + # if (condition1) { + # // Something followed by a blank line + # + # } else if (condition2) { + # // Something else + # } + if linenum + 1 < clean_lines.NumLines(): + next_line = raw[linenum + 1] + if (next_line + and Match(r'\s*}', next_line) + and next_line.find('namespace') == -1 + and next_line.find('} else ') == -1): + error(filename, linenum, 'whitespace/blank_line', 3, + 'Blank line at the end of a code block. Is this needed?') + + matched = Match(r'\s*(public|protected|private):', prev_line) + if matched: + error(filename, linenum, 'whitespace/blank_line', 3, + 'Do not leave a blank line after "%s:"' % matched.group(1)) + + # Next, we complain if there's a comment too near the text + commentpos = line.find('//') + if commentpos != -1: + # Check if the // may be in quotes. If so, ignore it + # Comparisons made explicit for clarity -- pylint: disable-msg=C6403 + if (line.count('"', 0, commentpos) - + line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes + # Allow one space for new scopes, two spaces otherwise: + if (not Match(r'^\s*{ //', line) and + ((commentpos >= 1 and + line[commentpos-1] not in string.whitespace) or + (commentpos >= 2 and + line[commentpos-2] not in string.whitespace))): + error(filename, linenum, 'whitespace/comments', 2, + 'At least two spaces is best between code and comments') + # There should always be a space between the // and the comment + commentend = commentpos + 2 + if commentend < len(line) and not line[commentend] == ' ': + # but some lines are exceptions -- e.g. if they're big + # comment delimiters like: + # //---------------------------------------------------------- + # or are an empty C++ style Doxygen comment, like: + # /// + # or they begin with multiple slashes followed by a space: + # //////// Header comment + match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or + Search(r'^/$', line[commentend:]) or + Search(r'^/+ ', line[commentend:])) + if not match: + error(filename, linenum, 'whitespace/comments', 4, + 'Should have a space between // and comment') + CheckComment(line[commentpos:], filename, linenum, error) + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Don't try to do spacing checks for operator methods + line = re.sub(r'operator(==|!=|<|<<|<=|>=|>>|>)\(', 'operator\(', line) + + # We allow no-spaces around = within an if: "if ( (a=Foo()) == 0 )". + # Otherwise not. Note we only check for non-spaces on *both* sides; + # sometimes people put non-spaces on one side when aligning ='s among + # many lines (not that this is behavior that I approve of...) + if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): + error(filename, linenum, 'whitespace/operators', 4, + 'Missing spaces around =') + + # It's ok not to have spaces around binary operators like + - * /, but if + # there's too little whitespace, we get concerned. It's hard to tell, + # though, so we punt on this one for now. TODO. + + # You should always have whitespace around binary operators. + # Alas, we can't test < or > because they're legitimately used sans spaces + # (a->b, vector a). The only time we can tell is a < with no >, and + # only if it's not template params list spilling into the next line. + match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) + if not match: + # Note that while it seems that the '<[^<]*' term in the following + # regexp could be simplified to '<.*', which would indeed match + # the same class of strings, the [^<] means that searching for the + # regexp takes linear rather than quadratic time. + if not Search(r'<[^<]*,\s*$', line): # template params spill + match = Search(r'[^<>=!\s](<)[^<>=!\s]([^>]|->)*$', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + # We allow no-spaces around << and >> when used like this: 10<<20, but + # not otherwise (particularly, not when used as streams) + match = Search(r'[^0-9\s](<<|>>)[^0-9\s]', line) + if match: + error(filename, linenum, 'whitespace/operators', 3, + 'Missing spaces around %s' % match.group(1)) + + # There shouldn't be space around unary operators + match = Search(r'(!\s|~\s|[\s]--[\s;]|[\s]\+\+[\s;])', line) + if match: + error(filename, linenum, 'whitespace/operators', 4, + 'Extra space for operator %s' % match.group(1)) + + # A pet peeve of mine: no spaces after an if, while, switch, or for + match = Search(r' (if\(|for\(|while\(|switch\()', line) + if match: + error(filename, linenum, 'whitespace/parens', 5, + 'Missing space before ( in %s' % match.group(1)) + + # For if/for/while/switch, the left and right parens should be + # consistent about how many spaces are inside the parens, and + # there should either be zero or one spaces inside the parens. + # We don't want: "if ( foo)" or "if ( foo )". + # Exception: "for ( ; foo; bar)" and "for (foo; bar; )" are allowed. + match = Search(r'\b(if|for|while|switch)\s*' + r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$', + line) + if match: + if len(match.group(2)) != len(match.group(4)): + if not (match.group(3) == ';' and + len(match.group(2)) == 1 + len(match.group(4)) or + not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): + error(filename, linenum, 'whitespace/parens', 5, + 'Mismatching spaces inside () in %s' % match.group(1)) + if not len(match.group(2)) in [0, 1]: + error(filename, linenum, 'whitespace/parens', 5, + 'Should have zero or one spaces inside ( and ) in %s' % + match.group(1)) + + # You should always have a space after a comma (either as fn arg or operator) + if Search(r',[^\s]', line): + error(filename, linenum, 'whitespace/comma', 3, + 'Missing space after ,') + + # You should always have a space after a semicolon + # except for few corner cases + # TODO(unknown): clarify if 'if (1) { return 1;}' is requires one more + # space after ; + if Search(r';[^\s};\\)/]', line): + error(filename, linenum, 'whitespace/semicolon', 3, + 'Missing space after ;') + + # Next we will look for issues with function calls. + CheckSpacingForFunctionCall(filename, line, linenum, error) + + # Except after an opening paren, or after another opening brace (in case of + # an initializer list, for instance), you should have spaces before your + # braces. And since you should never have braces at the beginning of a line, + # this is an easy test. + if Search(r'[^ \t({]{', line): # The check for tab was added by Telldus + error(filename, linenum, 'whitespace/braces', 5, + 'Missing whitespace before {') + + # Make sure '} else {' has spaces. + if Search(r'}else', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Missing space before else') + + # You shouldn't have spaces before your brackets, except maybe after + # 'delete []' or 'new char * []'. + if Search(r'\w\s+\[', line) and not Search(r'delete\s+\[', line): + error(filename, linenum, 'whitespace/braces', 5, + 'Extra space before [') + + # You shouldn't have a space before a semicolon at the end of the line. + # There's a special case for "for" since the style guide allows space before + # the semicolon there. + if Search(r':\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Semicolon defining empty statement. Use { } instead.') + elif Search(r'^\s*;\s*$', line): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Line contains only semicolon. If this should be an empty statement, ' + 'use { } instead.') + elif (Search(r'\s+;\s*$', line) and + not Search(r'\bfor\b', line)): + error(filename, linenum, 'whitespace/semicolon', 5, + 'Extra space before last semicolon. If this should be an empty ' + 'statement, use { } instead.') + + +def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error): + """Checks for additional blank line issues related to sections. + + Currently the only thing checked here is blank line before protected/private. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + class_info: A _ClassInfo objects. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + # Skip checks if the class is small, where small means 25 lines or less. + # 25 lines seems like a good cutoff since that's the usual height of + # terminals, and any class that can't fit in one screen can't really + # be considered "small". + # + # Also skip checks if we are on the first line. This accounts for + # classes that look like + # class Foo { public: ... }; + # + # If we didn't find the end of the class, last_line would be zero, + # and the check will be skipped by the first condition. + if (class_info.last_line - class_info.linenum <= 24 or + linenum <= class_info.linenum): + return + + matched = Match(r'\s*(public|protected|private):', clean_lines.lines[linenum]) + if matched: + # Issue warning if the line before public/protected/private was + # not a blank line, but don't do this if the previous line contains + # "class" or "struct". This can happen two ways: + # - We are at the beginning of the class. + # - We are forward-declaring an inner class that is semantically + # private, but needed to be public for implementation reasons. + prev_line = clean_lines.lines[linenum - 1] + if (not IsBlankLine(prev_line) and + not Search(r'\b(class|struct)\b', prev_line)): + # Try a bit harder to find the beginning of the class. This is to + # account for multi-line base-specifier lists, e.g.: + # class Derived + # : public Base { + end_class_head = class_info.linenum + for i in range(class_info.linenum, linenum): + if Search(r'\{\s*$', clean_lines.lines[i]): + end_class_head = i + break + if end_class_head < linenum - 1: + error(filename, linenum, 'whitespace/blank_line', 3, + '"%s:" should be preceded by a blank line' % matched.group(1)) + + +def GetPreviousNonBlankLine(clean_lines, linenum): + """Return the most recent non-blank line and its line number. + + Args: + clean_lines: A CleansedLines instance containing the file contents. + linenum: The number of the line to check. + + Returns: + A tuple with two elements. The first element is the contents of the last + non-blank line before the current line, or the empty string if this is the + first non-blank line. The second is the line number of that line, or -1 + if this is the first non-blank line. + """ + + prevlinenum = linenum - 1 + while prevlinenum >= 0: + prevline = clean_lines.elided[prevlinenum] + if not IsBlankLine(prevline): # if not a blank line... + return (prevline, prevlinenum) + prevlinenum -= 1 + return ('', -1) + + +def CheckBraces(filename, clean_lines, linenum, error): + """Looks for misplaced braces (e.g. at the end of line). + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + line = clean_lines.elided[linenum] # get rid of comments and strings + + if Match(r'\s*{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, + # which is commonly used to control the lifetime of + # stack-allocated variables. We don't detect this perfectly: we + # just don't complain if the last non-whitespace character on the + # previous non-blank line is ';', ':', '{', or '}'. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if not Search(r'[;:}{]\s*$', prevline): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end of the previous line') + + # An else clause should be on the same line as the preceding closing brace. + if Match(r'\s*else\s*', line): + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if Match(r'\s*}\s*$', prevline): + error(filename, linenum, 'whitespace/newline', 4, + 'An else should appear on the same line as the preceding }') + + # If braces come on one side of an else, they should be on both. + # However, we have to worry about "else if" that spans multiple lines! + if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): + if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if + # find the ( after the if + pos = line.find('else if') + pos = line.find('(', pos) + if pos > 0: + (endline, _, endpos) = CloseExpression(clean_lines, linenum, pos) + if endline[endpos:].find('{') == -1: # must be brace after if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + else: # common case: else not followed by a multi-line if + error(filename, linenum, 'readability/braces', 5, + 'If an else has a brace on one side, it should have it on both') + + # Likewise, an else should never have the else clause on the same line + if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): + error(filename, linenum, 'whitespace/newline', 4, + 'Else clause should never be on same line as else (use 2 lines)') + + # In the same way, a do/while should never be on one line + if Match(r'\s*do [^\s{]', line): + error(filename, linenum, 'whitespace/newline', 4, + 'do/while clauses should not be on a single line') + + # Braces shouldn't be followed by a ; unless they're defining a struct + # or initializing an array. + # We can't tell in general, but we can for some common cases. + prevlinenum = linenum + while True: + (prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum) + if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'): + line = prevline + line + else: + break + if (Search(r'{.*}\s*;', line) and + line.count('{') == line.count('}') and + not Search(r'struct|class|enum|\s*=\s*{', line)): + error(filename, linenum, 'readability/braces', 4, + "You don't need a ; after a }") + + +def ReplaceableCheck(operator, macro, line): + """Determine whether a basic CHECK can be replaced with a more specific one. + + For example suggest using CHECK_EQ instead of CHECK(a == b) and + similarly for CHECK_GE, CHECK_GT, CHECK_LE, CHECK_LT, CHECK_NE. + + Args: + operator: The C++ operator used in the CHECK. + macro: The CHECK or EXPECT macro being called. + line: The current source line. + + Returns: + True if the CHECK can be replaced with a more specific one. + """ + + # This matches decimal and hex integers, strings, and chars (in that order). + match_constant = r'([-+]?(\d+|0[xX][0-9a-fA-F]+)[lLuU]{0,3}|".*"|\'.*\')' + + # Expression to match two sides of the operator with something that + # looks like a literal, since CHECK(x == iterator) won't compile. + # This means we can't catch all the cases where a more specific + # CHECK is possible, but it's less annoying than dealing with + # extraneous warnings. + match_this = (r'\s*' + macro + r'\((\s*' + + match_constant + r'\s*' + operator + r'[^<>].*|' + r'.*[^<>]' + operator + r'\s*' + match_constant + + r'\s*\))') + + # Don't complain about CHECK(x == NULL) or similar because + # CHECK_EQ(x, NULL) won't compile (requires a cast). + # Also, don't complain about more complex boolean expressions + # involving && or || such as CHECK(a == b || c == d). + return Match(match_this, line) and not Search(r'NULL|&&|\|\|', line) + + +def CheckCheck(filename, clean_lines, linenum, error): + """Checks the use of CHECK and EXPECT macros. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + + # Decide the set of replacement macros that should be suggested + raw_lines = clean_lines.raw_lines + current_macro = '' + for macro in _CHECK_MACROS: + if raw_lines[linenum].find(macro) >= 0: + current_macro = macro + break + if not current_macro: + # Don't waste time here if line doesn't contain 'CHECK' or 'EXPECT' + return + + line = clean_lines.elided[linenum] # get rid of comments and strings + + # Encourage replacing plain CHECKs with CHECK_EQ/CHECK_NE/etc. + for operator in ['==', '!=', '>=', '>', '<=', '<']: + if ReplaceableCheck(operator, current_macro, line): + error(filename, linenum, 'readability/check', 2, + 'Consider using %s instead of %s(a %s b)' % ( + _CHECK_REPLACEMENT[current_macro][operator], + current_macro, operator)) + break + + +def GetLineWidth(line): + """Determines the width of the line in column positions. + + Args: + line: A string, which may be a Unicode string. + + Returns: + The width of the line in column positions, accounting for Unicode + combining characters and wide characters. + """ + if isinstance(line, unicode): + width = 0 + for uc in unicodedata.normalize('NFC', line): + if unicodedata.east_asian_width(uc) in ('W', 'F'): + width += 2 + elif not unicodedata.combining(uc): + width += 1 + return width + else: + return len(line) + + +def CheckStyle(filename, clean_lines, linenum, file_extension, class_state, + error): + """Checks rules from the 'C++ style rules' section of cppguide.html. + + Most of these rules are hard to test (naming, comment style), but we + do what we can. In particular we check for 2-space indents, line lengths, + tab usage, spaces inside code, etc. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + error: The function to call with any errors found. + """ + + raw_lines = clean_lines.raw_lines + line = raw_lines[linenum] + + if line.find('\t') != -1: + error(filename, linenum, 'whitespace/tab', 1, + 'Tab found; better to use spaces') + + if line and line[0] == ' ': + error(filename, linenum, 'whitespace/use_tab_for_indentation', 1, + 'Space found; use tabs for indentation') + + # One or three blank spaces at the beginning of the line is weird; it's + # hard to reconcile that with 2-space indents. + # NOTE: here are the conditions rob pike used for his tests. Mine aren't + # as sophisticated, but it may be worth becoming so: RLENGTH==initial_spaces + # if(RLENGTH > 20) complain = 0; + # if(match($0, " +(error|private|public|protected):")) complain = 0; + # if(match(prev, "&& *$")) complain = 0; + # if(match(prev, "\\|\\| *$")) complain = 0; + # if(match(prev, "[\",=><] *$")) complain = 0; + # if(match($0, " <<")) complain = 0; + # if(match(prev, " +for \\(")) complain = 0; + # if(prevodd && match(prevprev, " +for \\(")) complain = 0; + initial_spaces = 0 + cleansed_line = clean_lines.elided[linenum] + while initial_spaces < len(line) and line[initial_spaces] == ' ': + initial_spaces += 1 + if line and line[-1].isspace(): + error(filename, linenum, 'whitespace/end_of_line', 4, + 'Line ends in whitespace. Consider deleting these extra spaces.') + # There are certain situations we allow one space, notably for labels + elif ((initial_spaces == 1 or initial_spaces == 3) and + not Match(r'\s*\w+\s*:\s*$', cleansed_line)): + error(filename, linenum, 'whitespace/indent', 3, + 'Weird number of spaces at line-start. ' + 'Are you using a 2-space indent?') + # Labels should always be indented at least one space. + elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$', + line): + error(filename, linenum, 'whitespace/labels', 4, + 'Labels should always be indented at least one space. ' + 'If this is a member-initializer list in a constructor or ' + 'the base class list in a class definition, the colon should ' + 'be on the following line.') + + + # Check if the line is a header guard. + is_header_guard = False + if file_extension == 'h': + cppvar = GetHeaderGuardCPPVariable(filename) + if (line.startswith('#ifndef %s' % cppvar) or + line.startswith('#define %s' % cppvar) or + line.startswith('#endif // %s' % cppvar)): + is_header_guard = True + # #include lines and header guards can be long, since there's no clean way to + # split them. + # + # URLs can be long too. It's possible to split these, but it makes them + # harder to cut&paste. + # + # The "$Id:...$" comment may also get very long without it being the + # developers fault. + if (not line.startswith('#include') and not is_header_guard and + not Match(r'^\s*//.*http(s?)://\S*$', line) and + not Match(r'^// \$Id:.*#[0-9]+ \$$', line)): + line_width = GetLineWidth(line) + if line_width > 100: + error(filename, linenum, 'whitespace/line_length', 4, + 'Lines should very rarely be longer than 100 characters') + elif line_width > 80: + error(filename, linenum, 'whitespace/line_length', 2, + 'Lines should be <= 80 characters long') + + if (cleansed_line.count(';') > 1 and + # for loops are allowed two ;'s (and may run over two lines). + cleansed_line.find('for') == -1 and + (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or + GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and + # It's ok to have many commands in a switch case that fits in 1 line + not ((cleansed_line.find('case ') != -1 or + cleansed_line.find('default:') != -1) and + cleansed_line.find('break;') != -1)): + error(filename, linenum, 'whitespace/newline', 4, + 'More than one command on the same line') + + # Some more style checks + CheckBraces(filename, clean_lines, linenum, error) + CheckSpacing(filename, clean_lines, linenum, error) + CheckCheck(filename, clean_lines, linenum, error) + if class_state and class_state.classinfo_stack: + CheckSectionSpacing(filename, clean_lines, + class_state.classinfo_stack[-1], linenum, error) + + +_RE_PATTERN_INCLUDE_NEW_STYLE = re.compile(r'#include +"[^/]+\.h"') +_RE_PATTERN_INCLUDE = re.compile(r'^\s*#\s*include\s*([<"])([^>"]*)[>"].*$') +# Matches the first component of a filename delimited by -s and _s. That is: +# _RE_FIRST_COMPONENT.match('foo').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo-bar_baz.cc').group(0) == 'foo' +# _RE_FIRST_COMPONENT.match('foo_bar-baz.cc').group(0) == 'foo' +_RE_FIRST_COMPONENT = re.compile(r'^[^-_.]+') + + +def _DropCommonSuffixes(filename): + """Drops common suffixes like _test.cc or -inl.h from filename. + + For example: + >>> _DropCommonSuffixes('foo/foo-inl.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/bar/foo.cc') + 'foo/bar/foo' + >>> _DropCommonSuffixes('foo/foo_internal.h') + 'foo/foo' + >>> _DropCommonSuffixes('foo/foo_unusualinternal.h') + 'foo/foo_unusualinternal' + + Args: + filename: The input filename. + + Returns: + The filename with the common suffix removed. + """ + for suffix in ('test.cc', 'regtest.cc', 'unittest.cc', + 'inl.h', 'impl.h', 'internal.h'): + if (filename.endswith(suffix) and len(filename) > len(suffix) and + filename[-len(suffix) - 1] in ('-', '_')): + return filename[:-len(suffix) - 1] + return os.path.splitext(filename)[0] + + +def _IsTestFilename(filename): + """Determines if the given filename has a suffix that identifies it as a test. + + Args: + filename: The input filename. + + Returns: + True if 'filename' looks like a test, False otherwise. + """ + if (filename.endswith('_test.cc') or + filename.endswith('_unittest.cc') or + filename.endswith('_regtest.cc')): + return True + else: + return False + + +def _ClassifyInclude(fileinfo, include, is_system): + """Figures out what kind of header 'include' is. + + Args: + fileinfo: The current file cpplint is running over. A FileInfo instance. + include: The path to a #included file. + is_system: True if the #include used <> rather than "". + + Returns: + One of the _XXX_HEADER constants. + + For example: + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'stdio.h', True) + _C_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'string', True) + _CPP_SYS_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/foo.h', False) + _LIKELY_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo_unknown_extension.cc'), + ... 'bar/foo_other_ext.h', False) + _POSSIBLE_MY_HEADER + >>> _ClassifyInclude(FileInfo('foo/foo.cc'), 'foo/bar.h', False) + _OTHER_HEADER + """ + # This is a list of all standard c++ header files, except + # those already checked for above. + is_stl_h = include in _STL_HEADERS + is_cpp_h = is_stl_h or include in _CPP_HEADERS + + if is_system: + if is_cpp_h: + return _CPP_SYS_HEADER + else: + return _C_SYS_HEADER + + # If the target file and the include we're checking share a + # basename when we drop common extensions, and the include + # lives in . , then it's likely to be owned by the target file. + target_dir, target_base = ( + os.path.split(_DropCommonSuffixes(fileinfo.RepositoryName()))) + include_dir, include_base = os.path.split(_DropCommonSuffixes(include)) + if target_base == include_base and ( + include_dir == target_dir or + include_dir == os.path.normpath(target_dir + '/../public')): + return _LIKELY_MY_HEADER + + # If the target and include share some initial basename + # component, it's possible the target is implementing the + # include, so it's allowed to be first, but we'll never + # complain if it's not there. + target_first_component = _RE_FIRST_COMPONENT.match(target_base) + include_first_component = _RE_FIRST_COMPONENT.match(include_base) + if (target_first_component and include_first_component and + target_first_component.group(0) == + include_first_component.group(0)): + return _POSSIBLE_MY_HEADER + + return _OTHER_HEADER + + + +def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): + """Check rules that are applicable to #include lines. + + Strings on #include lines are NOT removed from elided line, to make + certain tasks easier. However, to prevent false positives, checks + applicable to #include lines in CheckLanguage must be put here. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + fileinfo = FileInfo(filename) + + line = clean_lines.lines[linenum] + + # "include" should use the new style "foo/bar.h" instead of just "bar.h" + if _RE_PATTERN_INCLUDE_NEW_STYLE.search(line): + error(filename, linenum, 'build/include', 4, + 'Include the directory when naming .h files') + + # we shouldn't include a file more than once. actually, there are a + # handful of instances where doing so is okay, but in general it's + # not. + match = _RE_PATTERN_INCLUDE.search(line) + if match: + include = match.group(2) + is_system = (match.group(1) == '<') + if include in include_state: + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) + else: + include_state[include] = linenum + + # We want to ensure that headers appear in the right order: + # 1) for foo.cc, foo.h (preferred location) + # 2) c system files + # 3) cpp system files + # 4) for foo.cc, foo.h (deprecated location) + # 5) other google headers + # + # We classify each include statement as one of those 5 types + # using a number of techniques. The include_state object keeps + # track of the highest type seen, and complains if we see a + # lower type after that. + error_message = include_state.CheckNextIncludeOrder( + _ClassifyInclude(fileinfo, include, is_system)) + if error_message: + error(filename, linenum, 'build/include_order', 4, + '%s. Should be: %s.h, c system, c++ system, other.' % + (error_message, fileinfo.BaseName())) + if not include_state.IsInAlphabeticalOrder(include): + error(filename, linenum, 'build/include_alpha', 4, + 'Include "%s" not in alphabetical order' % include) + + # Look for any of the stream classes that are part of standard C++. + match = _RE_PATTERN_INCLUDE.match(line) + if match: + include = match.group(2) + if Match(r'(f|ind|io|i|o|parse|pf|stdio|str|)?stream$', include): + # Many unit tests use cout, so we exempt them. + if not _IsTestFilename(filename): + error(filename, linenum, 'readability/streams', 3, + 'Streams are highly discouraged.') + + +def _GetTextInside(text, start_pattern): + """Retrieves all the text between matching open and close parentheses. + + Given a string of lines and a regular expression string, retrieve all the text + following the expression and between opening punctuation symbols like + (, [, or {, and the matching close-punctuation symbol. This properly nested + occurrences of the punctuations, so for the text like + printf(a(), b(c())); + a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'. + start_pattern must match string having an open punctuation symbol at the end. + + Args: + text: The lines to extract text. Its comments and strings must be elided. + It can be single line and can span multiple lines. + start_pattern: The regexp string indicating where to start extracting + the text. + Returns: + The extracted text. + None if either the opening string or ending punctuation could not be found. + """ + # TODO(sugawarayu): Audit cpplint.py to see what places could be profitably + # rewritten to use _GetTextInside (and use inferior regexp matching today). + + # Give opening punctuations to get the matching close-punctuations. + matching_punctuation = {'(': ')', '{': '}', '[': ']'} + closing_punctuation = set(matching_punctuation.itervalues()) + + # Find the position to start extracting text. + match = re.search(start_pattern, text, re.M) + if not match: # start_pattern not found in text. + return None + start_position = match.end(0) + + assert start_position > 0, ( + 'start_pattern must ends with an opening punctuation.') + assert text[start_position - 1] in matching_punctuation, ( + 'start_pattern must ends with an opening punctuation.') + # Stack of closing punctuations we expect to have in text after position. + punctuation_stack = [matching_punctuation[text[start_position - 1]]] + position = start_position + while punctuation_stack and position < len(text): + if text[position] == punctuation_stack[-1]: + punctuation_stack.pop() + elif text[position] in closing_punctuation: + # A closing punctuation without matching opening punctuations. + return None + elif text[position] in matching_punctuation: + punctuation_stack.append(matching_punctuation[text[position]]) + position += 1 + if punctuation_stack: + # Opening punctuations left without matching close-punctuations. + return None + # punctuations match. + return text[start_position:position - 1] + + +def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, + error): + """Checks rules from the 'C++ language rules' section of cppguide.html. + + Some of these rules are hard to test (function overloading, using + uint32 inappropriately), but we do the best we can. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + file_extension: The extension (without the dot) of the filename. + include_state: An _IncludeState instance in which the headers are inserted. + error: The function to call with any errors found. + """ + # If the line is empty or consists of entirely a comment, no need to + # check it. + line = clean_lines.elided[linenum] + if not line: + return + + match = _RE_PATTERN_INCLUDE.search(line) + if match: + CheckIncludeLine(filename, clean_lines, linenum, include_state, error) + return + + # Create an extended_line, which is the concatenation of the current and + # next lines, for more effective checking of code that may span more than one + # line. + if linenum + 1 < clean_lines.NumLines(): + extended_line = line + clean_lines.elided[linenum + 1] + else: + extended_line = line + + # Make Windows paths like Unix. + fullname = os.path.abspath(filename).replace('\\', '/') + + # TODO(unknown): figure out if they're using default arguments in fn proto. + + # Check for non-const references in functions. This is tricky because & + # is also used to take the address of something. We allow <> for templates, + # (ignoring whatever is between the braces) and : for classes. + # These are complicated re's. They try to capture the following: + # paren (for fn-prototype start), typename, &, varname. For the const + # version, we're willing for const to be before typename or after + # Don't check the implementation on same line. + fnline = line.split('{', 1)[0] + if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > + len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' + r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + + len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', + fnline))): + + # We allow non-const references in a few standard places, like functions + # called "swap()" or iostream operators like "<<" or ">>". + if not Search( + r'(swap|Swap|operator[<>][<>])\s*\(\s*(?:[\w:]|<.*>)+\s*&', + fnline): + error(filename, linenum, 'runtime/references', 2, + 'Is this a non-const reference? ' + 'If so, make const or use a pointer.') + + # Check to see if they're using an conversion function cast. + # I just try to capture the most common basic types, though there are more. + # Parameterless conversion functions, such as bool(), are allowed as they are + # probably a member operator declaration or default constructor. + match = Search( + r'(\bnew\s+)?\b' # Grab 'new' operator, if it's there + r'(int|float|double|bool|char|int32|uint32|int64|uint64)\([^)]', line) + if match: + # gMock methods are defined using some variant of MOCK_METHODx(name, type) + # where type may be float(), int(string), etc. Without context they are + # virtually indistinguishable from int(x) casts. Likewise, gMock's + # MockCallback takes a template parameter of the form return_type(arg_type), + # which looks much like the cast we're trying to detect. + if (match.group(1) is None and # If new operator, then this isn't a cast + not (Match(r'^\s*MOCK_(CONST_)?METHOD\d+(_T)?\(', line) or + Match(r'^\s*MockCallback<.*>', line))): + error(filename, linenum, 'readability/casting', 4, + 'Using deprecated casting style. ' + 'Use static_cast<%s>(...) instead' % + match.group(2)) + + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'static_cast', + r'\((int|float|double|bool|char|u?int(16|32|64))\)', error) + + # This doesn't catch all cases. Consider (const char * const)"hello". + # + # (char *) "foo" should always be a const_cast (reinterpret_cast won't + # compile). + if CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'const_cast', r'\((char\s?\*+\s?)\)\s*"', error): + pass + else: + # Check pointer casts for other than string constants + CheckCStyleCast(filename, linenum, line, clean_lines.raw_lines[linenum], + 'reinterpret_cast', r'\((\w+\s?\*+\s?)\)', error) + + # In addition, we look for people taking the address of a cast. This + # is dangerous -- casts can assign to temporaries, so the pointer doesn't + # point where you think. + if Search( + r'(&\([^)]+\)[\w(])|(&(static|dynamic|reinterpret)_cast\b)', line): + error(filename, linenum, 'runtime/casting', 4, + ('Are you taking an address of a cast? ' + 'This is dangerous: could be a temp var. ' + 'Take the address before doing the cast, rather than after')) + + # Check for people declaring static/global STL strings at the top level. + # This is dangerous because the C++ language does not guarantee that + # globals with constructors are initialized before the first access. + match = Match( + r'((?:|static +)(?:|const +))string +([a-zA-Z0-9_:]+)\b(.*)', + line) + # Make sure it's not a function. + # Function template specialization looks like: "string foo(...". + # Class template definitions look like: "string Foo::Method(...". + if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', + match.group(3)): + error(filename, linenum, 'runtime/string', 4, + 'For a static/global string constant, use a C style string instead: ' + '"%schar %s[]".' % + (match.group(1), match.group(2))) + + # Check that we're not using RTTI outside of testing code. + if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename): + error(filename, linenum, 'runtime/rtti', 5, + 'Do not use dynamic_cast<>. If you need to cast within a class ' + "hierarchy, use static_cast<> to upcast. Google doesn't support " + 'RTTI.') + + if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): + error(filename, linenum, 'runtime/init', 4, + 'You seem to be initializing a member variable with itself.') + + if file_extension == 'h': + # TODO(unknown): check that 1-arg constructors are explicit. + # How to tell it's a constructor? + # (handled in CheckForNonStandardConstructs for now) + # TODO(unknown): check that classes have DISALLOW_EVIL_CONSTRUCTORS + # (level 1 error) + pass + + # Check if people are using the verboten C basic types. The only exception + # we regularly allow is "unsigned short port" for port. + if Search(r'\bshort port\b', line): + if not Search(r'\bunsigned short port\b', line): + error(filename, linenum, 'runtime/int', 4, + 'Use "unsigned short" for ports, not "short"') + else: + match = Search(r'\b(short|long(?! +double)|long long)\b', line) + if match: + error(filename, linenum, 'runtime/int', 4, + 'Use int16/int64/etc, rather than the C type %s' % match.group(1)) + + # When snprintf is used, the second argument shouldn't be a literal. + match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) + if match and match.group(2) != '0': + # If 2nd arg is zero, snprintf is used to calculate size. + error(filename, linenum, 'runtime/printf', 3, + 'If you can, use sizeof(%s) instead of %s as the 2nd arg ' + 'to snprintf.' % (match.group(1), match.group(2))) + + # Check if some verboten C functions are being used. + if Search(r'\bsprintf\b', line): + error(filename, linenum, 'runtime/printf', 5, + 'Never use sprintf. Use snprintf instead.') + match = Search(r'\b(strcpy|strcat)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Almost always, snprintf is better than %s' % match.group(1)) + + if Search(r'\bsscanf\b', line): + error(filename, linenum, 'runtime/printf', 1, + 'sscanf can be ok, but is slow and can overflow buffers.') + + # Check if some verboten operator overloading is going on + # TODO(unknown): catch out-of-line unary operator&: + # class X {}; + # int operator&(const X& x) { return 42; } // unary operator& + # The trick is it's hard to tell apart from binary operator&: + # class Y { int operator&(const Y& x) { return 23; } }; // binary operator& + if Search(r'\boperator\s*&\s*\(\s*\)', line): + error(filename, linenum, 'runtime/operator', 4, + 'Unary operator& is dangerous. Do not use it.') + + # Check for suspicious usage of "if" like + # } if (a == b) { + if Search(r'\}\s*if\s*\(', line): + error(filename, linenum, 'readability/braces', 4, + 'Did you mean "else if"? If not, start a new line for "if".') + + # Check for potential format string bugs like printf(foo). + # We constrain the pattern not to pick things like DocidForPrintf(foo). + # Not perfect but it can catch printf(foo.c_str()) and printf(foo->c_str()) + # TODO(sugawarayu): Catch the following case. Need to change the calling + # convention of the whole function to process multiple line to handle it. + # printf( + # boy_this_is_a_really_long_variable_that_cannot_fit_on_the_prev_line); + printf_args = _GetTextInside(line, r'(?i)\b(string)?printf\s*\(') + if printf_args: + match = Match(r'([\w.\->()]+)$', printf_args) + if match: + function_name = re.search(r'\b((?:string)?printf)\s*\(', + line, re.I).group(1) + error(filename, linenum, 'runtime/printf', 4, + 'Potential format string bug. Do %s("%%s", %s) instead.' + % (function_name, match.group(1))) + + # Check for potential memset bugs like memset(buf, sizeof(buf), 0). + match = Search(r'memset\s*\(([^,]*),\s*([^,]*),\s*0\s*\)', line) + if match and not Match(r"^''|-?[0-9]+|0x[0-9A-Fa-f]$", match.group(2)): + error(filename, linenum, 'runtime/memset', 4, + 'Did you mean "memset(%s, 0, %s)"?' + % (match.group(1), match.group(2))) + + if Search(r'\busing namespace\b', line): + error(filename, linenum, 'build/namespaces', 5, + 'Do not use namespace using-directives. ' + 'Use using-declarations instead.') + + # Detect variable-length arrays. + match = Match(r'\s*(.+::)?(\w+) [a-z]\w*\[(.+)];', line) + if (match and match.group(2) != 'return' and match.group(2) != 'delete' and + match.group(3).find(']') == -1): + # Split the size using space and arithmetic operators as delimiters. + # If any of the resulting tokens are not compile time constants then + # report the error. + tokens = re.split(r'\s|\+|\-|\*|\/|<<|>>]', match.group(3)) + is_const = True + skip_next = False + for tok in tokens: + if skip_next: + skip_next = False + continue + + if Search(r'sizeof\(.+\)', tok): continue + if Search(r'arraysize\(\w+\)', tok): continue + + tok = tok.lstrip('(') + tok = tok.rstrip(')') + if not tok: continue + if Match(r'\d+', tok): continue + if Match(r'0[xX][0-9a-fA-F]+', tok): continue + if Match(r'k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue + if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue + # A catch all for tricky sizeof cases, including 'sizeof expression', + # 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)' + # requires skipping the next token because we split on ' ' and '*'. + if tok.startswith('sizeof'): + skip_next = True + continue + is_const = False + break + if not is_const: + error(filename, linenum, 'runtime/arrays', 1, + 'Do not use variable-length arrays. Use an appropriately named ' + "('k' followed by CamelCase) compile-time constant for the size.") + + # If DISALLOW_EVIL_CONSTRUCTORS, DISALLOW_COPY_AND_ASSIGN, or + # DISALLOW_IMPLICIT_CONSTRUCTORS is present, then it should be the last thing + # in the class declaration. + match = Match( + (r'\s*' + r'(DISALLOW_(EVIL_CONSTRUCTORS|COPY_AND_ASSIGN|IMPLICIT_CONSTRUCTORS))' + r'\(.*\);$'), + line) + if match and linenum + 1 < clean_lines.NumLines(): + next_line = clean_lines.elided[linenum + 1] + # We allow some, but not all, declarations of variables to be present + # in the statement that defines the class. The [\w\*,\s]* fragment of + # the regular expression below allows users to declare instances of + # the class or pointers to instances, but not less common types such + # as function pointers or arrays. It's a tradeoff between allowing + # reasonable code and avoiding trying to parse more C++ using regexps. + if not Search(r'^\s*}[\w\*,\s]*;', next_line): + error(filename, linenum, 'readability/constructors', 3, + match.group(1) + ' should be the last thing in the class') + + # Check for use of unnamed namespaces in header files. Registration + # macros are typically OK, so we allow use of "namespace {" on lines + # that end with backslashes. + if (file_extension == 'h' + and Search(r'\bnamespace\s*{', line) + and line[-1] != '\\'): + error(filename, linenum, 'build/namespaces', 4, + 'Do not use unnamed namespaces in header files. See ' + 'http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces' + ' for more information.') + + +def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern, + error): + """Checks for a C-style cast by looking for the pattern. + + This also handles sizeof(type) warnings, due to similarity of content. + + Args: + filename: The name of the current file. + linenum: The number of the line to check. + line: The line of code to check. + raw_line: The raw line of code to check, with comments. + cast_type: The string for the C++ cast to recommend. This is either + reinterpret_cast, static_cast, or const_cast, depending. + pattern: The regular expression used to find C-style casts. + error: The function to call with any errors found. + + Returns: + True if an error was emitted. + False otherwise. + """ + match = Search(pattern, line) + if not match: + return False + + # e.g., sizeof(int) + sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) + if sizeof_match: + error(filename, linenum, 'runtime/sizeof', 1, + 'Using sizeof(type). Use sizeof(varname) instead if possible') + return True + + remainder = line[match.end(0):] + + # The close paren is for function pointers as arguments to a function. + # eg, void foo(void (*bar)(int)); + # The semicolon check is a more basic function check; also possibly a + # function pointer typedef. + # eg, void foo(int); or void foo(int) const; + # The equals check is for function pointer assignment. + # eg, void *(*foo)(int) = ... + # The > is for MockCallback<...> ... + # + # Right now, this will only catch cases where there's a single argument, and + # it's unnamed. It should probably be expanded to check for multiple + # arguments with some unnamed. + function_match = Match(r'\s*(\)|=|(const)?\s*(;|\{|throw\(\)|>))', remainder) + if function_match: + if (not function_match.group(3) or + function_match.group(3) == ';' or + ('MockCallback<' not in raw_line and + '/*' not in raw_line)): + error(filename, linenum, 'readability/function', 3, + 'All parameters should be named in a function') + return True + + # At this point, all that should be left is actual casts. + error(filename, linenum, 'readability/casting', 4, + 'Using C-style cast. Use %s<%s>(...) instead' % + (cast_type, match.group(1))) + + return True + + +_HEADERS_CONTAINING_TEMPLATES = ( + ('', ('deque',)), + ('', ('unary_function', 'binary_function', + 'plus', 'minus', 'multiplies', 'divides', 'modulus', + 'negate', + 'equal_to', 'not_equal_to', 'greater', 'less', + 'greater_equal', 'less_equal', + 'logical_and', 'logical_or', 'logical_not', + 'unary_negate', 'not1', 'binary_negate', 'not2', + 'bind1st', 'bind2nd', + 'pointer_to_unary_function', + 'pointer_to_binary_function', + 'ptr_fun', + 'mem_fun_t', 'mem_fun', 'mem_fun1_t', 'mem_fun1_ref_t', + 'mem_fun_ref_t', + 'const_mem_fun_t', 'const_mem_fun1_t', + 'const_mem_fun_ref_t', 'const_mem_fun1_ref_t', + 'mem_fun_ref', + )), + ('', ('numeric_limits',)), + ('', ('list',)), + ('', ('map', 'multimap',)), + ('', ('allocator',)), + ('', ('queue', 'priority_queue',)), + ('', ('set', 'multiset',)), + ('', ('stack',)), + ('', ('char_traits', 'basic_string',)), + ('', ('pair',)), + ('', ('vector',)), + + # gcc extensions. + # Note: std::hash is their hash, ::hash is our hash + ('', ('hash_map', 'hash_multimap',)), + ('', ('hash_set', 'hash_multiset',)), + ('', ('slist',)), + ) + +_RE_PATTERN_STRING = re.compile(r'\bstring\b') + +_re_pattern_algorithm_header = [] +for _template in ('copy', 'max', 'min', 'min_element', 'sort', 'swap', + 'transform'): + # Match max(..., ...), max(..., ...), but not foo->max, foo.max or + # type::max(). + _re_pattern_algorithm_header.append( + (re.compile(r'[^>.]\b' + _template + r'(<.*?>)?\([^\)]'), + _template, + '')) + +_re_pattern_templates = [] +for _header, _templates in _HEADERS_CONTAINING_TEMPLATES: + for _template in _templates: + _re_pattern_templates.append( + (re.compile(r'(\<|\b)' + _template + r'\s*\<'), + _template + '<>', + _header)) + + +def FilesBelongToSameModule(filename_cc, filename_h): + """Check if these two filenames belong to the same module. + + The concept of a 'module' here is a as follows: + foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the + same 'module' if they are in the same directory. + some/path/public/xyzzy and some/path/internal/xyzzy are also considered + to belong to the same module here. + + If the filename_cc contains a longer path than the filename_h, for example, + '/absolute/path/to/base/sysinfo.cc', and this file would include + 'base/sysinfo.h', this function also produces the prefix needed to open the + header. This is used by the caller of this function to more robustly open the + header file. We don't have access to the real include paths in this context, + so we need this guesswork here. + + Known bugs: tools/base/bar.cc and base/bar.h belong to the same module + according to this implementation. Because of this, this function gives + some false positives. This should be sufficiently rare in practice. + + Args: + filename_cc: is the path for the .cc file + filename_h: is the path for the header path + + Returns: + Tuple with a bool and a string: + bool: True if filename_cc and filename_h belong to the same module. + string: the additional prefix needed to open the header file. + """ + + if not filename_cc.endswith('.cc'): + return (False, '') + filename_cc = filename_cc[:-len('.cc')] + if filename_cc.endswith('_unittest'): + filename_cc = filename_cc[:-len('_unittest')] + elif filename_cc.endswith('_test'): + filename_cc = filename_cc[:-len('_test')] + filename_cc = filename_cc.replace('/public/', '/') + filename_cc = filename_cc.replace('/internal/', '/') + + if not filename_h.endswith('.h'): + return (False, '') + filename_h = filename_h[:-len('.h')] + if filename_h.endswith('-inl'): + filename_h = filename_h[:-len('-inl')] + filename_h = filename_h.replace('/public/', '/') + filename_h = filename_h.replace('/internal/', '/') + + files_belong_to_same_module = filename_cc.endswith(filename_h) + common_path = '' + if files_belong_to_same_module: + common_path = filename_cc[:-len(filename_h)] + return files_belong_to_same_module, common_path + + +def UpdateIncludeState(filename, include_state, io=codecs): + """Fill up the include_state with new includes found from the file. + + Args: + filename: the name of the header to read. + include_state: an _IncludeState instance in which the headers are inserted. + io: The io factory to use to read the file. Provided for testability. + + Returns: + True if a header was succesfully added. False otherwise. + """ + headerfile = None + try: + headerfile = io.open(filename, 'r', 'utf8', 'replace') + except IOError: + return False + linenum = 0 + for line in headerfile: + linenum += 1 + clean_line = CleanseComments(line) + match = _RE_PATTERN_INCLUDE.search(clean_line) + if match: + include = match.group(2) + # The value formatting is cute, but not really used right now. + # What matters here is that the key is in include_state. + include_state.setdefault(include, '%s:%d' % (filename, linenum)) + return True + + +def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, + io=codecs): + """Reports for missing stl includes. + + This function will output warnings to make sure you are including the headers + necessary for the stl containers and functions that you use. We only give one + reason to include a header. For example, if you use both equal_to<> and + less<> in a .h file, only one (the latter in the file) of these will be + reported as a reason to include the . + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + include_state: An _IncludeState instance. + error: The function to call with any errors found. + io: The IO factory to use to read the header file. Provided for unittest + injection. + """ + required = {} # A map of header name to linenumber and the template entity. + # Example of required: { '': (1219, 'less<>') } + + for linenum in xrange(clean_lines.NumLines()): + line = clean_lines.elided[linenum] + if not line or line[0] == '#': + continue + + # String is special -- it is a non-templatized type in STL. + matched = _RE_PATTERN_STRING.search(line) + if matched: + # Don't warn about strings in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[''] = (linenum, 'string') + + for pattern, template, header in _re_pattern_algorithm_header: + if pattern.search(line): + required[header] = (linenum, template) + + # The following function is just a speed up, no semantics are changed. + if not '<' in line: # Reduces the cpu time usage by skipping lines. + continue + + for pattern, template, header in _re_pattern_templates: + if pattern.search(line): + required[header] = (linenum, template) + + # The policy is that if you #include something in foo.h you don't need to + # include it again in foo.cc. Here, we will look at possible includes. + # Let's copy the include_state so it is only messed up within this function. + include_state = include_state.copy() + + # Did we find the header for this file (if any) and succesfully load it? + header_found = False + + # Use the absolute path so that matching works properly. + abs_filename = FileInfo(filename).FullName() + + # For Emacs's flymake. + # If cpplint is invoked from Emacs's flymake, a temporary file is generated + # by flymake and that file name might end with '_flymake.cc'. In that case, + # restore original file name here so that the corresponding header file can be + # found. + # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' + # instead of 'foo_flymake.h' + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename) + + # include_state is modified during iteration, so we iterate over a copy of + # the keys. + header_keys = include_state.keys() + for header in header_keys: + (same_module, common_path) = FilesBelongToSameModule(abs_filename, header) + fullpath = common_path + header + if same_module and UpdateIncludeState(fullpath, include_state, io): + header_found = True + + # If we can't find the header file for a .cc, assume it's because we don't + # know where to look. In that case we'll give up as we're not sure they + # didn't include it in the .h file. + # TODO(unknown): Do a better job of finding .h files so we are confident that + # not having the .h file means there isn't one. + if filename.endswith('.cc') and not header_found: + return + + # All the lines have been processed, report the errors found. + for required_header_unstripped in required: + template = required[required_header_unstripped][1] + if required_header_unstripped.strip('<>"') not in include_state: + error(filename, required[required_header_unstripped][0], + 'build/include_what_you_use', 4, + 'Add #include ' + required_header_unstripped + ' for ' + template) + + +_RE_PATTERN_EXPLICIT_MAKEPAIR = re.compile(r'\bmake_pair\s*<') + + +def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error): + """Check that make_pair's template arguments are deduced. + + G++ 4.6 in C++0x mode fails badly if make_pair's template arguments are + specified explicitly, and such use isn't intended in any case. + + Args: + filename: The name of the current file. + clean_lines: A CleansedLines instance containing the file. + linenum: The number of the line to check. + error: The function to call with any errors found. + """ + raw = clean_lines.raw_lines + line = raw[linenum] + match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line) + if match: + error(filename, linenum, 'build/explicit_make_pair', + 4, # 4 = high confidence + 'Omit template arguments from make_pair OR use pair directly OR' + ' if appropriate, construct a pair directly') + + +def ProcessLine(filename, file_extension, + clean_lines, line, include_state, function_state, + class_state, error, extra_check_functions=[]): + """Processes a single line in the file. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + clean_lines: An array of strings, each representing a line of the file, + with comments stripped. + line: Number of line being processed. + include_state: An _IncludeState instance in which the headers are inserted. + function_state: A _FunctionState instance which counts function lines, etc. + class_state: A _ClassState instance which maintains information about + the current stack of nested class declarations being parsed. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + raw_lines = clean_lines.raw_lines + ParseNolintSuppressions(filename, raw_lines[line], line, error) + CheckForFunctionLengths(filename, clean_lines, line, function_state, error) + CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) + CheckStyle(filename, clean_lines, line, file_extension, class_state, error) + CheckLanguage(filename, clean_lines, line, file_extension, include_state, + error) + CheckForNonStandardConstructs(filename, clean_lines, line, + class_state, error) + CheckPosixThreading(filename, clean_lines, line, error) + CheckInvalidIncrement(filename, clean_lines, line, error) + CheckMakePairUsesDeduction(filename, clean_lines, line, error) + for check_fn in extra_check_functions: + check_fn(filename, clean_lines, line, error) + +def ProcessFileData(filename, file_extension, lines, error, + extra_check_functions=[]): + """Performs lint checks and reports any errors to the given error function. + + Args: + filename: Filename of the file that is being processed. + file_extension: The extension (dot not included) of the file. + lines: An array of strings, each representing a line of the file, with the + last element being empty if the file is terminated with a newline. + error: A callable to which errors are reported, which takes 4 arguments: + filename, line number, error level, and message + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + lines = (['// marker so line numbers and indices both start at 1'] + lines + + ['// marker so line numbers end in a known way']) + + include_state = _IncludeState() + function_state = _FunctionState() + class_state = _ClassState() + + ResetNolintSuppressions() + + CheckForCopyright(filename, lines, error) + + if file_extension == 'h': + CheckForHeaderGuard(filename, lines, error) + + RemoveMultiLineComments(filename, lines, error) + clean_lines = CleansedLines(lines) + for line in xrange(clean_lines.NumLines()): + ProcessLine(filename, file_extension, clean_lines, line, + include_state, function_state, class_state, error, + extra_check_functions) + class_state.CheckFinished(filename, error) + + CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error) + + # We check here rather than inside ProcessLine so that we see raw + # lines rather than "cleaned" lines. + CheckForUnicodeReplacementCharacters(filename, lines, error) + + CheckForNewlineAtEOF(filename, lines, error) + +def ProcessFile(filename, vlevel, extra_check_functions=[]): + """Does google-lint on a single file. + + Args: + filename: The name of the file to parse. + + vlevel: The level of errors to report. Every error of confidence + >= verbose_level will be reported. 0 is a good default. + + extra_check_functions: An array of additional check functions that will be + run on each source line. Each function takes 4 + arguments: filename, clean_lines, line, error + """ + + _SetVerboseLevel(vlevel) + + try: + # Support the UNIX convention of using "-" for stdin. Note that + # we are not opening the file with universal newline support + # (which codecs doesn't support anyway), so the resulting lines do + # contain trailing '\r' characters if we are reading a file that + # has CRLF endings. + # If after the split a trailing '\r' is present, it is removed + # below. If it is not expected to be present (i.e. os.linesep != + # '\r\n' as in Windows), a warning is issued below if this file + # is processed. + + if filename == '-': + lines = codecs.StreamReaderWriter(sys.stdin, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace').read().split('\n') + else: + lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n') + + carriage_return_found = False + # Remove trailing '\r'. + for linenum in range(len(lines)): + if lines[linenum].endswith('\r'): + lines[linenum] = lines[linenum].rstrip('\r') + carriage_return_found = True + + except IOError: + sys.stderr.write( + "Skipping input '%s': Can't open for reading\n" % filename) + return + + # Note, if no dot is found, this will give the entire filename as the ext. + file_extension = filename[filename.rfind('.') + 1:] + + # When reading from stdin, the extension is unknown, so no cpplint tests + # should rely on the extension. + if (filename != '-' and file_extension != 'cc' and file_extension != 'h' + and file_extension != 'cpp'): + sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename) + else: + ProcessFileData(filename, file_extension, lines, Error, + extra_check_functions) + if carriage_return_found and os.linesep != '\r\n': + # Use 0 for linenum since outputting only one error for potentially + # several lines. + Error(filename, 0, 'whitespace/newline', 1, + 'One or more unexpected \\r (^M) found;' + 'better to use only a \\n') + + sys.stderr.write('Done processing %s\n' % filename) + + +def PrintUsage(message): + """Prints a brief usage string and exits, optionally with an error message. + + Args: + message: The optional error message. + """ + sys.stderr.write(_USAGE) + if message: + sys.exit('\nFATAL ERROR: ' + message) + else: + sys.exit(1) + + +def PrintCategories(): + """Prints a list of all the error-categories used by error messages. + + These are the categories used to filter messages via --filter. + """ + sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES)) + sys.exit(0) + + +def ParseArguments(args): + """Parses the command line arguments. + + This may set the output format and verbosity level as side-effects. + + Args: + args: The command line arguments: + + Returns: + The list of filenames to lint. + """ + try: + (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', + 'counting=', + 'filter=']) + except getopt.GetoptError: + PrintUsage('Invalid arguments.') + + verbosity = _VerboseLevel() + output_format = _OutputFormat() + filters = '' + counting_style = '' + + for (opt, val) in opts: + if opt == '--help': + PrintUsage(None) + elif opt == '--output': + if not val in ('emacs', 'vs7'): + PrintUsage('The only allowed output formats are emacs and vs7.') + output_format = val + elif opt == '--verbose': + verbosity = int(val) + elif opt == '--filter': + filters = val + if not filters: + PrintCategories() + elif opt == '--counting': + if val not in ('total', 'toplevel', 'detailed'): + PrintUsage('Valid counting options are total, toplevel, and detailed') + counting_style = val + + if not filenames: + PrintUsage('No files were specified.') + + _SetOutputFormat(output_format) + _SetVerboseLevel(verbosity) + _SetFilters(filters) + _SetCountingStyle(counting_style) + + return filenames + + +def main(): + filenames = ParseArguments(sys.argv[1:]) + + # Change stderr to write with replacement characters so we don't die + # if we try to print something containing non-ASCII characters. + sys.stderr = codecs.StreamReaderWriter(sys.stderr, + codecs.getreader('utf8'), + codecs.getwriter('utf8'), + 'replace') + + _cpplint_state.ResetErrorCounts() + for filename in filenames: + ProcessFile(filename, _cpplint_state.verbose_level) + _cpplint_state.PrintErrorCounts() + + sys.exit(_cpplint_state.error_count > 0) + + +if __name__ == '__main__': + main() diff --git a/telldus-core/tests/cppunit.cpp b/telldus-core/tests/cppunit.cpp new file mode 100644 index 0000000..8a05906 --- /dev/null +++ b/telldus-core/tests/cppunit.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "common/CommonTests.h" +#include "service/ServiceTests.h" + +int main (int argc, char* argv[]) +{ + // Setup our tests + CommonTests::setup(); + ServiceTests::setup(); + + // informs test-listener about testresults + CPPUNIT_NS :: TestResult testresult; + + // register listener for collecting the test-results + CPPUNIT_NS :: TestResultCollector collectedresults; + testresult.addListener (&collectedresults); + + // register listener for per-test progress output + CPPUNIT_NS :: BriefTestProgressListener progress; + testresult.addListener (&progress); + + // insert test-suite at test-runner by registry + CPPUNIT_NS :: TestRunner testrunner; + testrunner.addTest (CPPUNIT_NS :: TestFactoryRegistry :: getRegistry ().makeTest ()); + testrunner.run (testresult); + + // output results in compiler-format + CPPUNIT_NS :: CompilerOutputter compileroutputter (&collectedresults, std::cerr); + compileroutputter.write (); + + std::ofstream xmlFileOut("cpptestresults.xml"); + CPPUNIT_NS :: XmlOutputter xmlOut(&collectedresults, xmlFileOut); + xmlOut.write(); + + // return 0 if tests were successful + return collectedresults.wasSuccessful () ? 0 : 1; +} diff --git a/telldus-core/tests/service/CMakeLists.txt b/telldus-core/tests/service/CMakeLists.txt new file mode 100644 index 0000000..057f609 --- /dev/null +++ b/telldus-core/tests/service/CMakeLists.txt @@ -0,0 +1,9 @@ +FILE(GLOB SRCS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*Test.cpp" ) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}) + +ADD_LIBRARY(TelldusServiceTests STATIC ${SRCS} ) + +TARGET_LINK_LIBRARIES( TelldusServiceTests TelldusServiceStatic ${CPPUNIT} ) +ADD_DEPENDENCIES( TelldusServiceTests ${telldus-service_TARGET} ) + diff --git a/telldus-core/tests/service/ProtocolEverflourishTest.cpp b/telldus-core/tests/service/ProtocolEverflourishTest.cpp new file mode 100644 index 0000000..2d9ec66 --- /dev/null +++ b/telldus-core/tests/service/ProtocolEverflourishTest.cpp @@ -0,0 +1,30 @@ +#include "ProtocolEverflourishTest.h" +#include "service/ProtocolEverflourish.h" + +class ProtocolEverflourishTest::PrivateData { +public: + ProtocolEverflourish *protocol; +}; + +void ProtocolEverflourishTest :: setUp (void) { + d = new PrivateData; + d->protocol = new ProtocolEverflourish(); +} + +void ProtocolEverflourishTest :: tearDown (void) { + delete d->protocol; + delete d; +} + +void ProtocolEverflourishTest :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Everflourish 4242:3 ON", + std::string("class:command;protocol:everflourish;model:selflearning;house:4242;unit:3;method:turnon;"), + d->protocol->decodeData(ControllerMessage("protocol:everflourish;data:0x424A6F;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Everflourish 5353:4 OFF", + std::string("class:command;protocol:everflourish;model:selflearning;house:5353;unit:4;method:turnoff;"), + d->protocol->decodeData(ControllerMessage("protocol:everflourish;data:0x53A7E0;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolEverflourishTest.h b/telldus-core/tests/service/ProtocolEverflourishTest.h new file mode 100644 index 0000000..2261220 --- /dev/null +++ b/telldus-core/tests/service/ProtocolEverflourishTest.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLEVERFLOURISHTEST_H +#define PROTOCOLEVERFLOURISHTEST_H + +#include +#include + +class ProtocolEverflourishTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolEverflourishTest); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // PROTOCOLEVERFLOURISHTEST_H diff --git a/telldus-core/tests/service/ProtocolHastaTest.cpp b/telldus-core/tests/service/ProtocolHastaTest.cpp new file mode 100644 index 0000000..4b11866 --- /dev/null +++ b/telldus-core/tests/service/ProtocolHastaTest.cpp @@ -0,0 +1,40 @@ +#include "ProtocolHastaTest.h" +#include "service/ProtocolHasta.h" + +class ProtocolHastaTest::PrivateData { +public: + ProtocolHasta *protocol; +}; + +void ProtocolHastaTest :: setUp (void) { + d = new PrivateData; + d->protocol = new ProtocolHasta(); +} + +void ProtocolHastaTest :: tearDown (void) { + delete d->protocol; + delete d; +} + +void ProtocolHastaTest :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Hasta Version 1 26380 1 DOWN", + std::string("class:command;protocol:hasta;model:selflearning;house:26380;unit:1;method:down;"), + d->protocol->decodeData(ControllerMessage("protocol:hasta;model:selflearning;data:0xC671100;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Hasta Version 1 26380 1 UP", + std::string("class:command;protocol:hasta;model:selflearning;house:26380;unit:1;method:up;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:selflearning;data:0xC670100;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Hasta Version 2 19337 15 DOWN", + std::string("class:command;protocol:hasta;model:selflearningv2;house:19337;unit:15;method:down;"), + d->protocol->decodeData(ControllerMessage("protocol:hasta;model:selflearningv2;data:0x4B891F01;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Hasta Version 2 19337 15 UP", + std::string("class:command;protocol:hasta;model:selflearningv2;house:19337;unit:15;method:up;"), + d->protocol->decodeData(ControllerMessage("protocol:hasta;model:selflearningv2;data:0x4B89CF01;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolHastaTest.h b/telldus-core/tests/service/ProtocolHastaTest.h new file mode 100644 index 0000000..8c7b9ed --- /dev/null +++ b/telldus-core/tests/service/ProtocolHastaTest.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLHASTATEST_H +#define PROTOCOLHASTATEST_H + +#include +#include + +class ProtocolHastaTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolHastaTest); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif //PROTOCOLHASTATEST_H diff --git a/telldus-core/tests/service/ProtocolNexaTest.cpp b/telldus-core/tests/service/ProtocolNexaTest.cpp new file mode 100644 index 0000000..e2c9f8e --- /dev/null +++ b/telldus-core/tests/service/ProtocolNexaTest.cpp @@ -0,0 +1,40 @@ +#include "ProtocolNexaTest.h" +#include "service/ProtocolNexa.h" + +class ProtocolNexaTest::PrivateData { +public: + ProtocolNexa *protocol; +}; + +void ProtocolNexaTest :: setUp (void) { + d = new PrivateData; + d->protocol = new ProtocolNexa(); +} + +void ProtocolNexaTest :: tearDown (void) { + delete d->protocol; + delete d; +} + +void ProtocolNexaTest :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Arctech Codeswitch A1 ON", + std::string("class:command;protocol:arctech;model:codeswitch;house:A;unit:1;method:turnon;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:codeswitch;data:0xE00;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Arctech Codeswitch A1 OFF", + std::string("class:command;protocol:arctech;model:codeswitch;house:A;unit:1;method:turnoff;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:codeswitch;data:0x600;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Arctech Selflearning 1329110 1 ON", + std::string("class:command;protocol:arctech;model:selflearning;house:1329110;unit:1;group:0;method:turnon;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:selflearning;data:0x511F590;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Arctech Selflearning 1329110 1 OFF", + std::string("class:command;protocol:arctech;model:selflearning;house:1329110;unit:1;group:0;method:turnoff;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:selflearning;data:0x511F580;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolNexaTest.h b/telldus-core/tests/service/ProtocolNexaTest.h new file mode 100644 index 0000000..74c0377 --- /dev/null +++ b/telldus-core/tests/service/ProtocolNexaTest.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLNEXATEST_H +#define PROTOCOLNEXATEST_H + +#include +#include + +class ProtocolNexaTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolNexaTest); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif //PROTOCOLNEXATEST_H diff --git a/telldus-core/tests/service/ProtocolOregonTest.cpp b/telldus-core/tests/service/ProtocolOregonTest.cpp new file mode 100644 index 0000000..2d5fd0f --- /dev/null +++ b/telldus-core/tests/service/ProtocolOregonTest.cpp @@ -0,0 +1,67 @@ +#include "ProtocolOregonTest.h" +#include "service/ProtocolOregon.h" + +class ProtocolOregonTest::PrivateData { +public: +}; + +void ProtocolOregonTest :: setUp (void) { + d = new PrivateData; +} + +void ProtocolOregonTest :: tearDown (void) { + delete d; +} + +void ProtocolOregonTest :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 77.3", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:77.3;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177307700E4;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 74.7", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:74.7;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177707410A4;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 77.7", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:77.7;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:217770774054;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 66.5", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:66.5;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177506600E4;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 122.5", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:122.5;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177502291A3;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 120.1", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:120.1;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177102031B3;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 120.6", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:120.6;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:217760208193;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 23, temp: 202.7", + std::string("class:sensor;protocol:oregon;model:EA4C;id:23;temp:202.7;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:2177702A2D3;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: 202.7", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:202.7;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:21777002A2D3;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Oregon, id: 119, temp: -23.1", + std::string("class:sensor;protocol:oregon;model:EA4C;id:119;temp:-23.1;"), + ProtocolOregon::decodeData(ControllerMessage("class:sensor;protocol:oregon;model:0xEA4C;data:21771023D8B3;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolOregonTest.h b/telldus-core/tests/service/ProtocolOregonTest.h new file mode 100644 index 0000000..a296599 --- /dev/null +++ b/telldus-core/tests/service/ProtocolOregonTest.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLOREGONTEST_H +#define PROTOCOLOREGONTEST_H + +#include +#include + +class ProtocolOregonTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolOregonTest); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif // PROTOCOLOREGONTEST_H diff --git a/telldus-core/tests/service/ProtocolSartanoTest.cpp b/telldus-core/tests/service/ProtocolSartanoTest.cpp new file mode 100644 index 0000000..655dbb5 --- /dev/null +++ b/telldus-core/tests/service/ProtocolSartanoTest.cpp @@ -0,0 +1,25 @@ +#include "ProtocolSartanoTest.h" +#include "service/ProtocolSartano.h" + +class ProtocolSartanoTest::PrivateData { +public: + ProtocolSartano *protocol; +}; + +void ProtocolSartanoTest :: setUp (void) { + d = new PrivateData; + d->protocol = new ProtocolSartano(); +} + +void ProtocolSartanoTest :: tearDown (void) { + delete d->protocol; + delete d; +} + +void ProtocolSartanoTest :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Sartano 0101010101 ON", + std::string("class:command;protocol:sartano;model:codeswitch;code:0101010101;method:turnon;"), + d->protocol->decodeData(ControllerMessage("protocol:arctech;model:codeswitch;data:0x955;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolSartanoTest.h b/telldus-core/tests/service/ProtocolSartanoTest.h new file mode 100644 index 0000000..04ad232 --- /dev/null +++ b/telldus-core/tests/service/ProtocolSartanoTest.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLSARTANOTEST_H +#define PROTOCOLSARTANOTEST_H + +#include +#include + +class ProtocolSartanoTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolSartanoTest); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif //PROTOCOLSARTANOTEST_H diff --git a/telldus-core/tests/service/ProtocolX10Test.cpp b/telldus-core/tests/service/ProtocolX10Test.cpp new file mode 100644 index 0000000..572608f --- /dev/null +++ b/telldus-core/tests/service/ProtocolX10Test.cpp @@ -0,0 +1,30 @@ +#include "ProtocolX10Test.h" +#include "service/ProtocolX10.h" + +class ProtocolX10Test::PrivateData { +public: + ProtocolX10 *protocol; +}; + +void ProtocolX10Test :: setUp (void) { + d = new PrivateData; + d->protocol = new ProtocolX10(); +} + +void ProtocolX10Test :: tearDown (void) { + delete d->protocol; + delete d; +} + +void ProtocolX10Test :: decodeDataTest (void) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "X10 A1 ON", + std::string("class:command;protocol:x10;model:codeswitch;house:A;unit:1;method:turnon;"), + d->protocol->decodeData(ControllerMessage("protocol:x10;data:0x609F00FF;")) + ); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "X10 E11 OFF", + std::string("class:command;protocol:x10;model:codeswitch;house:E;unit:11;method:turnoff;"), + d->protocol->decodeData(ControllerMessage("protocol:x10;data:0x847B28D7;")) + ); +} diff --git a/telldus-core/tests/service/ProtocolX10Test.h b/telldus-core/tests/service/ProtocolX10Test.h new file mode 100644 index 0000000..e490e53 --- /dev/null +++ b/telldus-core/tests/service/ProtocolX10Test.h @@ -0,0 +1,25 @@ +#ifndef PROTOCOLX10TEST_H +#define PROTOCOLX10TEST_H + +#include +#include + +class ProtocolX10Test : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ProtocolX10Test); + CPPUNIT_TEST (decodeDataTest); + CPPUNIT_TEST_SUITE_END (); + +public: + void setUp (void); + void tearDown (void); + +protected: + void decodeDataTest(void); + +private: + class PrivateData; + PrivateData *d; +}; + +#endif //PROTOCOLX10TEST_H diff --git a/telldus-core/tests/service/ServiceTests.h b/telldus-core/tests/service/ServiceTests.h new file mode 100644 index 0000000..7dde4e8 --- /dev/null +++ b/telldus-core/tests/service/ServiceTests.h @@ -0,0 +1,21 @@ +#ifndef SERVICETESTS_H +#define SERVICETESTS_H + +#include "ProtocolEverflourishTest.h" +#include "ProtocolHastaTest.h" +#include "ProtocolNexaTest.h" +#include "ProtocolOregonTest.h" +#include "ProtocolSartanoTest.h" +#include "ProtocolX10Test.h" + +namespace ServiceTests { + inline void setup() { + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolEverflourishTest); + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolHastaTest); + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolNexaTest); + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolOregonTest); + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolSartanoTest); + CPPUNIT_TEST_SUITE_REGISTRATION (ProtocolX10Test); + } +} +#endif // SERVICETESTS_H diff --git a/telldus/telldus.go b/telldus/telldus.go new file mode 100644 index 0000000..b41981e --- /dev/null +++ b/telldus/telldus.go @@ -0,0 +1,221 @@ +package telldus + +/* +#cgo CFLAGS: -I/usr/local/include +#cgo LDFLAGS: -L/usr/local/lib -ltelldus-core +#include +#include + +// Extern declarations for exported Go callback functions +extern void goDeviceEvent(int deviceId, int method, char *data, int callbackId, void *context); +extern void goDeviceChangeEvent(int deviceId, int changeEvent, int changeType, int callbackId, void *context); +extern void goRawDeviceEvent(char *data, int controllerId, int callbackId, void *context); +extern void goSensorEvent(char *protocol, char *model, int id, int dataType, char *value, int timestamp, int callbackId, void *context); +extern void goControllerEvent(int controllerId, int changeEvent, int changeType, char *newValue, int callbackId, void *context); +*/ +import "C" +import "unsafe" + +// Callback function types +type DeviceEventFunc func(deviceId, method int, data string, callbackId int) +type DeviceChangeEventFunc func(deviceId, changeEvent, changeType, callbackId int) +type RawDeviceEventFunc func(data string, controllerId, callbackId int) +type SensorEventFunc func(protocol, model string, id, dataType int, value string, timestamp, callbackId int) +type ControllerEventFunc func(controllerId, changeEvent, changeType int, newValue string, callbackId int) + +// Global callback variables +var deviceEventCallback DeviceEventFunc +var deviceChangeEventCallback DeviceChangeEventFunc +var rawDeviceEventCallback RawDeviceEventFunc +var sensorEventCallback SensorEventFunc +var controllerEventCallback ControllerEventFunc + +//export goDeviceEvent +func goDeviceEvent(deviceId C.int, method C.int, data *C.char, callbackId C.int, context unsafe.Pointer) { + if deviceEventCallback != nil { + deviceEventCallback(int(deviceId), int(method), C.GoString(data), int(callbackId)) + } +} + +//export goDeviceChangeEvent +func goDeviceChangeEvent(deviceId C.int, changeEvent C.int, changeType C.int, callbackId C.int, context unsafe.Pointer) { + if deviceChangeEventCallback != nil { + deviceChangeEventCallback(int(deviceId), int(changeEvent), int(changeType), int(callbackId)) + } +} + +//export goRawDeviceEvent +func goRawDeviceEvent(data *C.char, controllerId C.int, callbackId C.int, context unsafe.Pointer) { + if rawDeviceEventCallback != nil { + rawDeviceEventCallback(C.GoString(data), int(controllerId), int(callbackId)) + } +} + +//export goSensorEvent +func goSensorEvent(protocol *C.char, model *C.char, id C.int, dataType C.int, value *C.char, timestamp C.int, callbackId C.int, context unsafe.Pointer) { + if sensorEventCallback != nil { + sensorEventCallback(C.GoString(protocol), C.GoString(model), int(id), int(dataType), C.GoString(value), int(timestamp), int(callbackId)) + } +} + +//export goControllerEvent +func goControllerEvent(controllerId C.int, changeEvent C.int, changeType C.int, newValue *C.char, callbackId C.int, context unsafe.Pointer) { + if controllerEventCallback != nil { + controllerEventCallback(int(controllerId), int(changeEvent), int(changeType), C.GoString(newValue), int(callbackId)) + } +} + +// Init initializes the Telldus library +func Init() { + C.tdInit() +} + +// Close closes the Telldus library +func Close() { + C.tdClose() +} + +// TurnOn turns on a device +func TurnOn(deviceId int) int { + return int(C.tdTurnOn(C.int(deviceId))) +} + +// TurnOff turns off a device +func TurnOff(deviceId int) int { + return int(C.tdTurnOff(C.int(deviceId))) +} + +// Dim dims a device to a level (0-255) +func Dim(deviceId int, level int) int { + return int(C.tdDim(C.int(deviceId), C.uchar(level))) +} + +// Bell rings the bell on a device +func Bell(deviceId int) int { + return int(C.tdBell(C.int(deviceId))) +} + +// GetNumberOfDevices returns the number of devices +func GetNumberOfDevices() int { + return int(C.tdGetNumberOfDevices()) +} + +// GetDeviceId returns the device ID at the given index +func GetDeviceId(index int) int { + return int(C.tdGetDeviceId(C.int(index))) +} + +// GetName returns the name of a device +func GetName(deviceId int) string { + cstr := C.tdGetName(C.int(deviceId)) + defer C.tdReleaseString(cstr) + return C.GoString(cstr) +} + +// GetProtocol returns the protocol of a device +func GetProtocol(deviceId int) string { + cstr := C.tdGetProtocol(C.int(deviceId)) + defer C.tdReleaseString(cstr) + return C.GoString(cstr) +} + +// GetModel returns the model of a device +func GetModel(deviceId int) string { + cstr := C.tdGetModel(C.int(deviceId)) + defer C.tdReleaseString(cstr) + return C.GoString(cstr) +} + +// Methods returns the supported methods for a device +func Methods(deviceId int, methodsSupported int) int { + return int(C.tdMethods(C.int(deviceId), C.int(methodsSupported))) +} + +// Sensor retrieves sensor data +func Sensor(protocol *string, model *string, id *int, dataTypes *int) int { + var cProtocol [20]C.char + var cModel [20]C.char + var cId C.int + var cDataTypes C.int + ret := int(C.tdSensor(&cProtocol[0], 20, &cModel[0], 20, &cId, &cDataTypes)) + if ret == 0 { + *protocol = C.GoString(&cProtocol[0]) + *model = C.GoString(&cModel[0]) + *id = int(cId) + *dataTypes = int(cDataTypes) + } + return ret +} + +// SensorValue retrieves a specific sensor value +func SensorValue(protocol, model string, id, dataType int) (string, int, int) { + var value [20]C.char + var timestamp C.int + ret := int(C.tdSensorValue(C.CString(protocol), C.CString(model), C.int(id), C.int(dataType), &value[0], 20, ×tamp)) + if ret == 0 { + return C.GoString(&value[0]), int(timestamp), ret + } + return "", 0, ret +} + +// GetErrorString returns the error string for an error code +func GetErrorString(errorNo int) string { + cstr := C.tdGetErrorString(C.int(errorNo)) + defer C.tdReleaseString(cstr) + return C.GoString(cstr) +} + +// RegisterDeviceEvent registers a callback for device events +func RegisterDeviceEvent(cb DeviceEventFunc) int { + deviceEventCallback = cb + return int(C.tdRegisterDeviceEvent(C.TDDeviceEvent(C.goDeviceEvent), nil)) +} + +// RegisterDeviceChangeEvent registers a callback for device change events +func RegisterDeviceChangeEvent(cb DeviceChangeEventFunc) int { + deviceChangeEventCallback = cb + return int(C.tdRegisterDeviceChangeEvent(C.TDDeviceChangeEvent(C.goDeviceChangeEvent), nil)) +} + +// RegisterRawDeviceEvent registers a callback for raw device events +func RegisterRawDeviceEvent(cb RawDeviceEventFunc) int { + rawDeviceEventCallback = cb + return int(C.tdRegisterRawDeviceEvent(C.TDRawDeviceEvent(C.goRawDeviceEvent), nil)) +} + +// RegisterSensorEvent registers a callback for sensor events +func RegisterSensorEvent(cb SensorEventFunc) int { + sensorEventCallback = cb + return int(C.tdRegisterSensorEvent(C.TDSensorEvent(C.goSensorEvent), nil)) +} + +// RegisterControllerEvent registers a callback for controller events +func RegisterControllerEvent(cb ControllerEventFunc) int { + controllerEventCallback = cb + return int(C.tdRegisterControllerEvent(C.TDControllerEvent(C.goControllerEvent), nil)) +} + +// UnregisterCallback unregisters a callback +func UnregisterCallback(callbackId int) int { + return int(C.tdUnregisterCallback(C.int(callbackId))) +} + +// Constants for methods +const ( + MethodTurnOn = 1 + MethodTurnOff = 2 + MethodBell = 4 + MethodDim = 16 + MethodLearn = 32 +) + +// Constants for sensor data types +const ( + DataTypeTemperature = 1 + DataTypeHumidity = 2 + DataTypeRainRate = 4 + DataTypeRainTotal = 8 + DataTypeWindDirection = 16 + DataTypeWindAverage = 32 + DataTypeWindGust = 64 +)