Chamberlain Garagentor Antrieb + Starter Kit

Hallo,

ich wollte euch mal fragen ob das überhaupt möglich wäre.

Ich habe mit diesen Antrieb gekauft: (Ps. den gibt es nur heute für 165,55 mit Gutschein)

Chamberlain ML1000EV Premium Garagentorantrieb - Plus.de Online Shop

Hierzu gibt es das MyQ Starter-Kit womit man das Tor über eine App /Handy/PC /Tablet steuern kann.

Chamberlain 830REV MyQ Starter-Kit - Plus.de Online Shop

Jetzt meine Frage wäre es möglich dieses MyQ in IPS einzubinden ? so das ich das Tor über PS steuern kann mit diesem Gateway. Habe keine lust später immer zwischen den Apps hin und Herr zu switchen.

Vielen Dank.

Vielleicht hat auch der eine oder andere bereits das Tor in Betrieb und kann mir seine Lösung sagen.

Hallo zusammen,

Ich schließe mich, wenn auch schon etwas Zeit vergangen ist, meinem Vorredner an.

Hat denn jemand von euch bereits eine Lösung gefunden?

Gruß
Steffen

Ohne da jetzt explizit etwas dafür zu schreiben kannst Du das auf alle Fälle aus IP-Symcon über IFTTT steuern.

Dazu nimmst Du das IFTTT Modul und MyQ in IFTTT.

Hi
gibts denn keine Hardwarebastler mehr ?
Einfach einen der Handsender opfern und die entsprechnden Tasten mit einem potentialfreiem Kontakt deines bevorzugten System „drücken“.

Ich hab hier einen älteren Chamberlain, da war ein Taster zur Innenmontage dabei. Dieser wird per 1Wire überbrückt.
Funktioniert problemlos seit vielen vielen Jahren.

Alles über zig Server/Dienste/Gateways welche womöglich irgendwo in der Cloud rumschwirren zu steuern ist doch gaga.
Viel zu anfällig und wartungsintensiv. Heute weißt du was du einrichtest, in fünf Jahren kriegst Haarausfall wenn plötzlich nichts mehr geht.

Keep it simple, insbesondere bei der Home Automation.

gruß
bb

Da gebe ich Dir vollkommen Recht dieses ganz Cloud Zeug ist auch immer nur ein Notnagel, vor allem hat man da immer eine Zeitverzögerung drinnen. Nachteil ist halt nicht jeder kann löten und es sollte auch nicht jemand ohne Fachkentniss von elektrischen Schaltungen eine Gehäuse öffnen und das Innenleben modifizieren. Wenn man sich mit so was auskennt ist das aber ganz sicher der zu bevorzugende Weg.

Bei mir habe ich ein Sonoff Basic mit Tasmota geflasht und umgebaut, damit der Ausgang potentialfrei ist.
Der potentialfrei Kontakt ist parallel zum „Tastereingang“ am Garagentorantrieb angeschlossen.
Somit könntest du mit dem Tasmota Modul von Kai dein Garagentor aus IPS öffnen bzw. schließen.

Sollte noch ein potentialfreier Ausgang am Garagentorantrieb vorhanden sein, welcher bei geöffnetem bzw. geschlossenem Tor geschaltet wird, kann dieser über den GPIO 4 am Sonoff mit abgefragt werden.

Genau so wie Tkg sagt,
habe ich auch meine 3 Tore angebunden, aber mit je einem LCN Relais, welches den Taster kurz „überbrückt“.
Da braucht es das ganze Cloud Zeug nicht, und alles ist im lokalem Netz.
Und Rückmeldung kommt von einem Reedkontakt am Tor, wenn geschlossen. Besser wäre aber beide Endlagen zu überwachen.

Danke euch erstmal für die Inspirationen. Ein Schaltaktor wäre natürlich eine elegante Alternative, jedoch für mich nicht praktikabel da mit zu viel Aufwand verbunden. Vor allem die Rückmeldung müsste man wie beschrieben erfassen.

Ich habe eine Lösung mittels PHP und jetzt freier API gefunden. Den Code konnte ich jedoch nur kurz testen. Wenn ich fertig bin, werde ich euch diesen selbstverständlich in den nächsten Tagen hier bereit stellen.
Aktuell fehlt noch die Rückmeldung, falls die teuflische Cloud mal nicht erreichbar / das Gateway down / oder der Funkkontakt schlicht und einfach abbricht, implementiert ist.

Vielen Dank
Steffen

Hi Steffen,

da wäre ich auch dran interesiert, da ich ebenfalls diese Steuerung von Chamberlain habe.

