1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
# -*- encoding: utf-8 -*-
from __future__ import print_function, unicode_literals, division, absolute_import
import logging
import datetime
import threading
try:
import queue
except ImportError:
import Queue as queue
from enocean.protocol.packet import Packet, UTETeachInPacket
from enocean.protocol.constants import PACKET, PARSE_RESULT, RETURN_CODE
class Communicator(threading.Thread):
'''
Communicator base-class for EnOcean.
Not to be used directly, only serves as base class for SerialCommunicator etc.
'''
logger = logging.getLogger('enocean.communicators.Communicator')
def __init__(self, callback=None, teach_in=True):
super(Communicator, self).__init__()
# Create an event to stop the thread
self._stop_flag = threading.Event()
# Input buffer
self._buffer = []
# Setup packet queues
self.transmit = queue.Queue()
self.receive = queue.Queue()
# Set the callback method
self.__callback = callback
# Internal variable for the Base ID of the module.
self._base_id = None
# Should new messages be learned automatically? Defaults to True.
# TODO: Not sure if we should use CO_WR_LEARNMODE??
self.teach_in = teach_in
def _get_from_send_queue(self):
''' Get message from send queue, if one exists '''
try:
packet = self.transmit.get(block=False)
self.logger.info('Sending packet')
self.logger.debug(packet)
return packet
except queue.Empty:
pass
return None
def send(self, packet):
if not isinstance(packet, Packet):
self.logger.error('Object to send must be an instance of Packet')
return False
self.transmit.put(packet)
return True
def stop(self):
self._stop_flag.set()
def parse(self):
''' Parses messages and puts them to receive queue '''
# Loop while we get new messages
while True:
status, self._buffer, packet = Packet.parse_msg(self._buffer)
# If message is incomplete -> break the loop
if status == PARSE_RESULT.INCOMPLETE:
return status
# If message is OK, add it to receive queue or send to the callback method
if status == PARSE_RESULT.OK and packet:
packet.received = datetime.datetime.now()
if isinstance(packet, UTETeachInPacket) and self.teach_in:
response_packet = packet.create_response_packet(self.base_id)
self.logger.info('Sending response to UTE teach-in.')
self.send(response_packet)
if self.__callback is None:
self.receive.put(packet)
else:
self.__callback(packet)
self.logger.debug(packet)
@property
def base_id(self):
''' Fetches Base ID from the transmitter, if required. Otherwise returns the currently set Base ID. '''
# If base id is already set, return it.
if self._base_id is not None:
return self._base_id
# Send COMMON_COMMAND 0x08, CO_RD_IDBASE request to the module
self.send(Packet(PACKET.COMMON_COMMAND, data=[0x08]))
# Loop over 10 times, to make sure we catch the response.
# Thanks to timeout, shouldn't take more than a second.
# Unfortunately, all other messages received during this time are ignored.
for i in range(0, 10):
try:
packet = self.receive.get(block=True, timeout=0.1)
# We're only interested in responses to the request in question.
if packet.packet_type == PACKET.RESPONSE and packet.response == RETURN_CODE.OK and len(packet.response_data) == 4: # noqa: E501
# Base ID is set in the response data.
self._base_id = packet.response_data
# Put packet back to the Queue, so the user can also react to it if required...
self.receive.put(packet)
break
# Put other packets back to the Queue.
self.receive.put(packet)
except queue.Empty:
continue
# Return the current Base ID (might be None).
return self._base_id
@base_id.setter
def base_id(self, base_id):
''' Sets the Base ID manually, only for testing purposes. '''
self._base_id = base_id
|