Coole Uhr für Symcon - Ulanzi / La Metric Clone

Ich hatte bisher noch nichts an Code geteilt, weil ich leider meine Integration von Anfang an sehr tief mit den Eigenheiten meines Systems verwoben hatte. Aber hier einmal etwas Code zum Anzeigen von momentanen Meldungen oder wiederkehrenden („Apps“).

Das ganze ist so gedacht, dass es als Include-Datei innerhalb anderer Skripte verwendet werden kann. Zum Einrichten ein Skript erstellen, Code hinein kopieren und einmal ausführen. Skript ist dann fortan als AWTL.ips.php inkludierbar.

// Enter all sounds you defined in the MELODIES folder
$awtl_sounds = ["blip", "notification", (...)];

// Give string names to numeric icon indexes. It's possible
// to put indexes in quotes so the IPS console doesn't confuse
// them with object IDs.
$awtl_icons = array(
    "door" => "20969",
    "key" => 106,
    (...)
);

// Set the ID of the MQTT Server isntance
$awtl_server_id = (...);

// can be handy to define arrays for when you want to
// address several clocks at the same time
define('ALL_CLOCKS', ["uhr1", "uhr2", (...)]);
define('KITCHEN_CLOCK', ["uhr1", "uhr4"]);

//////////////////////////////////////////////////////////////////////////////////////
// No changes required beyond this point

// Setup routine. Execute this script in the console.
$script_dir = IPS_GetKernelDir() . '/scripts/';
$include_file_script_id = @IPS_GetScriptIDByFile(substr(__FILE__, strlen($script_dir) - 1));
if($include_file_script_id === false || $include_file_script_id == 0) {
    die("Script ID of include file could not be determined!");
}
if($include_file_script_id == $_IPS['SELF']) {
    /* Upon launching this script, ensure it's got a memorable name for
    easy including
    */
    $script_pretty_name = "AWTL.ips.php";
    $script_file_name = IPS_GetScript($_IPS['SELF'])['ScriptFile'];
    if($script_file_name != $script_pretty_name) {
        echo "File name changed to " . $script_pretty_name . " for easy including.\n";
        rename(
            $script_dir . $script_file_name,
            $script_dir . $script_pretty_name
        );
        IPS_SetScriptFile($_IPS['SELF'], $script_pretty_name);
    } else {
        echo "File name is " . $script_pretty_name . " for easy including.\n";
    }
}