Wäre natürlich das non + ultra wenn man die visu von der Webseite mit dem Tor auch ins WF bekommt :slight_smile:

Na dann, ich hab da zwar noch ein bisschen mehr mit gemacht aber hier mal alles was man zur korrekten Funktion benötigt.
So kann man darauf aufbauen und seiner Fantasie freien lauf lassen.

los geht es mit der Klasse.


<?php
/**
 * MyQ Klasse
 *
 * open/close/status Funktion
 *
 */


const MYQ_DOOR_ACTION_CLOSE = 0;

const MYQ_DOOR_ACTION_OPEN = 1;

const MYQ_DOOR_STATE_UNKNOWN = -1;

const MYQ_DOOR_STATE_OPEN = 1;

const MYQ_DOOR_STATE_CLOSED = 2;

const MYQ_DOOR_STATE_OPENING = 4;

const MYQ_DOOR_STATE_CLOSING = 5;


class MyQException extends Exception {}

class MyQState {

    protected $_state = MYQ_DOOR_STATE_UNKNOWN;

    protected $_stateTime = 0;

    protected $_stateDescriptions = array (
        MYQ_DOOR_STATE_UNKNOWN => 'unknown',
        MYQ_DOOR_STATE_OPEN => 'offen',
        MYQ_DOOR_STATE_CLOSED => 'geschlossen',
        MYQ_DOOR_STATE_OPENING => 'wird geöffnet',
        MYQ_DOOR_STATE_CLOSING => 'wird geschlossen',
    );

    public function __construct ($state=false, $timestamp=false) {
        if ($state) {
            $this->_state = $state;
        }
        $this->_stateTime = time();
        if ($timestamp) {
            $this->_stateTime = (int)$timestamp / 1000;            
        }
        return $this;
    }

    public function __get ($attr) {
        switch ($attr) {
            case 'desc':
                return $this->_stateDescriptions[$this->_state];
            case 'time':
            case 'updated':
            case 'date':
                return $this->_stateTime;
            case 'delta':
                return $this->_getTimeDelta('str');
            case 'seconds':
                return $this->_getTimeDelta();
        }
        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    public function __toString () {
        return $this->_stateDescriptions[$this->_state];
    }

    private function _getTimeDelta($opt=null) {
        $currentTz = date_default_timezone_get();
        date_default_timezone_set('UTC');
        $delta = time() - $this->_stateTime;

        if ($opt != 'str') {
            return $delta;
        }

        // Convert delta in human-readable format
        // via https://stackoverflow.com/a/43956977/98030
        $secondsInAMinute = 60;
        $secondsInAnHour = 60 * $secondsInAMinute;
        $secondsInADay = 24 * $secondsInAnHour;

        // Extract days
        $days = floor($delta / $secondsInADay);

        // Extract hours
        $hourSeconds = $delta % $secondsInADay;
        $hours = floor($hourSeconds / $secondsInAnHour);

        // Extract minutes
        $minuteSeconds = $hourSeconds % $secondsInAnHour;
        $minutes = floor($minuteSeconds / $secondsInAMinute);

        // Extract the remaining seconds
        $remainingSeconds = $minuteSeconds % $secondsInAMinute;
        $seconds = ceil($remainingSeconds);

        // Format and return
        $timeParts = [];
        $sections = [
            'Tag' => (int)$days,
            'Stunde' => (int)$hours,
            'Minute' => (int)$minutes,
            'Sekunde' => (int)$seconds,
        ];

        foreach ($sections as $name => $value){
            if ($value > 0){
                $timeParts[] = $value. ' '.$name.($value == 1 ? '' : 'n');
            }
        }

        return sizeof($timeParts) ? implode(', ', $timeParts) : '0 Sekunden';
    }

}

class MyQ {
    
    /** @var string|null $username contains the username used to authenticate with the MyQ API */
    protected $username = null;

    /** @var string|null $password contains the password used to authenticate with the MyQ API */
    protected $password = null;

    /** @var string|null $appId is the application ID used to register with the MyQ API */
    protected $appId = 'Vj8pQggXLhLy0WHahglCD4N1nAkkXQtGYpq2HrHD7H1nvmbT55KqtN6RSF4ILB/i';

    /** @var string|null $securityToken is the auth token returned after a successful login */
    protected $securityToken = null;

    /** @var string|null $userAgent is the User-Agent header value sent with each API request */
    protected $userAgent = 'Chamberlain/3.4.1';

    /** @var string|null $culture is the API culture code for the API */
    protected $culture = 'en';

