Perso je pense que ca sert plus à rien de faire ca, on s’en fiche de pas être compatible avec python2 donc au final ca rend le code moins lisible: ce bout de code est au début et lorsqu’on lit la fonction on voit pas forcément que le code au début à un impact => compliqué à suivre.
donc au débaut j’utilisais ord(i) mais mnt je n’utilise plus la fonction stripped, je fais ceci
Perso je n’ai pas changé le jeedom.py et ça fonctionne. La fonction que j’utilise:
def read_socket(cycle):
while True:
try:
global JEEDOM_SOCKET_MESSAGE
if not JEEDOM_SOCKET_MESSAGE.empty():
logging.debug("SOCKET-READ------ Message received in socket JEEDOM_SOCKET_MESSAGE")
message = json.loads(JEEDOM_SOCKET_MESSAGE.get())
logging.debug("SOCKET-READ------ Message received in socket JEEDOM_SOCKET_MESSAGE " + message['cmd'])
if message['apikey'] != globals.apikey:
logging.error("SOCKET-READ------ Invalid apikey from socket : " + str(message))
return
logging.debug('SOCKET-READ------ Received command from jeedom : ' + str(message['cmd']))
if message['cmd'] == 'action':
logging.debug('SOCKET-READ------ Attempt an action on a device')
_thread.start_new_thread(action_handler, (message,))
logging.debug('SOCKET-READ------ Action Thread Launched')
elif message['cmd'] == 'changelog':
log = logging.getLogger()
for hdlr in log.handlers[:]:
log.removeHandler(hdlr)
jeedom_utils.set_log_level('info')
logging.info('SOCKET-READ------ Passage des log du demon en mode ' + message['level'])
for hdlr in log.handlers[:]:
log.removeHandler(hdlr)
jeedom_utils.set_log_level(message['level'])
except Exception as e:
logging.error("SOCKET-READ------ Exception on socket : %s" % str(e))
logging.debug(traceback.format_exc())
time.sleep(cycle)
Comme @Mips si je ne me trompe mais sans le decode utf-8
OK merci @Noyax37 et @Mips je teste ça dès que possible pour avancer sur la suite. Mais forcement sans communication Jeedom → Daemon je risque pas de pouvoir aller plus loin
Bon alors, c’est bon avec message = json.loads(JEEDOM_SOCKET_MESSAGE.get().decode('utf-8')), les messages passent.
@tomdom j’ai donc essayé de mettre en place ta technique mais j’ai un soucis.
Je passe le message depuis Jeedom et il est récupéré dans read_socket() :
def read_socket():
global JEEDOM_SOCKET_MESSAGE
global my_event
if not JEEDOM_SOCKET_MESSAGE.empty():
#message = json.loads(jeedom_utils.stripped(JEEDOM_SOCKET_MESSAGE.get()))
message = json.loads(JEEDOM_SOCKET_MESSAGE.get().decode('utf-8'))
if message['apikey'] != _apikey:
logging.error("Invalid apikey from socket : " + str(message))
return
# Check for there is a cmd in message
if 'cmd' not in message or not isinstance(message['cmd'], str) or message['cmd'] == '':
logging.error('Bad cmd parameter in message dump=%s', json.dumps(message))
return
try:
#logging.info("test cmd")
if message['cmd'] == 'start':
my_event.set() # pour débloquer run()
logging.info("Message to start")
#main()
if message['cmd'] == 'stop':
my_event.clear() # pour le remettre en attente
logging.info("Message to stop")
#main()
except Exception as e:
logging.error('Send command to demon error : '+str(e))
La chaîne start au travers de la fonction que Mips a présenté dans son tuto
Avant que je rajoute le my_event.set() ça affichait bien le message donc ça passe bien ici : if message['cmd'] == 'start':
J’ai lu la doc du event et la façon dont ils utilisent main et waiter mais je n’arrive pas à voir comment adapter ça avec l’ensemble des fonctions notamment liste qui n’est pas async.
sauf si je dis une connerie (fort possible!) … ton « my_event » n’est pas déclarer au « niveau parent » pour être considéré comme global !?
la 1ere fois qu’on le voit il est dans main
Tu veux pouvoir envoyer un/des ordres au demon via Jeedom (pendant que le demon tourne)
Tu veux aussi pouvoir retourner des valeurs à Jeedom depuis ton démon.
C’est ca?
Normalement aucune nécessité de modifier jeedom.py.
C’est un script type fournit par Jeedom pour les échanges de données demon<>jeedom (et jeedom<>demon).
Tien je viens de faire ca rapidement en esperant que ca t’aident à comprendre le processus d’echange de données entre Jeedom et le demon (et vice versa).
C’est juste pour te servir d’exemple, il faut que tu l’adapte a tes besoin
C’est la fonction listen qui tourne en boucle et dans read_socket ca permet de recevoir les cmd que tu envoi de jeedom
import logging
import sys
import os
import json
try:
from jeedom.jeedom import *
except ImportError as error:
print(error.__class__.__name__ + ": " + str(error))
print("Error: importing module jeedom.jeedom")
sys.exit(1)
# DECLARE CE QUE TU AS BESOIN ICI
# ...#
# ...#
def read_socket(monsocket):
global JEEDOM_SOCKET_MESSAGE
global ret
#ICI ON RECUPERE LE MESSAGE VENANT DE JEEDOM
if not JEEDOM_SOCKET_MESSAGE.empty():
logging.debug("Message received in socket JEEDOM_SOCKET_MESSAGE")
message = json.loads(JEEDOM_SOCKET_MESSAGE.get())
if message['apikey'] != _apikey:
logging.error("Invalid apikey from socket : " + str(message))
return
logging.debug(message['cmd'])
#ICI ON TEST LA VALEUR RECU (cmd ou event pour toi je crois)
try:
if message['cmd'] == 'startRead':
logging.debug("MESSAGE RECU DE JEEDOM: startRead ")
parameter = True
elif message['cmd'] == 'stopRead':
logging.debug("MESSAGE RECU DE JEEDOM: stopRead ")
parameter = False
if parameter :
#FAIS ICI TON ACTION QUE TU VEUX ET RENOIE LES DONNEES A JEEDOM
#ret = .....
jeedom_com.send_change_immediate(ret)
logging.debug(json.dumps(ret))
except Exception as e:
logging.error('Send command to demon error : ' + str(e))
def listen():
jeedom_socket.open()
global JEEDOM_SOCKET_MESSAGE
global parameter
parameter = False
mysocket = "ton socket de connexion http par exemple"
try:
while 1:
time.sleep(0.5)
if mysocket != None:
read_socket(mysocket, parameter)
except KeyboardInterrupt:
shutdown()
# ----------------------------------------------------------------------------
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:
pass
try:
jeedom_socket.close()
except:
pass
try:
jeedom_serial.close()
except:
pass
logging.debug("Exit 0")
sys.stdout.flush()
os._exit(0)
# ----------------------------------------------------------------------------
_log_level = "error"
_socket_port = 55079
_socket_host = 'localhost'
_internal_addr = ''
_device = 'auto'
_pidfile = '/tmp/myplugind.pid'
_apikey = ''
_callback = ''
for arg in sys.argv:
if arg.startswith("--loglevel="):
temp, _log_level = arg.split("=")
elif arg.startswith("--socketport="):
temp, _socket_port = arg.split("=")
elif arg.startswith("--internaladdr="):
temp, _internal_addr = arg.split("=")
elif arg.startswith("--sockethost="):
temp, _socket_host = arg.split("=")
elif arg.startswith("--pidfile="):
temp, _pidfile = arg.split("=")
elif arg.startswith("--apikey="):
temp, _apikey = arg.split("=")
elif arg.startswith("--device="):
temp, _device = arg.split("=")
elif arg.startswith("--callback="):
temp, _callback = arg.split("=")
#_socket_port = int(_socket_port)
jeedom_utils.set_log_level(_log_level)
logging.info('Start demond')
logging.info('Log level : '+str(_log_level))
logging.info('Socket port : ' + str(_socket_port))
logging.info('IP Jeedom : ' + str(_internal_addr))
logging.info('Socket host : '+str(_socket_host))
logging.info('PID file : '+str(_pidfile))
logging.info('Apikey : '+str(_apikey))
logging.info('Device : '+str(_device))
signal.signal(signal.SIGINT, handler)
signal.signal(signal.SIGTERM, handler)
try:
jeedom_utils.write_pid(str(_pidfile))
jeedom_socket = jeedom_socket(port=int(_socket_port), address=_socket_host)
jeedom_com = jeedom_com(apikey=_apikey, url=_callback)
listen()
except Exception as e:
logging.error('Fatal error : '+str(e))
shutdown()
et coté jeedom dans ta class cmd :
public static function startRead(){
$value = json_encode(array('apikey' => jeedom::getApiKey('myplugin'), 'cmd' => 'startRead' ));
self::socketConnection($value);
}
Cela ne me dérange pas que ce soit pratiquement le code du plugin template puisque je cherche à partir de là et ajouter quelques minis bouts de ce qu’il manque pour arriver au résultat. Mais là je ne vois pas bien ce qui permet d’y arriver avec ces modifications.
@c.miorin, merci beaucoup pour ton code, mais à première vue je ne vois pas ce qui permet d’arriver à ce que je voudrais faire. C’est peut–être parce que tu n’as pas compris le but (ou c’est moi qui ne comprends pas le code proposé, ce qui est possible aussi ).
En résumé :
Communication Socket → Jeedom : c’est OK depuis le début du plugin, pas de soucis
Communication Jeedom → Socket pendant qu’il tourne : c’est OK depuis la modification d’une ligne et l’utilisation de message = json.loads(JEEDOM_SOCKET_MESSAGE.get().decode('utf-8')) dans la fonction que Mips utilise
Maintenant ce qui m’interesse cest de pouvoir faire passer un ordre de stop ou start au socket pour qu’il mettent en pause son traitement habituel.
C’est à dire ici :
async def run(my_event):
while True:
my_event.wait()
try:
# Là il y a le traitement avec une boucle et l'ouverture d'un websocket vers l'extérieur
# et c'est cette partie que je souhaite interrompre puis pouvoir relancer
# alors même que le daemon continue de vivre évidemment
On m’a proposé l’utilisation de Event() mais pour l’heure j’arrive pas à mettre en place
8186|[2023-08-26 08:00:10]ERROR : Fatal error : This event loop is already running
8187|[2023-08-26 08:00:10]INFO : Traceback (most recent call last):
8188|File "/var/www/html/plugins/blitzortung/resources/blitzortungd/blitzortungd.py", line 253, in <module>
8189|asyncio.run(main())
8190|File "/usr/lib/python3.7/asyncio/runners.py", line 43, in run
8191|return loop.run_until_complete(main)
8192|File "/usr/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
8193|return future.result()
8194|File "/var/www/html/plugins/blitzortung/resources/blitzortungd/blitzortungd.py", line 188, in main
8195|asyncio.get_event_loop().run_until_complete(run(my_event))
8196|File "/usr/lib/python3.7/asyncio/base_events.py", line 571, in run_until_complete
8197|self.run_forever()
8198|File "/usr/lib/python3.7/asyncio/base_events.py", line 526, in run_forever
8199|raise RuntimeError('This event loop is already running')
8200|RuntimeError: This event loop is already running
8201|[2023-08-26 08:00:10]DEBUG : Shutdown
Faudrait que j’arrive à coller à l’exemple qu’ils donnent dans la doc mais pour le moment je trouve pas comment faire cette partie en plus du Listen() qui écoute ce qui arrive de Jeedom.
async def waiter(event):
print('waiting for it ...')
await event.wait()
print('... got it!')
async def main():
# Create an Event object.
event = asyncio.Event()
# Spawn a Task to wait until 'event' is set.
waiter_task = asyncio.create_task(waiter(event))
# Sleep for 1 second and set the event.
await asyncio.sleep(1)
event.set()
# Wait until the waiter task is finished.
await waiter_task
asyncio.run(main())
Doit y avoir un truc qui m’échappe dans ce que tu veux faire parceque là tu as listen en boucle et tu analyses ce qui est envoyé par jeedom, si c’est une commande de start alors tu lances ton prog principal, si c’est une commande de stop alors tu arrêtes et tu retournes à l’état d’avant et si tu n’as pas de commande bah tu fais rien… Ce n’est pas ça ce que tu veux faire?
Ca ne marchera jamais car les 2 sont bloquant: listen() est une boucle qui ne s’arrête que lors du shutdown et ta boucle main() c’est pareil certainement
pour l’event, je cherche si je peux te partager un autre exemple
Si ça ressemble bien à ça. Mais une fois que j’ai lancé ce qui avant était un def run(): et que ça tourne en boule là dedans, comment l’arrêter (sans que le daemon s’arrette aussi) ?