...
 
Commits (24)
......@@ -146,8 +146,12 @@ find_package(Qpid)
log_pkg_found(qpid "${QPID_FOUND}" "${QPID_VERSION}" STATUS ", QPID communication support will not be built")
set(WITH_QPID ${QPID_FOUND})
if(NOT ${QPID_FOUND})
message(FATAL_ERROR "qpid not found. Required for communications")
find_package(Mosquittopp)
log_pkg_found(mosquittopp "${MOSQUITTOPP_FOUND}" "${MOSQUITTOPP_VERSION}" WARNING ", MQTT communication support will not be built")
set(WITH_MQTT ${MOSQUITTOPP_FOUND})
if(NOT ${MOSQUITTOPP_FOUND} AND NOT ${QPID_FOUND})
message(FATAL_ERROR "Neither mosquittopp nor qpid found. One or both is required")
endif()
if(OS_LINUX)
......@@ -214,10 +218,8 @@ endif()
install(DIRECTORY DESTINATION ${BINDIR})
# add sub directories
option(BUILD_SHARED "Build the agoclient shared library" ON)
if (BUILD_SHARED)
add_subdirectory (shared)
endif()
add_subdirectory (shared)
add_subdirectory (python)
option(BUILD_TESTS "Build the unittests" ON)
if (BUILD_TESTS AND CPPUNIT_FOUND)
......
import json
import os
import threading
import time
import traceback
from collections import namedtuple
import pytest
import logging
try:
import paho.mqtt.client as mqtt
HAS_MQTT = True
except ImportError:
HAS_MQTT = False
@pytest.fixture(scope='session')
def mqttc():
assert HAS_MQTT
#broker = os.environ.get('AGO_BROKER', 'localhost')
#username = os.environ.get('AGO_USERNAME', 'agocontrol')
#password = os.environ.get('AGO_PASSWORD', 'letmein')
mqttc = mqtt.Client(client_id='ago-autotest', clean_session=True)
mqttc.loop_start()
ready_cond = threading.Condition()
def mqtt_on_connect(client, userdata, flags, rc):
#print("MQTT connected, subscibing")
client.subscribe('com.agocontrol/legacy')
def mqtt_on_subscribe(client, userdata, mid, granted_qos):
#print("MQTT subscribed")
ready_cond.acquire()
ready_cond.notify()
ready_cond.release()
mqttc.on_connect = mqtt_on_connect
mqttc.on_subscribe = mqtt_on_subscribe
mqttc.connect('localhost')
ready_cond.acquire()
ready_cond.wait(10)
ready_cond.release()
# print("Setup ready")
yield mqttc
mqttc.disconnect()
mqttc.loop_stop()
@pytest.fixture(scope='function')
def mqtt_transport_adapter(mqttc):
adapter = MqttTransportAdapter(mqttc)
return adapter
MqttTransportMessage = namedtuple('MqttTransportMessage', 'content,subject')
class MqttTransportAdapter:
def __init__(self, mqttc, timeout=10):
self.mqttc = mqttc
def set_handler(self, handler):
self.handler = handler
self.mqttc.on_message = self.on_message
def on_message(self, client, userdata, message):
#print("Got message on ", message.topic, ":", message.payload)
try:
json_payload = json.loads(message.payload)
if 'UT-EXP' not in json_payload['content']:
# Ignore other spurious msgs
#print("Ignoring message")
return
tpm = MqttTransportMessage(json_payload['content'], json_payload.get('subject', None))
rep = self.handler(tpm)
if rep:
logging.info("Got msg %s, replying with %s", (json_payload, rep))
self.mqttc.publish(json_payload['reply-to'], json.dumps(rep))
#else:
#print("Handler called, but no reply")
except:
traceback.print_exc()
def send(self, content, subject):
topic = 'com.agocontrol/legacy'
self.mqttc.publish(topic, payload=json.dumps(dict(content=content, subject=subject)))
def shutdown(self):
self.mqttc.on_message = None
......@@ -2,7 +2,7 @@ import logging
import pytest
# Automatically imports
pytest_plugins = ['testlib.qpid_transport']
pytest_plugins = ['testlib.qpid_transport', 'testlib.mqtt_transport']
logging.basicConfig()
......@@ -23,6 +23,8 @@ def pytest_addoption(parser):
def transport_adapter(request, variables):
if variables['transport'] == 'qpid':
adapter = request.getfixturevalue('qpid_transport_adapter')
elif variables['transport'] == 'mqtt':
adapter = request.getfixturevalue('mqtt_transport_adapter')
else:
raise AssertionError('Invalid transport configured')
......
......@@ -8,6 +8,7 @@
#cmakedefine HAVE_XLOCALE_H @HAVE_XLOCALE_H@
#cmakedefine WITH_MQTT @WITH_MQTT@
#cmakedefine WITH_QPID @WITH_QPID@
#endif
\ No newline at end of file
......@@ -14,11 +14,10 @@ function(CopyFilesFromSource TARGET SOURCE_FILES)
if (NOT IN_SOURCE_BUILD)
add_custom_target(${TARGET} ALL SOURCES ${SOURCE_FILES})
foreach (infile ${SOURCE_FILES})
string(REPLACE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} outfile ${infile})
#message("-- staging for copy: ${CMAKE_CURRENT_SOURCE_DIR}/${infile} -> ${CMAKE_CURRENT_BINARY_DIR}/${outfile}")
#message("-- staging for copy: ${CMAKE_CURRENT_SOURCE_DIR}/${infile} -> ${CMAKE_CURRENT_BINARY_DIR}/${infile}")
add_custom_command(
TARGET ${TARGET}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${infile} ${outfile}
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${infile} ${infile}
VERBATIM
)
endforeach()
......
# From https://github.com/romaniucradu/Calculated/blob/master/cmake_modules/FindMosquittopp.cmake
# - Find libmosquitto
# Find the native libmosquitto includes and libraries
#
# MOSQUITTOPP_INCLUDE_DIR - where to find mosquitto.h, etc.
# MOSQUITTOPP_LIBRARIES - List of libraries when using libmosquitto.
# MOSQUITTOPP_FOUND - True if libmosquitto found.
if(MOSQUITTOPP_INCLUDE_DIR)
# Already in cache, be silent
set(MOSQUITTOPP_FIND_QUIETLY TRUE)
endif(MOSQUITTOPP_INCLUDE_DIR)
find_path(MOSQUITTOPP_INCLUDE_DIR mosquitto.h)
find_library(MOSQUITTOPP_LIBRARY NAMES libmosquittopp mosquittopp)
find_library(MOSQUITTO_LIBRARY NAMES libmosquitto mosquitto)
# Handle the QUIETLY and REQUIRED arguments and set MOSQUITTO_FOUND to TRUE if
# all listed variables are TRUE.
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MOSQUITTOPP DEFAULT_MSG MOSQUITTOPP_LIBRARY MOSQUITTOPP_INCLUDE_DIR)
if(MOSQUITTOPP_FOUND)
set(MOSQUITTOPP_LIBRARIES ${MOSQUITTOPP_LIBRARY} ${MOSQUITTO_LIBRARY})
else(MOSQUITTOPP_FOUND)
set(MOSQUITTOPP_LIBRARIES)
endif(MOSQUITTOPP_FOUND)
mark_as_advanced(MOSQUITTOPP_INCLUDE_DIR MOSQUITTOPP_LIBRARY)
......@@ -16,6 +16,8 @@ foreach (infile ${INIT_FILES})
endforeach (infile)
CopyFilesFromSource(augeas_lens "agocontrol.aug")
add_subdirectory (conf.d)
add_subdirectory (schema.d)
add_subdirectory (sysvinit)
......
cmake_minimum_required (VERSION 3.0)
file (GLOB CONFD_FILES *.in)
file (GLOB CONFD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.in)
foreach (infile ${CONFD_FILES})
string(REGEX REPLACE ".in$" "" outfile ${infile})
string(REGEX REPLACE ".*/" "" outfile ${outfile})
configure_file(
"${infile}"
"${CMAKE_CURRENT_BINARY_DIR}/${outfile}"
@ONLY
)
LIST(APPEND CONFD_FILES_DONE ${CMAKE_CURRENT_BINARY_DIR}/${outfile})
LIST(APPEND CONFD_FILES_DONE ${outfile})
endforeach (infile)
file (GLOB CONFD_FILES *.conf)
install (FILES ${CONFD_FILES_DONE} ${CONFD_FILES} DESTINATION ${CONFDIR}/conf.d)
file (GLOB CONFD_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.conf)
CopyFilesFromSource(conf_files "${CONFD_FILES}")
InstallFiles(${CONFDIR}/conf.d "${CONFD_FILES_DONE};${CONFD_FILES}")
[system]
uuid=00000000-0000-0000-000000000000
# mqtt or qpid
messaging=qpid
# address to broker, hostname[:port]
broker=localhost
# when you change username and/or password, you also need to adjust the qpid sasl db to reflect the change
username=agocontrol
password=letmein
units=SI
# Please see http://wiki.agocontrol.com/index.php/Logging for more details on Logging
......@@ -11,3 +19,28 @@ units=SI
log_method=syslog
# log levels: TRACE, DEBUG, INFO, WARNING, ERROR, FATAL
log_level=INFO
[loggers]
# The main log_level controls the overall logging level.
# It is then possible to limit different loggers (python) / channels (C++) to a higher level.
# For example, if log_level is set to INFO, nothing lower than INFO will be logged,
# even if a specific logger is set to TRACE.
# Names are shared between both python & C++ implementations (where applicable).
# The low-level qpid python library is very verbose, so do not log anything lower
# than INFO from that library:
qpid=INFO
# Same goes for low-level mqtt library, both on C++ and Python. Cap it to INFO.
mqtt=INFO
# The following are internal application logging in the AgoClient library
# transport is the abstraction of the MQTT/QPID connectivity, and only deals with simple messaging.
transport = TRACE
# connection is a higher-level interface between each application and the transport
connection = TRACE
# This is the "main" logger for C++, if no specific one is used. It is also the default for all applications.
app = TRACE
......@@ -21,7 +21,7 @@ private:
qpid::messaging::Connection *connection;
// Override, we do not use a AgoConnection in drain
void setupAgoConnection() { }
bool setupAgoConnection(boost::unique_lock<boost::mutex> &lock) { return true; }
void doShutdown() ;
int appMain();
......
#!/usr/bin/env python
from __future__ import print_function
#Returns all or specified directory structure
#@params get: all|plugins|configs|helps|supported
......@@ -30,7 +31,7 @@ def loadMetadatasInDir(d):
items[obj["dir"]] = obj
except Exception as error:
pass
for key in sorted(items.iterkeys()):
for key in sorted(items.keys()):
out.append(items[key])
return out
......@@ -83,9 +84,9 @@ try:
except Exception as e:
#TODO add message to agolog
pass
result['error'] = str(e)
#send output
print "Content-type: application/json\n"
print json.dumps(result)
print("Content-type: application/json\n")
print(json.dumps(result))
#!/usr/bin/env python
from __future__ import print_function
import os
import sys
......@@ -142,6 +144,6 @@ except Exception as e:
pass
#send output
print "Content-type: application/json\n"
print json.dumps(result)
print("Content-type: application/json\n")
print(json.dumps(result))
......@@ -30,7 +30,6 @@ opt/agocontrol/bin/agoscenario
opt/agocontrol/bin/agoevent
opt/agocontrol/bin/agolua
opt/agocontrol/bin/myavahi.py
opt/agocontrol/bin/agodrain
opt/agocontrol/bin/messagesend
opt/agocontrol/bin/agotimer
opt/agocontrol/bin/agorpc
......
......@@ -2,13 +2,13 @@ Source: agocontrol
Maintainer: Harald Klein <hari@vt100.at>
Section: misc
Priority: optional
Build-Depends: debhelper (>= 8), libola-dev (>= 0.9.1) | ola-dev, libprocps3-dev (>= 2:3.3.9) | libprocps0-dev (>= 1:3.1.11), libyaml-cpp0.3-dev (<< 0.4) | libyaml-cpp-dev (<< 0.5), python, libqpidmessaging2-dev, libqpidtypes1-dev, libqpidcommon2-dev, libudev-dev, libqpidclient2-dev, uuid-dev, libopenzwave1.3-dev, libtinyxml2-dev, libsqlite3-dev, libi2c-dev, libssl-dev, libboost-dev, intltool, libboost-date-time-dev,realpath,libcurl4-openssl-dev,libhdate-dev,liblua5.2-dev,knxd-dev (>= 0.10.9) | libeibclient-dev (<= 0.0.5), libboost-regex-dev, libaugeas-dev, librrd-dev, cmake, libboost-filesystem-dev, libboost-system-dev, libboost-thread-dev, libboost-program-options-dev, lsb-release, libopencv-dev, libopencv-core-dev, libopencv-imgproc-dev, libopencv-objdetect-dev, libcppdb-dev, libopencv-highgui-dev, libprotobuf9
Build-Depends: debhelper (>= 8), libola-dev (>= 0.9.1) | ola-dev, libprocps3-dev (>= 2:3.3.9) | libprocps0-dev (>= 1:3.1.11), libyaml-cpp0.3-dev (<< 0.4) | libyaml-cpp-dev (<< 0.5), python, libudev-dev, uuid-dev, libopenzwave1.3-dev, libtinyxml2-dev, libsqlite3-dev, libi2c-dev, libssl-dev, libboost-dev, intltool, libboost-date-time-dev,realpath,libcurl4-openssl-dev,libhdate-dev,liblua5.2-dev,knxd-dev (>= 0.10.9) | libeibclient-dev (<= 0.0.5), libboost-regex-dev, libaugeas-dev, librrd-dev, cmake, libboost-filesystem-dev, libboost-system-dev, libboost-thread-dev, libboost-program-options-dev, lsb-release, libopencv-dev, libopencv-core-dev, libopencv-imgproc-dev, libopencv-objdetect-dev, libcppdb-dev, libopencv-highgui-dev, libprotobuf9
Build-Conflicts: libopenzwave1.0-dev
Standards-Version: 3.9.2
Package: agocontrol
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}, python, uuid-runtime, sqlite3, python-pysqlite2, libqpidmessaging2, libqpidtypes1, sasl2-bin, libsasl2-2, libsasl2-modules, qpidd, libsqlite3-0, libagoclient1.0, python-agoclient, libssl-dev, python-nss, libhdate1, python-dbus, librrd4, procps, libcppdb0, libcppdb-sqlite3-0
Depends: ${shlibs:Depends}, ${misc:Depends}, ${dist:Depends}, python, uuid-runtime, sqlite3, python-pysqlite2, sasl2-bin, libsasl2-2, libsasl2-modules, libsqlite3-0, libagoclient1.0, python-agoclient, libssl-dev, python-nss, libhdate1, python-dbus, librrd4, procps, libcppdb0, libcppdb-sqlite3-0
Description: automation system
Package: agocontrol-dbg
......@@ -21,7 +21,7 @@ Description: debug info for agocontrol
Package: libagoclient1.0
Section: libs
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libqpidmessaging2, libqpidtypes1, libqpidclient2, libqpidcommon2, libuuid1, libaugeas0
Depends: ${shlibs:Depends}, ${misc:Depends}, libuuid1, libaugeas0
Description: client routines and utility functions for ago control device interfaces
Package: libagoclient1.0-dev
......@@ -30,6 +30,18 @@ Depends: ${misc:Depends}, libagoclient1.0
Architecture: any
Description: Development header files for the agocontrol client library
Package: libagotransport-mqtt1.0
Section: libs
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libuuid1, libmosquittopp
Description: agoclient support for MQTT backend transport
Package: libagotransport-qpid1.0
Section: libs
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libuuid1, libqpidmessaging2, libqpidtypes1, libqpidclient2, libqpidcommon2, qpidd, libagoclient1.0
Description: agoclient support for Qpid backend transport
Package: python-agoclient
Section: libs
Architecture: all
......
/usr/lib/libagoclient.so.1 /usr/lib/libagoclient.so
/usr/lib/libagohttp.so.1 /usr/lib/libagohttp.so
usr/lib/libagoclient.so.1
usr/lib/libagohttp.so.1
usr/lib/libagocore.so.1
/usr/lib/libagoclient.so.1 /usr/lib/libagoclient.so.1.0
/usr/lib/libagohttp.so.1 /usr/lib/libagohttp.so.1.0
/usr/lib/libagocore.so.1 /usr/lib/libagocore.so.1.0
usr/lib/libagotransport-mqtt.so.1
/usr/lib/libagotransport-mqtt.so.1 /usr/lib/libagotransport-mqtt.so.1.0
\ No newline at end of file
usr/lib/libagotransport-qpid.so.1
opt/agocontrol/bin/agodrain
/usr/lib/libagotransport-qpid.so.1 /usr/lib/libagotransport-qpid.so.1.0
\ No newline at end of file
......@@ -19,5 +19,9 @@ override_dh_auto_configure:
override_dh_auto_build:
dh_auto_build --parallel
# Default runs make test which is very silent in what fails
override_dh_auto_test:
$(MAKE) -j1 check
%:
dh $@ --with python2
......@@ -6,28 +6,29 @@ import time
import logging
import agoclient
from agoclient import agoproto
class AgoSimulator(agoclient.AgoApp):
def message_handler(self, internalid, content):
if "command" in content:
if content["command"] == "on":
print "switching on: " + internalid
print("switching on: " + internalid)
self.connection.emit_event(internalid, "event.device.statechanged", 255, "")
elif content["command"] == "off":
print "switching off: " + internalid
print("switching off: " + internalid)
self.connection.emit_event(internalid, "event.device.statechanged", 0, "")
elif content["command"] == "push":
print "push button: " + internalid
print("push button: " + internalid)
elif content['command'] == 'setlevel':
if 'level' in content:
print "device level changed", content["level"]
print("device level changed", content["level"])
self.connection.emit_event(internalid, "event.device.statechanged", content["level"], "")
else:
return self.connection.response_unknown_command()
return agoproto.response_unknown_command()
return self.connection.response_success()
return agoproto.response_success()
else:
return self.connection.response_bad_parameters()
return agoproto.response_bad_parameters()
def app_cmd_line_options(self, parser):
......@@ -85,11 +86,11 @@ class TestEvent(threading.Thread):
hum = random.randint(20, 75) + random.randint(0,90)/100.0
log.debug("Sending enviromnet changes on sensor 126 (%.2f dgr C, %.2f %% humidity)",
temp, hum)
self.connection.emit_event("126", "event.environment.temperaturechanged", temp, "degC");
self.connection.emit_event("126", "event.environment.humiditychanged", hum, "percent");
self.app.connection.emit_event("126", "event.environment.temperaturechanged", temp, "degC")
self.app.connection.emit_event("126", "event.environment.humiditychanged", hum, "percent")
log.debug("Sending sensortriggered for internal-ID 125, level %d", level)
self.connection.emit_event("125", "event.security.sensortriggered", level, "")
self.app.connection.emit_event("125", "event.security.sensortriggered", level, "")
if (level == 0):
level = 255
......
This diff is collapsed.
This diff is collapsed.
......@@ -194,7 +194,7 @@ class PullStatus(threading.Thread):
else:
self.log.error('PullStatus: Could not get status from light')
except TypeError as e:
self.log.error("PullStatus: Exception occurred in background thread. {}".format(e.message))
self.log.error("PullStatus: Exception occurred in background thread. {}".format(str(e)))
time.sleep(float(self.PollDelay)) # TODO: Calculate ((60-n)/NoDevices)/60
if __name__ == "__main__":
......
This diff is collapsed.
......@@ -8,6 +8,7 @@
#
import agoclient
from agoclient import agoproto
import urllib2
import base64
import picamera
......@@ -33,7 +34,7 @@ def messageHandler(internalid, content):
camera.capture(stream, format='jpeg')
frame = stream.getvalue()
return client.response_success({'image':buffer(base64.b64encode(frame))})
return agoproto.response_success({'image':buffer(base64.b64encode(frame))})
client.add_handler(messageHandler)
......
......@@ -5,6 +5,7 @@
import sys
import agoclient
from agoclient import agoproto
import pylmsserver
import pylmsplaylist
import pylmslibrary
......@@ -199,7 +200,7 @@ def messageHandler(internalid, content):
#check parameters
if not content.has_key("command"):
logging.error('No command specified in content')
return client.response_unknown_command(message='No command specified')
return agoproto.response_unknown_command(message='No command specified')
if internalid==host:
......@@ -208,27 +209,27 @@ def messageHandler(internalid, content):
logging.info("Command ALLON: %s" % internalid)
for player in getPlayers():
player.on()
return client.response_success()
return agoproto.response_success()
elif content["command"]=="alloff":
logging.info("Command ALLOFF: %s" % internalid)
for player in getPlayers():
player.off()
return client.response_success()
return agoproto.response_success()
elif content["command"]=="displaymessage":
if content.has_key('line1') and content.has_key('line2') and content.has_key('duration'):
logging.info("Command DISPLAYMESSAGE: %s" % internalid)
for player in getPlayers():
player.display(content['line1'], content['line2'], content['duration'])
return client.response_success()
return agoproto.response_success()
else:
logging.error('Missing parameters to command DISPLAYMESSAGE')
return client.response_missing_parameters(data={'command': 'displaymessage', 'params': ['line1', 'line2', 'duration']})
return agoproto.response_missing_parameters(data={'command': 'displaymessage', 'params': ['line1', 'line2', 'duration']})
#unhandled command
logging.warn('Unhandled server command')
return client.response_unknown_command(message='Unhandled server command', data=content["command"])
return agoproto.response_unknown_command(message='Unhandled server command', data=content["command"])
else:
......@@ -238,69 +239,69 @@ def messageHandler(internalid, content):
logging.info('Found player: %s' % player)
if not player:
logging.error('Player %s not found!' % internalid)
return client.response_failed('Player "%s" not found!' % internalid)
return agoproto.response_failed('Player "%s" not found!' % internalid)
if content["command"] == "on":
logging.info("Command ON: %s" % internalid)
player.on()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "off":
logging.info("Command OFF: %s" % internalid)
player.off()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "play":
logging.info("Command PLAY: %s" % internalid)
player.play()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "pause":
logging.info("Command PAUSE: %s" % internalid)
player.pause()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "stop":
logging.info("Command STOP: %s" % internalid)
player.stop()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "next":
logging.info("Command NEXT: %s" % internalid)
player.next()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "previous":
logging.info("Command PREVIOUS: %s" % internalid)
player.prev()
return client.response_success()
return agoproto.response_success()
elif content["command"] == "setvolume":
logging.info("Command SETVOLUME: %s" % internalid)
if content.has_key('volume'):
player.set_volume(content['volume'])
return client.response_success()
return agoproto.response_success()
else:
logging.error('Missing parameter "volume" to command SETVOLUME')
return client.response_missing_parameters(data={'command': 'setvolume', 'params': ['volume']})
return agoproto.response_missing_parameters(data={'command': 'setvolume', 'params': ['volume']})
elif content["command"] == "displaymessage":
if content.has_key('line1') and content.has_key('line2') and content.has_key('duration'):
logging.info("Command DISPLAYMESSAGE: %s" % internalid)
player.display(content['line1'], content['line2'], content['duration'])
return client.response_success()
return agoproto.response_success()
else:
logging.error('Missing parameters to command DISPLAYMESSAGE')
return client.response_missing_parameters(data={'command': 'displaymessage', 'params': ['line1', 'line2', 'duration']})
return agoproto.response_missing_parameters(data={'command': 'displaymessage', 'params': ['line1', 'line2', 'duration']})
elif content["command"] == "mediainfos":
infos = get_media_infos(internalid, None)
logging.info(infos)
return client.response_success(infos)
return agoproto.response_success(infos)
#unhandled device command
logging.warn('Unhandled device command')
return client.response_unknown_command(message='Unhandled device command', data=content["command"])
return agoproto.response_unknown_command(message='Unhandled device command', data=content["command"])
#init
......
#!/usr/bin/python
from __future__ import print_function
import time
import agoclient
from agoclient import agoproto
AGO_TELLSTICK_VERSION = '0.0.91'
############################################
......@@ -32,16 +35,16 @@ class AgoTellstick(agoclient.AgoApp):
self.log.trace("rescode = %s", resCode)
# res = self.tellstick.getErrorString(resCode)
self.log.error("Failed to turn on device, res=%s", resCode)
return self.connection.response_failed("Failed to turn on device (%s)" % resCode)
return agoproto.response_failed("Failed to turn on device (%s)" % resCode)
else:
self.connection.emit_event(internalid, "event.device.statechanged", 255, "")
self.log.debug("Turning on device: %s res=%s", internalid, resCode)
return self.connection.response_success()
return agoproto.response_success()
# Allon - TODO: will require changes in schema.yaml + somewhere else too
if content["command"] == "allon":
return self.connection.response_unknown_command()
return agoproto.response_unknown_command()
# Off
if content["command"] == "off":
......@@ -49,13 +52,13 @@ class AgoTellstick(agoclient.AgoApp):
if resCode != 'success': # 0:
# res = self.tellstick.getErrorString(resCode)
self.log.error("Failed to turn off device, res=%s", resCode)
return self.connection.response_failed("Failed to turn off device (%s)" % resCode)
return agoproto.response_failed("Failed to turn off device (%s)" % resCode)
else:
# res = 'Success'
self.connection.emit_event(internalid, "event.device.statechanged", 0, "")
self.log.debug("Turning off device: %s res=%s", internalid, resCode)
return self.connection.response_success()
return agoproto.response_success()
# Setlevel for dimmer
if content["command"] == "setlevel":
......@@ -63,17 +66,17 @@ class AgoTellstick(agoclient.AgoApp):
255 * int(content["level"])) / 100) # Different scales: aGo use 0-100, Tellstick use 0-255
if resCode != 'success': # 0:
self.log.error("Failed dimming device, res=%s", resCode)
return self.connection.response_failed("Failed to dim device (%s)" % resCode)
return agoproto.response_failed("Failed to dim device (%s)" % resCode)
else:
# res = 'Success'
self.connection.emit_event(internalid, "event.device.statechanged", content["level"], "")
self.log.debug("Dimming device=%s res=%s level=%s", internalid, resCode, str(content["level"]))
return self.connection.response_success()
return agoproto.response_success()
return self.connection.response_unknown_command()
return agoproto.response_unknown_command()
return self.connection.response_missing_parameters()
return agoproto.response_missing_parameters()
# Event handlers for device and sensor events
# This method is a call-back, triggered when there is a device event
......@@ -194,7 +197,7 @@ class AgoTellstick(agoclient.AgoApp):
def listNewSensors(self):
sensors = self.tellstick.listSensors()
self.log.debug("listSensors returned %d items", len(sensors))
for id, value in sensors.iteritems():
for id, value in sensors.items():
self.log.trace("listNewSensors: devId: %s ", str(id))
if not value["new"]:
continue
......@@ -311,7 +314,7 @@ class AgoTellstick(agoclient.AgoApp):
from tellstickduo import tellstickduo
self.tellstick = tellstickduo(self)
self.log.debug("Stick: Defaulting to Tellstick Duo")
except OSError, e:
except OSError as e:
self.log.error("Failed to load Tellstick stick version code: %s", e)
raise agoclient.agoapp.StartupError()
......@@ -383,7 +386,7 @@ class AgoTellstick(agoclient.AgoApp):
def listNewDevices(self):
switches = self.tellstick.listSwitches()
for devId, dev in switches.iteritems():
for devId, dev in switches.items():
model = dev["model"]
name = dev["name"]
......
#!/usr/bin/python
from __future__ import print_function
# Filter class, used to store a series of samples and determine if
# a new sample is noise or real sample before adding it to the series
......@@ -60,8 +61,8 @@ class sampleseries():
def load(self):
self.samples = [3.3, 3.4, 3.5, 3.6, 3.5, 3.6, 3.5, 3.6, 3.5, 3.4, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 4.0, 3.9,
4.0]
print self.samples
print ("len of samples=" + str(self.samples.__len__()))
print(self.samples)
print("len of samples=" + str(self.samples.__len__()))
self.times = [datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 00:56", "%y %b %d %H:%M"))),
datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 01:00", "%y %b %d %H:%M"))),
......@@ -84,11 +85,11 @@ class sampleseries():
datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 04:04", "%y %b %d %H:%M"))),
datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 04:08", "%y %b %d %H:%M")))]
self.createT(self.samples, self.times)
print self.samples2
print self.samples2.__len__()
print(self.samples2)
print(self.samples2.__len__())
# print self.times
print ("len of times=" + str(self.times.__len__()))
print("len of times=" + str(self.times.__len__()))
def createT(self, samples, times):
for x in range(0, times.__len__()):
......@@ -101,12 +102,12 @@ class sampleseries():
self.samples.append(sample)
self.times.append(tm if tm is not None else now)
print ("Now=" + str(now) + " sample=" + str(sample))
print(("Now=" + str(now) + " sample=" + str(sample)))
def removeold(self):
print ("len of samples=" + str(self.samples.__len__()))
print("len of samples=" + str(self.samples.__len__()))
if self.samples.__len__() >= self.maxitems:
print "maxitems reached"
print("maxitems reached")
# find oldest, remove it
def avg(self):
......@@ -129,11 +130,11 @@ if __name__ == "__main__":
# 1/31/2016 4:10 AM 22.4
ret = a.test(22.4, datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 04:10", "%y %b %d %H:%M"))))
print "ret="
print ret
print("ret=")
print(ret)
x = a.times[1] - a.times[0]
print x
print(x)
a.insert(1.0, datetime.fromtimestamp(mktime(time.strptime("16 Feb 07 04:10", "%y %b %d %H:%M"))))
# a.insert(1.1)
......@@ -141,7 +142,7 @@ if __name__ == "__main__":
# a.insert(1.1)
# a.insert(-1.1)
print "avg=" + str(a.avg())
print("avg=" + str(a.avg()))
# print a.times
# print ("len of times=" + str(a.times.__len__()))
......
This diff is collapsed.
from __future__ import print_function
import sys, getopt, httplib, urllib, json, os
import logging, syslog
import oauth.oauth as oauth
......@@ -7,22 +9,22 @@ def info (text):
logging.info (text)
syslog.syslog(syslog.LOG_INFO, text)
if debug:
print "INF " + text + "\n"
print("INF " + text + "\n")
def debug (text):
logging.debug (text)
syslog.syslog(syslog.LOG_DEBUG, text)
if debug:
print "DBG " + text + "\n"
print("DBG " + text + "\n")
def error (text):
logging.error(text)
syslog.syslog(syslog.LOG_ERR, text)
if debug:
print "ERR " + text + "\n"
print("ERR " + text + "\n")
def warning(text):
logging.warning (text)
syslog.syslog(syslog.LOG_WARNING, text)
if debug:
print "WRN " + text + "\n"
print("WRN " + text + "\n")
class LogErr:
def write(self, data):
......@@ -55,13 +57,13 @@ def requestToken():
consumer = oauth.OAuthConsumer(PUBLIC_KEY, PRIVATE_KEY)
request = oauth.OAuthRequest.from_consumer_and_token(consumer, http_url='http://api.telldus.com/oauth/requestToken')
request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, None)
conn = httplib.HTTPConnection('api.telldus.com:80')
conn = http.client.HTTPConnection('api.telldus.com:80')
conn.request(request.http_method, '/oauth/requestToken', headers=request.to_header())
resp = conn.getresponse().read()
token = oauth.OAuthToken.from_string(resp)
print 'Open the following url in your webbrowser:\nhttp://api.telldus.com/oauth/authorize?oauth_token=%s\n' % token.key
print 'After logging in and accepting to use this application run:\n%s --authenticate' % (sys.argv[0])
print('Open the following url in your webbrowser:\nhttp://api.telldus.com/oauth/authorize?oauth_token=%s\n' % token.key)
print('After logging in and accepting to use this application run:\n%s --authenticate' % (sys.argv[0]))
config['telldus']['requestToken'] = str(token.key)
config['telldus']['requestTokenSecret'] = str(token.secret)
saveConfig()
......@@ -77,14 +79,14 @@ def getAccessToken():
resp = conn.getresponse()
if resp.status != 200:
print 'Error retreiving access token, the server replied:\n%s' % resp.read()
print('Error retreiving access token, the server replied:\n%s' % resp.read())
return
token = oauth.OAuthToken.from_string(resp.read())
config['telldus']['requestToken'] = None
config['telldus']['requestTokenSecret'] = None
config['telldus']['token'] = str(token.key)
config['telldus']['tokenSecret'] = str(token.secret)
print 'Authentication successful, you can now use your Tellstick Net with aGo control'
print('Authentication successful, you can now use your Tellstick Net with aGo control')
saveConfig()
def authenticate():
......
......@@ -140,7 +140,7 @@ class tellstickduo(tellstickbase):
sensors = td.listSensors()
if len(sensors) != 0:
for id, value in sensors.iteritems():
for id, value in sensors.items():
self.log.trace("listSensors: devId: %s ", str(id))
if id not in self.ignoreDevices:
......
......@@ -15,6 +15,7 @@ __status__ = "Experimental"
__version__ = AGO_TELLSTICK_VERSION
############################################
from __future__ import print_function
from tellstickbase import tellstickbase
import sys, getopt, httplib, urllib, json, os, thread, time
import oauth.oauth as oauth
......@@ -89,7 +90,7 @@ class tellsticknet(tellstickbase):
if ('error' in response):
name = ''
retString = response['error']
print ("retString=" + retString)
print("retString=" + retString)
else:
name = response['name']
self.names[devId] = response['name']
......@@ -126,7 +127,7 @@ class tellsticknet(tellstickbase):
if ('error' in response):
model = ''
retString = response['error']
print ("retString=" + retString)
print("retString=" + retString)
else:
if response['type'] == 'device':
self.models[devId] = response['model']
......@@ -282,7 +283,7 @@ class tellsticknet(tellstickbase):
def sensorThread(self, sensorCallback, dummy):
while (True):
for devId, dev in self.sensors.iteritems():
for devId, dev in self.sensors.items():
response = self.doRequest('sensor/info', {'id': dev["id"]})
lastupdate = datetime.datetime.fromtimestamp(int(response['lastUpdated']))
......@@ -300,7 +301,7 @@ class tellsticknet(tellstickbase):
if data['name'] == 'humidity' and float(data['value']) != self.sensors[devId]["lastHumidity"]:
humidity = float(data['value'])
self.sensors[devId]["lastHumidity"] = humidity
if humidity <> self.sensors[devId]["humidity"]:
if humidity != self.sensors[devId]["humidity"]:
self.sensors[devId]["humidity"] = humidity
sensorCallback(protocol="", model="humidity", devId=devId, dataType=0, value=humidity,
timestamp=lastupdate, callbackId=None)
......
cmake_minimum_required (VERSION 3.0)
configure_file (
"../python/agoclient/_directories.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/agoclient/_directories.py"
@ONLY
)
set(PYTHON_AGOCLIENT_SOURCE_FILES
agoclient/agoapp.py
agoclient/agoconnection.py
agoclient/agoproto.py
agoclient/agotransport.py
agoclient/agotransport_qpid.py
agoclient/agotransport_mqtt.py
agoclient/config.py
agoclient/__init__.py
agoclient/_logging.py
)
CopyFilesFromSource(agoclient-python "${PYTHON_AGOCLIENT_SOURCE_FILES}")
InstallFiles(${PYTHON2DIST}/agoclient "${PYTHON_AGOCLIENT_SOURCE_FILES};agoclient/_directories.py")
......@@ -9,9 +9,9 @@ import syslog
import sys
# Re-export each export module
from agoapp import *
from config import *
from agoconnection import *
from .agoapp import *
from .config import *
from .agoconnection import *
syslog.openlog(sys.argv[0], syslog.LOG_PID, syslog.LOG_DAEMON)
......@@ -2,21 +2,17 @@ import logging
# We want to keep C++ and Python ago-coding as similar as possible.
# In C++ we have levels TRACE and CRITICAL.
#
# Add extra Trace level
TRACE = logging.TRACE = 5
logging._levelNames[TRACE] = TRACE
logging._levelNames['TRACE'] = TRACE
# C++ impl has FATAL, in python we have CRITICAL
# Add this alias so we can use the same logging consts
FATAL = logging.FATAL = logging.CRITICAL
logging._levelNames[FATAL] = FATAL
logging._levelNames['FATAL'] = FATAL
LOGGING_LOGGER_CLASS = logging.getLoggerClass()
class AgoLoggingClass(LOGGING_LOGGER_CLASS):
def trace(self, msg, *args, **kwargs):
if self.isEnabledFor(TRACE):
......@@ -27,10 +23,11 @@ class AgoLoggingClass(LOGGING_LOGGER_CLASS):
self._log(FATAL, msg, args, **kwargs)
if logging.getLoggerClass() is not AgoLoggingClass:
logging.setLoggerClass(AgoLoggingClass)
logging.addLevelName(TRACE, 'TRACE')
logging.addLevelName(FATAL, 'FATAL')
def init():
if logging.getLoggerClass() is not AgoLoggingClass:
logging.setLoggerClass(AgoLoggingClass)
logging.addLevelName(TRACE, 'TRACE')
logging.addLevelName(FATAL, 'FATAL')
# TODO: Syslog priority mapping
# TODO: Syslog priority mapping
"""Response codes"""
RESPONSE_SUCCESS = 'success'
RESPONSE_ERR_FAILED = 'failed'
RESPONSE_ERR_UNKNOWN_COMMAND = 'unknown.command'
RESPONSE_ERR_BAD_PARAMETERS = 'bad.parameters'
RESPONSE_ERR_NO_COMMANDS_FOR_DEVICE = 'no.commands.for.device'
RESPONSE_ERR_MISSING_PARAMETERS = 'missing.parameters'
class AgoResponse:
"""This class represents a response as obtained from AgoConnection.send_request"""
def __init__(self, response=None, identifier=None, message=None):
"""Create a response holder, either from a received response or implicitly via
an identifier & optional message.
"""
if not response:
response = response_error(identifier, message)
else:
assert not identifier
assert not message
self.response = response
if self.is_ok():
self.root = self.response["result"]
elif self.is_error():
self.root = self.response["error"]
else:
raise Exception("Invalid response, neither result or error present")
def __str__(self):
if self.is_error():
return 'AgoResponse[ERROR] message="%s" data=[%s]' % (self.message(), str(self.data()))
else:
return 'AgoResponse[OK] message="%s" data=[%s]' % (self.message(), str(self.data()))
def is_error(self):
return "error" in self.response
def is_ok(self):
return "result" in self.response
def identifier(self):
return self.root["identifier"]
def message(self):
if "message" in self.root:
return self.root["message"]
return None
def data(self):
if "data" in self.root:
return self.root["data"]
return None
class ResponseError(Exception):
def __init__(self, response):
if not response.is_error():
raise Exception("Not an error response")
self.response = response
super(ResponseError, self).__init__(self.message())
def identifier(self):
return self.response.identifier()
def message(self):
return self.response.message()
def data(self):
return self.response.data()
# Helpers to create proper responses
def response_result(iden, mess=None, data=None):
"""
Construct a response message.
:param iden: response 'identifier'
:param mess: response 'message' <optional>
:param data: response 'data' <optional>
"""
response = {}
result = {}
if iden is not None:
result['identifier'] = iden
else:
raise Exception('Response without identifier (param "iden") not permitted')
if mess is not None:
result['message'] = mess
if data is not None:
result['data'] = data
response['result'] = result
return response
def response_success(data=None, message=None):
"""
Construct a response message with the very generic 'success' identifier.
First parameter is data because data is more often returned than message when response is successful
"""
return response_result(iden=RESPONSE_SUCCESS, mess=message, data=data)
def response_error(iden, mess, data=None):
"""
Construct an response message indicating an error.
parameters are voluntary shortened!
:param iden: response identifier <mandatory>
:param mess: response message <optional>
:param data: response data <optional>
"""
response = {}
error = {}
# identifier
if iden is not None:
error['identifier'] = iden
else:
# iden is mandatory
raise Exception('Response without identifier (param "iden") not permitted')
# message
if mess is not None:
error['message'] = mess
else:
# mess is mandatory
raise Exception('Error response without message (param "mess") not permitted')
# data
if data is not None:
error['data'] = data
response['error'] = error
return response
def response_unknown_command(message="Unhandled command", data=None):
"""
Create a response which indicates that an unknown command was sent.
:param message: A human readable message giving a hint of what failed.
:param data: Any optional machine readable data.
:return:
"""
return response_error(iden=RESPONSE_ERR_UNKNOWN_COMMAND, mess=message, data=data)
def response_missing_parameters(message="Missing parameter", data=None):
"""
Create a response which indicates missing parameter was passed to the command.
:param message: A human readable message giving a hint of what failed.
:param data: Any optional machine readable data.
:return:
"""
return response_error(iden=RESPONSE_ERR_MISSING_PARAMETERS, mess=message, data=data)
def response_bad_parameters(message="Bad parameter", data=None):
"""
Create a response which indicates bad parameter was passed to the command.
:param message: A human readable message giving a hint of what failed.
:param data: Any optional machine readable data.
:return:
"""
return response_error(iden=RESPONSE_ERR_BAD_PARAMETERS, mess=message, data=data)
def response_failed(message, data=None):
"""
Create a response which indicate a general failure (RESPONSE_ERR_FAILED)
:param message: A human readable message giving a hint of what failed.
:param data: Any optional machine readable data.
:return:
"""
return response_error(iden=RESPONSE_ERR_FAILED, mess=message, data=data)
from abc import abstractmethod
class AgoTransport:
@abstractmethod
def start(self):
raise NotImplementedError()
@abstractmethod
def prepare_shutdown(self):
raise NotImplementedError()
@abstractmethod
def shutdown(self):
raise NotImplementedError()
@abstractmethod
def is_active(self):
raise NotImplementedError()
@abstractmethod
def send_message(self, message):
raise NotImplementedError()
@abstractmethod
def send_request(self, message, timeout):
raise NotImplementedError()
@abstractmethod
def fetch_message(self, timeout):
"""
Wait for an incoming messages from the message bus.
If the returned object has a reply_function defined, the sender expects the receiver to send
a reply to the message using this method.
:param timeout: How long to wait, in seconds.
:return: An object which holds the message body and an optional reply_function
:rtype: AgoTransportMessage
"""
raise NotImplementedError()
class AgoTransportMessage:
def __init__(self, message, reply_function):
self.message = message
self.reply_function = reply_function
@property
def content(self):
return self.message['content']
import functools
import json
import logging
import re
import sys
import threading
import uuid
import paho.mqtt.client as mqtt
import agoclient.agotransport
from agoclient import agoproto
TOPIC_BASE = 'com.agocontrol'
PUBLISH_TOPIC = TOPIC_BASE + '/legacy'
class AgoMqttTransport(agoclient.agotransport.AgoTransport):
def __init__(self, client_id, broker, username, password):
self.log = logging.getLogger('transport')
self.client_id = client_id
self.broker = broker
self.username = username
self.password = password
self.connection_uuid = str(uuid.uuid4())
self.reply_topic_base = TOPIC_BASE + "/" + self.connection_uuid + "/"
self.reply_seq = 1
self.mqtt = None # type: mqtt.Client
self._shutting_down = False
self.lock = threading.Lock()
self.ready_event = threading.Event()
self.queue = []
self.queue_condition = threading.Condition(self.lock)
self.pending_replies = {}
def start(self):
self.mqtt = mqtt.Client(self.broker, clean_session=True)
if self.username and self.password:
self.mqtt.username_pw_set(self.username, self.password)
self.mqtt.on_connect = self._on_connect
self.mqtt.on_subscribe = self._on_subscribe
self.mqtt.on_message = self._on_message
self.mqtt.enable_logger(logging.getLogger('mqtt')) # Ues custom logger for lowlevel mqtt
host = self.broker
port = 1883
m = re.match('^([^:]+)(?::?(\d+))?$', self.broker)
if m:
host = m.group(1)
if m.group(2):
port = int(m.group(2))
try:
self.log.info("Connecting to MQTT broker %s:%d", host, port)
self.mqtt.connect_async(self.broker, port)
except ValueError as e:
self.log.error("Invalid MQTT broker configuration (%s:%d): %s", host, port, e)
return False
self.mqtt.loop_start()
# Block until connected
while not self._shutting_down:
try:
self.log.trace("Waiting for MQTT connection readiness")