// Power to the matrix (or not)
function AWTL_SetPower($clock_prefix, $status) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SetPower(
                $this_clock_prefix, $status
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_bool($status)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/power";

    $payload = array(
        "power" => $status
    );

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SetPower

function AWTL_Reboot($clock_prefix) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Reboot(
                $this_clock_prefix
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    // prepare topic name
    $topic = $clock_prefix . "/reboot";

    $payload = "";

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_Reboot

function AWTL_SetIndicator($clock_prefix, $color, $bottom = false, $blink = false) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SetIndicator(
                $this_clock_prefix, $color, $bottom, $blink
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_bool($bottom)) return false;
    if(!is_bool($blink)) return false;
    if(!is_string($color)) return false;

    // prepare topic name
    if($bottom) {
        $topic = $clock_prefix . "/indicator2";
    } else {
        $topic = $clock_prefix . "/indicator1";
    }

    $payload = array(
        "color" => $color,
        "blink" => $blink
    );

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SetIndicator

/* Display a message or create/update an "app" on the Awtrix Lite clock.
    $clock_prefix - The first part of the topic name specific to the clock you want
        to address. Can also be an array of strings if you want to address several
        clocks at the same time.
    $text - The text message to display or an empty string to close an app / dis-
        appear a "hold" message
    $color - A hex string specifying the color for the text message
    $duration - Number of seconds for message to display. Can be false to automa-
        tically set duration or negative value which stands for "times to scroll
        through message"
    $icon - Numeric icon index or string referring to a key entry in the
        $awtl_icons associative array, or false for no icon
    $app_name_or_sound - String either specifying a "melody" entry specified in
        the $awtl_sounds array, or a name for an "app" to create/update/vanish
        or false to display notification without sound
    $options - optional additional settings as an associative array
*/
function AWTL_Message($clock_prefix, $text, $color = "FFFFFF", $duration = false, $icon = false, $app_name_or_sound = false, $options = false) {
    global $awtl_sounds, $awtl_icons, $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Message(
                $this_clock_prefix,
                $text, $color, $duration, $icon, $app_name_or_sound, $options
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    // prepare topic name
    $topic = $clock_prefix . "/";

    // if additional options array specified, use it as basis for payload array
    if(is_array($options)) {
        $payload = $options;
    } else if($options === false) { // otherwise create emtpy array
        $payload = array();
    } else {
        AWTL_Error('Wrong data type for parameter $options');
        return false;
    }
    
    $payload["text"] = $text; // text can be string or compley array for multicolored text

    // allow specifying no color (false) if not needed
    if(is_string($color)) {
        $payload["color"] = $color;
    } else if($color !== false) {
        AWTL_Error('Wrong data type for parameter $color');
        return false;
    }
    
    if(in_array($app_name_or_sound, $awtl_sounds)) { // parameter specifies a "melody"
        $payload["sound"] = $app_name_or_sound;
        $topic .= "notify";
    } else if(is_string($app_name_or_sound) && strlen($app_name_or_sound) > 0) { // parameter specifies custom "app"
        $topic .= "custom/" . $app_name_or_sound;
    } else if($app_name_or_sound === false) { // just a notification with no sound
        $topic .= "notify";
    } else {
        AWTL_Error('Wrong data type for parameter $app_name_or_sound');
        return false;
    }

    if(is_int($icon)) { // numeric index
        $payload["icon"] = $icon;
    } else if(is_string($icon) && array_key_exists($icon, $awtl_icons)) { // string key
        $payload["icon"] = intval($awtl_icons[$icon]);
    } else if($icon !== false) {
        AWTL_Error('Wrong data type for parameter $icon or icon not found');
        return false;
    }

    // automatically set duration if false is specified
    if($duration === false && is_string($text)) {
        $duration = strlen($text);
        if($duration < 3) $duration = 3; 
        if($duration > 10) $duration = 10; 
    } else if(!is_int($duration)) {
        AWTL_Error('Wrong data type for parameter $duration');
        return false;
    }

    if($duration < 0) { // negative value for duration means number of repeats
        $payload["repeat"] = -$duration;
    } else {
        $payload["duration"] = $duration;
    }

    // no text means we want to disappear something
    if($text == "") {
        if($app_name_or_sound === false) $topic .= "/dismiss";
        $payload = "";
    }

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
}

// change a set of settings
function AWTL_Settings($clock_prefix, $payload) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_Settings(
                $this_clock_prefix, $payload
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_array($payload)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/settings";

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_Settings

/* Switch to another app
    $app_name - Name of a builtin or user defined app, or "previous" or "next"
*/
function AWTL_SwitchApp($clock_prefix, $app_name) {
    global $awtl_server_id;

    // allow specifying an array of prefixes in order to address several clocks at the same time
    if(is_array($clock_prefix)) {
        $result = true;
        foreach($clock_prefix as $this_clock_prefix) {
            if(!AWTL_SwitchApp(
                $this_clock_prefix, $app_name
            )){
                $result = false;
            }
        }
        return $result; // return true if all were successful
    }

    if(!is_string($app_name)) return false;

    // prepare topic name
    $topic = $clock_prefix . "/";
    
    if($app_name == "next") {
        $topic .= "nextapp";
        $payload = "";
    } else if($app_name == "previous") {
        $topic .= "previousapp";
        $payload = "";
    } else {
        $topic .= "switch";
        $payload = array(
            "name" => $app_name
        );
    }

    return MQTT_Publish(
        $awtl_server_id,
        $topic,
        $payload
    );
} // AWTL_SwitchApp

/* Publish a given payload over a MQTT server under
   a given topic without having to manually create a
   MQTT server device */
function MQTT_Publish($server_id, $topic, $payload, $retain = false) {
    global $include_file_script_id;

    // ensure server instance exists
    if(!IPS_InstanceExists($server_id)) {
        AWTL_Error('Server instance not found');
        return false;
    }

    // convert array structure to json string
    if(is_array($payload)) $payload = json_encode($payload, JSON_UNESCAPED_UNICODE);

    // determine data type
    if(is_string($payload)) {
        $ips_var_type = 3;
    } else if(is_float($payload)) {
        $ips_var_type = 2;
    } else if(is_int($payload)) {
        $ips_var_type = 1;
    } else if(is_bool($payload)) {
        $ips_var_type = 0;
    } else { // unsupported
        AWTL_Error('Unsupported data type for parameter $payload');
        return false;
    }
    //echo $payload;
    $module_id = "{01C00ADD-D04E-452E-B66A-D253278743FE}" /* Module ID of MQTT Server Device */;
    $ident = "TempMQTTDevice";

    // enter semaphore to ensure the temporary device gets used by one thread at a time
    if(IPS_SemaphoreEnter($ident, 100)) {
        // get temporary MQTT Server Device or create if needed
        $id = @IPS_GetObjectIDByIdent($ident, $include_file_script_id);
        if($id === false) {
            $id = @IPS_CreateInstance($module_id);
            if($id === false) {
                AWTL_Error('Failed to create temporary MQTT Server Device');
                return false;
            }
            IPS_SetParent($id, $include_file_script_id);
            IPS_SetIdent($id, $ident);
        }

        // ensure the specified server instance is actually compatible
        if(!IPS_IsInstanceCompatible($id, $server_id)) {
            AWTL_Error('Server instance is not compatible');
            return false;
        }

        // ensure that the temporary device is actually connected to the correct server instance
        $inst_config = IPS_GetInstance($id);
        if($inst_config["ConnectionID"] != $server_id) {
            IPS_DisconnectInstance($id);
            if(!@IPS_ConnectInstance($id, $server_id)) {
                AWTL_Error('Failed to connect temporary MQTT Server Device');
                return false;
            }
        }

        // name object to help with debugging
        IPS_SetName($id, "Temporary MQTT Server Device for topic " . $topic);

        // configure temporary device
        $config_arr = array(
            "Retain" => $retain,
            "Topic" => $topic,
            "Type" => $ips_var_type
        );
        $config_str = json_encode($config_arr);
        IPS_SetConfiguration($id, $config_str);
        IPS_ApplyChanges($id);

        // get Value variable and use it to publish the payload
        $var_id = @IPS_GetObjectIDByIdent("Value", $id);
        RequestAction($var_id, $payload);

        IPS_SemaphoreLeave($ident);
    } else { // semaphore timeout
        AWTL_Error('Semaphore timeout');
        return false;
    }

    return true;
} // MQTT_Publish

function AWTL_Error($msg) {
    if($_IPS['SENDER'] == 'Execute') {
        echo "Fehler: " . $msg . "\n";
    }
}
1 „Gefällt mir“