Merge branch 'master' into ticket96

Conflicts:
	telldus-gui/Plugins/Sensors/main.qml
	telldus-gui/Plugins/Sensors/sensor.cpp
This commit is contained in:
Micke Prag 2012-01-16 16:30:10 +01:00
commit bdb766e7cf
108 changed files with 3128 additions and 678 deletions

5
.gitignore vendored
View file

@ -1 +1,6 @@
build/
qtcreator-build/
Doxyfile
html/
latex/
CMakeLists.txt.user

View file

@ -187,10 +187,54 @@
* tdReleaseString(name);
* \endcode
*
* \subsection sec_bu_sensors Sensors
*
* Retrieving sensor values can be done in two ways. Either by a polling
* interface or by callbacks. The client application can implement one or both
* of these interfaces. For callbacks, read more under \ref sec_events.
*
* Each of the sensors can have one or several value types. Currently only
* temperature and humidity are implemented.
*
* There is no API to add, remove or edit sensors. Each sensor that
* TellStick Duo has got any data from is added to an internal list. It is up to
* the client application to filter and only show the sensors your are
* interested in.
*
* To iterate over the list of sensors, call tdSensor() repeatedly as long as it
* returns \c TELLSTICK_SUCCESS. The parameters \c protocol, \c model,
* \c sensorId, and \c dataTypes are sent by reference and will be filled with
* the values.
*
* Example:
* \code
* char protocol[DATA_LENGTH], model[DATA_LENGTH];
* int sensorId = 0, dataTypes = 0;
* while(tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes) == TELLSTICK_SUCCESS) {
* //Print the sensor
* printf("%s,\t%s,\t%i\n", protocol, model, sensorId);
* }
* \endcode
*
* The type of sensor values the sensor supports are stored as flags in the
* parameter \c sensorId. Call tdSensorValue() for each type.
*
* Example:
* \code
* char value[DATA_LENGTH];
* char timeBuf[80];
* time_t timestamp = 0;
* if (dataTypes & TELLSTICK_TEMPERATURE) {
* tdSensorValue(protocol, model, sensorId, TELLSTICK_TEMPERATURE, value, DATA_LENGTH, (int *)&timestamp);
* strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
* printf("Temperature:\t%sº\t(%s)\n", value, timeBuf);
* }
* \endcode
*
* \section sec_events Events
*
* To get events from either a TellStick Duo or if another software changes the
* status of a device you have to register for a callback.
* To get events from either a TellStick Duo, another software changes the
* status of a device, or new sensors values you have to register for a callback.
*
* \subsection sec_events_registering Registering for callbacks
*
@ -198,6 +242,7 @@
* \li tdRegisterDeviceEvent()
* \li tdRegisterDeviceChangeEvent()
* \li tdRegisterRawDeviceEvent()
* \li tdRegisterSensorEvent()
*
* These all work in the same way. The first parameter is a function-pointer to
* the callback function. The second parameter is an optional void pointer. This
@ -212,13 +257,14 @@
*
* Many devices (for example motion detectors) resends their messages many times
* to ensure that they are received correctly. If a deviceeventcallback or
* rawdeviceeventcallback in turn is calling a controlling function, for example tdTurnOn,
* it may be neccessary to implement some solution to wait for the device to finish its
* resending, before executing the controlling function. See how this can be done in the python example.
* rawdeviceeventcallback in turn is calling a controlling function, for example
* tdTurnOn, it may be neccessary to implement some solution to wait for the
* device to finish its resending, before executing the controlling function.
* See how this can be done in the python example.
*
* \subsection sec_events_callbacks Callbacks
*
* telldus-core currently implements three different callback function for
* telldus-core currently implements four different callback function for
* different purposes.
*
* \subsubsection sec_events_callbacks_deviceevent DeviceEvent
@ -234,9 +280,10 @@
* - const char *data - For some methods this contains data. For TELLSTICK_DIM
* this hold the current value.
* - int callbackId - id of callback
* - void *context - see "Registering for callbacks" for description
* - void *context - see \ref sec_events_registering for description
*
* \subsubsection sec_events_callbacks_devicechangeevent DeviceChangeEvent
*
* This event is fired when the data around a device is changed. It can only be
* triggered by another software. Use this callback to keep your list of devices
* in sync.
@ -256,19 +303,33 @@
* - TELLSTICK_CHANGE_PROTOCOL - Use tdGetProtocol() to read the new value.
* - TELLSTICK_CHANGE_MODEL - Use tdGetModel() to read the new value.
* - int callbackId - id of callback
* - void *context - see "Registering for callbacks" for description
* - void *context - see \ref sec_events_registering for description
*
* \subsubsection sec_events_callbacks_rawdeviceevent RawDeviceEvent
*
* Use this callback with caution. It outputs everything from the Duo without
* any preprocessing. This can be used to get events from devices not already
* configured.
* Use this callback with caution. It outputs everything from a TellStick Duo
* without any preprocessing. This can be used to get events from devices not
* already configured.
*
* Parameters:
* - const char *data - raw device data
* - int controllerId - id of receiving controller, can identify the TellStick if several exists in the system
* - int callbackId - id of callback
* - void *context - see "Registering for callbacks" for description
* - void *context - see \ref sec_events_registering for description
*
* \subsubsection sec_events_callbacks_sensorevent SensorEvent
*
* This event is fired when a new sensor value is retrieved.
*
* Parameters:
* - const char *protocol - The sensors protocol
* - const char *model - The model of the sensor
* - int id - The unique id for the sensor.
* - int dataType - Flags for which types of data the sensor supports
* - const char *value - A human readable string of the data
* - int timestamp - The timestamp when the latest value was received
* - int callbackId - id of callback
* - void *context - See \ref sec_events_registering for description
*
* \subsection sec_events_example Example
*

View file

@ -0,0 +1,19 @@
<?php
session_start();
require_once 'HTTP/OAuth/Consumer.php';
define('PUBLIC_KEY', '');
define('PRIVATE_KEY', '');
define('URL', 'http://api.telldus.net');
define('REQUEST_TOKEN', constant('URL').'/oauth/requestToken');
define('AUTHORIZE_TOKEN', constant('URL').'/oauth/authorize');
define('ACCESS_TOKEN', constant('URL').'/oauth/accessToken');
define('REQUEST_URI', constant('URL').'/xml');
define('BASE_URL', 'http://'.$_SERVER["SERVER_NAME"].dirname($_SERVER['REQUEST_URI']));
define('TELLSTICK_TURNON', 1);
define('TELLSTICK_TURNOFF', 2);

View file

@ -0,0 +1,21 @@
<?php
require_once 'common.php';
$consumer = new HTTP_OAuth_Consumer(constant('PUBLIC_KEY'), constant('PRIVATE_KEY'), $_SESSION['token'], $_SESSION['tokenSecret']);
try {
$consumer->getAccessToken(constant('ACCESS_TOKEN'));
$_SESSION['accessToken'] = $consumer->getToken();
$_SESSION['accessTokenSecret'] = $consumer->getTokenSecret();
header('Location:index.php');
} catch (Exception $e) {
?>
<p>Authorization failed!</p>
<p><a href="index.php">Go back</a></p>
<?php
}

View file

@ -0,0 +1,13 @@
<?php
require_once 'common.php';
$consumer = new HTTP_OAuth_Consumer(constant('PUBLIC_KEY'), constant('PRIVATE_KEY'));
$consumer->getRequestToken(constant('REQUEST_TOKEN'), constant('BASE_URL').'/getAccessToken.php');
$_SESSION['token'] = $consumer->getToken();
$_SESSION['tokenSecret'] = $consumer->getTokenSecret();
$url = $consumer->getAuthorizeUrl(constant('AUTHORIZE_TOKEN'));
header('Location:'.$url);

View file

@ -0,0 +1,47 @@
<?php
require_once 'common.php';
if (isset($_GET['clear'])) {
session_destroy();
header('location:index.php');
exit();
}
if (!isset($_SESSION['accessToken'])) {
?>We have no access token, <a href="getRequestToken.php">connect us</a><?php
exit();
}
?>
<p>We have access!</p>
<p>
In your system, store these values to do requests for this user:<br>
Token: <?php echo $_SESSION['accessToken']; ?><br>
Secret: <?php echo $_SESSION['accessTokenSecret']; ?>
</p>
<p><a href="index.php?clear">Clear the token and restart</a></p>
<p><a href="index.php?listDevices">List users devices</a></p>
<?php
if (isset($_GET['listDevices'])) {
$consumer = new HTTP_OAuth_Consumer(constant('PUBLIC_KEY'), constant('PRIVATE_KEY'), $_SESSION['accessToken'], $_SESSION['accessTokenSecret']);
$params = array(
'supportedMethods' => constant('TELLSTICK_TURNON') | constant('TELLSTICK_TURNOFF'),
);
$response = $consumer->sendRequest(constant('REQUEST_URI').'/devices/list', $params, 'GET');
echo '<pre>';
echo( htmlentities($response->getBody()));
}
?><p><a href="index.php?listClients">List users clients</a></p><?php
if (isset($_GET['listClients'])) {
$consumer = new HTTP_OAuth_Consumer(constant('PUBLIC_KEY'), constant('PRIVATE_KEY'), $_SESSION['accessToken'], $_SESSION['accessTokenSecret']);
$params = array();
$response = $consumer->sendRequest(constant('REQUEST_URI').'/clients/list', $params, 'GET');
echo '<pre>';
echo( htmlentities($response->getBody()));
}

88
rfcmd/CMakeLists.txt Normal file
View file

@ -0,0 +1,88 @@
cmake_minimum_required(VERSION 2.4)
INCLUDE_DIRECTORIES(
/usr/src/linux/include
)
#### Project: rfcmd ####
PROJECT(rfcmd)
SET(rfcmd_DESCRIPTION
"Sends RF remote commands through a Telldus TellStick"
)
SET(rfcmd_SRCS
rfcmd.c
)
IF(${RFCMD_DEBUG})
ADD_DEFINITIONS( -DRFCMD_DEBUG )
ENDIF(${RFCMD_DEBUG})
IF (BUILD_RFCMD_WITH_LIBFTDI)
ADD_DEFINITIONS( -DLIBFTDI )
SET(rfcmd_SRCS
${rfcmd_SRCS}
ftdi.c
)
ENDIF (BUILD_RFCMD_WITH_LIBFTDI)
ADD_EXECUTABLE(rfcmd
${rfcmd_SRCS}
)
IF (BUILD_RFCMD_WITH_SEMAPHORES)
FIND_LIBRARY(SEM_LIBRARY rt)
TARGET_LINK_LIBRARIES(rfcmd
${SEM_LIBRARY}
)
ELSE (BUILD_RFCMD_WITH_SEMAPHORES)
ADD_DEFINITIONS( -DNO_SEMAPHORES )
ENDIF (BUILD_RFCMD_WITH_SEMAPHORES)
IF (BUILD_RFCMD_WITH_LIBFTDI)
FIND_LIBRARY(FTDI_LIBRARY ftdi)
TARGET_LINK_LIBRARIES(rfcmd
${FTDI_LIBRARY}
)
ENDIF (BUILD_RFCMD_WITH_LIBFTDI)
INSTALL(TARGETS rfcmd RUNTIME DESTINATION bin)
IF (UNIX)
IF (GENERATE_MAN)
ADD_CUSTOM_COMMAND(
TARGET rfcmd
POST_BUILD
COMMAND help2man -n ${rfcmd_DESCRIPTION} ./rfcmd > rfcmd.1
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating man file rfcmd.1"
)
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/rfcmd.1 DESTINATION share/man/man1)
ENDIF (GENERATE_MAN)
ENDIF (UNIX)
#### Project: find_telldus ####
IF (BUILD_RFCMD_WITH_LIBFTDI)
PROJECT(find_telldus)
SET(find_telldus_SRCS
find_telldus.c
)
ADD_EXECUTABLE(find_telldus
${find_telldus_SRCS}
)
TARGET_LINK_LIBRARIES(find_telldus
${FTDI_LIBRARY}
)
INSTALL(TARGETS find_telldus RUNTIME DESTINATION bin)
ENDIF (BUILD_RFCMD_WITH_LIBFTDI)

340
rfcmd/COPYING Normal file
View file

@ -0,0 +1,340 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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 or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.
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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. However, as a
special exception, the source code 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.
7. 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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.
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.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), 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 Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program 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 program 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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

22
rfcmd/Makefile Normal file
View file

@ -0,0 +1,22 @@
SRCS= rfcmd.c ftdi.c
CFLAGS=-O2 -Wall -I/usr/local/include
LFLAGS= -L/usr/local/lib -R/usr/local/lib
#linux:LFLAGS=-Wl,-rpath,/usr/local/lib
LIBS= -lftdi -lusb
PROG= rfcmd
FT= find_telldus
OBJS= $(SRCS:.c=.o)
CC= gcc
all: $(PROG) $(FT)
$(PROG): $(OBJS)
$(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LFLAGS) $(LIBS)
rfcmd.o: rfcmd.c
ftdi.o: ftdi.c
$(FT): $(FT).c
$(CC) $(CFLAGS) -o $@ $(FT).c $(LFLAGS) $(LIBS)

6
rfcmd/build.sh Normal file
View file

@ -0,0 +1,6 @@
#!/bin/sh
set -e
make clean || { echo "Warning: make clean failed"; }
make || { echo "make failed"; exit 1; }
make install || { echo "make install failed"; exit 1; }
echo rfcmd built and installed!

