# This file is part of Jeedom. # # Jeedom 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 3 of the License, or # (at your option) any later version. # # Jeedom 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 Jeedom. If not, see . import logging import string import sys import os import time import threading import json import re import signal import globals import argparse from optparse import OptionParser from shutil import which from os.path import join import subprocess from pathlib import Path from websocket import create_connection try: from jeedom.jeedom import * except ImportError as e: print("Error: importing module from jeedom folder " + str(e)) sys.exit(1) # ---------------------------------------------------------------------------- def listen(): logging.debug("Start listening...") jeedom_socket.open() checkLocalDeconZ() try: socket = threading.Thread(target=read_socket, args=('socket',)) logging.debug("Start socket thread") socket.start() except KeyboardInterrupt as e: shutdown() def read_socket(name): while 1: try: global JEEDOM_SOCKET_MESSAGE if not JEEDOM_SOCKET_MESSAGE.empty(): logging.debug("Message received in socket JEEDOM_SOCKET_MESSAGE") message = JEEDOM_SOCKET_MESSAGE.get().decode('utf-8') message =json.loads(message) if message['apikey'] != _apikey: logging.error("Invalid apikey from socket : " + str(message)) return if message['cmd'] == 'addgateway': closeAllWs() time.sleep(1) for gateway in message['gateways']: logging.debug('Add gateway : '+str(gateway['name'])) globals.KNOWN_GATEWAYS[gateway['internalipaddress']] = message startWsMonitor(gateway) elif message['cmd'] == 'send': logging.debug('Send command') except Exception as e: logging.error(str(e)) time.sleep(0.05) def checkLocalDeconZ(): if which('deCONZ'): logging.info("Local DeconZ found") handleLaunchDeconz() else: logging.info("No local DeconZ found") def handleLaunchDeconz(): logging.info("Launching local DeconZ") logging.info("Kill all deCONZ process") os.system("ps -A | grep deCONZ | awk '{print $1}' | xargs kill -9 $1 >> /dev/null") log = open(str(Path(Path(Path(__file__).resolve().parents[4],'log'),'deconzServer')), 'a') try: opts = ["sudo","deCONZ","--auto-connect=1","--dbg-error=1", "--http-port=8484", "--ws-port=20877", "--upnp=0", "-platform", "minimal"] logging.info("Log level deconz deamon "+str(globals.LOG_LEVEL)) if globals.LOG_LEVEL == 'debug': opts.append("--dbg-aps=2") opts.append("--dbg-info=2") opts.append("--dbg-zcl=2") opts.append("--dbg-zdp=2") opts.append("--dbg-http=2") elif globals.LOG_LEVEL == 'info': opts.append("--dbg-info=1") opts.append("--dbg-aps=0") opts.append("--dbg-zcl=0") opts.append("--dbg-zdp=0") opts.append("--dbg-http=0") else : opts.append("--dbg-info=0") opts.append("--dbg-aps=0") opts.append("--dbg-zcl=0") opts.append("--dbg-zdp=0") opts.append("--dbg-http=0") log = open(os.devnull, 'w') logging.info("Opt deconz deamon "+str(opts)) globals.PROC = subprocess.Popen(opts, stdout=log,stderr=log,shell=False) logging.info("Launched DeconZ with pid " + str(globals.PROC.pid)) except Exception as e: logging.warning("Cant Launch Local Deconz " +str(e)) pass def startWsMonitor(gatewayInfos): for thread in threading.enumerate(): if thread.name == 'deconz_'+str(gatewayInfos['internalipaddress']): logging.debug("Closing WebSocket for " + str(gatewayInfos['name']) + " on ip " + str(gatewayInfos['internalipaddress']) + " and port " + str(gatewayInfos['websocketport'])) globals.THREADS_FLAG[str(gatewayInfos['internalipaddress'])] = 0 time.sleep(0.2) globals.WS[str(gatewayInfos['internalipaddress'])].close() break globals.THREADS_FLAG[str(gatewayInfos['internalipaddress'])] = 1 threading.Thread(target=wshandling,args=(gatewayInfos,), name='deconz_'+str(gatewayInfos['internalipaddress']), daemon=True).start() def wshandling(gatewayInfos): logging.debug("Starting WebSocket for " + str(gatewayInfos['name']) + " on ip " + str(gatewayInfos['internalipaddress']) + " and port " + str(gatewayInfos['websocketport'])) connected = False while not connected: try: globals.WS[str(gatewayInfos['internalipaddress'])] = create_connection("ws://" + str(gatewayInfos['internalipaddress']) + ":" + str(gatewayInfos['websocketport'])) connected = True except Exception as e: logging.debug("Error while connecting to Thread for " + str(gatewayInfos['internalipaddress'])+ ' because '+str(e)) logging.debug("Try to restart WebSocket in 1s for"+str(gatewayInfos['internalipaddress'])) time.sleep(1) while globals.THREADS_FLAG[str(gatewayInfos['internalipaddress'])] == 1: try: result = globals.WS[str(gatewayInfos['internalipaddress'])].recv() if result : logging.debug("Received message from gateway " + str(gatewayInfos['internalipaddress']) + " : " + str(result)) globals.JEEDOM_COM.add_changes(str(gatewayInfos['id']) ,json.loads(result)); time.sleep(0.02) except Exception as e: logging.debug("Closing Thread for " + str(gatewayInfos['internalipaddress'])+ ' because '+str(e)) globals.THREADS_FLAG[str(gatewayInfos['internalipaddress'])] = 0 globals.WS[str(gatewayInfos['internalipaddress'])].close() if str(gatewayInfos['internalipaddress']) == '127.0.0.1' : logging.debug("Shutting down because it's local gateway") shutdown() else: logging.debug("Try to restart WebSocket in 1s for"+str(gatewayInfos['internalipaddress'])) time.sleep(1) globals.THREADS_FLAG[str(gatewayInfos['internalipaddress'])] = 1 wshandling(gatewayInfos) def closeAllWs(): logging.debug("Closing All Websockets") for thread in threading.enumerate(): if 'deconz_' in thread.name: details = thread.name.split('_') globals.THREADS_FLAG[details[1]] = 0 time.sleep(0.2) logging.debug("Closing WebSocket for " + thread.name) globals.WS[details[1]].close() # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- def handler(signum=None, frame=None): logging.debug("Signal %i caught, exiting..." % int(signum)) shutdown() def shutdown(): logging.debug("Shutdown") logging.debug("Removing PID file " + str(_pidfile)) try: os.remove(_pidfile) except Exception as e: logging.debug(str(e)) try: jeedom_socket.close() except Exception as e: logging.debug(str(e)) pass try: os.system("ps -C deCONZ -o pid=|xargs sudo kill -9") except Exception as e: logging.debug(str(e)) pass logging.debug("Exit 0") sys.stdout.flush() os._exit(0) # ---------------------------------------------------------------------------- _log_level = "debug" _socket_port = 55088 _socket_host = 'localhost' _pidfile = '/tmp/deconzd.pid' _apikey = '' _callback = '' _cycle = 0 _prevMessage = '' _auto_connect=1 parser = argparse.ArgumentParser(description='Deconz Daemon for Jeedom plugin') parser.add_argument("--device", help="Device", type=str) parser.add_argument("--socketport", help="Socketport for server", type=str) parser.add_argument("--loglevel", help="Log Level for the daemon", type=str) parser.add_argument("--callback", help="Callback", type=str) parser.add_argument("--apikey", help="Apikey", type=str) parser.add_argument("--cycle", help="Cycle to send event", type=str) parser.add_argument("--pid", help="Pid file", type=str) args = parser.parse_args() if args.device: _device = args.device if args.socketport: _socket_port = int(args.socketport) if args.loglevel: _log_level = args.loglevel if args.callback: _callback = args.callback if args.apikey: _apikey = args.apikey if args.pid: _pidfile = args.pid if args.cycle: _cycle = float(args.cycle) jeedom_utils.set_log_level(_log_level) globals.LOG_LEVEL = _log_level logging.info('Start deconzd') logging.info('Log level : '+str(_log_level)) logging.info('Socket port : '+str(_socket_port)) logging.info('Socket host : '+str(_socket_host)) logging.info('PID file : '+str(_pidfile)) logging.info('Apikey : '+str(_apikey)) logging.info('Callback : '+str(_callback)) logging.info('Cycle : '+str(_cycle)) signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) try: jeedom_utils.write_pid(str(_pidfile)) globals.JEEDOM_COM = jeedom_com(apikey = _apikey,url = _callback,cycle=_cycle) if not globals.JEEDOM_COM.test(): logging.error('Network communication issues. Please fixe your Jeedom network configuration.') shutdown() jeedom_socket = jeedom_socket(port=_socket_port,address=_socket_host) listen() except Exception as e: logging.error('Fatal error : '+str(e)) shutdown()