Compare commits

...
10 Commits
Author SHA1 Message Date
jhartworks 3df3995da5 Refactor AwtrixApp module: streamline variable registration, enhance payload handling, and improve JSON request methods. Update library version to 1.3 build 5. 2026-06-12 09:03:39 +02:00
jhartworks 3b83bf552c Update library version and add AwtrixApp module
- Updated library.json to version 1.3 and compatibility version to 9.0.
- Added new AwtrixApp module with comprehensive functionality for managing app payloads, including variable registration, JSON request handling, and app lifecycle management.
- Implemented methods for building app payloads, sending requests, and handling app deletion.
- Introduced various configuration options for text display, colors, gradients, and progress indicators.
2026-06-12 08:48:48 +02:00
jhartworks f2a35e1eff False ip check 2026-04-11 12:45:54 +02:00
jhartworks 5a892e12f1 added app 2026-03-02 22:39:23 +01:00
jhartworks eda387abf4 Added App but not ready 2026-03-02 22:38:34 +01:00
jhartworks 0800b12a7f Add timeout setting for cURL requests in CheckIP method 2026-03-02 13:09:11 +01:00
jhartworks a1743dbaa4 Remove initial status setting in Create method of AwtrixSettings class 2026-03-02 12:56:25 +01:00
jhartworks e7e892e766 Refactor SendSetting and SendReboot methods to include IP check; update module status codes and increment build number in library.json 2026-03-02 12:54:16 +01:00
jhartworks 5b5e28ee50 Add IP checking functionality and improve Create method in AwtrixSettings 2026-03-02 12:46:29 +01:00
jhartworks 1d55d7eaa2 version change 2026-03-02 12:44:42 +01:00
6 changed files with 1092 additions and 377 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"elements":
[
{ "type": "ValidationTextBox", "name": "AwtrixIp", "caption": "IP or Hostname of Awtrix Clock" },
{ "type": "ValidationTextBox", "name": "Prefix", "caption": "" },
{ "type": "ValidationTextBox", "name": "Suffix", "caption": "" }
],
"actions":
[
{ "type": "Button", "label": "Send App", "onClick": "AWAPP_SendApp($id);" }
],
"status":
[
]
}
+14
View File
@@ -0,0 +1,14 @@
{
"id": "{6637E1B4-E90A-68C3-4FFD-CE8969F9F693}",
"name": "Awtrix App",
"type": 3,
"vendor": "Awtrix",
"aliases":
[
],
"url": "https://blueforcer.github.io/",
"parentRequirements": [],
"childRequirements": [],
"implemented": [],
"prefix": "AWAPP"
}
+545
View File
@@ -0,0 +1,545 @@
<?php
class AwtrixApp extends IPSModule
{
public function Create()
{
parent::Create();
$this->RegisterAttributeString('LastPayload', '');
$this->RegisterAttributeString('LastAppName', '');
$this->RegisterPropertyString('AwtrixIp', '172.78.88.67');
$this->RegisterPropertyString('AppName', '');
$this->RegisterPropertyString('Prefix', '');
$this->RegisterPropertyString('Suffix', '');
$this->RegisterVariables();
if ($this->ReadAttributeString('LastPayload') === '') {
$this->WriteAttributeString('LastPayload', $this->NormalizeForCompare($this->BuildAppPayload()));
}
if ($this->ReadAttributeString('LastAppName') === '') {
$this->WriteAttributeString('LastAppName', $this->GetAppName());
}
}
public function ApplyChanges()
{
parent::ApplyChanges();
$payload = $this->BuildAppPayload();
$payloadChanged = $this->NormalizeForCompare($payload) !== $this->ReadAttributeString('LastPayload');
$appNameChanged = $this->GetAppName() !== $this->ReadAttributeString('LastAppName');
if (!$payloadChanged && !$appNameChanged) {
$this->SetStatus(102);
return;
}
$this->SendApp();
}
public function RequestAction($Ident, $Value)
{
SetValue($this->GetIDForIdent($Ident), $Value);
$this->SendApp();
}
public function SendApp()
{
$appName = $this->GetAppName();
$payload = $this->BuildAppPayload();
$previousAppName = $this->ReadAttributeString('LastAppName');
if ($previousAppName !== '' && $previousAppName !== $appName) {
$this->DeleteAppByName($previousAppName);
}
if (!$this->SendJsonRequest('/api/custom?name=' . rawurlencode($appName), $payload)) {
return false;
}
$this->WriteAttributeString('LastPayload', $this->NormalizeForCompare($payload));
$this->WriteAttributeString('LastAppName', $appName);
return true;
}
public function DeleteApp()
{
$deleted = $this->DeleteAppByName($this->GetAppName());
if ($deleted) {
$this->WriteAttributeString('LastPayload', '');
$this->WriteAttributeString('LastAppName', '');
}
return $deleted;
}
private function RegisterVariables()
{
$p = 0;
$this->RegisterStringVariableWithDefault('text', 'Text', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterIntegerVariableWithDefault('textCase', 'Text Case', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 2,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5
], $p += 10, 0);
$this->RegisterBooleanVariableWithDefault('topText', 'Top Text', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, false);
$this->RegisterIntegerVariableWithDefault('textOffset', 'Text Offset', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 32,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5
], $p += 10, 0);
$this->RegisterBooleanVariableWithDefault('center', 'Center Short Text', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, true);
$this->RegisterIntegerVariableWithDefault('color', 'Text/Chart Color', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 16777215);
$this->RegisterBooleanVariableWithDefault('gradientEnabled', 'Gradient Enabled', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, false);
$this->RegisterIntegerVariableWithDefault('gradient01', 'Gradient Color 1', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 16777215);
$this->RegisterIntegerVariableWithDefault('gradient02', 'Gradient Color 2', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 16777215);
$this->RegisterIntegerVariableWithDefault('blinkText', 'Blink Text (ms)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 10000,
'STEP_SIZE' => 100,
'USAGE_TYPE' => 5,
'SUFFIX' => ' ms'
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('fadeText', 'Fade Text (ms)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 10000,
'STEP_SIZE' => 100,
'USAGE_TYPE' => 5,
'SUFFIX' => ' ms'
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('background', 'Background Color', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 0);
$this->RegisterBooleanVariableWithDefault('rainbow', 'Rainbow Text', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, false);
$this->RegisterStringVariableWithDefault('icon', 'Icon (ID/Filename/Base64)', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterIntegerVariableWithDefault('pushIcon', 'Push Icon', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 2,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('repeat', 'Repeat (-1 Forever)', $this->NumericInputPresentation(), $p += 10, -1);
$this->RegisterIntegerVariableWithDefault('duration', 'Duration (s)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 1,
'MAX' => 300,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5,
'SUFFIX' => ' s'
], $p += 10, 5);
$this->RegisterStringVariableWithDefault('bar', 'Bar Values (JSON/CSV)', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterStringVariableWithDefault('line', 'Line Values (JSON/CSV)', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterBooleanVariableWithDefault('autoscale', 'Autoscale Bar/Line', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, true);
$this->RegisterIntegerVariableWithDefault('barBC', 'Bar Background Color', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('progress', 'Progress (-1 Off, 0-100)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => -1,
'MAX' => 100,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5
], $p += 10, -1);
$this->RegisterIntegerVariableWithDefault('progressC', 'Progress Color', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 16777215);
$this->RegisterIntegerVariableWithDefault('progressBC', 'Progress Background Color', [
'PRESENTATION' => VARIABLE_PRESENTATION_COLOR,
'ENCODING' => 0,
'COLOR_SPACE' => 0,
'COLOR_CURVE' => 0
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('pos', 'Loop Position', $this->NumericInputPresentation(), $p += 10, 0);
$this->RegisterStringVariableWithDefault('draw', 'Draw Instructions (JSON)', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterIntegerVariableWithDefault('lifetime', 'Lifetime (s)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 86400,
'STEP_SIZE' => 10,
'USAGE_TYPE' => 5,
'SUFFIX' => ' s'
], $p += 10, 0);
$this->RegisterIntegerVariableWithDefault('lifetimeMode', 'Lifetime Mode', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 0,
'MAX' => 1,
'STEP_SIZE' => 1,
'USAGE_TYPE' => 5
], $p += 10, 0);
$this->RegisterBooleanVariableWithDefault('noScroll', 'Disable Scrolling', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, false);
$this->RegisterIntegerVariableWithDefault('scrollSpeed', 'Scroll Speed (%)', [
'PRESENTATION' => VARIABLE_PRESENTATION_SLIDER,
'MIN' => 10,
'MAX' => 200,
'STEP_SIZE' => 5,
'USAGE_TYPE' => 5,
'SUFFIX' => ' %'
], $p += 10, 100);
$this->RegisterStringVariableWithDefault('effect', 'Background Effect', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterStringVariableWithDefault('effectSettings', 'Effect Settings (JSON Map)', $this->StringInputPresentation(), $p += 10, '');
$this->RegisterBooleanVariableWithDefault('save', 'Save App To Flash', [
'PRESENTATION' => VARIABLE_PRESENTATION_SWITCH
], $p += 10, false);
$this->RegisterStringVariableWithDefault('overlay', 'Overlay', $this->StringInputPresentation(), $p += 10, 'clear');
$this->RegisterVariableString('LastPayloadPreview', 'Last Payload', $this->StringInputPresentation(), $p += 10);
}
private function RegisterIntegerVariableWithDefault($ident, $name, array $presentation, $position, $defaultValue)
{
$exists = $this->HasIdent($ident);
$this->RegisterVariableInteger($ident, $name, $presentation, $position);
$this->EnableAction($ident);
if (!$exists) {
SetValueInteger($this->GetIDForIdent($ident), $defaultValue);
}
}
private function RegisterBooleanVariableWithDefault($ident, $name, array $presentation, $position, $defaultValue)
{
$exists = $this->HasIdent($ident);
$this->RegisterVariableBoolean($ident, $name, $presentation, $position);
$this->EnableAction($ident);
if (!$exists) {
SetValueBoolean($this->GetIDForIdent($ident), $defaultValue);
}
}
private function RegisterStringVariableWithDefault($ident, $name, array $presentation, $position, $defaultValue)
{
$exists = $this->HasIdent($ident);
$this->RegisterVariableString($ident, $name, $presentation, $position);
$this->EnableAction($ident);
if (!$exists) {
SetValueString($this->GetIDForIdent($ident), $defaultValue);
}
}
private function HasIdent($ident)
{
return (@$this->GetIDForIdent($ident) > 0);
}
private function BuildAppPayload()
{
$payload = [
'textCase' => GetValueInteger($this->GetIDForIdent('textCase')),
'topText' => GetValueBoolean($this->GetIDForIdent('topText')),
'textOffset' => GetValueInteger($this->GetIDForIdent('textOffset')),
'center' => GetValueBoolean($this->GetIDForIdent('center')),
'color' => $this->ColorToApi(GetValueInteger($this->GetIDForIdent('color'))),
'blinkText' => GetValueInteger($this->GetIDForIdent('blinkText')),
'fadeText' => GetValueInteger($this->GetIDForIdent('fadeText')),
'background' => $this->ColorToApi(GetValueInteger($this->GetIDForIdent('background'))),
'rainbow' => GetValueBoolean($this->GetIDForIdent('rainbow')),
'pushIcon' => GetValueInteger($this->GetIDForIdent('pushIcon')),
'repeat' => GetValueInteger($this->GetIDForIdent('repeat')),
'duration' => GetValueInteger($this->GetIDForIdent('duration')),
'autoscale' => GetValueBoolean($this->GetIDForIdent('autoscale')),
'noScroll' => GetValueBoolean($this->GetIDForIdent('noScroll')),
'scrollSpeed' => GetValueInteger($this->GetIDForIdent('scrollSpeed'))
];
$text = $this->ReadPropertyString('Prefix') . GetValueString($this->GetIDForIdent('text')) . $this->ReadPropertyString('Suffix');
if ($text !== '') {
$payload['text'] = $text;
}
$icon = trim(GetValueString($this->GetIDForIdent('icon')));
if ($icon !== '') {
$payload['icon'] = $icon;
}
if (GetValueBoolean($this->GetIDForIdent('gradientEnabled'))) {
$payload['gradient'] = [
$this->ColorToApi(GetValueInteger($this->GetIDForIdent('gradient01'))),
$this->ColorToApi(GetValueInteger($this->GetIDForIdent('gradient02')))
];
}
$bar = $this->ParseIntListVariable('bar');
if ($bar !== null) {
$payload['bar'] = $bar;
$payload['barBC'] = $this->ColorToApi(GetValueInteger($this->GetIDForIdent('barBC')));
}
$line = $this->ParseIntListVariable('line');
if ($line !== null) {
$payload['line'] = $line;
}
$progress = GetValueInteger($this->GetIDForIdent('progress'));
if ($progress >= 0) {
$payload['progress'] = $progress;
$payload['progressC'] = $this->ColorToApi(GetValueInteger($this->GetIDForIdent('progressC')));
$payload['progressBC'] = $this->ColorToApi(GetValueInteger($this->GetIDForIdent('progressBC')));
}
$draw = $this->ParseJsonVariable('draw', true);
if ($draw !== null) {
$payload['draw'] = $draw;
}
$pos = GetValueInteger($this->GetIDForIdent('pos'));
if ($pos >= 0) {
$payload['pos'] = $pos;
}
$lifetime = GetValueInteger($this->GetIDForIdent('lifetime'));
if ($lifetime > 0) {
$payload['lifetime'] = $lifetime;
$payload['lifetimeMode'] = GetValueInteger($this->GetIDForIdent('lifetimeMode'));
}
$effect = trim(GetValueString($this->GetIDForIdent('effect')));
if ($effect !== '') {
$payload['effect'] = $effect;
}
$effectSettings = $this->ParseJsonVariable('effectSettings', false);
if ($effectSettings !== null) {
$payload['effectSettings'] = $effectSettings;
}
if (GetValueBoolean($this->GetIDForIdent('save'))) {
$payload['save'] = true;
}
$overlay = trim(GetValueString($this->GetIDForIdent('overlay')));
if ($overlay !== '') {
$payload['overlay'] = $overlay;
}
return $payload;
}
private function ParseIntListVariable($ident)
{
$value = trim(GetValueString($this->GetIDForIdent($ident)));
if ($value === '') {
return null;
}
$json = json_decode($value, true);
if (is_array($json)) {
return array_map('intval', $json);
}
$parts = preg_split('/[\s,;]+/', $value, -1, PREG_SPLIT_NO_EMPTY);
if (!is_array($parts) || count($parts) === 0) {
return null;
}
return array_map('intval', $parts);
}
private function ParseJsonVariable($ident, $expectList)
{
$value = trim(GetValueString($this->GetIDForIdent($ident)));
if ($value === '') {
return null;
}
$decoded = json_decode($value, true);
if (json_last_error() !== JSON_ERROR_NONE || !is_array($decoded)) {
IPS_LogMessage('AWTRIX', 'Invalid JSON in ' . $ident . ': ' . json_last_error_msg());
return null;
}
if ($expectList && array_keys($decoded) !== range(0, count($decoded) - 1)) {
IPS_LogMessage('AWTRIX', 'Expected JSON array in ' . $ident);
return null;
}
if (!$expectList && array_keys($decoded) === range(0, count($decoded) - 1)) {
IPS_LogMessage('AWTRIX', 'Expected JSON object/map in ' . $ident);
return null;
}
return $decoded;
}
private function GetAppName()
{
$appName = trim($this->ReadPropertyString('AppName'));
if ($appName === '') {
$appName = IPS_GetName($this->InstanceID);
}
$appName = preg_replace('/\s+/', '_', $appName);
$appName = preg_replace('/[^A-Za-z0-9_-]/', '', $appName);
if ($appName === '' || $appName === null) {
$appName = 'app_' . $this->InstanceID;
}
return $appName;
}
private function DeleteAppByName($appName)
{
if ($appName === '') {
return true;
}
return $this->SendRawPost('/api/custom?name=' . rawurlencode($appName), '');
}
private function SendJsonRequest($path, array $payload)
{
$response = $this->RequestAwtrix('POST', $path, json_encode($payload), ['Content-Type: application/json']);
if ($response['success']) {
SetValueString($this->GetIDForIdent('LastPayloadPreview'), json_encode($payload));
}
return $response['success'];
}
private function SendRawPost($path, $body)
{
return $this->RequestAwtrix('POST', $path, $body, [])['success'];
}
private function RequestAwtrix($method, $path, $body = null, array $headers = [])
{
$awtrixIp = trim($this->ReadPropertyString('AwtrixIp'));
$url = 'http://' . $awtrixIp . $path;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 2000);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 5000);
if ($method === 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
if (count($headers) > 0) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
} else {
curl_setopt($ch, CURLOPT_HTTPGET, true);
}
$response = curl_exec($ch);
$error = curl_error($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($response === false || $error !== '') {
$this->SetStatus(104);
IPS_LogMessage('AWTRIX', 'CURL error on ' . $path . ': ' . $error);
return ['success' => false, 'raw' => $response];
}
if ($httpCode < 200 || $httpCode >= 300) {
$this->SetStatus(104);
IPS_LogMessage('AWTRIX', 'HTTP ' . $httpCode . ' on ' . $path . ': ' . $response);
return ['success' => false, 'raw' => $response];
}
$this->SetStatus(102);
return ['success' => true, 'raw' => $response];
}
private function ColorToApi($value)
{
$value = max(0, min((int) $value, 16777215));
return '#' . strtoupper(str_pad(dechex($value), 6, '0', STR_PAD_LEFT));
}
private function NormalizeForCompare(array $value)
{
return json_encode($this->NormalizeValue($value));
}
private function NormalizeValue($value)
{
if (!is_array($value)) {
return $value;
}
if (array_keys($value) === range(0, count($value) - 1)) {
return array_map([$this, 'NormalizeValue'], $value);
}
ksort($value);
foreach ($value as $key => $item) {
$value[$key] = $this->NormalizeValue($item);
}
return $value;
}
private function StringInputPresentation()
{
if (defined('VARIABLE_PRESENTATION_INPUT')) {
return ['PRESENTATION' => VARIABLE_PRESENTATION_INPUT];
}
return [];
}
private function NumericInputPresentation()
{
if (defined('VARIABLE_PRESENTATION_VALUE_INPUT')) {
return ['PRESENTATION' => VARIABLE_PRESENTATION_VALUE_INPUT];
}
if (defined('VARIABLE_PRESENTATION_INPUT')) {
return ['PRESENTATION' => VARIABLE_PRESENTATION_INPUT];
}
return [];
}
}
?>
+3 -1
View File
@@ -7,6 +7,7 @@
{ "type": "CheckBox", "name": "HUM", "caption": "Enable Humidity App" }, { "type": "CheckBox", "name": "HUM", "caption": "Enable Humidity App" },
{ "type": "CheckBox", "name": "TEMP", "caption": "Enable Temp App" }, { "type": "CheckBox", "name": "TEMP", "caption": "Enable Temp App" },
{ "type": "CheckBox", "name": "BAT", "caption": "Enable Battery App" }, { "type": "CheckBox", "name": "BAT", "caption": "Enable Battery App" },
{ "type": "Label", "caption": "Changes to app enablement are sent on Apply and trigger an automatic reboot after a successful update." },
{ "type": "Select", "name": "TFORMAT", "caption": "Time-Format", { "type": "Select", "name": "TFORMAT", "caption": "Time-Format",
"options": [ "options": [
{ "caption": "13:30:45", "value": "%H:%M:%S" }, { "caption": "13:30:45", "value": "%H:%M:%S" },
@@ -35,7 +36,8 @@
{ "type": "CheckBox", "name": "SOM", "caption": "Start the week on Monday" }, { "type": "CheckBox", "name": "SOM", "caption": "Start the week on Monday" },
{ "type": "CheckBox", "name": "CEL", "caption": "Shows the temperature in Celsius (Fahrenheit when false)." }, { "type": "CheckBox", "name": "CEL", "caption": "Shows the temperature in Celsius (Fahrenheit when false)." },
{ "type": "SelectColor", "name": "CCORRECTION", "caption": "Color Correction","allowTransparent": false }, { "type": "SelectColor", "name": "CCORRECTION", "caption": "Color Correction","allowTransparent": false },
{ "type": "SelectColor", "name": "CTEMP", "caption": "Color Temperatur","allowTransparent": false } { "type": "SelectColor", "name": "CTEMP", "caption": "Color Temperatur","allowTransparent": false },
{ "type": "Label", "caption": "Use Apply to send changed configuration values. 'Send Settings' resends the currently saved instance configuration." }
], ],
"actions": "actions":
[ [
+493 -356
View File
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -4,10 +4,9 @@
"name": "Awtrix Tools", "name": "Awtrix Tools",
"url": "https://jh-artworks.com", "url": "https://jh-artworks.com",
"compatibility": { "compatibility": {
"version": "6.0", "version": "9.0"
"date": 1491343200
}, },
"version": "1.1", "version": "1.3",
"build": 1, "build": 5,
"date": 0 "date": 0
} }