42
rfcmd/find_telldus.c Normal file
View file

@ -0,0 +1,42 @@
/* find_all.c
Example for ftdi_usb_find_all()
This program is distributed under the GPL, version 2
*/
#include <stdio.h>
#include <ftdi.h>
int main(int argc, char **argv)
{
int ret, i;
struct ftdi_context ftdic;
struct ftdi_device_list *devlist, *curdev;
char manufacturer[128], description[128];
ftdi_init(&ftdic);
if((ret = ftdi_usb_find_all(&ftdic, &devlist, 0x1781, 0x0c30)) < 0) {
fprintf(stderr, "ftdi_usb_find_all failed: %d (%s)\n", ret, ftdi_get_error_string(&ftdic));
return EXIT_FAILURE;
}
printf("Number of FTDI devices (telldus) found: %d\n", ret);
i = 0;
for (curdev = devlist; curdev != NULL; i++) {
printf("Checking device: %d\n", i);
if((ret = ftdi_usb_get_strings(&ftdic, curdev->dev, manufacturer, 128, description, 128, NULL, 0)) < 0) {
fprintf(stderr, "ftdi_usb_get_strings failed: %d (%s)\n", ret, ftdi_get_error_string(&ftdic));
return EXIT_FAILURE;
}
printf("Manufacturer: %s, Description: %s\n\n", manufacturer, description);
curdev = curdev->next;
}
ftdi_list_free(&devlist);
ftdi_deinit(&ftdic);
return EXIT_SUCCESS;
}

110
rfcmd/ftdi.c Normal file
View file

@ -0,0 +1,110 @@
/*
* TellStick libftdi libusb version
* tested with 0.15 : http://www.intra2net.com/en/developer/libftdi
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ftdi.h>
#define BAUD 4800
void ftdiCleanup(struct ftdi_context *ctx) {
ftdi_usb_close( ctx );
ftdi_deinit( ctx );
}
/*
* use libftdi and libusb to send command
* no kernel driver needed
*/
int usbWriteFtdi(char *cmdstr)
{
struct ftdi_context ctx;
int device=0x0c30, vendor=0x1781;
int retrycnt;
int retval = 0;
if (ftdi_init( &ctx )) {
char *err = ftdi_get_error_string(&ctx);
fprintf(stderr, "usb - init error: %s\n", err);
return 1;
}
retval = ftdi_usb_open(&ctx, vendor, device);
if (retval) {
char *err = ftdi_get_error_string(&ctx);
// FreeBSD says -3 when another rfcmd is running...
// Same on other systems?
if(retval == -3) {
fprintf(stderr, "usb - open error: %s. Is it busy?\n", err);
} else {
fprintf(stderr, "usb - open error: %s\n", err);
}
ftdi_deinit( &ctx );
return 2;
}
if (ftdi_usb_reset( &ctx )) {
char *err = ftdi_get_error_string(&ctx);
fprintf(stderr, "usb - reset error: %s\n", err);
retval = 3;
ftdiCleanup(&ctx);
return retval;
}
if (ftdi_disable_bitbang( &ctx ) || ftdi_set_baudrate(&ctx, BAUD)) {
char *err = ftdi_get_error_string(&ctx);
fprintf(stderr, "usb - init failed: %s\n", err);
ftdiCleanup(&ctx);
return 4;
}
retval = ftdi_write_data( &ctx, cmdstr, strlen(cmdstr) );
if (retval < 0) {
char *err = ftdi_get_error_string(&ctx);
fprintf(stderr, "usb - write failed: %s\n", err);
ftdiCleanup(&ctx);
return 5;
} else if(retval != strlen(cmdstr)) {
fprintf(stderr, "usb - warning: %d bytes written instead of %d\n",
retval, (int)strlen(cmdstr));
}
/**
* Wait for Tellstick to be done with cmd, read back until we've received
* a \n indicating end of response.
* Wait max 5000 * 1000uS.
* XXX: Can the tellstick report errors?
*/
retval = 0;
retrycnt = 5000;
while (retrycnt--) {
unsigned char inb;
int bytes;
bytes = ftdi_read_data(&ctx, &inb, 1);
if (bytes == 0) {
usleep(1000);
} else if (bytes > 0) {
// Done when newline is received
if(inb == '\n') {
ftdiCleanup(&ctx);
return retval;
}
} else {
char *err = ftdi_get_error_string(&ctx);
fprintf(stderr, "usb - read error: %s\n", err);
ftdiCleanup(&ctx);
return 6;
}
}
// if we get here we failed to readback
fprintf(stderr, "usb - warning: never got newline response, giving up on wait\n");
return retval;
}

663
rfcmd/rfcmd.c Normal file
View file

