Ankündigung

Einklappen
Keine Ankündigung bisher.

Plugin TEAC AG980

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    Plugin TEAC AG980

    Hallo zusammen,

    ich habe versucht aus mehreren Plugins (Comfoair, Russound, und andere) zu lernen und ein Plugin für meinen TEAC AG980 Audio Receiver zu erstellen.
    Das ist meist per copy und paste erfolgt, aber einiges habe ich auf selbst erstellt.

    Das RS232 Protokoll des Teac habe ich noch mal angehängt.

    Mein größtes Problem ist die Statusabfrage des Teac und die entsprechende Umsetzung dessen auf die Items. Im RS232 Protokol ist eigentlich die IR-Fernbedienung abgebildet, und nicht eine direkte Steuerung des Geräts.
    Eine Status wird nicht aktiv vom Gerät verschickt, sondern muss abgefragt werden.

    Wenn ich nun den Status Abfrage werden die Statusbytes ausgewertet und entsprechend die Items gesetzt. Jetzt hat sich das Item evtl. geändert (weil z.B. über die Fernbedienung etwas umgeschaltet wurde) und es wird ein update_item durchgeführt. Die Statusabfrage, die ja auch durch update_item getriggert ist, ist aber noch gar nicht fertig und somit wartet der eine Befehl auf den anderen. Wie kann ich sowas verhindern bzw. umgehen?

    Die Initalisierung des ersten Status habe ich hinbekommen, bin mir aber nicht sicher ob der Scheduler wirklich nach dem ersten Lauf beendet wird.
    Das habe ich vom Confoair Plugin, aber noch nicht genau erfasst wie es funktioniert.

    Die Lautstärke kann ich nur "hoch" und "runter" tasten. Im Status des Teac habe ich aber einen Nummerischen Wert der Lautstärke. Der wird aber im entsprechenden Item nicht aktualisiert, dafür müßte ich den Status wieder abfragen. Da bin ich aber wieder bei dem Problem, der Schleife siehe oben Statusabfrage. Wenn das gelöst ist könnte man über Logik nachdenken um z.B. direkte Werte anzuspringen und die durch "hoch" bzw. "runter" anzusteuern. Gehört diese Logic dann extern oder mit ins Plugin?

    ./etc/plugin.conf:
    Code:
    [AG980]
        class_name = ag980
        class_path = plugins.ag980
        host = 192.168.35.15
        port = 7777
        #serialport = /dev/ttyUSB0  # Enable this if you want to use a serial connection
    ./plugins/ag980/__init__.py

    Code:
    #!/usr/bin/env python3
    # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
    #########################################################################
    # Copyright 2013 Frank Plass
    #########################################################################
    #  This file is part of SmartHome.py.    http://mknx.github.io/smarthome/
    #
    #  SmartHome.py 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.
    #
    #  SmartHome.py 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 SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
    #########################################################################
    
    import logging
    import socket
    import time
    import serial
    #import re
    import threading
    
    logger = logging.getLogger('ag980')
    
    CONST_MAP_COMMANDS = [
            'status',
            'display',
            'speakeroff',
            'speakera',
            'speakerb',
            'speakerc',
            'speakerd',
            'main',
            'mainvolstat',
            'mainmute',
            'mainvolumeup',
            'mainvolumedown',
            'maininput',
            'room2',
            'r2volstat',
            'r2mute',
            'r2volumeup',
            'r2volumedown',
            'r2input'
            ]
    
    CONST_SEND_COMMANDS = {
            'on'        : 0x01,
            'off'       : 0x02,
            'maintuner'     : 0x03,
            'maincd'        : 0x04,
            'mainaux'       : 0x05,
            'mainphono'     : 0x06,
            'maindvd'       : 0x07,
            'maintape'      : 0x08,
            'mainvolumeup'  : 0x0f,
            'mainvolumedown'    : 0x10,
            'mainmute'  : 0x11,
            'tone'      : 0x12,
            'countup'   : 0x13,
            'countdown' : 0x14,
            'speakera'  : 0x1d,
            'speakerb'  : 0x1e,
            'speakerc'  : 0x1f,
            'speakerd'  : 0x20,
            'speakeroff'    : 0x21,
            'room2'     : 0x3f,
            'r2volumeup'   : 0x41,
            'r2volumedown' : 0x42,
            'r2mute'    : 0x43,
            'r2tuner'   : 0x44,
            'r2cd'      : 0x45,
            'r2aux'     : 0x46,
            'r2phone'   : 0x47,
            'r2dvd'     : 0x48,
            'r2tape'    : 0x49,
            'status'    : 0x53
            }
    
    
    class ag980():
    
        def __init__(self, smarthome, host=None, port=0, serialport=None, debug=False):
            self.connected = False
            self._sh = smarthome
            self._params = {}
            self._lock = threading.Lock()
            self._host = host
            self._port = int(port)
            self._serialport = serialport
            self._connection_attempts = 0
            self._connection_errorlog = 60
            smarthome.connections.monitor(self)
            self._packetstart = 0x8173  #System ID des AG980
            self._initread = False
    
        def connect(self):
            if self._serialport is not None:
                self.connect_serial()
            else:
                self.connect_tcp()
            
        def connect_tcp(self):
            self._lock.acquire()
            try:
                self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                self._sock.settimeout(2)
                self._sock.connect((self._host, self._port))
            except Exception as e:
                self._connection_attempts -= 1
                if self._connection_attempts <= 0:
                    self.log_err('could not connect to {}:{}: {}'.format(self._host, self._port, e))
                    self._connection_attempts = self._connection_errorlog
                self._lock.release()
                return
            else:
                self.connected = True
                self.log_info('connected to {}:{}'.format(self._host, self._port))
                self._connection_attempts = 0
                self._lock.release()
        
        def connect_serial(self):
            self._lock.acquire()
            try:
                self._serialconnection = serial.Serial(
                        self._serialport, 9600, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, timeout=2)
            except Exception as e:
                self._connection_attempts -= 1
                if self._connection_attempts <= 0:
                    logger.err('could not connect to {}: {}'.format(self._serialport, e))
                    self._connection_attempts = self._connection_errorlog
                self._lock.release()
                return
            else:
                self.connected = True
                self.log_info('connected to {}'.format(self._serialport))
                self._connection_attempts = 0
                self._lock.release()
    
        def disconnect(self):
            if self._serialport is not None:
                self.disconnect_serial()
            else:
                self.disconnect_tcp()
            
        def disconnect_tcp(self): 
            self.connected = False
            try:
                self._sock.shutdown(socket.SHUT_RDWR)
            except:
                pass
            try:
                self._sock.close()
            except:
                pass
    
        def disconnect_serial(self): 
            self.connected = False
            try:
                self._serialconnection.close()
                self._serialconnection = None
            except:
                pass
    
        def send_bytes(self, packet):
            if self._serialport is not None:
                self.send_bytes_serial(packet)
            else:
                self.send_bytes_tcp(packet)
            
        def send_bytes_tcp(self, packet):
            self._sock.sendall(packet)
    
        def send_bytes_serial(self, packet):
            self._serialconnection.write(packet)
            
        def read_bytes(self, length):
            if self._serialport is not None:
                return self.read_bytes_serial(length)
            else:
                return self.read_bytes_tcp(length)
            
        def read_bytes_tcp(self, length):
            return self._sock.recv(length)
    
        def read_bytes_serial(self, length):
            return self._serialconnection.read(length)
    
        def run(self):
            # if you want to create child threads, do not make them daemon = True!
            # They will not shutdown properly. (It's a python bug)
            self.alive = True
            self._sh.scheduler.add('ag980-init', self._getfirststatus, prio=5, cycle=600, offset=2)
            maxloops = 20
            loops = 0 
            while self.alive and not self._initread and loops < maxloops:  # wait for init read to finish
                time.sleep(0.5)
                loops += 1
            self._sh.scheduler.remove('ag980-init')
    
        def stop(self):
            self._sh.scheduler.remove('ag980-init')
            self.alive = False
            self.disconnect()
    
        def parse_item(self, item):
            if 'ag980_cmd' in item.conf:
                commandname = item.conf['ag980_cmd']
                if commandname in CONST_MAP_COMMANDS:
                    self.log_debug('Item {} writes by using command \'{}\''.format(item, commandname))
                    self._params[commandname] = {'cmd': item.id(), 'item': item}
    
                    #self.log_debug('Parameterliste: {} '.format(self._params))
                    
                    return self.update_item
                else:
                    self.log_err('Item {} contains invalid command \'{}\'!'.format(item, commandname))
                    return None
                
            else:
                return None
    
        def parse_logic(self, logic):
            pass
    
        def update_item(self, item, caller=None, source=None, dest=None):
            if caller != 'ag980':
                cmd = item.conf['ag980_cmd']
                value = item()
                self.log_debug('Update command: "{}" with value "{}"'.format(cmd,value))
    
                if self._initread:
                    if cmd == 'status':
                        self.send_command(cmd, value)
                    elif cmd == 'speakeroff':
                        self.send_command(cmd, value)
                    elif cmd == 'speakera':
                        self.send_command(cmd, value)
                    elif cmd == 'speakerb':
                        self.send_command(cmd, value)
                    elif cmd == 'speakerc':
                        self.send_command(cmd, value)
                    elif cmd == 'speakerd':
                        self.send_command(cmd, value)
                    # Main Zone
                    elif cmd == 'main':
                        self.send_command('on' if value else 'off', value)
                    elif cmd == 'mainmute':
                        self.send_command(cmd, value)
                    elif cmd == 'mainvolumeup':
                        self.send_command(cmd, value)
                    elif cmd == 'mainvolumedown':
                        self.send_command(cmd, value)
                    elif cmd == 'maininput':
                        # restrict possible Inputs
                        value == self._restrictinput(value)
                        if value == 2:
                            self.send_command('maintuner', value)
                        elif value == 4:
                            self.send_command('maincd', value)
                        elif value == 5:
                            self.send_command('mainaux', value)
                        elif value == 6:
                            self.send_command('mainphono', value)
                        elif value == 7:
                            self.send_command('maindvd', value)
                        elif value == 8:
                            self.send_command('maintape', value)
                    elif cmd == 'room2':
                        self.send_command('room2', value)
                    elif cmd == 'r2mute':
                        self.send_command(cmd, value)
                    elif cmd == 'r2volumeup':
                        self.send_command(cmd, value)
                    elif cmd == 'r2volumedown':
                        self.send_command(cmd, value)
                    elif cmd == 'r2input':
                        # restrict possible Inputs
                        value == self._restrictinput(value)
                        if value == 2:
                            self.send_command('r2tuner', value)
                        elif value == 4:
                            self.send_command('r2cd', value)
                        elif value == 5:
                            self.send_command('r2aux', value)
                        elif value == 6:
                            self.send_command('r2phone', value)
                        elif value == 7:
                            self.send_command('r2dvd', value)
                        elif value == 8:
                            self.send_command('r2tape', value)
            
        def send_command(self, commandname, value=None):
            try:
                self.log_debug('Got a new send job: Command "{}"'.format(commandname))
                
                # Get command hexcode
                commandcode = CONST_SEND_COMMANDS[commandname]
                packet = bytearray()
                packet.extend(self.int2bytes(self._packetstart, 2))
                packet.extend(self.int2bytes(commandcode, 1))
                #self.log_debug('packet: "{}"'.format(packet))
                checksum = self.calc_checksum(packet)
                packet.extend(self.int2bytes(checksum, 1))
    
                # Use a lock to allow only one sender at a time
                self._lock.acquire()
    
                if not self.connected:
                    raise Exception("No connection to ag980.")
                try:
                    self.log_info('Preparing command {} with hexcode \'{}\' to be sent.'.format(commandname, self.bytes2hexstring(packet)))
                    self.send_bytes(packet)
                    self.log_info('Successfully sent packet: {}'.format(self.bytes2hexstring(packet)))
                except Exception as e:
                    raise Exception('Exception while sending: {}'.format(e))
    
                #if send status request, get the response
                if commandname == 'status':
                    packet = bytearray()
    
                    firstpartlen = 3
                    while self.alive and len(packet) < firstpartlen:
                        try:
                            bytestoreceive = firstpartlen - len(packet)
                            self.log_debug('Trying to receive {} bytes for the first part of the response.'.format(bytestoreceive))
                            chunk = self.read_bytes(bytestoreceive)
                            self.log_debug('Received chunk of response: {}'.format(self.bytes2hexstring(chunk)))
                            packet.extend(chunk)
                        except socket.timeout:
                            raise Exception("error receiving first part of packet: timeout")
                        except Exception as e:
                            raise Exception("error receiving first part of packet: {}".format(e))
        
                    #packet length fix at 20 bytes
                    packetlen = 20
    
                    # Try to receive the second part of the packet
                    while self.alive and len(packet) < packetlen:
                        try:
                            #Receive next chunk
                            bytestoreceive = packetlen - len(packet)
                            self.log_debug('Trying to receive {} bytes for the second part of the response.'.format(bytestoreceive))
                            chunk = self.read_bytes(bytestoreceive)
                            self.log_debug('Received chunk of response: {}'.format(self.bytes2hexstring(chunk)))
                            packet.extend(chunk)
                        except socket.timeout:
                            raise Exception("error receiving second part of packet: timeout")
                        except Exception as e:
                            raise Exception("error receiving second part of packet: {}".format(e))
    
                    # Parse response
                    self.parse_response(packet)
    
            except Exception as e:
                self.disconnect()
                self.log_err("send_command failed: {}".format(e))
    
            finally:            
                # At the end, release the lock
                self._lock.release()
    
        def parse_response(self, response):
            resph = self.bytes2int(response)
            self.log_info('Successfully received response: {}'.format(self.bytes2hexstring(response)))
                    
            # Remove System ID and checksum to get the data
            rawdatabytes = response[2:-1]
            # Received Checksum
            receivedchecksum = response[-1]
            #self.log_debug('Corresponding read command: {}, decoded data: {} (raw: {})'.format(self.bytes2hexstring(commandcodebytes), self.bytes2hexstring(databytes), self.bytes2hexstring(rawdatabytes)))
    
            # Validate checksum
            checksum = self.calc_checksum(rawdatabytes)
            if (receivedchecksum != checksum):
                self.log_err('Calculated checksum of {} does not match received checksum of {}! Ignoring reponse.'.format(self.byte2hexstring(checksum), self.byte2hexstring(receivedchecksum)))
                return
    
            # self.log_debug('--')
            textstring = response[3:12]
            # self.log_debug('Successfully received Textstring: "{}"'.format(textstring.decode("utf-8")))
            self._params['display']['item'](textstring.decode("utf-8"))
    
            systemstatus = bin(response[13])[2:].zfill(8)
            # self.log_debug('Successfully received systemstatus: "{}"'.format(systemstatus))
            # self.log_debug('Successfully received System Power Status: "{}"'.format(systemstatus[7]))
            self._params['main']['item'](self._str2bool(systemstatus[7]))
    
            # self.log_debug('Successfully received System Request Error: "{}"'.format(systemstatus[6]))
    
            # self.log_debug('Successfully received R2 Status: "{}"'.format(systemstatus[5]))
            self._params['room2']['item'](self._str2bool(systemstatus[5]))
    
            # self.log_debug('Successfully received Main Mute: "{}"'.format(systemstatus[4]))
            self._params['mainmute']['item'](self._str2bool(systemstatus[4]))
    
            # self.log_debug('Successfully received R2 Mute: "{}"'.format(systemstatus[3]))
            self._params['r2mute']['item'](self._str2bool(systemstatus[3]))
    
            # self.log_debug('Successfully received Sleep Mode: "{}"'.format(systemstatus[2]))
            # self.log_debug('Successfully received Cinema EQ: "{}"'.format(systemstatus[1]))
            # self.log_debug('Successfully received Tone defeat: "{}"'.format(systemstatus[0]))
            
            # self.log_debug('--')
            maininput = response[14]
            self.log_debug('Successfully received maininput "{}"'.format(maininput))
            self._params['maininput']['item'](maininput)
            r2input = response[15]
            self._params['r2input']['item'](r2input)
            mainvolume = response[16]
            self._params['mainvolstat']['item'](mainvolume)
            r2volume = response[17]
            self._params['r2volstat']['item'](r2volume)
    
            speakerstatus = bin(response[18])[2:].zfill(8) 
            # self.log_debug('Successfully received r2input: "{}"'.format(self.byte2hexstring(r2input)))
            # self.log_debug('--')
            # self.log_debug('Successfully received mainvolume: "{}"'.format(self.byte2hexstring(mainvolume)))
            # self.log_debug('Successfully received r2volume: "{}"'.format(self.byte2hexstring(r2volume)))
            self.log_debug('--')
            self.log_debug('Successfully received Speakerstatusbyte: "{}"'.format(speakerstatus))
            #self.log_debug('Successfully received Speakerstatus A: "{}"'.format(self._str2bool(speakerstatus[7])))
            #self.log_debug('Test: "{}"'.format(self._params['speakera']['item']))
            self._params['speakera']['item'](self._str2bool(speakerstatus[7]))
            self.log_debug('Successfully received Speakerstatus B: "{}"'.format(self._str2bool(speakerstatus[6])))
            #self.log_debug('Test: "{}"'.format(self._params['speakerb']['item']))
            self._params['speakerb']['item'](self._str2bool(speakerstatus[6]))
            self._params['speakerc']['item'](self._str2bool(speakerstatus[5]))
            self._params['speakerd']['item'](self._str2bool(speakerstatus[4]))
        
        def _str2bool(self, var):
            if var == '1':
                return True
            else:
                return False
        
        def _getfirststatus(self):
            try:
                # Do the init read commands
                if self.connected:
                    self.log_info('Starting initial read commands.')
                    self.send_command('status', None )
            finally:
                self._initread = True
            
        def _restrictinput(self, val):
            if val == 0 or val == 1:
                return 2
            elif val == 3:
                return 2
            elif val > 8:
                return 8
            return val
                
        def calc_checksum(self, packetpart):
            return (sum(packetpart)) % 256
            
        def log_debug(self, text):
            logger.debug('AG980: {}'.format(text))    
    
        def log_info(self, text):    
            logger.info('AG980: {}'.format(text))    
    
        def log_err(self, text):    
            logger.error('AG980: {}'.format(text))    
    
        def int2bytes(self, value, length):
            # Limit value to the passed byte length
            value = value % (2 ** (length * 8))
            return value.to_bytes(length, byteorder='big')
    
        def bytes2int(self, bytesvalue):
            return int.from_bytes(bytesvalue, byteorder='big', signed=False)
        
        def bytes2hexstring(self, bytesvalue):
            return ":".join("{:02x}".format(c) for c in bytesvalue)
    
        def byte2hexstring(self, bytevalue):
            return "{:02x}".format(bytevalue)
    ./item/ag980.conf

    Code:
    ['teac']
        [['status']]
            name = Statusabfrage
            ag980_cmd = status
            type = bool
            knx_dpt = 1
            knx_send = 6/0/0
            knx_listen = 6/0/0
            enforce_updates = on
        [['display']]
            name = Text Display
            ag980_cmd = display
            type = str
            knx_dpt = 16.001
            knx_send = 6/0/1
            knx_reply = 6/0/1
        [['speaker']]
            name = Lautsprecher alle Aus
            ag980_cmd = speakeroff
            type = bool
            knx_dpt = 1
            knx_send = 6/0/10
            knx_reply = 6/0/10
            enforce_updates = on
            [[['speakerA']]]
                name = Lautsprecher A
                ag980_cmd = speakera
                type = bool
                knx_dpt = 1
                knx_send = 6/0/11
                knx_listen = 6/0/11
            [[['speakerB']]]
                name = Lautsprecher B
                ag980_cmd = speakerb
                type = bool
                knx_dpt = 1
                knx_send = 6/0/12
                knx_listen = 6/0/12
             [[['speakerC']]]
                name = Lautsprecher C
                ag980_cmd = speakerc
                type = bool
                knx_dpt = 1
                knx_send = 6/0/13
                knx_listen = 6/0/13
            [[['speakerD']]]
                name = Lautsprecher D
                ag980_cmd = speakerd
                type = bool
                knx_dpt = 1
                knx_send = 6/0/14
                knx_listen = 6/0/14
       [['main']]
            name = Haupzone
            [[['an']]]
                name = Main Ein- Ausschalten
                ag980_cmd = main
                type = bool
                knx_dpt = 1
                knx_send = 6/0/20
                knx_listen = 6/0/20
            [[['mainvolstatus']]]
                name = Main Volume Status
                ag980_cmd = mainvolstat
                type = num
                knx_dpt = 5
                knx_send = 6/0/21
                knx_reply = 6/0/21
            [[['mainmute']]]
                name = Main Stumm AN/AUS
                ag980_cmd = mainmute
                type = bool
                knx_dpt = 1
                knx_send = 6/0/22
                knx_listen = 6/0/22
            [[['volumeup']]]
                name = Main Volume up
                ag980_cmd = mainvolumeup
                type = bool
                knx_dpt = 1
                knx_send = 6/0/23
                knx_listen = 6/0/23
                enforce_updates = on
            [[['volumedown']]]
                name = Main Volume down
                ag980_cmd = mainvolumedown
                type = bool
                knx_dpt = 1
                knx_send = 6/0/24
                knx_listen = 6/0/24
                enforce_updates = on
            [[['quelle']]]
                name = Main Quelle
                ag980_cmd = maininput
                type = num
                knx_dpt = 5
                knx_send = 6/0/25
                knx_listen = 6/0/25
                enforce_updates = on
        [['room2']]
            name = Raum 2
            [[['an']]]
                name = Raum2 Ein- Ausschalten
                ag980_cmd = room2
                type = bool
                knx_dpt = 1
                knx_send = 6/0/30
                knx_listen = 6/0/30
            [[['r2volstatus']]]
                name = Raum2 Volume Status
                ag980_cmd = r2volstat
                type = num
                knx_dpt = 5
                knx_send = 6/0/31
                knx_listen = 6/0/31
            [[['r2mute']]]
                name = Raum2 Stumm AN/AUS
                ag980_cmd = r2mute
                type = bool
                knx_dpt = 1
                knx_send = 6/0/32
                knx_listen = 6/0/32
            [[['r2volup']]]
                name = Raum2 Volume up
                ag980_cmd = r2volumeup
                type = bool
                knx_dpt = 1
                knx_send = 6/0/33
                knx_listen = 6/0/33
                enforce_updates = on
            [[['r2voldown']]]
                name = Raum2 Volume down
                ag980_cmd = r2volumedown
                type = bool
                knx_dpt = 1
                knx_send = 6/0/34
                knx_listen = 6/0/34
                enforce_updates = on
            [[['quelle']]]
                name = Raum2 Quelle
                ag980_cmd = r2input
                type = num
                knx_dpt = 5
                knx_send = 6/0/35
                knx_listen = 6/0/35
    ./plugins/ag980/Readme.md

    als Readme.txt angehängt

    Das ist (m)ein erster Versuch, hoffe nicht kompletter Mist, den ich da zusammengebaut habe.
    Auf jeden Fall kann ich damit per USB-RS232 oder per Moxa (in meinem Fall ist es ein XPORT) den Teac steuern.
    Wenn Ihr jetzt noch etwas helfen könntet wirds vielleicht ganz gut nutzbar (hoffe ich).

    @mknx: Einen Git Account habe ich erstellt, jetzt muss ich noch ne Erklärung finden wie ich das hochlade...

    Danke an mknx für smarthome.py und die Ersteller der anderen Plugins als Vorlagenlieferanten!
    Angehängte Dateien
    Gruß
    Lapheus

    #2
    Hallo,
    danke für deine Arbeit.
    ich hab den gleichen Receiver mit dem MDT rs232 Interface in Betrieb. Das Gerät kommt mit dem Kruden Protokoll aber an seine Grenzen. Momentan bin ich unterwegs, helfe dir aber gerne.
    Das Problem hab ich aber leider noch nicht verstanden.

    Gruß, Hendrik

    Kommentar


      #3
      Hallo Hendrik,

      ja das Interface von MDT fand ich zu teuer, vor allem weil ich noch den Xport übrig hatte.
      Die Byte zu Bit Konvertierung habe ich aus Deinem Thread.

      Das Problem nennt sich Deadlock, meine ich.

      Innerhalb eines Sendevorgangs (hier wird eine Sperre gesetzt) wird bei der Statusabfrage ein Item angepasst. Jetzt wird durch dieses geänderte Item ein zweiter Sendevorgang gestartet. Der läuft jetzt aber gegen die Sperre des ersten Sendevorgangs. Beide warten auf einander somit wartet alles.
      Die Anwendung steht!

      Das darf eigentlich nicht, passiert aber so (jetziger Stand). Aufgabe ist es, das an der Stelle zu unterbinden. Der Status der zurückgegeben wird hat Vorrang (meiner Meinung nach) und sollte keine neuen Sendevorgänge und somit Aktualisierungen auslösen. Es darf nur das Item vom Wert angepasst werden. Weis aber nicht ob das geht/darf.

      Muss ich mir nochmal ansehen ob man beim Statusbefehl eine "Prio" setzt und somit ein senden von anderen Befehlen verhindern kann.
      Hab das hier gefunden. Das probiere ich, wenn ich wieder zuhause bin.

      Verständlicher?
      Gruß
      Lapheus

      Kommentar


        #4
        Zitat von Lapheus Beitrag anzeigen
        Muss ich mir nochmal ansehen ob man beim Statusbefehl eine "Prio" setzt und somit ein senden von anderen Befehlen verhindern kann.
        Hi,

        so funktioniert es!

        Hab mit dem jetztigen Code auch die Möglichkeit nach Volume Up/Down eine Statusabfrage nachzuschiessen und das entsprechend eingebaut.

        Code:
        #!/usr/bin/env python3
        # vim: set encoding=utf-8 tabstop=4 softtabstop=4 shiftwidth=4 expandtab
        #########################################################################
        # Copyright 2013 Frank Plass
        #########################################################################
        #  This file is part of SmartHome.py.    http://mknx.github.io/smarthome/
        #
        #  SmartHome.py 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.
        #
        #  SmartHome.py 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 SmartHome.py. If not, see <http://www.gnu.org/licenses/>.
        #########################################################################
        
        import logging
        import socket
        import time
        import serial
        #import re
        import threading
        
        logger = logging.getLogger('ag980')
        
        CONST_MAP_COMMANDS = [
                'status',
                'display',
                'speakeroff',
                'speakera',
                'speakerb',
                'speakerc',
                'speakerd',
                'main',
                'mainvolstat',
                'mainmute',
                'mainvolumeup',
                'mainvolumedown',
                'maininput',
                'room2',
                'r2volstat',
                'r2mute',
                'r2volumeup',
                'r2volumedown',
                'r2input'
                ]
        
        CONST_SEND_COMMANDS = {
                'on'        : 0x01,
                'off'       : 0x02,
                'maintuner'     : 0x03,
                'maincd'        : 0x04,
                'mainaux'       : 0x05,
                'mainphono'     : 0x06,
                'maindvd'       : 0x07,
                'maintape'      : 0x08,
                'mainvolumeup'  : 0x0f,
                'mainvolumedown'    : 0x10,
                'mainmute'  : 0x11,
                'tone'      : 0x12,
                'countup'   : 0x13,
                'countdown' : 0x14,
                'speakera'  : 0x1d,
                'speakerb'  : 0x1e,
                'speakerc'  : 0x1f,
                'speakerd'  : 0x20,
                'speakeroff'    : 0x21,
                'room2'     : 0x3f,
                'r2volumeup'   : 0x41,
                'r2volumedown' : 0x42,
                'r2mute'    : 0x43,
                'r2tuner'   : 0x44,
                'r2cd'      : 0x45,
                'r2aux'     : 0x46,
                'r2phone'   : 0x47,
                'r2dvd'     : 0x48,
                'r2tape'    : 0x49,
                'status'    : 0x53
                }
        
        
        class ag980():
        
            def __init__(self, smarthome, host=None, port=0, serialport=None, debug=False):
                self.connected = False
                self._sh = smarthome
                self._params = {}
                self._lock = threading.Lock()
                self._host = host
                self._port = int(port)
                self._serialport = serialport
                self._connection_attempts = 0
                self._connection_errorlog = 60
                smarthome.connections.monitor(self)
                self._packetstart = 0x8173  #System ID des AG980
                self._initread = False
                self._statuslock = False
        
            def connect(self):
                if self._serialport is not None:
                    self.connect_serial()
                else:
                    self.connect_tcp()
                
            def connect_tcp(self):
                self._lock.acquire()
                try:
                    self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    self._sock.settimeout(2)
                    self._sock.connect((self._host, self._port))
                except Exception as e:
                    self._connection_attempts -= 1
                    if self._connection_attempts <= 0:
                        self.log_err('could not connect to {}:{}: {}'.format(self._host, self._port, e))
                        self._connection_attempts = self._connection_errorlog
                    self._lock.release()
                    return
                else:
                    self.connected = True
                    self.log_info('connected to {}:{}'.format(self._host, self._port))
                    self._connection_attempts = 0
                    self._lock.release()
            
            def connect_serial(self):
                self._lock.acquire()
                try:
                    self._serialconnection = serial.Serial(
                            self._serialport, 9600, serial.EIGHTBITS, serial.PARITY_NONE, serial.STOPBITS_ONE, timeout=2)
                except Exception as e:
                    self._connection_attempts -= 1
                    if self._connection_attempts <= 0:
                        logger.err('could not connect to {}: {}'.format(self._serialport, e))
                        self._connection_attempts = self._connection_errorlog
                    self._lock.release()
                    return
                else:
                    self.connected = True
                    self.log_info('connected to {}'.format(self._serialport))
                    self._connection_attempts = 0
                    self._lock.release()
        
            def disconnect(self):
                if self._serialport is not None:
                    self.disconnect_serial()
                else:
                    self.disconnect_tcp()
                
            def disconnect_tcp(self): 
                self.connected = False
                try:
                    self._sock.shutdown(socket.SHUT_RDWR)
                except:
                    pass
                try:
                    self._sock.close()
                except:
                    pass
        
            def disconnect_serial(self): 
                self.connected = False
                try:
                    self._serialconnection.close()
                    self._serialconnection = None
                except:
                    pass
        
            def send_bytes(self, packet):
                if self._serialport is not None:
                    self.send_bytes_serial(packet)
                else:
                    self.send_bytes_tcp(packet)
                
            def send_bytes_tcp(self, packet):
                self._sock.sendall(packet)
        
            def send_bytes_serial(self, packet):
                self._serialconnection.write(packet)
                
            def read_bytes(self, length):
                if self._serialport is not None:
                    return self.read_bytes_serial(length)
                else:
                    return self.read_bytes_tcp(length)
                
            def read_bytes_tcp(self, length):
                return self._sock.recv(length)
        
            def read_bytes_serial(self, length):
                return self._serialconnection.read(length)
        
            def run(self):
                # if you want to create child threads, do not make them daemon = True!
                # They will not shutdown properly. (It's a python bug)
                self.alive = True
                self._sh.scheduler.add('ag980-init', self._getfirststatus, prio=5, cycle=600, offset=2)
                maxloops = 20
                loops = 0 
                while self.alive and not self._initread and loops < maxloops:  # wait for init read to finish
                    time.sleep(0.5)
                    loops += 1
                self._sh.scheduler.remove('ag980-init')
        
            def stop(self):
                self._sh.scheduler.remove('ag980-init')
                self.alive = False
                self.disconnect()
        
            def parse_item(self, item):
                if 'ag980_cmd' in item.conf:
                    commandname = item.conf['ag980_cmd']
                    if commandname in CONST_MAP_COMMANDS:
                        self.log_debug('Item {} writes by using command \'{}\''.format(item, commandname))
                        self._params[commandname] = {'cmd': item.id(), 'item': item}
        
                        #self.log_debug('Parameterliste: {} '.format(self._params))
                        
                        return self.update_item
                    else:
                        self.log_err('Item {} contains invalid command \'{}\'!'.format(item, commandname))
                        return None
                    
                else:
                    return None
        
            def parse_logic(self, logic):
                pass
        
            def update_item(self, item, caller=None, source=None, dest=None):
                if caller != 'ag980':
                    cmd = item.conf['ag980_cmd']
                    value = item()
                    self.log_debug('Update command: "{}" with value "{}"'.format(cmd,value))
        
                    if self._initread:
                        if cmd == 'status':
                            self.send_command(cmd, value)
                        elif cmd == 'speakeroff':
                            self.send_command(cmd, value)
                        elif cmd == 'speakera':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        elif cmd == 'speakerb':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        elif cmd == 'speakerc':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        elif cmd == 'speakerd':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        # Main Zone
                        elif cmd == 'main':
                            if not self._statuslock:
                                self.send_command('on' if value else 'off', value)
                        elif cmd == 'mainmute':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        elif cmd == 'mainvolumeup':
                            self.send_command(cmd, value)
                            self.send_command('status', value)
                        elif cmd == 'mainvolumedown':
                            self.send_command(cmd, value)
                            self.send_command('status', value)
                        elif cmd == 'maininput':
                            if not self._statuslock:
                            # restrict possible Inputs
                                value == self._restrictinput(value)
                                if value == 2:
                                    self.send_command('maintuner', value)
                                elif value == 4:
                                    self.send_command('maincd', value)
                                elif value == 5:
                                    self.send_command('mainaux', value)
                                elif value == 6:
                                    self.send_command('mainphono', value)
                                elif value == 7:
                                    self.send_command('maindvd', value)
                                elif value == 8:
                                    self.send_command('maintape', value)
                        elif cmd == 'room2':
                            if not self._statuslock:
                                self.send_command('room2', value)
                        elif cmd == 'r2mute':
                            if not self._statuslock:
                                self.send_command(cmd, value)
                        elif cmd == 'r2volumeup':
                            self.send_command(cmd, value)
                            self.send_command('status', value)
                        elif cmd == 'r2volumedown':
                            self.send_command(cmd, value)
                            self.send_command('status', value)
                        elif cmd == 'r2input':
                            if not self._statuslock:
                                # restrict possible Inputs
                                value == self._restrictinput(value)
                                if value == 2:
                                    self.send_command('r2tuner', value)
                                elif value == 4:
                                    self.send_command('r2cd', value)
                                elif value == 5:
                                    self.send_command('r2aux', value)
                                elif value == 6:
                                    self.send_command('r2phone', value)
                                elif value == 7:
                                    self.send_command('r2dvd', value)
                                elif value == 8:
                                    self.send_command('r2tape', value)
                
            def send_command(self, commandname, value=None):
                try:
                    self.log_debug('Got a new send job: Command "{}"'.format(commandname))
        
                    # Get command hexcode
                    commandcode = CONST_SEND_COMMANDS[commandname]
                    packet = bytearray()
                    packet.extend(self.int2bytes(self._packetstart, 2))
                    packet.extend(self.int2bytes(commandcode, 1))
                    #self.log_debug('packet: "{}"'.format(packet))
                    checksum = self.calc_checksum(packet)
                    packet.extend(self.int2bytes(checksum, 1))
        
                    # Use a lock to allow only one sender at a time
                    self._lock.acquire()
        
                    if not self.connected:
                        raise Exception("No connection to ag980.")
                    try:
                        self.log_info('Preparing command {} with hexcode \'{}\' to be sent.'.format(commandname, self.bytes2hexstring(packet)))
                        self.send_bytes(packet)
                        self.log_info('Successfully sent packet: {}'.format(self.bytes2hexstring(packet)))
                    except Exception as e:
                        raise Exception('Exception while sending: {}'.format(e))
        
                    #if send status request, get the response
                    if commandname == 'status':
                        self._statuslock = True #Sendecommandos sperren
        
                        packet = bytearray()
                        firstpartlen = 3
                        while self.alive and len(packet) < firstpartlen:
                            try:
                                bytestoreceive = firstpartlen - len(packet)
                                self.log_debug('Trying to receive {} bytes for the first part of the response.'.format(bytestoreceive))
                                chunk = self.read_bytes(bytestoreceive)
                                self.log_debug('Received chunk of response: {}'.format(self.bytes2hexstring(chunk)))
                                packet.extend(chunk)
                            except socket.timeout:
                                raise Exception("error receiving first part of packet: timeout")
                            except Exception as e:
                                raise Exception("error receiving first part of packet: {}".format(e))
            
                        #packet length fix at 20 bytes
                        packetlen = 20
        
                        # Try to receive the second part of the packet
                        while self.alive and len(packet) < packetlen:
                            try:
                                #Receive next chunk
                                bytestoreceive = packetlen - len(packet)
                                self.log_debug('Trying to receive {} bytes for the second part of the response.'.format(bytestoreceive))
                                chunk = self.read_bytes(bytestoreceive)
                                self.log_debug('Received chunk of response: {}'.format(self.bytes2hexstring(chunk)))
                                packet.extend(chunk)
                            except socket.timeout:
                                raise Exception("error receiving second part of packet: timeout")
                            except Exception as e:
                                raise Exception("error receiving second part of packet: {}".format(e))
        
                        # Parse response
                        self.parse_response(packet)
        
                except Exception as e:
                    self.disconnect()
                    self.log_err("send_command failed: {}".format(e))
        
                finally:            
                    # At the end, release the lock
                    self._lock.release()
        
            def parse_response(self, response):
                resph = self.bytes2int(response)
                self.log_info('Successfully received response: {}'.format(self.bytes2hexstring(response)))
                        
                # Remove System ID and checksum to get the data
                rawdatabytes = response[2:-1]
                # Received Checksum
                receivedchecksum = response[-1]
                #self.log_debug('Corresponding read command: {}, decoded data: {} (raw: {})'.format(self.bytes2hexstring(commandcodebytes), self.bytes2hexstring(databytes), self.bytes2hexstring(rawdatabytes)))
        
                # Validate checksum
                checksum = self.calc_checksum(rawdatabytes)
                if (receivedchecksum != checksum):
                    self.log_err('Calculated checksum of {} does not match received checksum of {}! Ignoring reponse.'.format(self.byte2hexstring(checksum), self.byte2hexstring(receivedchecksum)))
                    return
        
                # self.log_debug('--')
                textstring = response[3:12]
                # self.log_debug('Successfully received Textstring: "{}"'.format(textstring.decode("utf-8")))
                self._params['display']['item'](textstring.decode("utf-8"))
        
                systemstatus = bin(response[13])[2:].zfill(8)
                # self.log_debug('Successfully received systemstatus: "{}"'.format(systemstatus))
                # self.log_debug('Successfully received System Power Status: "{}"'.format(systemstatus[7]))
                self._params['main']['item'](self._str2bool(systemstatus[7]))
        
                # self.log_debug('Successfully received System Request Error: "{}"'.format(systemstatus[6]))
        
                # self.log_debug('Successfully received R2 Status: "{}"'.format(systemstatus[5]))
                self._params['room2']['item'](self._str2bool(systemstatus[5]))
        
                # self.log_debug('Successfully received Main Mute: "{}"'.format(systemstatus[4]))
                self._params['mainmute']['item'](self._str2bool(systemstatus[4]))
        
                # self.log_debug('Successfully received R2 Mute: "{}"'.format(systemstatus[3]))
                self._params['r2mute']['item'](self._str2bool(systemstatus[3]))
        
                # self.log_debug('Successfully received Sleep Mode: "{}"'.format(systemstatus[2]))
                # self.log_debug('Successfully received Cinema EQ: "{}"'.format(systemstatus[1]))
                # self.log_debug('Successfully received Tone defeat: "{}"'.format(systemstatus[0]))
                
                # self.log_debug('--')
                maininput = response[14]
                self.log_debug('Successfully received maininput "{}"'.format(maininput))
                self._params['maininput']['item'](maininput)
                r2input = response[15]
                self.log_debug('Successfully received r2input: "{}"'.format(self.byte2hexstring(r2input)))
                self._params['r2input']['item'](r2input)
                mainvolume = response[16]
                self.log_debug('Successfully received mainvolume: "{}"'.format(self.byte2hexstring(mainvolume)))
                self._params['mainvolstat']['item'](mainvolume)
                r2volume = response[17]
                self.log_debug('Successfully received r2volume: "{}"'.format(self.byte2hexstring(r2volume)))
                self._params['r2volstat']['item'](r2volume)
        
                speakerstatus = bin(response[18])[2:].zfill(8) 
                self.log_debug('Successfully received Speakerstatusbyte: "{}"'.format(speakerstatus))
                #self.log_debug('Successfully received Speakerstatus A: "{}"'.format(self._str2bool(speakerstatus[7])))
                self._params['speakera']['item'](self._str2bool(speakerstatus[7]))
                #self.log_debug('Successfully received Speakerstatus B: "{}"'.format(self._str2bool(speakerstatus[6])))
                self._params['speakerb']['item'](self._str2bool(speakerstatus[6]))
                self._params['speakerc']['item'](self._str2bool(speakerstatus[5]))
                self._params['speakerd']['item'](self._str2bool(speakerstatus[4]))
                self._statuslock = False
            
            def _str2bool(self, var):
                if var == '1':
                    return True
                else:
                    return False
            
            def _getfirststatus(self):
                try:
                    # Do the init read commands
                    if self.connected:
                        self.log_info('Starting initial read commands.')
                        self.send_command('status', None )
                finally:
                    self._initread = True
                
            def _restrictinput(self, val):
                if val == 0 or val == 1:
                    return 2
                elif val == 3:
                    return 2
                elif val > 8:
                    return 8
                return val
                    
            def calc_checksum(self, packetpart):
                return (sum(packetpart)) % 256
                
            def log_debug(self, text):
                logger.debug('AG980: {}'.format(text))    
        
            def log_info(self, text):    
                logger.info('AG980: {}'.format(text))    
        
            def log_err(self, text):    
                logger.error('AG980: {}'.format(text))    
        
            def int2bytes(self, value, length):
                # Limit value to the passed byte length
                value = value % (2 ** (length * 8))
                return value.to_bytes(length, byteorder='big')
        
            def bytes2int(self, bytesvalue):
                return int.from_bytes(bytesvalue, byteorder='big', signed=False)
            
            def bytes2hexstring(self, bytesvalue):
                return ":".join("{:02x}".format(c) for c in bytesvalue)
        
            def byte2hexstring(self, bytevalue):
                return "{:02x}".format(bytevalue)
        Gruß
        Lapheus

        Kommentar


          #5
          Toll, dann hab ich ja was zu Testen! ☺

          Kommentar


            #6
            Hallo,

            ich habe jetzt angefangen, das Plugin zu testen. Leider komme ich nicht sehr weit:
            Code:
            2014-09-09 18:44:21,142 DEBUG    Main         Item teac: no type specified. -- item.py:__init__:242
            2014-09-09 18:44:21,408 DEBUG    Main         Starting AG980 Plugin -- plugin.py:start:67
            2014-09-09 18:44:21,408 DEBUG    AG980        ag980-init next time: 2014-09-09 18:44:23+02:00 -- scheduler.py:_next_time:301
            2014-09-09 18:44:23,593 INFO     Connections  AG980: connected to /dev/TEAC -- __init__.py:log_info:481
            2014-09-09 18:44:23,871 DEBUG    Scheduler    ag980-init next time: 2014-09-09 18:54:23+02:00 -- scheduler.py:_next_time:301
            2014-09-09 18:44:23,881 INFO     ag980-init   AG980: Starting initial read commands. -- __init__.py:log_info:481
            2014-09-09 18:44:23,882 DEBUG    ag980-init   AG980: Got a new send job: Command "status" -- __init__.py:log_debug:478
            2014-09-09 18:44:23,882 INFO     ag980-init   AG980: Preparing command status with hexcode '81:73:53:47' to be sent. -- __init__.py:log_info:481
            2014-09-09 18:44:23,883 INFO     ag980-init   AG980: Successfully sent packet: 81:73:53:47 -- __init__.py:log_info:481
            2014-09-09 18:44:23,883 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:478
            2014-09-09 18:44:25,884 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:478
            2014-09-09 18:44:25,884 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:478
            2014-09-09 18:44:27,886 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:478
            2014-09-09 18:44:27,886 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:478
            2014-09-09 18:44:29,887 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:478
            2014-09-09 18:44:29,888 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:478
            2014-09-09 18:44:31,890 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:478
            2
            Ich benutze eine USB-Seriell-Verbindung. Kannst du mir sagen, wie ich manuell mal testen kann, ob der Teac antwortet? (ich habe das damals nur in Windows gemacht und es ist auch schon lange her...)

            Gruß,
            Hendrik

            Kommentar


              #7
              Hallo Hendrik,

              ich habe mitlerweile weiterentwickelt.

              Im Anhang das Zip enthält meine aktuelle Version.
              Ich nutze ja einen Xport als Anbindung.

              Versuch es mal mit der Version. Evtl. klappt es damit.
              Habe, meine ich, beim Empfang der Daten noch was angepasst.

              Bei mir funktioniert jetzt auch ein direktes Senden eines Volume-Wertes. Er berechnet anhand des Status Wertes ob er hoch oder runter muss, dann schickt er die entsprechende Anzahl an Volume Up (Down) Befehlen.
              Klappt bei mir einwandfrei.
              Angehängte Dateien
              Gruß
              Lapheus

              Kommentar


                #8
                Zitat von henfri Beitrag anzeigen
                Ich benutze eine USB-Seriell-Verbindung. Kannst du mir sagen, wie ich manuell mal testen kann, ob der Teac antwortet? (ich habe das damals nur in Windows gemacht und es ist auch schon lange her...)
                Unter Windows nutze ich hercules das sich mit dem Xport verbindet.
                Unter Linux hab ich den Artikel gefunden, vielleicht hilft der Dir weiter.
                Gruß
                Lapheus

                Kommentar


                  #9
                  Wäre schön wenn neue Plugins auch gleich immer ein paar Docstrings mitbringen würden

                  mfg Stefan Betz

                  Kommentar


                    #10
                    Hallo,

                    danke. Ich nutze jetzt dein neues Plugin. In der Readme fehlt, dass der Teac Strom braucht ;-) Nach dem Einstecken funktionierte es.

                    Eine Frage habe ich:
                    Code:
                        [['on']]
                            name = Teac an
                            ag980_cmd = on
                            type = bool
                            knx_dpt = 1
                            #knx_send = 
                            #knx_listen = 
                            enforce_updates = on
                        [['off']]
                            name = Teac aus
                            ag980_cmd = off
                            type = bool
                            knx_dpt = 1
                            #knx_send = 
                            #knx_listen = 
                            enforce_updates = on
                    Damit sollte ich den Teac doch an und aus schalten können, oder?
                    Allerdings sehe ich im Log, dass nix getriggert wird.

                    Davon abgesehen ist die Verbindung bei mir recht instabil.
                    Es funktioniert eine Weile, und dann kommt ein:
                    Code:
                    2014-09-10 10:41:44,497 INFO     ag980-init   AG980: Starting initial read commands. -- __init__.py:log_info:548
                    2014-09-10 10:41:44,498 DEBUG    ag980-init   AG980: Got a new send job: Command "status" -- __init__.py:log_debug:545
                    2014-09-10 10:41:44,498 INFO     ag980-init   AG980: Preparing command status with hexcode '81:73:53:47' to be sent. -- __init__.py:log_info:548
                    2014-09-10 10:41:44,498 INFO     ag980-init   AG980: Successfully sent packet: 81:73:53:47 -- __init__.py:log_info:548
                    2014-09-10 10:41:44,499 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:545
                    2014-09-10 10:41:44,997 DEBUG    Scheduler    ag980-init next time: 2014-09-10 10:51:44+02:00 -- scheduler.py:_next_time:301
                    2014-09-10 10:41:46,500 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:545
                    2014-09-10 10:41:46,501 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:545
                    2014-09-10 10:41:48,503 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:545
                    2014-09-10 10:41:48,503 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:545
                    Und damit ist Schluss. Hast du eine Idee, wie man ein automatisches Re-Connect erreichen kann?

                    Gruß,
                    Hendrik

                    Kommentar


                      #11
                      Zitat von henfri Beitrag anzeigen
                      Hallo,

                      Damit sollte ich den Teac doch an und aus schalten können, oder?
                      Allerdings sehe ich im Log, dass nix getriggert wird.
                      Mit dem Befehl "main" kannst du mit true/false den Hauptkanal ein- und ausschalten. Das Plugin weis welchen Befehl es schicken muss. Das habe ich angepasst.

                      Zitat von henfri Beitrag anzeigen
                      Davon abgesehen ist die Verbindung bei mir recht instabil.
                      Es funktioniert eine Weile, und dann kommt ein:
                      ...
                      Und damit ist Schluss. Hast du eine Idee, wie man ein automatisches Re-Connect erreichen kann?
                      Über meinen Xport wird nach jedem empfangen die Verbindung getrennt und automatisch selbst wieder aufgebaut.
                      Muss ich mir mal nen neuen USB-SERIEL Adapter kaufen und das testen.
                      Kann Dir zum Stammtisch auch nen Xport mitbringen.
                      Gruß
                      Lapheus

                      Kommentar


                        #12
                        Zitat von Lapheus Beitrag anzeigen
                        Mit dem Befehl "main" kannst du mit true/false den Hauptkanal ein- und ausschalten. Das Plugin weis welchen Befehl es schicken muss. Das habe ich angepasst.
                        Da bin ich nicht sicher, ob ich dich richtig verstehe. Ich möchte ja den Teac komplett abschalten, wenn kein Kanal mehr aktiv ist. macht das Plugin das automatisch?
                        Ansonsten hatte ich gesehen, dass die Kommandos on/off vom Plugin verstanden werden (HEX-Code ist hinterlegt) und vermutet, dass ich nur ein entsprechendes Item anlegen muss.



                        Über meinen Xport wird nach jedem empfangen die Verbindung getrennt und automatisch selbst wieder aufgebaut.
                        Ah, das macht der Xport selbst?
                        Vielleicht wäre es das Einfachste, das dann auch einfach im Plugin für den seriellen Zweig so zu implementieren (oder ein check 'antwortest du noch?' wenn nicht: Neu aufbauen)
                        Muss ich mir mal nen neuen USB-SERIEL Adapter kaufen und das testen.
                        Kann Dir zum Stammtisch auch nen Xport mitbringen.
                        Ich kann dir auch meinen USB-Seriell Adapter mitbringen. Xport macht für mich echt keinen Sinn: Der TEAC sitzt direkt über dem Server :-)

                        P.S: Was nutzt du als Zuspieler? Das Squeezebox-Plugin?

                        Gruß,
                        Hendrik

                        Kommentar


                          #13
                          Zitat von henfri Beitrag anzeigen
                          Da bin ich nicht sicher, ob ich dich richtig verstehe. Ich möchte ja den Teac komplett abschalten, wenn kein Kanal mehr aktiv ist. macht das Plugin das automatisch?
                          Ansonsten hatte ich gesehen, dass die Kommandos on/off vom Plugin verstanden werden (HEX-Code ist hinterlegt) und vermutet, dass ich nur ein entsprechendes Item anlegen muss.
                          Die Hex-Codes sind für den Sende-Befehl an die serielle Schnittstelle des Teac.
                          Die Befehle aus der "CONST_MAP_COMMANDS" sind als Befehle für die Items gedacht.
                          Du kannst den Teac nicht ganz ausschalten, nur über den Hauptschalter vorne am Gerät. Dann reagiert er aber nicht auf die serielle Schnittstelle. Aus dem Standby heraus kannst Du aber "Main" und "R2" jeweils ein- und ausschalten.

                          Zitat von henfri Beitrag anzeigen
                          Ah, das macht der Xport selbst?
                          Vielleicht wäre es das Einfachste, das dann auch einfach im Plugin für den seriellen Zweig so zu implementieren (oder ein check 'antwortest du noch?' wenn nicht: Neu aufbauen)
                          Nein, nicht der Xport sondern das sh.py macht das. Das sollte es auch bei ner Seriellen Schnittstelle machen, soweit ich das verstanden habe.

                          Zitat von henfri Beitrag anzeigen
                          Ich kann dir auch meinen USB-Seriell Adapter mitbringen. Xport macht für mich echt keinen Sinn: Der TEAC sitzt direkt über dem Server :-)
                          Wir könnten das auf dem Stammtisch mit beidem mal testen, wenn der Zeitrahmen passt.

                          Zitat von henfri Beitrag anzeigen
                          P.S: Was nutzt du als Zuspieler? Das Squeezebox-Plugin?
                          Nein, einmal den integrierten Tuner und einmal den mpd mit Internetradio, wobei ich den mpd neu einrichten muss. Mein ComGate wurde letzte Woche abgeschaltet.
                          Gruß
                          Lapheus

                          Kommentar


                            #14
                            Ich sehe schon: Da gibt es am Stammtisch einiges zu bereden. Ich denke, ich pausiere hier mal bis zum Stammtisch.

                            Zum MPD: Kenne ich. Hatte ich auch mal im Einsatz. Ich halte Squeezebox für vielseitiger. Können wir aber ja auch am Stammtisch drüber sprechen.

                            Gruß,
                            Hendrik

                            Kommentar


                              #15
                              Hallo,

                              leider funktioniert das Plugin hier nicht zuverlässig.

                              Das regelmäßige abfragen des Status schlägt fehl. Um dann ein re-connect zu veranlassen habe ich den Code so angepasst:
                              Code:
                                  def send_command(self, commandname, value):
                                      try:
                                          self.log_debug('Got a new send job: Command "{}"'.format(commandname))
                                          # Get command hexcode
                                          commandcode = CONST_SEND_COMMANDS[commandname]
                                          packet = bytearray()
                                          packet.extend(self.int2bytes(self._packetstart, 2))
                                          packet.extend(self.int2bytes(commandcode, 1))
                                          # self.log_debug('packet: "{}"'.format(packet))
                                          checksum = self.calc_checksum(packet)
                                          packet.extend(self.int2bytes(checksum, 1))
                              
                                          # Use a lock to allow only one sender at a time
                                          self._lock.acquire()
                              
                                          if not self.connected:
                                              raise Exception("No connection to ag980.")
                                          try:
                                              self.log_info('Preparing command {} with hexcode \'{}\' to be sent.'.format(commandname, self.bytes2hexstring(packet)))
                                              self.send_bytes(packet)
                                              packet_send=packet
                                              self.log_info('Successfully sent packet: {}'.format(self.bytes2hexstring(packet)))
                                          except Exception as e:
                                              raise Exception('Exception while sending: {}'.format(e))
                              
                                          # if send status request, get the response
                                          if commandname == 'status':
                                              self._statuslock = True # Sendecommandos sperren
                                              packet = bytearray()
                                              firstpartlen = 3
                                              counter=0
                              #                pydevd.settrace("192.168.177.40")                
                                              while self.alive and len(packet) < firstpartlen:
                                                  try:
                                                      if counter <5:
                                                          counter=counter+1
                                                          self.log_debug(counter)
                                                          bytestoreceive = firstpartlen - len(packet)
                                                          self.log_debug('Trying to receive {} bytes for the first part of the response.'.format(bytestoreceive))
                                                          chunk = self.read_bytes(bytestoreceive)
                                                          self.log_debug('Received chunk of response: {}'.format(self.bytes2hexstring(chunk)))
                                                          packet.extend(chunk)
                                                      else:
                                                          self.log_debug('No response from ag980. Disconnecting')
                                                          self.disconnect()
                                                          self.log_debug('No response from ag980. Connect.ing')                            
                                                          self.connect()
                                                          self.send_bytes(packet_send)
                                                          counter=0
                                                  except socket.timeout:
                                                      raise Exception("error receiving first part of packet: timeout")
                                                  except Exception as e:
                                                      raise Exception("error receiving first part of packet: {}".format(e))
                              Man sieht, dass das Plugin darauf wartet, dass der TEAC etwas schreibt, aber es kommt nix. Dann wird ein Disconnect erfolgreich durchgeführt. Aber der connect scheitert:

                              Code:
                              2014-10-03 12:18:22,956 INFO     Connections  AG980: connected to /dev/TEAC -- __init__.py:log_info:561
                              2014-10-03 12:18:24,451 INFO     ag980-init   AG980: Starting initial read commands. -- __init__.py:log_info:561
                              2014-10-03 12:18:24,451 DEBUG    ag980-init   AG980: Got a new send job: Command "status" -- __init__.py:log_debug:558
                              2014-10-03 12:18:24,451 INFO     ag980-init   AG980: Preparing command status with hexcode '81:73:53:47' to be sent. -- __init__.py:log_info:561
                              2014-10-03 12:18:24,452 INFO     ag980-init   AG980: Successfully sent packet: 81:73:53:47 -- __init__.py:log_info:561
                              2014-10-03 12:18:24,452 DEBUG    ag980-init   AG980: 1 -- __init__.py:log_debug:558
                              2014-10-03 12:18:24,452 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:558
                              2014-10-03 12:18:24,952 DEBUG    Scheduler    ag980-init next time: 2014-10-03 12:28:24+02:00 -- scheduler.py:_next_time:301
                              2014-10-03 12:18:29,456 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:558
                              2014-10-03 12:18:29,456 DEBUG    ag980-init   AG980: 2 -- __init__.py:log_debug:558
                              2014-10-03 12:18:29,456 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:558
                              2014-10-03 12:18:34,462 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:558
                              2014-10-03 12:18:34,462 DEBUG    ag980-init   AG980: 3 -- __init__.py:log_debug:558
                              2014-10-03 12:18:34,462 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:558
                              2014-10-03 12:18:39,467 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:558
                              2014-10-03 12:18:39,467 DEBUG    ag980-init   AG980: 4 -- __init__.py:log_debug:558
                              2014-10-03 12:18:39,472 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:558
                              2014-10-03 12:18:44,478 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:558
                              2014-10-03 12:18:44,478 DEBUG    ag980-init   AG980: 5 -- __init__.py:log_debug:558
                              2014-10-03 12:18:44,482 DEBUG    ag980-init   AG980: Trying to receive 3 bytes for the first part of the response. -- __init__.py:log_debug:558
                              2014-10-03 12:18:49,487 DEBUG    ag980-init   AG980: Received chunk of response:  -- __init__.py:log_debug:558
                              2014-10-03 12:18:49,487 DEBUG    ag980-init   AG980: No response from ag980. Disconnecting -- __init__.py:log_debug:558
                              2014-10-03 12:18:49,493 DEBUG    ag980-init   AG980: No response from ag980. Connect.ing -- __init__.py:log_debug:558
                              An dieser Stelle ist Schluss. Das Plugin macht nix mehr.

                              Woran kann das liegen?

                              Wie kann ich die serielle Kommunikation zuverlässiger machen?

                              Gruß,
                              Hendrik

                              Kommentar

                              Lädt...
                              X