Ankündigung

Einklappen
Keine Ankündigung bisher.

Neues Widget: Notification badge

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

    Neues Widget: Notification badge

    Hallo,

    ich habe nun meine Hikvision Cam konfiguriert. Um nun auf die Bilder des Archives per Visu zugreifen zu können, bin ich gerade dabei, ein Bilderarchiv-Plugin zu erstellen.
    Im Verlaufe des Programmierens dachte ich mir, dass ich ja zusätzlich auch meinen ekey Fingerprint integrieren könnte. Jeglicher Fehlversuch wird nun mittels Snapshot dokumentiert.

    Da ich aber nicht immer auf die Seite navigieren möchte um zu schauen, ob da ein neuer Fehlversuch war, habe ich ein neues Widget erstellt, das mir ähnlich wie beim iPhone die Anzahl neuer Events über dem Icon darstellt.

    Das Widget dazu ist eigentlich ganz einfach:
    1) item.conf: Ich errechne mir die Anzahl an neuen Events in einem Dict, letztendlich funktioniert der Code mit jedem Item, aufgrund der Badge-Darstellung (runder Kreis) sollte es aber nicht zu lang sein.
    Code:
    [Fingerprint]
        [[events]]
            type = dict
            [[[anzahl]]]
                type = num
                eval = len(sh.Fingerprint.events().get("last24h"))
                eval_trigger = Fingerprint.events
    2) Neues Widget erstellen, bspw folgende Datei erstellen: /pages/{design}/widgets/widget_notification_badge.html

    Code:
    /** 
    * Widget for displaying notification badges
    *
    * @id unique id for this widget
    * @gad a gad/item
    */
    
    {% macro badge(id, gad) %}
    
    	<span 
    		id="{{ uid(page, id) }}" 
    		data-widget="notification.badge"
    		data-item="{{ gad }}" 
            >
        </span>
    	
    {% endmacro %}
    3) In Eure visu.js folgenden Code hinzufügen:
    Code:
    // ----- notification.badge -------------------------------------------------------
    $(document).delegate('span[data-widget="notification.badge"]', {
    	'update': function (event, response) {
    		$('#' + this.id).attr('data-notifications', response);
    	},
    	'change': function (event) {
    		$('#' + this.id).attr('data-notifications', response);
    	}
    });
    4) In Eure visu.css folgenden Code hinzufügen (ohne dieses CSS geht's nicht):
    Code:
    /* make sure the element has position: relative */
    [data-notifications] {
      position: relative;
    }
    
    /* append the notification badge after it */
    [data-notifications]:after {
    
        /* the burger */
        content: attr(data-notifications);
    
        /* the fries */
        position: absolute;
        display: inline-block;
        height:1.5rem;
        right: 0px;
        top: -15px;       
        width:1.5rem;
        text-align: center;
        line-height: 1.5rem;;
        font-size: 1rem;
        border-radius: 50%;
    
        /* the menu */
        /* TODO: make it customizable */
        border:1px solid red;
        background: red;
        color:white;
    }
    5) In jeder HTML Datei, in der Ihr das nutzen wollt, folgenden Import durchführen
    Code:
    {% import "widgets/widget_notification_badge.html" as notification %}
    und dann mit
    Code:
    {{ notification.badge('Fingerprint_events', 'Fingerprint.events.anzahl') }}
    darstellen.

    Vielleicht gefällts ja jemandem...

    Beste Grüsse aus Snowtown...
    hhhc
    Angehängte Dateien
    ++ Der ultimative ETS Schnellkurs ++
    KNX und die ETS vom Profi lernen
    www.ets-schnellkurs.de

    #2
    Hi,

    das ist ja cool super Arbeit.

    Darf ich eine Frage loswerden.
    Gibt es ein How to oder etwas Änliches oder woher weis ich das auch ein dict geht und woher weis ich welche parameter unterstützt werden.
    Oder kann ich das ganze Python Progarmm in die conf schreiben ?
    Gruß

    Guido

    Kommentar


      #3
      Zitat von Höhlenbär Beitrag anzeigen

      das ist ja cool super Arbeit.
      Danke. Dafür arbeitet man im Open Source Bereich :-)


      Zitat von Höhlenbär Beitrag anzeigen
      Gibt es ein How to oder etwas Änliches oder woher weis ich das auch ein dict geht und woher weis ich welche parameter unterstützt werden.
      Oder kann ich das ganze Python Progarmm in die conf schreiben ?
      Naja, ich hatte eigentlich gedacht, dass die Anleitung oben schon recht detailliert war.
      Bzgl dict:
      Ich zeige nicht das Python Dictionary direkt an, sondern kalkuliere mir mittels eval die Anzahl an Items in diesem. Die Anzahl zeige ich dann an. Es ist deswegen egal, wie Du das Item generierst. Es ist sogar egal, ob das Item num oder str ist - der Inhalt wird einfach ausgegeben.
      Aufgrund des Badge-Layouts macht langer Texte aber keinen Sinn.

      Probier es einfach mal aus - ist super einfach. Falls ich Deine Frage falsch verstanden habe, lass es mich wissen.
      ++ Der ultimative ETS Schnellkurs ++
      KNX und die ETS vom Profi lernen
      www.ets-schnellkurs.de

      Kommentar


        #4
        Falls ich Deine Frage falsch verstanden habe, lass es mich wissen.
        Ein klein wenig.

        Ich bin Anfänger in der Smart / Home oder Visu Welt.
        Dementsprechend habe ich auch in die Doku von Smarthome.py gelesen allerdings nur im KNX Ordner.

        Und ich bin immer weider erstaunt welche Möglichkeiten es noch in der item.conf gib ohne das sie dort stehen.

        Darum die Frage wo steht was in die .conf alles rein darf.

        Vieleicht denke ich auch zu kompliziert oder die Unwissenheit ist noch zu gross
        Gruß

        Guido

        Kommentar


          #5
          Habe schon immer die Möglichkeit gesucht ein Bildarchiv mit Smartvisu und Smarthome.py zu erstellen. Dein Ansatz mit der Klingel ist gut. So würde ich es auch gerne machen.
          Die Baugleiche Kamera der Hikvision von Trendnet habe ich auch.
          Hast du schon eine Lösung für den Teil, das Bild über Smarthome.py abzulegen z.B. per Logik. Das habe ich bisher nicht gefunden.
          Deinem Screenshot nach läuft das schon?

          Kommentar


            #6
            Ich habe eine andere Frage. Welche Kamera verwendest du und wie hast du den Live-Feed und die Events der Kamera angebunden? Ich habe nun schon diverse Kameras durch und bin mit nichts so wirklich zufrieden. Eine kleine Hilfestellung wäre super.

            Kommentar


              #7
              Zitat von patrickgoll Beitrag anzeigen
              Ich habe eine andere Frage. Welche Kamera verwendest du und wie hast du den Live-Feed und die Events der Kamera angebunden? Ich habe nun schon diverse Kameras durch und bin mit nichts so wirklich zufrieden. Eine kleine Hilfestellung wäre super.
              Ich glaube gelesen zu haben das es sich um die Hikvision handelt baugleich der Trendnet TV IP 310 (/311)
              https://knx-user-forum.de/forum/%C3%B...wachungskamera

              Kommentar


                #8
                Zitat von cocoon Beitrag anzeigen

                Ich glaube gelesen zu haben das es sich um die Hikvision handelt baugleich der Trendnet TV IP 310 (/311)
                https://knx-user-forum.de/forum/%C3%B...wachungskamera

                Ganz genau.
                ++ Der ultimative ETS Schnellkurs ++
                KNX und die ETS vom Profi lernen
                www.ets-schnellkurs.de

                Kommentar


                  #9
                  hast du eine Lösung für das Bildarchiv (bezug auf deinen Screenshot)?

                  Kommentar


                    #10
                    Zitat von cocoon Beitrag anzeigen
                    Habe schon immer die Möglichkeit gesucht ein Bildarchiv mit Smartvisu und Smarthome.py zu erstellen. Dein Ansatz mit der Klingel ist gut. So würde ich es auch gerne machen.
                    Die Baugleiche Kamera der Hikvision von Trendnet habe ich auch.
                    Hast du schon eine Lösung für den Teil, das Bild über Smarthome.py abzulegen z.B. per Logik. Das habe ich bisher nicht gefunden.
                    Deinem Screenshot nach läuft das schon?

                    Den Prozess habe ich bei mir wie folgt abgebildet:

                    Trigger von Türklingel, falscher Fingerprint, Alarmanlage schlägt Alarm speichern jeweils das aktuelle Kamerabild (in unterschiedlichen Directories) auf meinem mac mini. Auf diesem läuft in einer VM Smarthome / Smartvisu mit einer Logik, die die Bilder jede Minute runterladen. Zusätzlich habe ich in der Camera (Hikvision) per Bewegungserkennung eingestellt, dass 5 Bilder gespeichert werden.

                    Die Logik bereitet die jeweiligen Dateinamen auf, so dass ich sie per Dict benutzen kann. Über ein bisschen JS Gefrickel habe ich die 5 zusammengehörigen Bilder dann immer noch gruppiert, so dass ich das als Art "Video" mir anschauen kann.

                    Bei "neuen" Fingerprint bzw Türklingel-Bildern wird dann eben das Notification Badge angezeigt - Zurücksetzen funktioniert mittels Setzen eines Timestamps.

                    Der Code ist extrem an meine Dateistrukturen angepasst und ugly. Nichtsdestotrotz hier einmal...

                    Code:
                    #!/usr/bin/python
                    import datetime
                    import time
                    
                    class PictureArchive:
                    
                        def __init__(self, sh):
                            import ftplib
                            import os 
                            import datetime
                            import time
                    
                            logger.info("Initialisiere PictureArchive")
                    
                            # Daten übernehmen
                            self.sh = sh
                            self.server = '192.168.2.10'
                            self.user = 'xxx'
                            self.password = 'xxx'
                            self.directory = 'IP CAMERA/Camera 01/'
                            self.smartvisu_dir = '/var/www/smartvisu/pics/cam/'
                            self.fingerprint_dir = '/var/www/smartvisu/pics/fingerprint/'
                            self.klingel_dir = '/var/www/smartvisu/pics/klingel/'
                            self.keepHistoryThreshold = 60 * 60 * 24 * 5 # define time in seconds for many pictures should be kept
                            self.groupEventThreshold = 120 # define time in seconds in which 2 different pictures still belong to the same event 
                            self.fdict = {}
                            self.fdict['last24h'] = {}
                            self.fdict['lastweek'] = {}
                            self.fdict['archive'] = {}
                            self.fingerprintDict = {}
                            self.fingerprintDict['last24h'] = {}
                            self.klingelDict = {}
                            self.klingelDict['last24h'] = {}
                            self.eventCounter = 0
                            
                            self.downloadPicturesByFtp()
                            
                            self.getPictureNamesGroupedByEvent()
                            self.sh.Kamera.events(self.fdict)
                            
                            self.getFailedFingerprintPictures()
                            self.sh.Fingerprint.events(self.fingerprintDict)
                    
                            self.getKlingelPictures()
                            self.sh.Haustuer.klingel.events(self.klingelDict)
                            
                    
                        def getTimestampFromFilename(self, filename):
                            year = int(filename[15:19])
                            month = int(filename[19:21])
                            day = int(filename[21:23])
                            hours = int(filename[23:25])
                            minutes = int(filename[25:27])
                            second = int(filename[27:29])
                            dt = datetime.datetime(year, month, day, hours, minutes, second)
                            return dt
                    
                        def downloadPicturesByFtp(self):
                            import ftplib
                    
                            if not os.path.exists(self.smartvisu_dir):
                                os.makedirs(self.smartvisu_dir)
                    
                            ftp = ftplib.FTP(self.server, self.user, self.password)
                            ftp.cwd(self.directory)
                            filenames = ftp.nlst()
                    
                            for filename in filenames:
                                if filename == 'test':
                                    continue
                                local_filename = os.path.join(self.smartvisu_dir, filename)
                                if not os.path.isfile(local_filename):
                        #            logger.info('Copy file ' + filename)
                        #            print "Copy picture " + filename
                        #            logger.debug('Copy file ' + filename)
                                    file = open(local_filename, 'wb')
                                    ftp.retrbinary('RETR '+ filename, file.write)
                                    file.close()
                            ftp.quit()
                            return True
                    
                        def getFailedFingerprintPictures(self):
                            for (dirpath, dirnames, filenames) in os.walk(self.fingerprint_dir):
                                filenames.sort()
                                index = 0
                                for filename in filenames:
                                    file_timestamp = os.path.getmtime(os.path.join(self.fingerprint_dir, filename))
                                    dfileTS = datetime.datetime.fromtimestamp(file_timestamp)
                                    if self.sh.Fingerprint.events.timestampFromWhen() < dfileTS:
                                        self.eventCounter += 1
                        #                logger.debug("FailedFingerprint picture:  " + filename + " eventCounter: " + str(self.eventCounter))
                                        self.fingerprintDict['last24h'][self.eventCounter] = {}
                                        self.fingerprintDict['last24h'][self.eventCounter][index] = filename
                    
                        def getKlingelPictures(self):
                            for (dirpath, dirnames, filenames) in os.walk(self.klingel_dir):
                                filenames.sort()
                                index = 0
                                for filename in filenames:
                                    file_timestamp = os.path.getmtime(os.path.join(self.klingel_dir, filename))
                                    dfileTS = datetime.datetime.fromtimestamp(file_timestamp)
                                    if self.sh.Haustuer.klingel.events.timestampFromWhen() < dfileTS:
                                        logger.debug("File TS: " + filename + " - " + str(file_timestamp))
                                        logger.debug("Tuerklingel picture:  " + filename + " eventCounter: " + str(self.eventCounter))
                                        self.eventCounter += 1
                                        self.klingelDict['last24h'][self.eventCounter] = {}
                                        self.klingelDict['last24h'][self.eventCounter][index] = filename
                        
                    
                        def getPictureNamesGroupedByEvent(self):
                            now = datetime.datetime.now()
                            
                            deltaLastFile = float(0)
                            indexLastFile = 0
                    #        eventCounter = 0
                            for (dirpath, dirnames, filenames) in os.walk(self.smartvisu_dir):
                                filenames.sort()
                                eventCounter2 = 0
                                for filename in filenames:
                                    dateOfFile = self.getTimestampFromFilename(filename);
                                    delta = now - dateOfFile
                                    deltaTimestamp = delta.total_seconds()
                                    if (deltaTimestamp < 86400): # 60*60*72
                                        indexOfFile = 'last24h'
                                    elif (deltaTimestamp < 60*60*24*7):
                                        indexOfFile = 'lastweek'
                                    else:
                                        indexOfFile = 'archive'
                                    if (deltaLastFile == 0) or (deltaLastFile - deltaTimestamp) > self.groupEventThreshold:
                        #                print "neues Event delta: " + str(deltaLastFile - deltaTimestamp)
                        #                print filename + " - " + str(dateOfFile) + " - " + str(deltaTimestamp)
                                        self.eventCounter += 1
                    #                    logger.debug("Neues Event: " + str(deltaLastFile - deltaTimestamp))
                    #                    logger.debug(filename + " - " + str(dateOfFile) + " - " + str(deltaTimestamp) + " eventCounter: " + str(self.eventCounter))
                                        indexLastFile = self.eventCounter
                                        self.fdict[indexOfFile][indexLastFile] = {}
                                        self.fdict[indexOfFile][indexLastFile][eventCounter2] = filename
                                        eventCounter2 = 0
                                    else:
                        #                print filename + " - " + str(dateOfFile) + " - " + str(deltaTimestamp) + "lastindex: " + str(indexLastFile)
                    #                    logger.debug(filename + " - " + str(dateOfFile) + " - " + str(deltaTimestamp)  + " eventCounter: " + str(self.eventCounter))
                                        self.fdict[indexOfFile][indexLastFile][eventCounter2] = filename
                                        eventCounter2 += 1
                                    deltaLastFile = deltaTimestamp
                    
                    # Klasse aufrufen (Klasse instanziieren, den Rest macht der Konstruktor ...)
                    PictureArchive(sh)
                    Item Bsp:
                    Code:
                     
                    [Fingerprint]
                        [[events]]
                            type = dict
                            [[[anzahl]]]
                                type = num
                                eval = len(sh.Fingerprint.events().get("last24h"))
                                eval_trigger = Fingerprint.events
                            [[[timestampFromWhen]]]
                                type = foo
                                cache = on
                                [[[[trigger]]]]
                                    type = bool
                                    enforce_updates = yes
                                    visu_acl = rw
                                    eval = sh.Fingerprint.events.timestampFromWhen(datetime.datetime.now())
                    widget_camery_history.html
                    Code:
                    {% macro camera_history(id, gad_camera, prefix, timeframe, innerHtmlId) %}
                    
                        <span 
                            id="{{ uid(page, id) }}" 
                            data-widget="camera.history" 
                            data-item="{{ gad_camera }}" 
                            data-prefix="{{ prefix|default('') }}"
                            data-innerHtmlId="{{ innerHtmlId|default('camera') }}"
                            data-timeframe="{{ timeframe|default('last24h') }}"
                            class="switch">
                        </span>
                    
                    {% endmacro %}
                    und dann noch ein bisschen JS (nehmts mir bitte nicht übel, bin kein Programmierer mehr und mir fehlt die Erfahrung um schönen Code zu schreiben...)
                    Code:
                    // initialisierung
                    items = [];
                    $(document).on("update",'[data-widget="camera.history"]', function(event, response) {
                    //$(document).delegate('div[data-widget="camera.history"]', {
                    //    'update': function (event, response) {
                    
                        console.log("************************ Camera widget");
                    //    console.log("response: ");
                    //    console.log(response);
                        
                        prefix = $(this).attr('data-prefix');
                        id = $(this).attr('id');
                        timeframe = $(this).attr('data-timeframe');
                        innerHtmlId = $(this).attr('data-innerHtmlId');
                        var id = $(this).attr('id');
                        
                        items[id] = response[0];
                        console.log("items: ");
                        console.log(items);
                        var index = 0;
                        var html = "";
                    
                        for (var key in items[id][timeframe]) {
                    //        console.log("timeframe: " + timeframe + " key: " + key);
                            var pictures = items[id][timeframe][key];
                    //        console.log("pictures: " + pictures);
                            for (var index in pictures) {
                    //            console.log("filename " + (pictures[index] + "key: " + key + "index: " + index));
                                if (index == 0) {
                                    tfs = new String(timeframe);
                                    html += '<a onclick="setSlideShowImages(\'' + id + '\', ' + key + ', \'' + tfs + '\', \''+ innerHtmlId + '\', \'' + prefix + '\')"><img src="' + prefix + pictures[index] + '"  class="camera-image"></a>';
                                }
                            }
                        }
                        document.getElementById(id).innerHTML = html;
                    //}
                    });
                    
                    $(document).on("update",'[data-notification="badge"]', function(event, response) {
                        value = $(this).attr('data-notification');
                        consoloe.log("==================== data-notification-badge: " + value);
                    });
                    
                    function showLiveView() {
                    //    console.log("showLiveView");
                        slideimages = [];
                        slideimages.push("http://192.168.xxx.xxx/Streaming/channels/2/picture?auth=xxxxxxxxx");
                        whichimage = 0;
                        slideshowspeed = 3000;
                    }
                    
                    function setSlideShowImages(id, key, timeframe, innerHtmlId, pf) {
                    //    console.log("items aus Funktion: " + items); 
                        var pictures = items[id][timeframe][key];
                        console.log(items);
                        console.log((pictures));
                        slideimages = [];
                        for (var index in pictures) {
                            console.log("filename " + (pictures[index] + "key: " + key + "index: " + index));
                            slideimages.push(pf + pictures[index]);
                        }
                        document.getElementById("liveview").innerHTML = '<a onclick="showLiveView()">Show Live View</a>';
                        console.log(slideimages);
                        slideshowspeed = 1500;
                        whichimage = 0;
                        window.location = '#camera';    
                    //    console.log((slideimages));
                    }
                    
                    whichimage = 0;
                    slideshowspeed = 3000;
                    slideimages = new Array();
                    
                    function slideit()
                    {
                        if (!document.images) return
                    //    document.images.slide.src = slideimages[whichimage].src
                        d = new Date();
                        var randomnumber = Math.floor(Math.random() * 100);
                        var url = slideimages[whichimage] + '?' + d.getTime();
                    //    console.log("img nb: " + whichimage + " url: " + url);
                        $("#updateCamera").attr("src", url);
                    
                        if (whichimage < slideimages.length - 1) {
                            whichimage++;
                        } else {
                            whichimage = 0;
                        }
                        setTimeout(function() {
                            slideit();
                        }, slideshowspeed)
                    }
                    
                    function showPicture(url, innerHtmlId)
                    {
                        var myInnerHtml = document.getElementById(innerHtmlId).innerHTML;
                        document.getElementById(innerHtmlId).innerHTML = '<img src="' + url + '" width="99%" height="99%">';
                        setTimeout(function(){ document.getElementById(innerHtmlId).innerHTML = myInnerHtml }, 10000);
                    }
                    Einbindung dann ins html
                    Code:
                    {{ camera.camera_history('camera_last24h', 'Kamera.events', 'pics/cam/', 'last24h') }} 
                    {{ camera.camera_history('camera_fingerprint', 'Fingerprint.events', 'pics/fingerprint/') }}
                    ++ Der ultimative ETS Schnellkurs ++
                    KNX und die ETS vom Profi lernen
                    www.ets-schnellkurs.de

                    Kommentar


                      #11
                      Hab grad ne PN erhalten und beantworte sie mal hier:
                      Code:
                      Hi Christian,
                      bin zufällig über Dein Notification badge -Widget gestolpert. Echt ne nette Idee.
                      [URL="https://knx-user-forum.de/forum/supportforen/smartvisu/41593-neues-widget-notification-badge"]https://knx-user-forum.de/forum/supp...fication-badge[/URL]
                      Ich suche etwas was mir die Anzahl der täglichen Einschaltzyklen der SolarPumpe anzeigt.
                      Was ich bei Deinem Item nicht ganz verstanden habe, ist wie bei Dir da das "Fingerprint.events" angestoßen wird um dann "Fingerprint.events.anzahl" auszulösen.
                      Also genauer gefragt. Wo müsste die GA (z.B. knx_listen = 0/0/50) eingetragen die beipsielsweise den Status einer Pumpe (Ein oder Aus) wiedergibt.
                      
                      Ich hoffe ich hab meine Frage einiger Maßen verständlich rüberbringen können.
                      Danke & Gruß
                      Die Anzahl der täglichen Einschaltzyklen wirst Du über eine Logik machen müssen. Bei mir ist das im obigen beispiel eine Liste, die die Bildernamen (URLs) der letzten Events beinhaltet. Diese zähle ich mittels
                      Code:
                       
                       eval = len(sh.Fingerprint.events().get("last24h"))
                      Du bräuchtest also ein Item, das auf Deine GA hört und dieses Item nutzt Du als trigger (watch_item) für eine Logic. In der Logik kannst Du dann Deine Anzahl um 1 erhöhen (bestimmt geht das ganze auch direkt in der eval eines Items...)

                      Hoffe, das beantwortet deine Frage.
                      ++ Der ultimative ETS Schnellkurs ++
                      KNX und die ETS vom Profi lernen
                      www.ets-schnellkurs.de

                      Kommentar


                        #12
                        hhhc Habe dein Widget nun auch bei mir implementiert. Danke dafür. Was mir nur etwas stört, ist die Tatsache, dass auch eine Badge angezeigt wird, wenn die Anzahl des GAD "0" ist. Kann man das durch das js oder Widget abfangen?

                        Kommentar


                          #13
                          In der kommenden Version der smartVISU wird das Widget als basic.badge integriert sein, vielen Dank hhhc für die Zustimmung.

                          Da lässt sich zusätzlich die Hintergrundfarbe abhängig vom Wert angeben und der Badge auch ganz ausblenden.
                          Folgendes würde Werte unter 1 (also 0) ausblenden, unter 4 grün, unter 7 orange und ab 7 in der Standardfarbe rot darstellen:
                          {{ basic.badge('', 'item', [1,4,7], ['hidden','green','orange','']) }}

                          Kommentar


                            #14
                            Hört sich super an!

                            Kommentar


                              #15
                              Auch ein von mir.

                              Gruss,

                              Stefan
                              Sonos

                              Kommentar

                              Lädt...
                              X