@ -0,0 +1,663 @@
/****************************************************************************
** rfcmd.c ***********************************************************
****************************************************************************
*
* rfcmd - utility to control NEXA and other RF remote receivers through a TellStick
* USB interface
*
* Copyright (C) 2007 Tord Andersson <tord.o.andersson@gmail.com>
*
* License: GPL v. 2
*
* Authors:
* Tord Andersson <tord.o.andersson@gmail.com>
* Micke Prag <micke.prag@telldus.se>
* Gudmund Berggren
*
* Tapani Rintala / userspace libusb / libftdi - version 02-2009
*/
/*******************************************************************************
* Modifications from rfcmd.c ver 0.2 done by Gudmund
* Added support for IKEA
* Note:
* 1. System code
* 2. Level 0 == Off (For dimmers and non dimmers)
* Level 1== 10%. (for dimmers)
* ....
* Level 9 == 90 % (for dimmers)
* Level 10 == 100 % (or ON for non dimmers)
* 3. Command line syntax:
* /usr/local/bin/rfcmd /dev/ttyUSB0 IKEA 0 1 10 1"
* Arg 1: device
* Arg 2: Protocoll
* Arg 3: System code
* Arg 4: Device code
* Arg 5: Level (0=off, 1 = 10%, 2=20%,...9=90%, 10=100%)
* Arg 6: DimStyle (0=instant change, 1=gradually change)
******************************************************************************/
/*******************************************************************************
* Modifications to rfcmd.c ver 2.1.0 done by Tord Andersson
* Introduced semaphore protection to avoid problems with simultaneous port
* access from several processes (typically cron jobs). Note! Need rt lib.
******************************************************************************/
/*******************************************************************************
* Modifications from rfcmd.c ver 2.1.0 done by Leo
* Added support for RISINGSUN
* Note:
* 1. Command line syntax:
* /usr/local/bin/rfcmd /dev/ttyUSB0 RISINGSUN 4 1 0"
* Arg 1: device
* Arg 2: protocol
* Arg 3: code
* Arg 4: device number
* Arg 5: Level (0=off, 1 = on)
******************************************************************************/
/*******************************************************************************
* Modifications from rfcmd.c ver 2.1.0 based on marvelous work by Snakehand
* See http://www.telldus.com/forum/viewtopic.php?t=97&start=63
* Added support for EVERFLOURISH
* Note:
* 1. Command line syntax:
* /usr/local/bin/rfcmd /dev/ttyUSB0 EVERFLOURISH 1 15
* Arg 1: device
* Arg 2: protocol
* Arg 3: device number (0..65535)
* Arg 4: Level (0=off, 15=on, 10=learn)
******************************************************************************/
/*******************************************************************************
* Modifications from rfcmd ver 2.1.1 done by Johan Ström
* Default disabled semaphores for FreeBSD.
* Added status readback in ftdi.c, instead of wasting time in sleep.
*
* FreeBSD does not have support in the GENERIC kernel semaphore.
* To enable usage of them, you'll have have the following option in your
* kernel configuration:
*
* options P1003_1B_SEMAPHORES
*
* However, on FreeBSD only libftdi seems to be working (at least on my system),
* since the device is not identified as a ucom device. And as we're accessing it
* via libftdi, libftdi makes sure simultaneous access is impossible.
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#ifndef NO_SEMAPHORES
#include <semaphore.h>
#endif
#define PROG_NAME "rfcmd"
#define PROG_VERSION "2.1.1"
/* #define RFCMD_DEBUG */
/* Local function declarations */
int createNexaString(const char * pHouseStr, const char * pChannelStr,
const char * pOn_offStr, char * pTxStr, int waveman);
int createSartanoString(const char * pChannelStr, const char * pOn_offStr,
char * pTxStr);
int createIkeaString(const char * pSystemStr, const char * pChannelStr,
const char * pLevelStr, const char *pDimStyle,
char * pStrReturn);
int createRisingSunString(const char * pCodeStr, const char* pUnitStr, const char * pOn_offStr,
char * pTxStr);
int createEverFlourishString(const char* pUnitStr, const char * pLevelStr,
char * pTxStr);
void printUsage(void);
void printVersion(void);
#ifdef LIBFTDI
int usbWriteFtdi(char *cmdstr);
#endif
int main( int argc, char **argv )
{
struct termios tio;
int fd = -1;
#ifndef NO_SEMAPHORES
sem_t * portMutex;
char SEM_NAME[]= "RFCMD_SEM"; /* Semaphore for multiple access ctrl */
#endif
char txStr[100];
if( (argc == 6) && (strcmp(*(argv+2), "NEXA") == 0)) {
if (createNexaString(*(argv+3), *(argv+4), *(argv+5), txStr, 0) == 0) {
printUsage();
exit(1);
}
/* else - a send cmd string was created */
} else if( (argc == 6) && (strcmp(*(argv+2), "WAVEMAN") == 0)) {
if (createNexaString(*(argv+3),*(argv+4), *(argv+5), txStr, 1) == 0) {
printUsage();
exit(1);
}
} else if( (argc == 5) && (strcmp(*(argv+2), "SARTANO") == 0)) {
if (createSartanoString(*(argv+3), *(argv+4), txStr) == 0) {
printUsage();
exit(1);
}
/* else - a send cmd string was created */
} else if ( (argc == 7) && (strcmp(*(argv+2),"IKEA")==0) ) {
// System, Channel, Level, DimStyle, TXString
if ( createIkeaString(*(argv+3), *(argv+4), *(argv+5), *(argv+6),txStr) == 0 ) {
printUsage();
exit(1);
}
/* else - a send cmd string was created */
} else if ( (argc == 6) && (strcmp(*(argv+2),"RISINGSUN")==0) ) {
// Code, Unit, Power
if ( createRisingSunString(*(argv+3), *(argv+4), *(argv+5), txStr) == 0 ) {
printUsage();
exit(1);
}
/* else - a send cmd string was created */
} else if ( (argc == 5) && (strcmp(*(argv+2),"EVERFLOURISH")==0) ) {
// Unit, Level
if ( createEverFlourishString(*(argv+3), *(argv+4), txStr) == 0 ) {
printUsage();
exit(1);
}
/* else - a send cmd string was created */
} else if ( (argc >= 2) && (strcmp(*(argv+1),"--version")==0) ) {
printVersion();
exit(1);
} else { /* protocol or parameters not recognized */
printUsage();
exit(1);
}
#ifdef RFCMD_DEBUG
printf("txStr: %s\n", txStr);
#endif
if(strlen(txStr) > 0) {
#ifndef NO_SEMAPHORES
/* create the semaphore - will reuse an existing one if it exists */
portMutex = sem_open(SEM_NAME,O_CREAT,0644,1);
if( portMutex == SEM_FAILED) {
fprintf(stderr, "%s - Error creating port semaphore\n", PROG_NAME);
perror("Semaphore open error");
sem_unlink(SEM_NAME);
exit(1);
}
/* lock semaphore to protect port from multiple access */
if(sem_wait(portMutex) != 0) {
fprintf(stderr, "%s - Error aquiring port semaphore\n", PROG_NAME);
sem_unlink(SEM_NAME);
sem_close(portMutex);
exit(1);
}
#endif
if (strcmp(*(argv+1), "LIBUSB") != 0) {
if( 0 > ( fd = open( *(argv+1), O_RDWR ) ) ) {
#ifdef __FreeBSD__
fprintf(stderr, "%s - Error opening %s; You're on a FreeBSD system, you should probably use LIBUSB.\n", PROG_NAME, *(argv+1));
#else
fprintf(stderr, "%s - Error opening %s\n", PROG_NAME, *(argv+1));
#endif
#ifndef NO_SEMAPHORES
if(sem_post(portMutex) != 0) {
fprintf(stderr, "%s - Error releasing port semaphore\n", PROG_NAME);
}
sem_unlink(SEM_NAME);
sem_close(portMutex);
#endif
exit(1);
}
/* adjust serial port parameters */
bzero(&tio, sizeof(tio)); /* clear struct for new port settings */
tio.c_cflag = B4800 | CS8 | CLOCAL | CREAD; /* CREAD not used yet */
tio.c_iflag = IGNPAR;
tio.c_oflag = 0;
tio.c_ispeed = 4800;
tio.c_ospeed = 4800;
tcflush(fd, TCIFLUSH);
tcsetattr(fd,TCSANOW,&tio);
write(fd, txStr, strlen(txStr));
sleep(1); /* one second sleep to avoid device 'choking' */
close(fd); /* Modified : Close fd to make a clean exit */
} else {
#ifdef LIBFTDI
usbWriteFtdi( txStr );
#else
fprintf(stderr, "%s - Support for libftdi is not compiled in, please recompile rfcmd with support for libftdi\n", PROG_NAME);
#endif
}
#ifndef NO_SEMAPHORES
/* Unlock semaphore */
if (sem_post(portMutex) != 0) {
fprintf(stderr, "%s - Error releasing port semaphore\n", PROG_NAME);
sem_unlink(SEM_NAME);
sem_close(portMutex);
exit(1);
} else {
sem_unlink(SEM_NAME);
sem_close(portMutex);
}
#endif
}
exit(0);
}
int createNexaString(const char * pHouseStr, const char * pChannelStr,
const char * pOn_offStr, char * pTxStr, int waveman)
{
* pTxStr = '\0'; /* Make sure tx string is empty */
int houseCode;
int channelCode;
int on_offCode;
int txCode = 0;
const int unknownCode = 0x6;
int bit;
int bitmask = 0x0001;
houseCode = (int)((* pHouseStr) - 65); /* House 'A'..'P' */
channelCode = atoi(pChannelStr) - 1; /* Channel 1..16 */
on_offCode = atoi(pOn_offStr); /* ON/OFF 0..1 */
#ifdef RFCMD_DEBUG
printf("House: %d, channel: %d, on_off: %d\n", houseCode, channelCode, on_offCode);
#endif
/* check converted parameters for validity */
if((houseCode < 0) || (houseCode > 15) || // House 'A'..'P'
(channelCode < 0) || (channelCode > 15) ||
(on_offCode < 0) || (on_offCode > 1))
{
} else {
/* b0..b11 txCode where 'X' will be represented by 1 for simplicity.
b0 will be sent first */
txCode = houseCode;
txCode |= (channelCode << 4);
if (waveman && on_offCode == 0) {
} else {
txCode |= (unknownCode << 8);
txCode |= (on_offCode << 11);
}
/* convert to send cmd string */
strcat(pTxStr,"S");
for(bit=0;bit<12;bit++)
{
if((bitmask & txCode) == 0) {
/* bit timing might need further refinement */
strcat(pTxStr," ` `"); /* 320 us high, 960 us low, 320 us high, 960 us low */
/* strcat(pTxStr,"$k$k"); *//* 360 us high, 1070 us low, 360 us high, 1070 us low */
} else { /* add 'X' (floating bit) */
strcat(pTxStr," `` "); /* 320 us high, 960 us low, 960 us high, 320 us low */
/*strcat(pTxStr,"$kk$"); *//* 360 us high, 1070 us low, 1070 us high, 360 us low */
}
bitmask = bitmask<<1;
}
/* add stop/sync bit and command termination char '+'*/
strcat(pTxStr," }+");
/* strcat(pTxStr,"$}+"); */
}
#ifdef RFCMD_DEBUG
printf("txCode: %04X\n", txCode);
#endif
return strlen(pTxStr);
}
int createSartanoString(const char * pChannelStr, const char * pOn_offStr,
char * pTxStr)
{
* pTxStr = '\0'; /* Make sure tx string is empty */
int on_offCode;
int bit;
on_offCode = atoi(pOn_offStr); /* ON/OFF 0..1 */
#ifdef RFCMD_DEBUG
printf("Channel: %s, on_off: %d\n", pChannelStr, on_offCode);
#endif
/* check converted parameters for validity */
if((strlen(pChannelStr) != 10) ||
(on_offCode < 0) || (on_offCode > 1)) {
} else {
strcat(pTxStr,"S");
for(bit=0;bit<=9;bit++)
{
if(strncmp(pChannelStr+bit, "1", 1) == 0) { //If it is a "1"
strcat(pTxStr,"$k$k");
} else {
strcat(pTxStr,"$kk$");
}
}
if (on_offCode >= 1)
strcat(pTxStr,"$k$k$kk$"); //the "turn on"-code
else
strcat(pTxStr,"$kk$$k$k"); //the "turn off"-code
/* add stop/sync bit and command termination char '+'*/
strcat(pTxStr,"$k+");
}
return strlen(pTxStr);
}
int createIkeaString( const char * pSystemStr, const char * pChannelStr, const char * pLevelStr, const char *pDimStyle, char * pStrReturn)
{
*pStrReturn = '\0'; /* Make sure tx string is empty */
const char STARTCODE[] = "STTTTTTª";
const char TT[] = "TT";
const char A[] = "ª";
int systemCode = atoi(pSystemStr) - 1; /* System 1..16 */
int channelCode = atoi(pChannelStr); /* Channel 1..10 */
int Level = atoi(pLevelStr); /* off,10,20,..,90,on */
int DimStyle = atoi(pDimStyle);
int intCode = 0;
int checksum1 = 0;
int checksum2 = 0;
int intFade ;
int i ;
int rawChannelCode = 0;
/* check converted parameters for validity */
if ( (channelCode <= 0) || (channelCode > 10) ||
(systemCode < 0) || (systemCode > 15) ||
(Level < 0) || (Level > 10) ||
(DimStyle < 0) || (DimStyle > 1))
{
return 0;
}
if (channelCode == 10) {
channelCode = 0;
}
rawChannelCode = (1<<(9-channelCode));
strcat(pStrReturn, STARTCODE ) ; //Startcode, always like this;
intCode = (systemCode << 10) | rawChannelCode;
for ( i = 13; i >= 0; --i) {
if ((intCode>>i) & 1) {
strcat(pStrReturn, TT );
if (i % 2 == 0) {
checksum2++;
} else {
checksum1++;
}
} else {
strcat(pStrReturn,A);
}
}
if (checksum1 %2 == 0) {
strcat(pStrReturn, TT );
} else {
strcat(pStrReturn, A) ; //1st checksum
}
if (checksum2 %2 == 0) {
strcat(pStrReturn, TT );
} else {
strcat(pStrReturn, A ) ; //2nd checksum
}
if (DimStyle == 1) {
intFade = 11 << 4; //Smooth
} else {
intFade = 1 << 4; //Instant
}
switch ( Level )
{
case 0 :
intCode = (10 | intFade) ; //Concat level and fade
break;
case 1 :
intCode = (1 | intFade) ; //Concat level and fade
break;
case 2 :
intCode = (2 | intFade) ; //Concat level and fade
break;
case 3 :
intCode = (3 | intFade) ; //Concat level and fade
break;
case 4 :
intCode = (4 | intFade) ; //Concat level and fade
break;
case 5 :
intCode = (5 | intFade) ; //Concat level and fade
break;
case 6 :
intCode = (6 | intFade) ; //Concat level and fade
break;
case 7 :
intCode = (7 | intFade) ; //Concat level and fade
break;
case 8 :
intCode = (8 | intFade) ; //Concat level and fade
break;
case 9 :
intCode = (9 | intFade) ; //Concat level and fade
break;
case 10 :
default :
intCode = (0 | intFade) ; //Concat level and fade
break;
}
checksum1 = 0;
checksum2 = 0;
for (i = 0; i < 6; ++i) {
if ((intCode>>i) & 1) {
strcat(pStrReturn, TT);
if (i % 2 == 0) {
checksum1++;
} else {
checksum2++;
}
} else {
strcat(pStrReturn, A );
}
}
if (checksum1 %2 == 0) {
strcat(pStrReturn, TT);
} else {
strcat(pStrReturn, A ) ; //2nd checksum
}
if (checksum2 %2 == 0) {
strcat(pStrReturn, TT );
} else {
strcat(pStrReturn, A ) ; //2nd checksum
}
strcat(pStrReturn, "+");
return strlen(pStrReturn);
}
int createRisingSunString(const char * pCodeStr, const char * pUnitStr, const char * pOn_offStr,
char * pTxStr)
{
* pTxStr = '\0'; /* Make sure tx string is empty */
int on_offCode;
int unit;
int code;
int i;
on_offCode = atoi(pOn_offStr); /* ON/OFF 0..1 */
code = atoi (pCodeStr);
unit = atoi (pUnitStr);
#ifdef RFCMD_DEBUG
printf("Code: %s, unit: %s, on_off: %d\n", pCodeStr, pUnitStr, on_offCode);
#endif
/* check converted parameters for validity */
if((code < 1) || (code > 4) ||
(unit < 1) || (unit > 4) ||
(on_offCode < 0) || (on_offCode > 1)) {
} else {
strcat(pTxStr,"S.e");
for (i = 1; i <= 4; ++i) {
if (i == code) {
strcat (pTxStr, ".e.e");
} else {
strcat (pTxStr, "e..e");
}
}
for (i = 1; i <= 4; ++i) {
if (i == unit) {
strcat (pTxStr, ".e.e");
} else {
strcat (pTxStr, "e..e");
}
}
if (on_offCode >= 1) {
strcat(pTxStr,"e..ee..ee..ee..e+"); //the "turn on"-code
} else {
strcat(pTxStr,"e..ee..ee..e.e.e+"); //the "turn off"-code
}
}
return strlen(pTxStr);
}
unsigned int everflourish_find_code(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;
}
int createEverFlourishString(const char * pUnitStr, const char * pLevelStr,
char * pTxStr)
{
int len = 0;
int level;
int unit;
unsigned int check;
int i;
unit = atoi(pUnitStr);
level = atoi(pLevelStr); /* ON=15, OFF=0, LEARN=10 */
check = everflourish_find_code(unit);
#ifdef RFCMD_DEBUG
printf("unit: %d, level: %d\n", unit, level);
#endif
/* check converted parameters for validity */
if((unit < 0) || (unit > 0xffff) ||
(level < 0) || (level > 15)) {
} else {
const char ssss = 85;
const char sssl = 84; // 0
const char slss = 69; // 1
const char bits[2] = {sssl,slss};
int i;
char preamble[] = {'R', 5, 'T', 114,60,1,1,105,ssss,ssss};
memcpy(pTxStr, preamble, sizeof(preamble));
len += sizeof(preamble);
for(i=15;i>=0;i--) pTxStr[len++]=bits[(unit>>i)&0x01];
for(i=3;i>=0;i--) pTxStr[len++]=bits[(check>>i)&0x01];
for(i=3;i>=0;i--) pTxStr[len++]=bits[(level>>i)&0x01];
pTxStr[len++] = ssss;
pTxStr[len++] = '+';
}
pTxStr[len] = '\0';
return strlen(pTxStr);
}
void printUsage(void)
{
printf("Usage: rfcmd DEVICE PROTOCOL [PROTOCOL_ARGUMENTS] \n");
printf("\n");
#ifdef LIBFTDI
printf("\t DEVICE: /dev/ttyUSB[0..n] | LIBUSB\n" );
#else
printf("\t DEVICE: /dev/ttyUSB[0..n]\n" );
#endif
printf("\t PROTOCOLS: NEXA, SARTANO, WAVEMAN, IKEA, RISINGSUN, EVERFLOURISH\n" );
printf("\n");
printf("\t PROTOCOL ARGUMENTS - NEXA, WAVEMAN:\n");
printf("\t\tHOUSE_CODE: A..P\n\t\tCHANNEL: 1..16\n\t\tOFF_ON: 0..1\n" );
printf("\n");
printf("\t PROTOCOL ARGUMENTS - SARTANO:\n");
printf("\t\tCHANNEL: 0000000000..1111111111\n\t\tOFF_ON: 0..1\n" );
printf("\n");
printf("\t PROTOCOL ARGUMENTS - IKEA:\n");
printf("\t\tSYSTEM: 1..16\n\t\tDEVICE: 1..10\n");
printf("\t\tDIM_LEVEL: 0..10\n\t\tDIM_STYLE: 0..1\n" );
printf("\n");
printf("\t PROTOCOL ARGUMENTS - RISINGSUN:\n");
printf("\t\tCODE: 1..4\n\t\tDEVICE: 1..4\n");
printf("\t\tOFF_ON: 0..1\n" );
printf("\n");
printf("\t PROTOCOL ARGUMENTS - EVERFLOURISH:\n");
printf("\t\tDEVICE: 0..65535\n");
printf("\t\tLEVEL: 0=off, 10=learn, 15=on\n" );
printf("\n");
printf("Report bugs to <info.tech@telldus.se>\n");
}
void printVersion(void) {
printf("%s v%s\n", PROG_NAME, PROG_VERSION);
printf("\n");
printf("Copyright (C) Tord Andersson 2007\n");
printf("\n");
printf("Written by:\n");
printf("Tord Andersson, Micke Prag, Gudmund Berggren, Tapani Rintala\n");
printf("and Johan Ström\n");
}

View file

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS><TS version="1.1" language="sv_SE">
<context>
<name>Icon</name>
<message>
<location filename="icon.cpp" line="25"/>
<source>TellStick</source>
<translation>TellStick</translation>
</message>
<message>
<location filename="icon.cpp" line="117"/>
<source>&amp;On</source>
<translation>&amp;</translation>
</message>
<message>
<location filename="icon.cpp" line="122"/>
<source>O&amp;ff</source>
<translation>&amp;Av</translation>
</message>
<message>
<location filename="icon.cpp" line="100"/>
<source>&amp;Configure devices</source>
<translation>&amp;Konfigurera enheter</translation>
</message>
<message>
<location filename="icon.cpp" line="105"/>
<source>&amp;Quit</source>
<translation>&amp;Avsluta</translation>
</message>
<message>
<location filename="icon.cpp" line="67"/>
<source>Error</source>
<translation>Fel</translation>
</message>
<message>
<location filename="icon.cpp" line="67"/>
<source>An error occurred while trying to transmit</source>
<translation>Ett fel uppstod vid försök att sända</translation>
</message>
</context>
</TS>