    /** @var string|null $contentType is the content type used for all cURL requests */
    protected $contentType = 'application/json';

    /** @var array $headers contain HTTP headers for cURL requests */
    protected $_headers = array();

    protected $_deviceId = null;

    protected $_locationName = null;

    protected $_doorName = null;

    protected $_doorState = null;

    protected $_baseUrl = 'https://myqexternal.myqdevice.com/api/v4';

    protected $_loginUri = '/User/Validate';

    protected $_getDeviceDetailUri = '/UserDeviceDetails/Get';

    protected $_putDeviceStateUri = '/DeviceAttribute/PutDeviceAttribute';

    /** @var resource|null $_conn is the web connection to the MyQ API */
    protected $_conn = null;

    /**
     * Initializes class. Optionally allows user to override variables
     *
     * @param array $params A associative array for overwriting class variables
     *
     * @return MyQ
     */
    public function __construct ($params = array()) {
        // Overwrite class variables
        foreach ($params as $k => $v) {
            $this->$k = $v;
        }

        // Initialize cURL request headers
        if (sizeof($this->_headers) == 0) {
            $this->_headers = array (
                'MyQApplicationId' => $this->appId,
                'Culture' => $this->culture,
                'Content-Type' => $this->contentType,
                'User-Agent' => $this->userAgent,
            );
        }

        // Initialize cURL connection
        $this->_init();

        return $this;
    }

    public function __get ($attr) {
        if ($attr == 'state') {
            return $this->_doorState;
        }
        if (isset($this->$attr)) {
            return $this->$attr;
        }
        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    public function __set ($attr, $value) {

        switch ($attr) {
            case 'open':
                return ($value) ? $this->open() : $this->close();
                break;
            case 'close':
                return ($value) ? $this->close() : $this->open();
                break;
        }

        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __set(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }

    /**
     * Perform a login request
     *
     * @param string|null $username Username to use when logging in
     * @param string|null $password Password to use for logging in
     *
     * @return MyQ
     */
    public function login ($username = null, $password = null) {
        // Set username/password if not null
        if (!is_null($username)) {
            $this->username = $username;
        }
        if (!is_null($password)) {
            $this->password = $password;
        }

        // confirm that we have a valid username/password
        $error = array();
        if (is_null($this->username)) {
            $error[] = 'username';
        }
        if (is_null($this->password)) {
            $error[] = 'password';
        }
        if (sizeof($error) > 0) {
            throw new MyQException('Missing required auth credential: ' . implode(',', $error));
        }

        return $this->_login();
    }

    public function refresh () {
        $this->_getDetails();
        return $this;
    }

    public function open () {
        return $this->_requestState(MYQ_DOOR_ACTION_OPEN);
    }

    public function close () {
        return $this->_requestState(MYQ_DOOR_ACTION_CLOSE);
    }

    private function _requestState ($action) {
        // Fetch current device information
        $this->_getDetails();

        curl_setopt($this->_conn, CURLOPT_CUSTOMREQUEST, 'PUT');
        curl_setopt($this->_conn, CURLOPT_URL, $this->_baseUrl . $this->_putDeviceStateUri);
        $payload = array (
            'AttributeName' => 'desireddoorstate',
            'AttributeValue' => $action,
            'MyQDeviceId' => $this->_deviceId,
        );
        curl_setopt($this->_conn, CURLOPT_POSTFIELDS, json_encode($payload));
        $output = curl_exec($this->_conn);
        $err = curl_error($this->_conn);

        if ($err) {
           throw new MyQException("cURL Error #:" . $err);
        }

        $data = json_decode($output);
        if ($data == false) {
            throw new MyQException("Error updating device state: $output");
        }

        if (strlen($data->ErrorMessage) > 0 || $data->ReturnCode != 0) {
            throw new MyQException("Error returned from API: " . var_export($data));
        }

        // Update was successful, fetch the new status and report
        return $this->refresh();

    }

    private function _init () {
        if (!isset($this->_conn)) {
            $this->_conn = curl_init();
            curl_setopt_array($this->_conn, array (
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_ENCODING => "",
                CURLOPT_MAXREDIRS => 10,
                CURLOPT_TIMEOUT => 30,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
                CURLOPT_FAILONERROR => true,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_FRESH_CONNECT => true,
                CURLOPT_FORBID_REUSE => true,
                CURLOPT_USERAGENT => $this->userAgent,
            ));
        }
        $this->_setHeaders();
    }

    private function _setHeaders () {
        $headers = array();
        foreach ($this->_headers as $k => $v) {
            $headers[] = "$k: $v";
        }
        curl_setopt($this->_conn, CURLOPT_HTTPHEADER, $headers);
    }

    private function _login () {
        $this->_init();

        curl_setopt($this->_conn, CURLOPT_CUSTOMREQUEST, 'POST');
        curl_setopt($this->_conn, CURLOPT_URL, $this->_baseUrl . $this->_loginUri);

        $post = json_encode(array('username' => $this->username, 'password' => $this->password));
        curl_setopt($this->_conn, CURLOPT_POSTFIELDS, $post);
        $output = curl_exec($this->_conn);
        $data = json_decode($output);
        if ($data == false || !isset($data->SecurityToken)) {
            throw new MyQException("Error processing login request: $output");
        }
        $this->_headers['SecurityToken'] = $data->SecurityToken;
        return $this;
    }

    private function _getDetails ($getState = false, $forceUpdate = false) {
        $this->_init();

        // always fetch state info from API. Location data is not expected to have changed
        $cachedLocation = true;
        if ($getState === false && $forceUpdate !== true) {
            // get the location information
            // check to see if we have this information cached already
            if ( $forceUpdate === false && ! (is_null($this->_doorName) || is_null($this->_locationName) ) ) {
                return;
            }
            $cachedLocation = false;
        }

        curl_setopt($this->_conn, CURLOPT_CUSTOMREQUEST, 'GET');
        curl_setopt($this->_conn, CURLOPT_URL, $this->_baseUrl . $this->_getDeviceDetailUri);

        $output = curl_exec($this->_conn);
        $data = json_decode($output);
        if ($data == false || !isset($data->Devices)) {
            throw new MyQException("Error fetching device details: $output");
        }

        // Find our door device ID
        // (we only look at the first listed device - later we can look for a device by name)
        foreach ($data->Devices as $device) {
            if ( $cachedLocation === false && stripos($device->MyQDeviceTypeName, "Gateway") !== false ) {
                // Find location name
                foreach ($device->Attributes as $attr) {
                    if ($attr->AttributeDisplayName == 'desc') {
                        $this->_locationName = $attr->Value;
                    }
                }
                continue; // we don't want device info on the location
            }
            
            // we should be looking at just our WGDO unit
            $this->_deviceId = $device->MyQDeviceId;
            
            foreach ($device->Attributes as $attr) {
                switch ($attr->AttributeDisplayName) {
                    case 'desc':
                        $this->_doorName = $attr->Value;
                        break;
                    case 'doorstate':
                        $this->_doorState = new MyQState($attr->Value, $attr->UpdatedTime);
                        break;
                    default:
                        continue;
                }
            }

            break; // skip any other devices
        }

    }

}
?>

(keine Eigenentwicklung, vielen Dank an den Entwickler)

und hier der Aufruf:


<?php

//absoluter Pfad zur Klasse
require_once('C:\IP-Symcon\scripts\47161 /*[Scripte\Eigenentwicklung\Garagentor\MyQ-Class]*/.ips.php');

$door = new MyQ();
$door->login("<EMAIL>", "<Passwort>");

//Aktueller Status ermitteln
$report = function() {
    global $door;
	$status = "{$door->refresh()->state}";
	$zeit = "{$door->state->delta}";

//Variablen für Zustand setzen

 $subst = "Garagentor seit $zeit $status";
 setvalue(42271 /*[Wohnung DG\Garage\Garage Status]*/,$subst);

};
// Current state aufruf
$report();
//--------------------


//Ein Beispiel Tor öffnen
$door->open();
//--------------------

//Ein Beispiel zum Tor schließen
$door->close = true;
//--------------------

?>

Wenn man jetzt je ein Script für „open“, „close“ und „status“ (alle Sekunden ausführen) erstellt, hat man eigentlich alles was man braucht. Selbstverständlich wird der Status auch bei einem bsp… per Handsender ausgelösten öffnen aktualisiert.

Einen Bug gibt es aktuell natürlich auch.
die Zeiten beim öffnen und schließen (also nur die paar Sekunden bis das Tor in Endstellung ist) werden noch wirr dargestellt. Mich stört dies jedoch nicht, da ich nur offen, geschlossen, öffnen und schließen anzeigen / bzw. per TTS ausgeben lassen.

und das beste dran. der berühmt berüchtigte WAF kann bei der all abendlichen Ansage „Die Garagentür wird geöffnet“ gleich das kühle Blonde bereitstellen. Das schlägt wohl alle Argumente gegen Cloud und Co…

Bei Fragen, fragen.

Viel Spaß mit dem Script

Gruß Steffen

Hey Steffen,

cool vielen Dank. Aktuel Stuere ich mein Tor mit einem Zwave aktor. Dies kann ich dann jetzt damit ablösen :slight_smile:

Vielen Dank für die Bereitstellung und erklärung.

Ich teste es mal.

Wenn das läuft versuche ich mal auch ein Tor als Visu einzubauen :slight_smile:

Danke

HI kurze Frage,

kannst du mir ein Beispiel senden wie du das meinst ?

Wenn man jetzt je ein Script für „open“, „close“ und „status“ (alle Sekunden ausführen) erstellt, hat man eigentlich alles was man braucht. Selbstverständlich wird der Status auch bei einem bsp… per Handsender ausgelösten öffnen aktualisiert.

Bekomme den Staus leider nicht hin.

Hi Padi,

Sorry ich war die letzte Zeit etwas verhindert.

Kannst du mir denn sagen wo du genau hängst?
Dann kann ich dir sicher weiterhelfen.

Gruß
Steffen

und hier der Aufruf:rein nur Status


<?php

//absoluter Pfad zur Klasse
require_once('C:\IP-Symcon\scripts\47161'); // hier kommt der Absolute Pfad zum Script bzw. Der Klasse rein

$door = new MyQ();
$door->login("<EMAIL>", "<Passwort>"); //Benutzername und Passwort

//Aktueller Status ermitteln
$report = function() {
    global $door;
	$status = "{$door->refresh()->state}";
	$zeit = "{$door->state->delta}";

//Variablen für Zustand setzen

 $subst = "Garagentor seit $zeit $status";
 setvalue(42271 /*[Wohnung DG\Garage\Garage Status]* //Setzen einer String Variable mit dem aktuellen Status

};
// Current state aufruf
$report();
//--------------------


?>

Hier das Script für die reine Statusabfrage. Dieses kann man alle Sekunde ausführen lassen.

Gruß
Steffen

Hi SteffenK,

danke für das Status Script.

Ich hänge bei den Befehlen. Wie sendest du die Befehle für Öffnen und Schließen?

Aktuell habe ich kein Inet daheim wegen umstellung -.- kann also nicht testen Vodafon bekommt es nicht auf die Kette.

Wenn du mit noch das Script für die Befehle Auf/Zu posten köntest hätte ich glaube ich soweit alles.

Status,AUF,ZU Vielen Dank

Na ganz einfach:
Öffnen


<?php

//absoluter Pfad zur Klasse
require_once('C:\IP-Symcon\scripts\47161 /*[Scripte\Eigenentwicklung\Garagentor\MyQ-Class]*/.ips.php');


$door = new MyQ();
$door->login("<Mail>", "<Passwort>");

// Open the door
$door->open();
?>

Schließen:


<?php

//absoluter Pfad zur Klasse
require_once('C:\IP-Symcon\scripts\47161 /*[Scripte\Eigenentwicklung\Garagentor\MyQ-Class]*/.ips.php');


$door = new MyQ();
$door->login("<Mail>", "<Passwort>");


$door->close = true;
?>

Hi Steffen,
danke ja manchmal sieht man vor lauter Bäumen den Wald kaum.
funktioniert soweit :slight_smile:

danke noch mal

Vielen Dank fürs posten, hatte das Skript gleich annektiert :slight_smile:

Den Entwickler findet man wohl hier:

https://github.com/angrychimp/php-myq-liftmaster/blob/master/MyQ.php

angrychimp/php-myq-liftmaster is licensed under the MIT License
A short and simple permissive license with conditions only requiring preservation of copyright and license notices. 
Licensed works, modifications, and larger works may be distributed under different terms and without source code.

Das Skript hat in meinem IPS-System 6 Monate gut funktioniert, seit kurzem bekomme ich diese Fehlermeldung:

Fatal error:  Uncaught MyQException: Error processing login request

Hab jetzt schon mein Passwort bei ‚www.mychamberlain.eu‘ geändert ohne Erfolg.

Hat jemand eine Lösung/Erklärung ?

Viele Grüsse
Harald

Geht bei mir leider auch nicht mehr.

Scheint an der API zu liegen. Wurde von 4 auf 5 geändert.

API login issues again · Issue #22 · arraylabs/pymyq · GitHub

Moin Moin zusammen,

Wildsau nennt doch hier schon die Lösung…

Hat denn schon jemand die Python Lösung gebastelt?
Wenn nicht würde ich mich schnell mal darum kümmern.

Wird dann aber etwas komplexer wenn IPS nicht bereits auf einem Raspi oder dergleichen ausgeführt wird.

Gruß
Steffen