View file

@ -1,216 +0,0 @@
//
// C++ Implementation: icon
//
// Description:
//
//
// Author: Micke Prag <micke.prag@telldus.se>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include "icon.h"
#include <QApplication>
#include <QMenu>
#include <QProcess>
#include <QFile>
#include "telldus-core.h"
Icon::Icon()
: QObject()
{
this->setMenu();
connect( &i, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(activated(QSystemTrayIcon::ActivationReason)));
i.setIcon( QIcon(":/images/lamp-on.png") );
i.setToolTip( tr("TellStick") );
i.show();
}
Icon::~Icon()
{
}
/**
* @fn Icon::activated( QSystemTrayIcon::ActivationReason reason )
*/
void Icon::activated( QSystemTrayIcon::ActivationReason reason )
{
if (reason == QSystemTrayIcon::DoubleClick) {
if (hasTelldusSetup()) {
configure();
}
}
}
/**
* @fn Icon::bell()
*/
void Icon::bell()
{
QAction *action = (QAction *) sender();
int retval = tdBell( action->data().toInt() );
if (retval != TELLSTICK_SUCCESS) {
char *errorString = tdGetErrorString( retval );
i.showMessage( tr("Error"), tr("An error occurred while trying to transmit!\n\n%1").arg(errorString), QSystemTrayIcon::Critical );
free( errorString );
}
}
/**
* @fn Icon::dim()
*/
void Icon::dim()
{
QAction *action = (QAction *) sender();
int intId = action->data().toString().section(":", 0, 0).toInt();
int intLevel = action->data().toString().section(":", 1, 1).toInt();
//i.showMessage( "", QString::number(intId));
int retval = tdDim( intId, intLevel );
if (retval != TELLSTICK_SUCCESS) {
char *errorString = tdGetErrorString( retval );
i.showMessage( tr("Error"), tr("An error occurred while trying to transmit!\n\n%1").arg(errorString), QSystemTrayIcon::Critical );
free( errorString );
}
}
/**
* @fn Icon::on()
*/
void Icon::on()
{
QAction *action = (QAction *) sender();
int retval = tdTurnOn( action->data().toInt() );
if (retval != TELLSTICK_SUCCESS) {
char *errorString = tdGetErrorString( retval );
i.showMessage( tr("Error"), tr("An error occurred while trying to transmit!\n\n%1").arg(errorString), QSystemTrayIcon::Critical );
free( errorString );
}
}
/**
* @fn Icon::off()
*/
void Icon::off()
{
QAction *action = (QAction *) sender();
int retval = tdTurnOff( action->data().toInt() );
if (retval != TELLSTICK_SUCCESS) {
char *errorString = tdGetErrorString( retval );
i.showMessage( tr("Error"), tr("An error occurred while trying to transmit!\n\n%1").arg(errorString), QSystemTrayIcon::Critical );
free( errorString );
}
}
/**
* @fn Icon::configure()
*/
void Icon::configure()
{
QProcess *process = new QProcess(this);
connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(setMenu()));
process->setWorkingDirectory(QCoreApplication::applicationDirPath());
process->start("TelldusSetup.exe");
}
/**
* @fn Icon::setMenu()
*/
void Icon::setMenu()
{
menu.clear();
int intNum = tdGetNumberOfDevices();
int index = 0;
while (index < intNum) {
addDevice(index, &menu);
index++;
}
menu.addSeparator();
if (hasTelldusSetup()) {
QAction *configure = menu.addAction(tr("&Configure devices"));
connect(configure, SIGNAL(triggered()), this, SLOT(configure()));
configure->setIcon( QIcon(":/images/preferences-system.png") );
}
QAction *quit = menu.addAction(tr("&Quit"));
quit->setIcon( QIcon(":/images/system-log-out.png") );
connect(quit, SIGNAL(triggered()), qApp, SLOT(quit()));
i.setContextMenu(&menu);
}
void Icon::addDevice( int index, QMenu *menu ) {
int intId = tdGetDeviceId(index);
QMenu *m = menu->addMenu( tdGetName(intId) );
int methods = tdMethods(intId);
if (methods & TELLSTICK_TURNON) {
QAction *on = m->addAction(tr("&On"));
on->setIcon(QIcon(":/images/lamp-on.png"));
on->setData( intId );
connect( on, SIGNAL(triggered()), this, SLOT(on()));
}
if (methods & TELLSTICK_DIM) {
QAction *dim = m->addAction(tr("90%"));
dim->setData( QString("%1:230").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("80%"));
dim->setData( QString("%1:204").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("70%"));
dim->setData( QString("%1:179").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("60%"));
dim->setData( QString("%1:153").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("50%"));
dim->setData( QString("%1:128").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("40%"));
dim->setData( QString("%1:102").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("30%"));
dim->setData( QString("%1:77").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("20%"));
dim->setData( QString("%1:51").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
dim = m->addAction(tr("10%"));
dim->setData( QString("%1:25").arg(intId) );
connect( dim, SIGNAL(triggered()), this, SLOT(dim()));
}
if (methods & TELLSTICK_TURNON) {
QAction *off = m->addAction(tr("O&ff"));
off->setData( intId );
off->setIcon(QIcon(":/images/lamp-off.png"));
connect( off, SIGNAL(triggered()), this, SLOT(off()));
}
if (methods & TELLSTICK_BELL) {
QAction *bell = m->addAction(tr("&Bell"));
bell->setData( intId );
bell->setIcon(QIcon(":/images/bell.png"));
connect( bell, SIGNAL(triggered()), this, SLOT(bell()));
}
}
bool Icon::hasTelldusSetup() {
return QFile::exists("TelldusSetup.exe");
}

View file

@ -1,51 +0,0 @@
//
// C++ Interface: icon
//
// Description:
//
//
// Author: Micke Prag <micke.prag@telldus.se>, (C) 2007
//
// Copyright: See COPYING file that comes with this distribution
//
//
#ifndef ICON_H
#define ICON_H
#include <QSystemTrayIcon>
#include <QMenu>
/**
@author Micke Prag <micke.prag@telldus.se>
*/
class Icon : public QObject
{
Q_OBJECT
public:
Icon();
~Icon();
protected:
QSystemTrayIcon i;
QMenu menu;
protected slots:
void activated( QSystemTrayIcon::ActivationReason reason );
void setMenu();
private slots:
void bell();
void dim();
void on();
void off();
public slots:
void configure();
private:
void addDevice( int index, QMenu *menu );
static bool hasTelldusSetup();
};
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,47 +0,0 @@
/***************************************************************************
* Copyright (C) 2006 by Micke Prag *
* micke.prag@telldus.se *
* *
* This program 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 program 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, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <QApplication>
#include <QTranslator>
#include <QLocale>
#include "icon.h"
int main(int argc, char *argv[])
{
QApplication a( argc, argv );
QCoreApplication::setOrganizationName("Telldus");
QCoreApplication::setOrganizationDomain("telldus.se");
QCoreApplication::setApplicationName("SysTray");
Q_INIT_RESOURCE(resource);
QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name());
a.installTranslator(&qtTranslator);
QTranslator myappTranslator;
myappTranslator.load("SysTray_" + QLocale::system().name());
a.installTranslator(&myappTranslator);
Icon icon;
return a.exec();
}

View file

@ -1,9 +0,0 @@
<RCC>
<qresource prefix="/" >
<file>images/bell.png</file>
<file>images/lamp-on.png</file>
<file>images/lamp-off.png</file>
<file>images/preferences-system.png</file>
<file>images/system-log-out.png</file>
</qresource>
</RCC>

View file

@ -1,23 +0,0 @@
TEMPLATE = app
SOURCES += main.cpp \
icon.cpp
!macx:LIBS += -ltelldus-core -L../bin
macx{
LIBS += -framework telldus-core
ICON = images/systray.icns
}
win32{
TARGET = ../../bin/SysTray
CONFIG += release
}
unix{
TARGET = ../bin/systray
CONFIG += debug
}
HEADERS += icon.h
#menu.h
RESOURCES += resource.qrc
RC_FILE = systray.rc
TRANSLATIONS = SysTray_sv.ts

View file

@ -1 +0,0 @@
IDI_ICON1 ICON DISCARDABLE "images/systray.ico"

View file

@ -1,4 +0,0 @@
TEMPLATE = subdirs
SUBDIRS = src
CONFIG += warn_on

View file

@ -8,6 +8,7 @@
*/
#include "CallbackDispatcher.h"
#include "common.h"
using namespace TelldusCore;
@ -28,7 +29,9 @@ bool TDDeviceEventDispatcher::done() const {
}
void TDDeviceEventDispatcher::run() {
char *str = wrapStdString(strData);
d->event(deviceId, method, strData.c_str(), d->id, d->context);
doneRunning = true;
}

View file

@ -114,6 +114,9 @@ bool Client::getBoolFromService(const Message &msg) {
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);
}
@ -181,7 +184,8 @@ void Client::run(){
}
}
std::wstring clientMessage = d->eventSocket.read(5000); //testing 5 second timeout
std::wstring clientMessage = d->eventSocket.read(1000); //testing 5 second timeout
while(clientMessage != L""){
//a message arrived
std::wstring type = Message::takeString(&clientMessage);
@ -282,16 +286,46 @@ void Client::cleanupCallbacks() {
}
std::wstring Client::sendToService(const Message &msg) {
Socket s;
s.connect(L"TelldusClient");
if (!s.isConnected()) { //Connection failed
TelldusCore::Message msg;
msg.addArgument(TELLSTICK_ERROR_CONNECTING_SERVICE);
return 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()) { //Connection failed
printf("Connection failed\n\r");
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)
printf("Connection failed after write\n\r");
msleep(500);
continue; //retry
}
readData = s.read(8000); //TODO changed to 10000 from 5000, how much does this do...?
if(readData == L""){
printf("Readdata nothing\n\r");
msleep(500);
continue; //TODO can we be really sure it SHOULD be anything?
//TODO perhaps break here instead?
}
if (!s.isConnected()) { //Connection failed sometime during operation...
printf("Connection failed in the end\n\r");
msleep(500);
continue; //retry
}
break;
}
s.write(msg.data());
return s.read(5000);
return readData;
}
void Client::stopThread(){

View file

@ -57,6 +57,9 @@ using namespace TelldusCore;
* @def TELLSTICK_SUCCESS
* Error code. Returned when the command succeeded.
*
* @def TELLSTICK_ERROR_BROKEN_PIPE
* Error code. Pipe broken during communication.
*
* @def TELLSTICK_ERROR_NOT_FOUND
* Error code. Returned if a TellStick was not found on the system.
*
@ -82,6 +85,13 @@ using namespace TelldusCore;
* 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_COMMUNICATING_SERVICE
* Error code. Timeout waiting for response from the Telldus Service.
*
* @def TELLSTICK_ERROR_UNKNOWN
* Error code. An unkown error has occurred.
*/
@ -479,10 +489,16 @@ int WINAPI tdMethods(int id, int methodsSupported){
* @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
*/
char * WINAPI tdGetErrorString(int intErrorNo) {
const int numResponses = 8;
const int numResponses = 10;
const char *responses[numResponses] = {
"Success",
"TellStick not found",
@ -491,7 +507,10 @@ char * WINAPI tdGetErrorString(int intErrorNo) {
"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"
"Received an unknown response",
"Syntax error",
"Broken pipe"
"An error occured while communicating with the Telldus Service"
};
std::string strReturn;
intErrorNo = abs(intErrorNo); //We don't use negative values here.
@ -511,8 +530,12 @@ char * WINAPI tdGetErrorString(int intErrorNo) {
* @returns TELLSTICK_SUCCESS on success or one of the errorcodes on failure
*/
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]);
}
Message msg(L"tdSendRawCommand");
msg.addArgument(command);
msg.addArgument(wcommand);
msg.addArgument(reserved);
return Client::getIntegerFromService(msg);
}
@ -534,11 +557,36 @@ void WINAPI tdDisconnectTellStickController(int vid, int pid, const char *serial
Client::getWStringFromService(msg);
}
/**
* Use this function to iterate over all sensors. Iterate until
* TELLSTICK_SUCCESS is not returned
* @param protocol A byref string where the protocol of the sensor will be placed
* @param protocolLen The length of the \c protocol parameter
* @param model A byref string where the model of the sensor will be placed
* @param modelLen The length of the \c model parameter
* @param id A byref int where the id of the sensor will be placed
* @param dataTypes A byref int with flags for the supported sensor values
* @returns TELLSTICK_SUCCESS if there is more sensors to be fetched
*/
int WINAPI tdSensor(char *protocol, int protocolLen, char *model, int modelLen, int *id, int *dataTypes) {
Client *client = 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 \c protocol,
* \c model, and \c id together identifies a sensor.
* @param protocol The protocol for the sensor
* @param model The model for the sensor
* @param id The id of the sensor
* @param dataType One of the datatype to retrieve
* @param value A byref string where the value will be places
* @param len The length of the \c value parameter
* @param timestamp A byref int where the timestamp of the value will be placed
* @returns TELLSTICK_SUCCESS if the value could be fetched or one of the
* errorcodes on failure
*/
int WINAPI tdSensorValue(const char *protocol, const char *model, int id, int dataType, char *value, int len, int *timestamp) {
Message msg(L"tdSensorValue");
msg.addArgument(protocol);
@ -562,4 +610,4 @@ int WINAPI tdSensorValue(const char *protocol, const char *model, int id, int da
}
/*\@}*/
/* @} */

View file

@ -115,6 +115,9 @@ extern "C" {
#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_UNKNOWN -99
//Device typedef

View file

@ -23,21 +23,70 @@ Message::~Message(void) {
}
void Message::addArgument(const std::wstring &value) {
std::wstringstream st;
st << (int)value.size();
this->append(st.str());
//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;
//std::wstringstream st;
//st << (int)value;
this->append(L"i");
this->append(st.str());
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));
}

View file

@ -11,6 +11,9 @@ namespace TelldusCore {
~Message(void);
void addArgument(const std::wstring &);
//void addSpecialArgument(const std::wstring &);
//void addSpecialArgument(int);
//void addSpecialArgument(const char *);
void addArgument(int);
void addArgument(const char *);

View file

@ -78,9 +78,9 @@ std::wstring Socket::read() {
std::wstring Socket::read(int timeout) {
struct timeval tv;
char inbuf[BUFSIZE];
memset(inbuf, '\0', sizeof(inbuf));
FD_SET(d->socket, &d->infds);
std::string msg;
while(isConnected()) {
tv.tv_sec = floor(timeout / 1000.0);
tv.tv_usec = timeout % 1000;
@ -93,21 +93,28 @@ std::wstring Socket::read(int timeout) {
continue;
}
int received = recv(d->socket, inbuf, BUFSIZE - 1, 0);
if (received <= 0) {
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;
}
std::string msg(inbuf);
return TelldusCore::charToWstring(msg.c_str());
}
void Socket::stopReadWait(){
TelldusCore::MutexLocker locker(&d->mutex);
d->connected = false;
//TODO somehow signal the socket here?
}
void Socket::write(const std::wstring &msg) {

View file

@ -37,6 +37,7 @@ Socket::~Socket(void){
SetEvent(d->readEvent); //signal for break
if (d->hPipe != INVALID_HANDLE_VALUE) {
CloseHandle(d->hPipe);
d->hPipe = 0;
}
delete d;
}
@ -89,39 +90,52 @@ std::wstring Socket::read(int timeout){
memset(&oOverlap, 0, sizeof(OVERLAPPED));
d->readEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
d->readEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
oOverlap.hEvent = d->readEvent;
BOOL fSuccess = false;
std::wstring returnString;
bool moreData = true;
memset(&buf, 0, BUFSIZE);
while(moreData){
moreData = false;
memset(&buf, 0, sizeof(buf));
ReadFile( d->hPipe, &buf, sizeof(wchar_t)*BUFSIZE, &cbBytesRead, &oOverlap);
result = WaitForSingleObject(oOverlap.hEvent, timeout);
if(!d->running){
CancelIo(d->hPipe);
CloseHandle(d->readEvent);
return L"";
}
if (result == WAIT_TIMEOUT) {
CancelIo(d->hPipe);
CloseHandle(d->readEvent);
return L"";
}
fSuccess = GetOverlappedResult(d->hPipe, &oOverlap, &cbBytesRead, false);
if (!fSuccess) {
DWORD err = GetLastError();
if (err == ERROR_BROKEN_PIPE) {
d->connected = false;
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"";
}
buf[0] = 0;
}
CancelIo(d->hPipe);
CloseHandle(d->readEvent);
return buf;
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;
}
}
returnString.append(buf);
}
d->readEvent = 0;
CloseHandle(oOverlap.hEvent);
return returnString;
}
void Socket::write(const std::wstring &msg){
@ -129,26 +143,33 @@ void Socket::write(const std::wstring &msg){
OVERLAPPED oOverlap;
DWORD bytesWritten = 0;
int result;
BOOL fSuccess;
BOOL fSuccess = false;
memset(&oOverlap, 0, sizeof(OVERLAPPED));
HANDLE writeEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
HANDLE writeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
oOverlap.hEvent = writeEvent;
WriteFile(d->hPipe, msg.data(), (DWORD)msg.length()*sizeof(wchar_t), &bytesWritten, &oOverlap);
result = WaitForSingleObject(writeEvent, 500);
if (result == WAIT_TIMEOUT) {
CancelIo(d->hPipe);
CloseHandle(writeEvent);
d->connected = false;
return;
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, 500);
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);
}
fSuccess = GetOverlappedResult(d->hPipe, &oOverlap, &bytesWritten, TRUE);
CloseHandle(writeEvent);
if (!fSuccess) {
CancelIo(d->hPipe);
CloseHandle(d->hPipe);
d->hPipe = 0;
d->connected = false;
return;
}

View file

@ -4,6 +4,7 @@
#include <sstream>
#include <string>
#include <string.h>
#include <stdio.h>
#ifdef _WINDOWS
#include <windows.h>
@ -91,17 +92,58 @@ bool TelldusCore::comparei(std::wstring stringA, std::wstring stringB) {
}
std::wstring TelldusCore::intToWstring(int value) {
#ifdef _WINDOWS
//no stream used
//TODO! 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
}
*/
int TelldusCore::wideToInteger(const std::wstring &input){
std::wstringstream inputstream;
inputstream << input;
@ -158,3 +200,48 @@ std::string TelldusCore::wideToString(const std::wstring &input) {
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 = (char*)malloc(size)) == NULL) {
return "";
}
while (1) {
/* Try to print in the allocated space. */
n = vsnprintf(p, size, format, ap);
/* 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 = (char *)realloc (p, size)) == NULL) {
free(p);
return "";
} else {
p = np;
}
}
}

View file

@ -2,6 +2,7 @@
#define STRING_H
#include <string>
#include <stdarg.h>
namespace TelldusCore {
std::wstring charToWstring(const char *value);
@ -10,10 +11,14 @@ namespace TelldusCore {
bool comparei(std::wstring stringA, std::wstring stringB);
std::wstring intToWstring(int value);
//std::wstring intToWStringSafe(int value);
std::string intToString(int value);
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 //STRING_H

View file

@ -19,10 +19,12 @@ SET( telldus-service_SRCS
Device.cpp
DeviceManager.cpp
Event.cpp
Log.cpp
Sensor.cpp
Settings.cpp
TelldusMain.cpp
TellStick.cpp
Timer.cpp
EventUpdateManager.cpp
)
SET( telldus-service_protocol_SRCS
@ -34,6 +36,8 @@ SET( telldus-service_protocol_SRCS
ProtocolComen.cpp
ProtocolEverflourish.h
ProtocolEverflourish.cpp
ProtocolFineoffset.h
ProtocolFineoffset.cpp
ProtocolFuhaote.h
ProtocolFuhaote.cpp
ProtocolGroup.h
@ -42,8 +46,12 @@ SET( telldus-service_protocol_SRCS
ProtocolHasta.cpp
ProtocolIkea.h
ProtocolIkea.cpp
ProtocolMandolyn.h
ProtocolMandolyn.cpp
ProtocolNexa.h
ProtocolNexa.cpp
ProtocolOregon.h
ProtocolOregon.cpp
ProtocolRisingSun.h
ProtocolRisingSun.cpp
ProtocolSartano.h
@ -72,11 +80,13 @@ SET( telldus-service_HDRS
DeviceManager.h
Event.h
EventHandler.h
EventUpdateManager.h
Log.h
Sensor.h
Settings.h
TelldusMain.h
TellStick.h
EventUpdateManager.h
Timer.h
)
FIND_PACKAGE(Threads REQUIRED)
LIST(APPEND telldus-service_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
@ -129,15 +139,27 @@ ELSEIF (WIN32) #### Windows ####
main_win.cpp
SettingsWinRegistry.cpp
TelldusWinService_win.cpp
Messages.mc
${CMAKE_CURRENT_BINARY_DIR}/Messages.rc
${CMAKE_CURRENT_BINARY_DIR}/Messages.h
)
LIST(APPEND telldus-service_HDRS
TelldusWinService_win.h
)
ADD_CUSTOM_COMMAND(
OUTPUT Messages.rc Messages.h
COMMAND mc.exe -u -r ${CMAKE_CURRENT_BINARY_DIR} -h ${CMAKE_CURRENT_BINARY_DIR} ${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

View file

@ -76,6 +76,7 @@ void ClientCommunicationHandler::parseMessage(const std::wstring &clientMessage,
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);
@ -206,11 +207,6 @@ void ClientCommunicationHandler::parseMessage(const std::wstring &clientMessage,
std::wstring command = TelldusCore::Message::takeString(&msg);
int reserved = TelldusCore::Message::takeInt(&msg);
(*intReturn) = d->deviceManager->sendRawCommand(command, reserved);
EventUpdateData *eventData = new EventUpdateData();
eventData->messageType = L"TDRawDeviceEvent";
eventData->controllerId = -1;
eventData->eventValue = command;
d->deviceUpdateEvent->signal(eventData);
} else if (function == L"tdConnectTellStickController") {
int vid = TelldusCore::Message::takeInt(&msg);

View file

@ -91,46 +91,57 @@ void ConnectionListener::run() {
d->hEvent = CreateEvent(NULL, true, false, NULL);
oOverlap.hEvent = d->hEvent;
bool recreate = true;
while (1) {
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
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) {
//TelldusCore::logMessage("Could not create named pipe");
return;
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);
ConnectNamedPipe(hPipe, &oOverlap);
DWORD result = WaitForSingleObject(oOverlap.hEvent, 1000);
if (!d->running) {
break;
}
if(result == WAIT_TIMEOUT){
CloseHandle(hPipe);
continue;
}
BOOL connected = GetOverlappedResult(hPipe, &oOverlap, &cbBytesRead, false);
if (!connected) {
CloseHandle(hPipe);
return;
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);

View file

@ -16,6 +16,7 @@ public:
virtual int firmwareVersion() = 0;
virtual int send( const std::string &message ) = 0;
virtual int reset() = 0;
protected:
Controller(int id, Event *event);

View file

@ -2,6 +2,8 @@
#include "Controller.h"
#include "Mutex.h"
#include "TellStick.h"
#include "Log.h"
#include "../client/telldus-core.h"
#include <map>
#include <stdio.h>
@ -119,3 +121,47 @@ void ControllerManager::loadControllers() {
d->controllers[d->lastControllerId] = controller;
}
}
void ControllerManager::queryControllerStatus(){
std::list<TellStick *> tellStickControllers;
{
TelldusCore::MutexLocker locker(&d->mutex);
for(ControllerMap::iterator it = d->controllers.begin(); it != d->controllers.end(); ++it) {
TellStick *tellstick = reinterpret_cast<TellStick*>(it->second);
if (tellstick) {
tellStickControllers.push_back(tellstick);
}
}
}
bool reloadControllers = false;
std::string noop = "N+";
for(std::list<TellStick *>::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<TellStick*>(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;
}

View file

@ -14,9 +14,9 @@ public:
void deviceInsertedOrRemoved(int vid, int pid, const std::string &serial, bool inserted);
Controller *getBestControllerById(int id);
protected:
void loadControllers();
void queryControllerStatus();
int resetController(Controller *controller);
private:
class PrivateData;

View file

@ -5,7 +5,7 @@
#include "Settings.h"
#include "Strings.h"
#include "Message.h"
#include "common.h"
#include "Log.h"
#include <map>
#include <memory>
@ -432,9 +432,31 @@ int DeviceManager::doAction(int deviceId, int action, unsigned char data){
}
else{
Controller *controller = d->controllerManager->getBestControllerById(device->getPreferredControllerId());
if(!controller){
Log::warning("Trying to execute action, but no controller found. Rescanning USB ports");
//no controller found, scan for one, and retry once
d->controllerManager->loadControllers();
controller = d->controllerManager->getBestControllerById(device->getPreferredControllerId());
}
if(controller){
retval = device->doAction(action, 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 TELLSTICK_ERROR_NOT_FOUND;
}
retval = device->doAction(action, data, controller); //retry one more time
}
} else {
Log::error("No contoller (TellStick) found after one retry. Giving up.");
return TELLSTICK_ERROR_NOT_FOUND;
}
}
@ -479,7 +501,7 @@ int DeviceManager::doGroupAction(const std::wstring devices, const int action, c
deviceReturnValue = doAction(deviceId, action, data);
}
else if(childType == TELLSTICK_TYPE_SCENE){
deviceReturnValue = doGroupAction(DeviceManager::getDeviceParameter(deviceId, L"devices", L""), action, data, childType, deviceId, duplicateDeviceIds); //TODO make scenes (and test) infinite loops-safe
deviceReturnValue = doGroupAction(DeviceManager::getDeviceParameter(deviceId, L"devices", L""), action, data, childType, deviceId, duplicateDeviceIds); //TODO make scenes infinite loops-safe
}
else{
//group (in group)
@ -719,10 +741,29 @@ void DeviceManager::setSensorValueAndSignal( const std::string &dataType, int da
int DeviceManager::sendRawCommand(const std::wstring &command, int reserved){
Controller *controller = d->controllerManager->getBestControllerById(-1);
if(controller){
return controller->send(TelldusCore::wideToString(command));
if(!controller){
//no controller found, scan for one, and retry once
d->controllerManager->loadControllers();
controller = d->controllerManager->getBestControllerById(-1);
}
else{
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;
}
}

View file

@ -51,15 +51,17 @@ void EventHandler::signal(Event *) {
}
bool EventHandler::waitForAny() {
int result = WaitForMultipleObjects(d->eventCount, d->eventArray, FALSE, INFINITE);
TelldusCore::MutexLocker locker(&d->mutex);
if (result == WAIT_TIMEOUT) {
return false;
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;
}
int eventIndex = result - WAIT_OBJECT_0;
if (eventIndex >= d->eventCount) {
return false;
}
return true;
}

View file

@ -73,10 +73,10 @@ void EventUpdateManager::run(){
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"){

View file

@ -0,0 +1,180 @@
#include "Log.h"
#include <stdarg.h>
#if defined(_LINUX)
#include <syslog.h>
#elif defined(_WINDOWS)
#include <windows.h>
#include "Strings.h"
#include "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;
}
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, (LPCWSTR*)pInsertStrings, NULL);
break;
case Notice:
ReportEvent(d->eventSource, EVENTLOG_INFORMATION_TYPE, NULL, LOG_NOTICE, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL);
break;
case Warning:
ReportEvent(d->eventSource, EVENTLOG_WARNING_TYPE, NULL, LOG_WARNING, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL);
break;
case Error:
ReportEvent(d->eventSource, EVENTLOG_ERROR_TYPE, NULL, LOG_ERR, NULL, 1, 0, (LPCWSTR*)pInsertStrings, NULL);
break;
}
#endif
}
}
Log *Log::instance() {
if (PrivateData::instance == 0) {
PrivateData::instance = new Log();
}
return PrivateData::instance;
}

View file

@ -0,0 +1,33 @@
#ifndef LOG_H
#define LOG_H
#include <stdio.h>
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 //LOG_H

Binary file not shown.

View file

@ -5,11 +5,14 @@
#include "ProtocolBrateck.h"
#include "ProtocolComen.h"
#include "ProtocolEverflourish.h"
#include "ProtocolFineoffset.h"
#include "ProtocolFuhaote.h"
#include "ProtocolGroup.h"
#include "ProtocolHasta.h"
#include "ProtocolIkea.h"
#include "ProtocolMandolyn.h"
#include "ProtocolNexa.h"
#include "ProtocolOregon.h"
#include "ProtocolRisingSun.h"
#include "ProtocolSartano.h"
#include "ProtocolScene.h"
@ -195,10 +198,10 @@ std::list<std::string> Protocol::getParametersForProtocol(const std::wstring &pr
} 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");
}
@ -209,7 +212,7 @@ std::list<std::string> Protocol::getParametersForProtocol(const std::wstring &pr
std::list<std::string> Protocol::decodeData(const std::string &fullData) {
std::list<std::string> retval;
std::string decoded = "";
ControllerMessage dataMsg(fullData);
if( TelldusCore::comparei(dataMsg.protocol(), L"arctech") ) {
decoded = ProtocolNexa::decodeData(dataMsg);
@ -231,12 +234,30 @@ std::list<std::string> Protocol::decodeData(const std::string &fullData) {
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);
}
}
return retval;
}

View file

@ -0,0 +1,50 @@
#include "ProtocolFineoffset.h"
#include <stdlib.h>
#include <sstream>
#include <iomanip>
#ifdef _MSC_VER
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
#else
#include <stdint.h>
#endif
std::string ProtocolFineoffset::decodeData(ControllerMessage &dataMsg)
{
std::string data = dataMsg.getParameter("data");
if (data.length() < 8) {
return "";
}
uint8_t checksum = strtol(data.substr(data.length()-2).c_str(), NULL, 16);
data = data.substr(0, data.length()-2);
uint8_t humidity = strtol(data.substr(data.length()-2).c_str(), NULL, 16);
data = data.substr(0, data.length()-2);
uint16_t value = strtol(data.substr(data.length()-3).c_str(), NULL, 16);
double temperature = (value & 0x7FF)/10.0;
value >>= 11;
if (value & 1) {
temperature = -temperature;
}
data = data.substr(0, data.length()-3);
uint16_t id = strtol(data.c_str(), NULL, 16) & 0xFF;
std::stringstream retString;
retString << "class:sensor;protocol:fineoffset;id:" << id << ";model:";
if (humidity <= 100) {
retString << "temperaturehumidity;humidity:" << (int)humidity << ";";
} else if (humidity == 0xFF) {
retString << "temperature;";
} else {
return "";
}
retString << "temp:" << std::fixed << std::setprecision(1) << temperature << ";";
return retString.str();
}

View file

@ -0,0 +1,13 @@
#ifndef PROTOCOLFINEOFFSET_H
#define PROTOCOLFINEOFFSET_H
#include "ControllerMessage.h"
#include "Protocol.h"
class ProtocolFineoffset : public Protocol
{
public:
static std::string decodeData(ControllerMessage &dataMsg);
};
#endif //PROTOCOLFINEOFFSET_H

View file

@ -0,0 +1,44 @@
#include "ProtocolMandolyn.h"
#include <stdlib.h>
#include <sstream>
#include <iomanip>
#ifdef _MSC_VER
typedef unsigned __int8 uint8_t;
typedef unsigned __int32 uint32_t;
#else
#include <stdint.h>
#endif
std::string ProtocolMandolyn::decodeData(ControllerMessage &dataMsg)
{
std::string data = dataMsg.getParameter("data");
uint32_t value = strtol(data.c_str(), NULL, 16);
bool parity = value & 0x1;
value >>= 1;
double temp = (value & 0x7FFF) - 6400;
temp = temp/128.0;
value >>= 15;
uint8_t humidity = (value & 0x7F);
value >>= 7;
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:" << (int)humidity << ";";
return retString.str();
}

View file

@ -0,0 +1,13 @@
#ifndef PROTOCOLMANDOLYN_H
#define PROTOCOLMANDOLYN_H
#include "ControllerMessage.h"
#include "Protocol.h"
class ProtocolMandolyn : public Protocol
{
public:
static std::string decodeData(ControllerMessage &dataMsg);
};
#endif //PROTOCOLMANDOLYN_H

View file

@ -3,7 +3,6 @@
#include <stdio.h>
#include "TellStick.h"
#include "Strings.h"
#include "common.h"
int ProtocolNexa::lastArctecCodeSwitchWasTurnOff=0; //TODO, always removing first turnon now, make more flexible (waveman too)

View file

@ -0,0 +1,120 @@
#include "ProtocolOregon.h"
#include <stdlib.h>
#include <sstream>
#include <iomanip>
#ifdef _MSC_VER
typedef unsigned __int8 uint8_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif
std::string ProtocolOregon::decodeData(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);
}
return "";
}
std::string ProtocolOregon::decodeEA4C(const std::string &data) {
uint64_t value = strtol(data.c_str(), NULL, 16);
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);
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);
uint8_t channel = (value >> 4) & 0x7;
if (checksum != checksumw) {
return "";
}
double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0;
if (neg) {
temperature = -temperature;
}
std::stringstream retString;
retString << "class:sensor;protocol:oregon;model:EA4C;id:" << (int)address
<< ";temp:" << std::fixed << std::setprecision(1) << temperature << ";";
return retString.str();
}
std::string ProtocolOregon::decode1A2D(const std::string &data) {
uint64_t value = strtol(data.c_str(), NULL, 16);
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);
uint8_t channel = (value >> 4) & 0x7;
checksum += 0x1 + 0xA + 0x2 + 0xD - 0xA;
//TODO: Find out how checksum2 works
if (checksum != checksum1) {
return "";
}
double temperature = ((temp1 * 100) + (temp2 * 10) + temp3)/10.0;
if (neg) {
temperature = -temperature;
}
std::stringstream retString;
retString << "class:sensor;protocol:oregon;model:1A2D;id:" << (int)address
<< ";temp:" << std::fixed << std::setprecision(1) << temperature << ";";
return retString.str();
}

View file

@ -0,0 +1,17 @@
#ifndef PROTOCOLOREGON_H
#define PROTOCOLOREGON_H
#include "ControllerMessage.h"
#include "Protocol.h"
class ProtocolOregon : public Protocol
{
public:
static std::string decodeData(ControllerMessage &dataMsg);
protected:
static std::string decodeEA4C(const std::string &data);
static std::string decode1A2D(const std::string &data);
};
#endif //PROTOCOLOREGON_H

View file

@ -375,6 +375,7 @@ bool readConfig(cfg_t **cfg) {
CFG_STR(const_cast<char *>("user"), const_cast<char *>("nobody"), CFGF_NONE),
CFG_STR(const_cast<char *>("group"), const_cast<char *>("plugdev"), CFGF_NONE),
CFG_STR(const_cast<char *>("deviceNode"), const_cast<char *>("/dev/tellstick"), CFGF_NONE),
CFG_STR(const_cast<char *>("ignoreControllerConfirmation"), const_cast<char *>("false"), CFGF_NONE),
CFG_SEC(const_cast<char *>("device"), device_opts, CFGF_MULTI),
CFG_END()
};

View file

@ -1,10 +1,12 @@
#include "Settings.h"
#include "Strings.h"
#include <Windows.h>
#include <sstream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include "common.h"
#include "../client/telldus-core.h"
@ -93,9 +95,8 @@ int Settings::addDevice() {
DWORD dwDisp;
intDeviceId = getNextDeviceId();
std::wostringstream ssRegPath;
ssRegPath << d->strRegPathDevice << intDeviceId;
std::wstring strCompleteRegPath = ssRegPath.str();
std::wstring strCompleteRegPath = d->strRegPathDevice;
strCompleteRegPath.append(TelldusCore::intToWstring(intDeviceId));
if (RegCreateKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hk, &dwDisp)) {
//fail
@ -122,7 +123,7 @@ int Settings::getNextDeviceId() const {
DWORD dwLength;
DWORD nResult(0);
long lngStatus = RegQueryValueEx(hk, L"LastUsedId", NULL, NULL, reinterpret_cast<LPBYTE>(&nResult), &dwLength); //(LPBYTE)Buff, &dwLength);
long lngStatus = RegQueryValueEx(hk, L"LastUsedId", NULL, NULL, reinterpret_cast<LPBYTE>(&nResult), &dwLength);
if(lngStatus == ERROR_SUCCESS){
intReturn = nResult + 1;
@ -143,9 +144,8 @@ int Settings::getNextDeviceId() const {
int Settings::removeDevice(int intDeviceId) {
TelldusCore::MutexLocker locker(&mutex);
std::wostringstream ssRegPath;
ssRegPath << d->strRegPathDevice << intDeviceId;
std::wstring strCompleteRegPath = ssRegPath.str();
std::wstring strCompleteRegPath = d->strRegPathDevice;
strCompleteRegPath.append(TelldusCore::intToWstring(intDeviceId));
long lngSuccess = RegDeleteKey(d->rootKey, strCompleteRegPath.c_str());
@ -157,13 +157,39 @@ int Settings::removeDevice(int intDeviceId) {
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(int intDeviceId, const std::wstring &name, bool parameter) const {
std::wstring strReturn;
HKEY hk;
std::wostringstream ssRegPath;
ssRegPath << d->strRegPathDevice << intDeviceId;
std::wstring strCompleteRegPath = ssRegPath.str();
std::wstring strCompleteRegPath = d->strRegPathDevice;
strCompleteRegPath.append(TelldusCore::intToWstring(intDeviceId));
long lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_QUERY_VALUE, &hk);
if(lnExists == ERROR_SUCCESS){
@ -191,9 +217,9 @@ int Settings::setStringSetting(int intDeviceId, const std::wstring &name, const
HKEY hk;
int ret = TELLSTICK_SUCCESS;
std::wostringstream ssRegPath;
ssRegPath << d->strRegPathDevice << intDeviceId;
std::wstring strCompleteRegPath = ssRegPath.str();
std::wstring bla = TelldusCore::intToWstring(intDeviceId);
std::wstring strCompleteRegPath = d->strRegPathDevice;
strCompleteRegPath.append(bla);
long lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_WRITE, &hk);
if (lnExists == ERROR_SUCCESS){
@ -223,9 +249,8 @@ int Settings::setIntSetting(int intDeviceId, const std::wstring &name, int value
int intReturn = TELLSTICK_ERROR_UNKNOWN;
HKEY hk;
std::wostringstream ssRegPath;
ssRegPath << d->strRegPathDevice << intDeviceId;
std::wstring strCompleteRegPath = ssRegPath.str();
std::wstring strCompleteRegPath = d->strRegPathDevice;
strCompleteRegPath.append(TelldusCore::intToWstring(intDeviceId));
long lnExists = RegOpenKeyEx(d->rootKey, strCompleteRegPath.c_str(), 0, KEY_WRITE, &hk);
if (lnExists == ERROR_SUCCESS) {
DWORD dwVal = value;

View file

@ -29,8 +29,12 @@ public:
virtual int firmwareVersion();
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;

View file

@ -10,8 +10,11 @@
//
//
#include "TellStick.h"
#include "common.h"
#include "Mutex.h"
#include "Settings.h"
#include "Strings.h"
#include "Log.h"
#include "../client/telldus-core.h"
#include <string.h>
#include <stdlib.h>
@ -20,7 +23,7 @@
class TellStick::PrivateData {
public:
bool open, running;
bool open, running, ignoreControllerConfirmation;
int vid, pid, fwVersion;
std::string serial, message;
FT_HANDLE ftHandle;
@ -53,6 +56,8 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
d->pid = td.pid;
d->fwVersion = 0;
d->serial = td.serial;
Settings set;
d->ignoreControllerConfirmation = set.getSetting(L"ignoreControllerConfirmation")==L"true";
char *tempSerial = new char[td.serial.size()+1];
#ifdef _WINDOWS
@ -61,6 +66,7 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
strcpy(tempSerial, 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) {
@ -76,10 +82,13 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
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;
@ -108,6 +117,14 @@ 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;
}
@ -144,6 +161,18 @@ void TellStick::processData( const std::string &data ) {
}
}
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;
@ -186,8 +215,7 @@ int TellStick::send( const std::string &strMessage ) {
if (!d->open) {
return TELLSTICK_ERROR_NOT_FOUND;
}
bool c = true;
//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
@ -205,26 +233,39 @@ int TellStick::send( const std::string &strMessage ) {
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(c) {
while(1) {
ftStatus = FT_Read(d->ftHandle,&in,1,&bytesRead);
if (ftStatus == FT_OK) {
if (bytesRead == 1) {
if (in == '\n') {
break;
return TELLSTICK_SUCCESS;
} else {
continue;
}
} else { //Timeout
c = false;
return TELLSTICK_ERROR_COMMUNICATION;
}
} else { //Error
c = false;
Log::debug("Broken pipe on read");
return TELLSTICK_ERROR_BROKEN_PIPE;
}
}
if (!c) {
return TELLSTICK_ERROR_COMMUNICATION;
}
return TELLSTICK_SUCCESS;
}
bool TellStick::stillConnected() const {

View file

@ -18,6 +18,8 @@
#include <ftdi.h>
#include "Thread.h"
#include "Mutex.h"
#include "Log.h"
#include "Settings.h"
#include "Strings.h"
#include "common.h"
@ -31,7 +33,7 @@ typedef int DWORD;
class TellStick::PrivateData {
public:
bool open;
bool open, ignoreControllerConfirmation;
int vid, pid, fwVersion;
std::string serial, message;
ftdi_context ftHandle;
@ -51,9 +53,13 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
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);
@ -62,6 +68,7 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
d->open = true;
ftdi_usb_reset( &d->ftHandle );
ftdi_disable_bitbang( &d->ftHandle );
ftdi_set_latency_timer(&d->ftHandle, 16);
if (d->open) {
@ -71,10 +78,13 @@ TellStick::TellStick(int controllerId, Event *event, const TellStickDescriptor &
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();
}
@ -94,6 +104,14 @@ 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;
}
@ -130,6 +148,14 @@ void TellStick::processData( const std::string &data ) {
}
}
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;
@ -186,33 +212,44 @@ int TellStick::send( const std::string &strMessage ) {
if(ret < 0) {
c = false;
} else if(ret != strMessage.length()) {
fprintf(stderr, "weird send length? retval %i instead of %d\n",
ret, (int)strMessage.length());
Log::debug("Weird send length? retval %i instead of %d\n", ret, (int)strMessage.length());
}
delete[] tempMessage;
int retrycnt = 200;
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(c && --retrycnt) {
while(--retrycnt) {
ret = ftdi_read_data( &d->ftHandle, &in, 1);
if (ret > 0) {
if (in == '\n') {
break;
return TELLSTICK_SUCCESS;
}
} else if(ret == 0) { // No data available
usleep(100);
} else { //Error
c = false;
Log::debug("Broken pipe on read");
return TELLSTICK_ERROR_BROKEN_PIPE;
}
}
if (!retrycnt) {
c = false;
}
if (!c) {
return TELLSTICK_ERROR_COMMUNICATION;
}
return TELLSTICK_SUCCESS;
return TELLSTICK_ERROR_COMMUNICATION;
}
void TellStick::setBaud(int baud) {

View file

@ -6,6 +6,8 @@
#include "ControllerManager.h"
#include "ControllerListener.h"
#include "EventUpdateManager.h"
#include "Timer.h"
#include "Log.h"
#include <stdio.h>
#include <list>
@ -39,6 +41,10 @@ void TelldusMain::deviceInsertedOrRemoved(int vid, int pid, bool inserted) {
void TelldusMain::start(void) {
EventRef clientEvent = d->eventHandler.addEvent();
EventRef dataEvent = d->eventHandler.addEvent();
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();
ControllerManager controllerManager(dataEvent.get());
EventUpdateManager eventUpdateManager;
@ -101,7 +107,16 @@ void TelldusMain::start(void) {
}
}
}
if (janitor->isSignaled()) {
//Clear all of them if there is more than one
while(janitor->isSignaled()) {
janitor->popSignal();
}
controllerManager.queryControllerStatus();
}
}
supervisor.stop();
}
void TelldusMain::stop(void){

View file

@ -1,5 +1,6 @@
#include "TelldusWinService_win.h"
#include "TelldusMain.h"
#include "Log.h"
#include <Dbt.h>
#include <string>
@ -111,6 +112,13 @@ DWORD WINAPI TelldusWinService::serviceControlHandler( DWORD controlCode, DWORD
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;
@ -140,8 +148,13 @@ void WINAPI TelldusWinService::serviceMain( DWORD argc, TCHAR* argv[] ) {
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;

View file

@ -0,0 +1,114 @@
#include "Timer.h"
#include "Mutex.h"
#ifdef _WINDOWS
#else
#include <sys/time.h>
#include <errno.h>
#endif
class Timer::PrivateData {
public:
PrivateData() : interval(0), running(false) {}
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(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 = 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
}

View file

@ -0,0 +1,24 @@
#ifndef TIMER_H
#define TIMER_H
#include "Event.h"
#include "Thread.h"
class Timer : public TelldusCore::Thread {
public:
Timer(EventRef event);
virtual ~Timer();
void setInterval(int sec);
void stop();
protected:
void run();
private:
class PrivateData;
PrivateData *d;
};
#endif //TIMER_H

View file

@ -1,16 +1,16 @@
#include "TelldusMain.h"
#include "Log.h"
#include <signal.h>
#include <stdio.h>
TelldusMain tm;
void shutdownHandler(int onSignal) {
printf("Shutting down\n");
Log::notice("Shutting down");
tm.stop();
}
void sigpipeHandler(int onSignal) {
printf("SIGPIPE received\n");
Log::notice("SIGPIPE received");
}
int main(int argc, char **argv) {
@ -19,7 +19,10 @@ int main(int argc, char **argv) {
signal(SIGINT, shutdownHandler);
signal(SIGPIPE, sigpipeHandler);
Log::notice("telldusd started");
tm.start();
printf("telldusd stopped gracefully\n");
Log::notice("telldusd stopped gracefully");
Log::destroy();
return 0;
}

View file

@ -4,7 +4,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
@ -12,6 +11,7 @@
#include "Settings.h"
#include "Strings.h"
#include "Log.h"
#define DAEMON_NAME "telldusd"
#define PID_FILE "/var/run/" DAEMON_NAME ".pid"
@ -21,19 +21,19 @@ TelldusMain tm;
void signalHandler(int sig) {
switch(sig) {
case SIGHUP:
syslog(LOG_WARNING, "Received SIGHUP signal.");
Log::warning("Received SIGHUP signal.");
break;
case SIGTERM:
case SIGINT:
syslog(LOG_WARNING, "Received SIGTERM or SIGINT signal.");
syslog(LOG_WARNING, "Shutting down");
Log::warning("Received SIGTERM or SIGINT signal.");
Log::warning("Shutting down");
tm.stop();
break;
case SIGPIPE:
syslog(LOG_WARNING, "Received SIGPIPE signal.");
Log::warning("Received SIGPIPE signal.");
break;
default:
syslog(LOG_WARNING, "Unhandled signal (%d) %s", sig, strsignal(sig));
Log::warning("Unhandled signal (%d) %s", sig, strsignal(sig));
break;
}
}
@ -46,17 +46,19 @@ int main(int argc, char **argv) {
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "--nodaemon") == 0) {
deamonize = false;
}
if (strcmp(argv[i], "--help") == 0) {
Log::setLogOutput(Log::StdOut);
} 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 <info.tech@telldus.com>\n");
exit(EXIT_SUCCESS);
}
if (strcmp(argv[i], "--version") == 0) {
} 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 <micke.prag@telldus.se>\n");
exit(EXIT_SUCCESS);
} else {
printf("Unknown option %s\n", argv[i]);
exit(EXIT_FAILURE);
}
}
@ -76,17 +78,14 @@ int main(int argc, char **argv) {
fprintf(fd,"%d\n",pid);
fclose(fd);
} else {
syslog(LOG_ERR, "Could not open pid file %s: %s", PID_FILE, strerror(errno));
Log::error("Could not open pid file %s: %s", PID_FILE, strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
}
setlogmask(LOG_UPTO(LOG_INFO));
openlog(DAEMON_NAME, LOG_CONS, LOG_USER);
syslog(LOG_NOTICE, "%s daemon starting up", DAEMON_NAME);
Log::notice("%s daemon starting up", DAEMON_NAME);
if (deamonize) {
/* Change the file mode mask */
@ -115,14 +114,14 @@ int main(int argc, char **argv) {
if (grp) {
setgid(grp->gr_gid);
} else {
syslog(LOG_WARNING, "Group %s could not be found", group.c_str());
Log::warning("Group %s could not be found", group.c_str());
exit(EXIT_FAILURE);
}
struct passwd *pw = getpwnam(user.c_str());
if (pw) {
setuid( pw->pw_uid );
} else {
syslog(LOG_WARNING, "User %s could not be found", user.c_str());
Log::warning("User %s could not be found", user.c_str());
exit(EXIT_FAILURE);
}
}
@ -139,6 +138,6 @@ int main(int argc, char **argv) {
tm.start();
syslog(LOG_NOTICE, "%s daemon exited", DAEMON_NAME);
Log::notice("%s daemon exited", DAEMON_NAME);
exit(EXIT_SUCCESS);
}

View file

@ -1,5 +1,6 @@
user = "nobody"
group = "plugdev"
ignoreControllerConfirmation = "false"
device {
id = 1
name = "Example device"

View file

@ -1,3 +1,3 @@
ID_VENDOR_ID=="1781", SUBSYSTEM=="usb", ACTION=="add", MODE="664", GROUP="plugdev" RUN+="${CMAKE_INSTALL_PREFIX}/share/telldus-core/helpers/udev.sh"
ID_VENDOR_ID=="1781", SUBSYSTEM=="usb", ACTION=="remove" RUN+="${CMAKE_INSTALL_PREFIX}/share/telldus-core/helpers/udev.sh"
ATTRS{idVendor}=="1781", SUBSYSTEM=="usb", ACTION=="add", MODE="664", GROUP="plugdev" RUN+="${CMAKE_INSTALL_PREFIX}/share/telldus-core/helpers/udev.sh"
ENV{ID_VENDOR_ID}=="1781", SUBSYSTEM=="usb", ACTION=="remove" RUN+="${CMAKE_INSTALL_PREFIX}/share/telldus-core/helpers/udev.sh"

View file

@ -2,6 +2,7 @@
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <ctime>
#include "../client/telldus-core.h"
const int SUPPORTED_METHODS =
@ -21,7 +22,7 @@ void print_usage( char *name ) {
printf(" [ --raw input ]\n");
printf("\n");
printf(" --list (-l short option)\n");
printf(" List currently configured devices.\n");
printf(" List currently configured devices and all discovered sensors.\n");
printf("\n");
printf(" --help (-h short option)\n");
printf(" Shows this screen.\n");
@ -77,6 +78,7 @@ void print_version() {
}
void print_device( int index ) {
tdInit();
int intId = tdGetDeviceId(index);
char *name = tdGetName(intId);
printf("%i\t%s\t", intId, name);
@ -101,13 +103,14 @@ void print_device( int index ) {
printf("\n");
}
void list_devices() {
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;
return intNum;
}
printf("Number of devices: %i\n", intNum);
int i = 0;
@ -115,9 +118,51 @@ void list_devices() {
print_device( i );
i++;
}
int DATA_LENGTH = 20;
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("\nSENSORS:\n%-20s\t%-20s\t%-5s\t%-5s\t%-8s\t%-20s\n", "PROTOCOL", "MODEL", "ID", "TEMP", "HUMIDITY", "LAST UPDATED");
}
while(sensorStatus == 0){
sensorStatus = tdSensor(protocol, DATA_LENGTH, model, DATA_LENGTH, &sensorId, &dataTypes);
char tempvalue[DATA_LENGTH];
tempvalue[0] = 0;
char humidityvalue[DATA_LENGTH];
humidityvalue[0] = 0;
char timeBuf[80];
time_t timestamp = 0;
if (dataTypes & TELLSTICK_TEMPERATURE) {
tdSensorValue(protocol, model, sensorId, TELLSTICK_TEMPERATURE, tempvalue, DATA_LENGTH, (int *)&timestamp);
strcat(tempvalue, "°");
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
}
if (dataTypes & TELLSTICK_HUMIDITY) {
tdSensorValue(protocol, model, sensorId, TELLSTICK_HUMIDITY, humidityvalue, DATA_LENGTH, (int *)&timestamp);
strcat(humidityvalue, "%");
strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
}
printf("%-20s\t%-20s\t%-5i\t%-5s\t%-8s\t%-20s\n", protocol, model, sensorId, tempvalue, humidityvalue, timeBuf);
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;
}
int find_device( char *device ) {
tdInit();
int deviceId = atoi(device);
if (deviceId == 0) { //Try to find the id from the name
int intNum = tdGetNumberOfDevices();
@ -137,11 +182,12 @@ int find_device( char *device ) {
return deviceId;
}
void switch_device( bool turnOn, char *device ) {
int switch_device( bool turnOn, char *device ) {
tdInit();
int deviceId = find_device( device );
if (deviceId == 0) {
printf("Device '%s', not found!\n", device);
return;
return TELLSTICK_ERROR_DEVICE_NOT_FOUND;
}
char *name = tdGetName( deviceId );
@ -158,17 +204,19 @@ void switch_device( bool turnOn, char *device ) {
printf(" - %s\n", errorString);
tdReleaseString(errorString);
return retval;
}
void dim_device( char *device, int level ) {
int dim_device( char *device, int level ) {
tdInit();
int deviceId = find_device( device );
if (deviceId == 0) {
printf("Device '%s', not found!\n", device);
return;
return TELLSTICK_ERROR_DEVICE_NOT_FOUND;
}
if (level < 0 || level > 255) {
printf("Level %i out of range!\n", level);
return;
return TELLSTICK_ERROR_SYNTAX;
}
char *name = tdGetName( deviceId );
@ -177,13 +225,15 @@ void dim_device( char *device, int level ) {
printf("Dimming device: %i %s to %i - %s\n", deviceId, name, level, errorString);
tdReleaseString(name);
tdReleaseString(errorString);
return retval;
}
void bell_device( char *device ) {
int bell_device( char *device ) {
tdInit();
int deviceId = find_device( device );
if (deviceId == 0) {
printf("Device '%s', not found!\n", device);
return;
return TELLSTICK_ERROR_DEVICE_NOT_FOUND;
}
char *name = tdGetName( deviceId );
@ -192,13 +242,15 @@ void bell_device( char *device ) {
printf("Sending bell to: %i %s - %s\n", deviceId, name, errorString);
tdReleaseString(name);
tdReleaseString(errorString);
return retval;
}
void learn_device( char *device ) {
int learn_device( char *device ) {
tdInit();
int deviceId = find_device( device );
if (deviceId == 0) {
printf("Device '%s', not found!\n", device);
return;
return TELLSTICK_ERROR_DEVICE_NOT_FOUND;
}
char *name = tdGetName( deviceId );
@ -207,9 +259,11 @@ void learn_device( char *device ) {
printf("Learning device: %i %s - %s\n", deviceId, name, errorString);
tdReleaseString(name);
tdReleaseString(errorString);
return retval;
}
void send_raw_command( char *command ) {
int send_raw_command( char *command ) {
tdInit();
const int MAX_LENGTH = 100;
char msg[MAX_LENGTH];
@ -221,7 +275,7 @@ void send_raw_command( char *command ) {
fd = fopen(command, "r");
if (fd == NULL) {
printf("Error opening file %s\n", command);
return;
return TELLSTICK_ERROR_UNKNOWN;
}
fgets(msg, MAX_LENGTH, fd);
}
@ -230,6 +284,7 @@ void send_raw_command( char *command ) {
char *errorString = tdGetErrorString(retval);
printf("Sending raw command: %s\n", errorString);
tdReleaseString(errorString);
return retval;
}
int main(int argc, char **argv)
@ -253,48 +308,56 @@ int main(int argc, char **argv)
if (argc < 2) {
print_usage( argv[0] );
return -1;
return -TELLSTICK_ERROR_SYNTAX;
}
while ( (optch = getopt_long(argc,argv,optstring,long_opts,&longindex)) != -1 )
int returnSuccess = 0;
while ( (optch = getopt_long(argc,argv,optstring,long_opts,&longindex)) != -1 ){
int success = 0;
switch (optch) {
case 'b' :
bell_device( &optarg[0] );
success = bell_device( &optarg[0] );
break;
case 'd' :
if (level >= 0) {
dim_device( &optarg[0], level );
success = dim_device( &optarg[0], level );
break;
}
printf("Dim level missing or incorrect value.\n");
success = TELLSTICK_ERROR_SYNTAX;
break;
case 'f' :
switch_device(false, &optarg[0]);
success = switch_device(false, &optarg[0]);
break;
case 'h' :
print_usage( argv[0] );
break;
success = TELLSTICK_SUCCESS;
case 'i' :
print_version( );
break;
success = TELLSTICK_SUCCESS;
case 'l' :
list_devices();
success = list_devices();
break;
case 'n' :
switch_device(true, &optarg[0]);
success = switch_device(true, &optarg[0]);
break;
case 'e' :
learn_device(&optarg[0]);
success = learn_device(&optarg[0]);
break;
case 'r' :
send_raw_command(&optarg[0]);
success = send_raw_command(&optarg[0]);
break;
case 'v' :
level = atoi( &optarg[0] );
break;
default :
print_usage( argv[0] );
return -1;
success = TELLSTICK_ERROR_SYNTAX;
}
if(success != TELLSTICK_SUCCESS){
returnSuccess = success; //return last error message
}
}
tdClose(); //Cleaning up
return 0;
return -returnSuccess;
}

View file

@ -11,6 +11,11 @@ LiveMessageToken::LiveMessageToken(const QString &value) {
stringVal = value;
}
LiveMessageToken::LiveMessageToken(int value) {
valueType = Int;
intVal = value;
}
QByteArray LiveMessageToken::toByteArray() const {
if (valueType == Int) {
return QString("i%1s").arg(intVal, 0, 16).toUtf8();
@ -122,7 +127,7 @@ void LiveMessageTokenScriptWrapper::add(LiveMessageTokenScriptWrapper *t) {
p_token.listVal << t->p_token;
}
int LiveMessageTokenScriptWrapper::getInt(const QString &key, int defaultValue) {
int LiveMessageTokenScriptWrapper::getInt(const QString &key, int defaultValue) const {
if (p_token.valueType != LiveMessageToken::Dictionary) {
return defaultValue;
}
@ -132,6 +137,16 @@ int LiveMessageTokenScriptWrapper::getInt(const QString &key, int defaultValue)
return p_token.dictVal[key].intVal;
}
QString LiveMessageTokenScriptWrapper::getString(const QString &key, const QString &defaultValue) const {
if (p_token.valueType != LiveMessageToken::Dictionary) {
return defaultValue;
}
if (!p_token.dictVal.contains(key)) {
return defaultValue;
}
return p_token.dictVal[key].stringVal;
}
int LiveMessageTokenScriptWrapper::intVal() const {
return p_token.intVal;
}

View file

@ -12,6 +12,7 @@ public:
LiveMessageToken();
LiveMessageToken(const QString &value);
LiveMessageToken(int value);
QByteArray toByteArray() const;
static LiveMessageToken parseToken(const QByteArray &string, int* start);
@ -34,13 +35,14 @@ public:
public slots:
void add(LiveMessageTokenScriptWrapper *token);
int getInt(const QString &key, int defaultValue = 0);
int getInt(const QString &key, int defaultValue = 0) const;
QString getString(const QString &key, const QString &defaultValue = "") const;
int intVal() const;
void set(const QString &key, int value);
void set(const QString &key, const QString &value);
private:
LiveMessageToken p_token;
};

View file

@ -13,7 +13,7 @@ public:
class Server;
QSslSocket *socket;
QTimer timer;
QTimer pingTimer, pongTimer;
bool registered;
QUrl registerUrl;
QString uuid, hashMethod;
@ -46,8 +46,11 @@ LiveObject::LiveObject( QScriptEngine *engine, QObject * parent )
connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)));
connect(d->socket, SIGNAL(sslErrors(const QList<QSslError> &)), this, SLOT(sslErrors(const QList<QSslError> &)));
d->timer.setInterval(60000); //Once a minute
connect(&d->timer, SIGNAL(timeout()), this, SLOT(pingServer()));
d->pingTimer.setInterval(120000); //Two minutes
d->pongTimer.setInterval(360000); //Six minutes
d->pongTimer.setSingleShot(true);
connect(&d->pingTimer, SIGNAL(timeout()), this, SLOT(pingServer()));
connect(&d->pongTimer, SIGNAL(timeout()), this, SLOT(pongTimeout()));
d->manager = new QNetworkAccessManager(this);
connect(d->manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serverAssignReply(QNetworkReply*)));
@ -102,6 +105,8 @@ void LiveObject::readyRead() {
//qDebug() << "HASH mismatch!" << msg->name();
return;
}
d->pongTimer.stop();
d->pongTimer.start();
if (msg->name() == "") {
return;
@ -122,6 +127,14 @@ void LiveObject::readyRead() {
s.setValue("Live/UUID", d->uuid);
emit notRegistered();
emit errorChanged("Not registered");
} else if (msg->name() == "command") {
if (msg->arg(0).valueType == LiveMessageToken::Dictionary && msg->arg(0).dictVal.contains("ACK")) {
int ack = msg->arg(0).dictVal["ACK"].intVal;
LiveMessage msg("ACK");
msg.append(ack);
this->sendMessage(msg);
}
emit messageReceived(msg.data());
} else {
emit messageReceived(msg.data());
}
@ -132,7 +145,7 @@ void LiveObject::refreshServerList() {
emit statusChanged("Discover servers");
d->serverList.clear();
QUrl url(TELLDUS_LIVE_URI);
QPair<QString, QString> version("protocolVersion", "1");
QPair<QString, QString> version("protocolVersion", "2");
QList<QPair<QString, QString> > query;
query.append(version);
url.setQueryItems(query);
@ -156,6 +169,8 @@ void LiveObject::sendMessage(const LiveMessage &message) {
d->socket->write(msg.toByteArray());
d->socket->flush();
d->pingTimer.stop();
d->pingTimer.start();
}
void LiveObject::sendMessage(LiveMessage *message) {
@ -170,7 +185,8 @@ void LiveObject::p_connected() {
QSettings settings;
d->uuid = settings.value("Live/UUID", "").toString();
d->timer.start(); //For pings
d->pingTimer.start(); //For pings
d->pongTimer.start(); //For pongs
LiveMessage msg("Register");
LiveMessageToken token;
@ -188,7 +204,12 @@ void LiveObject::p_connected() {
}
void LiveObject::p_disconnected() {
d->timer.stop();
d->pingTimer.stop();
d->pongTimer.stop();
if (d->registered) {
//Clear the registered status
emit errorChanged("Disconnected from server");
}
d->registered = false;
}
@ -198,11 +219,12 @@ void LiveObject::error( QAbstractSocket::SocketError socketError ) {
void LiveObject::stateChanged( QAbstractSocket::SocketState socketState ) {
if (socketState == QAbstractSocket::UnconnectedState) {
int timeout = rand() % 20 + 10; //Random timeout from 10-30s to avoid flooding the servers
int timeout = rand() % 40 + 10; //Random timeout from 10-50s to avoid flooding the servers
QTimer::singleShot(timeout*1000, this, SLOT(connectToServer()));
emit statusChanged("Reconnecting in " + QString::number(timeout) + " seconds...");
} else if (socketState == QAbstractSocket::ConnectingState) {
emit statusChanged("Connecting...");
emit errorChanged("");
}
}
@ -228,8 +250,10 @@ void LiveObject::sslErrors( const QList<QSslError> & errors ) {
void LiveObject::serverAssignReply( QNetworkReply *r ) {
r->deleteLater();
if (r->error() != QNetworkReply::NoError) {
int timeout = rand() % 300 + 60; //Random timeout from 60s-6min to avoid flooding the servers
emit errorChanged(r->errorString());
emit statusChanged("Error retrieving server list");
emit statusChanged("Retrying in " + QString::number(timeout) + " seconds...");
QTimer::singleShot(timeout * 1000, this, SLOT(connectToServer()));
return;
}
QXmlStreamReader xml(r);
@ -253,7 +277,7 @@ void LiveObject::serverAssignReply( QNetworkReply *r ) {
d->serverRefreshTime = QDateTime::currentDateTime();
QTimer::singleShot(0, this, SLOT(connectToServer()));
} else {
int timeout = rand() % 20 + 10; //Random timeout from 10-30s to avoid flooding the servers
int timeout = rand() % 300 + 60; //Random timeout from 60-6min to avoid flooding the servers
emit errorChanged("No servers found");
emit statusChanged("Retrying in " + QString::number(timeout) + " seconds...");
QTimer::singleShot(timeout * 1000, this, SLOT(connectToServer()));
@ -270,7 +294,7 @@ QByteArray LiveObject::signatureForMessage( const QByteArray &message ) {
LiveMessageToken LiveObject::generateVersionToken() {
LiveMessageToken token;
token.valueType = LiveMessageToken::Dictionary;
token.dictVal["protocol"] = LiveMessageToken("1");
token.dictVal["protocol"] = LiveMessageToken(2);
token.dictVal["version"] = LiveMessageToken(TELLDUS_CENTER_VERSION);
#if defined(Q_WS_WIN)
token.dictVal["os"] = LiveMessageToken("windows");
@ -314,3 +338,7 @@ LiveMessageToken LiveObject::generateVersionToken() {
#endif
return token;
}
void LiveObject::pongTimeout() {
this->disconnect();
}

View file

@ -39,6 +39,7 @@ private slots:
void p_connected();
void p_disconnected();
void readyRead();
void pongTimeout();
void error( QAbstractSocket::SocketError socketError );
void stateChanged( QAbstractSocket::SocketState socketState );
void sslErrors( const QList<QSslError> & errors );

Some files were not shown because too many files have changed in this diff Show more