From 4363fd47a58daad2afe477af49916ec96433a520 Mon Sep 17 00:00:00 2001 From: jhartworks Date: Wed, 15 Apr 2026 14:52:48 +0200 Subject: [PATCH] remove CounterTable and CounterClient modules; add CounterServer and update library version --- CounterClient/form.json | 70 ++++ CounterClient/module.json | 14 + CounterClient/module.php | 430 ++++++++++++++++++++ CounterServer/form.json | 24 ++ {CounterTable => CounterServer}/module.json | 4 +- CounterServer/module.php | 166 ++++++++ CounterTable/form.json | 89 ---- CounterTable/module.php | 190 --------- library.json | 2 +- 9 files changed, 707 insertions(+), 282 deletions(-) create mode 100644 CounterClient/form.json create mode 100644 CounterClient/module.json create mode 100644 CounterClient/module.php create mode 100644 CounterServer/form.json rename {CounterTable => CounterServer}/module.json (83%) create mode 100644 CounterServer/module.php delete mode 100644 CounterTable/form.json delete mode 100644 CounterTable/module.php diff --git a/CounterClient/form.json b/CounterClient/form.json new file mode 100644 index 0000000..09c4d5b --- /dev/null +++ b/CounterClient/form.json @@ -0,0 +1,70 @@ +{ + "elements": [ + { + "type": "SelectCategory", + "name": "CounterCategoryID", + "caption": "Counter Kategorie" + }, + { + "type": "SelectVariable", + "name": "JsonOutputVariableID", + "caption": "JSON Ausgabe Variable", + "validVariableTypes": [3] + }, + { + "type": "ExpansionPanel", + "caption": "MQTT (optional)", + "items": [ + { + "type": "CheckBox", + "name": "EnableMQTT", + "caption": "MQTT senden" + }, + { + "type": "SelectInstance", + "name": "MqttClientID", + "caption": "MQTT Client", + "visible": "EnableMQTT", + "validModules": ["{C6D2AEB3-6E1F-4B2E-8D6E-2F0A6C4C2A7F}"] + }, + { + "type": "ValidationTextBox", + "name": "MqttTopic", + "caption": "MQTT Topic", + "visible": "EnableMQTT", + "placeholder": "clients/counters/json" + } + ] + }, + { + "type": "NumberSpinner", + "name": "UpdateTime", + "caption": "Aktualisierungszeit (Sekunden)", + "digits": 0 + } + ], + "actions": [ + { + "type": "Button", + "caption": "Testlauf (JSON erzeugen)", + "onClick": "SECC_BuildAndStorePayload($id);" + } + ], + "status": [ + { + "code": 101, + "icon": "active", + "caption": "Aktiv" + }, + { + "code": 102, + "icon": "inactive", + "caption": "Keine Kategorie gewählt" + }, + { + "code": 103, + "icon": "inactive", + "caption": "Keine JSON Variable gewählt" + } + ] +} \ No newline at end of file diff --git a/CounterClient/module.json b/CounterClient/module.json new file mode 100644 index 0000000..d179c27 --- /dev/null +++ b/CounterClient/module.json @@ -0,0 +1,14 @@ +{ + "id": "{EF2E22EE-67DE-EF4F-42D0-C83C6E5E37C7}", + "name": "CounterClient", + "type": 3, + "vendor": "J.H. Artworks", + "aliases": + [ + ], + "url": "https://jh-artworks.com", + "parentRequirements": [], + "childRequirements": [], + "implemented": [], + "prefix": "SECC" +} \ No newline at end of file diff --git a/CounterClient/module.php b/CounterClient/module.php new file mode 100644 index 0000000..be21d46 --- /dev/null +++ b/CounterClient/module.php @@ -0,0 +1,430 @@ +RegisterPropertyInteger('CounterCategoryID', 0); + $this->RegisterPropertyInteger('JsonOutputVariableID', 0); + $this->RegisterPropertyInteger('UpdateTime', 60); + + $this->RegisterTimer('Update', 0, 'SECC_BuildAndStorePayload($_IPS[\'TARGET\']);'); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + + $updateTime = $this->ReadPropertyInteger('UpdateTime'); + if ($updateTime < 1) { + $updateTime = 60; + } + + $this->SetTimerInterval('Update', $updateTime * 1000); + } + + public function BuildAndStorePayload() + { + $json = $this->BuildPayload(); + if ($json === '') { + return; + } + + $targetVarId = $this->ReadPropertyInteger('JsonOutputVariableID'); + if ($targetVarId > 0 && IPS_VariableExists($targetVarId)) { + SetValueString($targetVarId, $json); + } + + IPS_LogMessage('CounterClient', $json); + } + + public function BuildPayload() + { + $counterCategoryId = $this->ReadPropertyInteger('CounterCategoryID'); + if ($counterCategoryId <= 0 || !IPS_ObjectExists($counterCategoryId)) { + IPS_LogMessage('CounterClient', 'Ungültige CounterCategoryID'); + return ''; + } + + $counterIds = IPS_GetChildrenIDs($counterCategoryId); + $payload = [ + 'timestamp' => date('Y-m-d H:i:s'), + 'counters' => [] + ]; + + foreach ($counterIds as $counterObjectId) { + $obj = IPS_GetObject($counterObjectId); + + if ($obj['ObjectType'] != 0) { + continue; + } + + $counterName = IPS_GetName($counterObjectId); + $counterId = $this->makeSlug($counterName); + + $varIds = $this->CollectVariablesRecursive($counterObjectId); + if (count($varIds) === 0) { + continue; + } + + $counterData = [ + 'id' => $counterId, + 'name' => $counterName + ]; + + $tempSlots = []; + $pressureSlots = []; + + foreach ($varIds as $varId) { + $classified = $this->ClassifyVariable($varId); + if ($classified === null) { + continue; + } + + $field = $classified['field']; + $value = $classified['value']; + $unit = $classified['unit']; + + if ($field === 'temperature_auto') { + $tempSlots[] = $classified; + continue; + } + + if ($field === 'pressure_auto') { + $pressureSlots[] = $classified; + continue; + } + + $counterData[$field] = $value; + + if ($field === 'total_value' && !isset($counterData['unit']) && $unit !== '') { + $counterData['unit'] = $unit; + } + } + + $this->assignInOutValues($counterData, $tempSlots, 'temperature_in_value', 'temperature_out_value'); + $this->assignInOutValues($counterData, $pressureSlots, 'pressure_in_value', 'pressure_out_value'); + + $counterData['type'] = $this->DetectCounterType($counterData); + + if (!isset($counterData['unit'])) { + $counterData['unit'] = $this->inferDefaultUnit($counterData); + } + + if (count($counterData) > 3) { + $payload['counters'][] = $counterData; + } + } + + return json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + } + + private function CollectVariablesRecursive(int $objectId): array + { + $result = []; + $children = IPS_GetChildrenIDs($objectId); + + foreach ($children as $childId) { + if (IPS_LinkExists($childId)) { + $link = IPS_GetLink($childId); + $targetId = (int) $link['TargetID']; + + if (IPS_VariableExists($targetId)) { + $result[$targetId] = $targetId; + } elseif (IPS_ObjectExists($targetId)) { + foreach ($this->CollectVariablesRecursive($targetId) as $subVarId) { + $result[$subVarId] = $subVarId; + } + } + + continue; + } + + if (IPS_VariableExists($childId)) { + $result[$childId] = $childId; + continue; + } + + if (IPS_ObjectExists($childId)) { + foreach ($this->CollectVariablesRecursive($childId) as $subVarId) { + $result[$subVarId] = $subVarId; + } + } + } + + return array_values($result); + } + + private function ClassifyVariable(int $varId): ?array + { + if (!IPS_VariableExists($varId)) { + return null; + } + + $var = IPS_GetVariable($varId); + $type = (int) $var['VariableType']; + + if ($type !== 1 && $type !== 2) { + return null; + } + + $profileName = ''; + if ($var['VariableCustomProfile'] !== '') { + $profileName = $var['VariableCustomProfile']; + } elseif ($var['VariableProfile'] !== '') { + $profileName = $var['VariableProfile']; + } + + if ($profileName === '' || !IPS_VariableProfileExists($profileName)) { + return null; + } + + $profile = IPS_GetVariableProfile($profileName); + $suffix = trim((string) $profile['Suffix']); + $prefix = trim((string) $profile['Prefix']); + $unit = trim($prefix . ' ' . $suffix); + + if ($unit === '') { + return null; + } + + $value = GetValue($varId); + if (!is_numeric($value)) { + return null; + } + + $value = round((float) $value, 3); + $varName = mb_strtolower(IPS_GetName($varId)); + $unitNorm = $this->normalizeUnit($unit); + + if (in_array($unitNorm, ['kwh', 'wh', 'mwh', 'm3', 'l'], true)) { + return [ + 'field' => 'total_value', + 'value' => $value, + 'unit' => $suffix !== '' ? trim($suffix) : $unitNorm, + 'name' => $varName + ]; + } + + if (in_array($unitNorm, ['kw', 'w', 'mw'], true)) { + return [ + 'field' => 'power_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if (in_array($unitNorm, ['m3/h', 'l/h', 'l/min'], true)) { + return [ + 'field' => 'flow_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($unitNorm === 'a') { + return [ + 'field' => 'current_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($unitNorm === 'v') { + return [ + 'field' => 'voltage_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($unitNorm === 'hz') { + return [ + 'field' => 'frequency_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if (in_array($unitNorm, ['bar', 'mbar', 'pa'], true)) { + if ($this->looksLikeIn($varName)) { + return [ + 'field' => 'pressure_in_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($this->looksLikeOut($varName)) { + return [ + 'field' => 'pressure_out_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + return [ + 'field' => 'pressure_auto', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if (in_array($unitNorm, ['°c', 'c', 'k'], true)) { + if ($this->looksLikeIn($varName)) { + return [ + 'field' => 'temperature_in_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($this->looksLikeOut($varName)) { + return [ + 'field' => 'temperature_out_value', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + return [ + 'field' => 'temperature_auto', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + if ($unitNorm === 'cos' || $unitNorm === 'cosphi' || $unitNorm === 'pf') { + return [ + 'field' => 'power_factor', + 'value' => $value, + 'unit' => $unitNorm, + 'name' => $varName + ]; + } + + return null; + } + + private function assignInOutValues(array &$counterData, array $items, string $fieldIn, string $fieldOut): void + { + foreach ($items as $item) { + if ($this->looksLikeIn($item['name']) && !isset($counterData[$fieldIn])) { + $counterData[$fieldIn] = $item['value']; + continue; + } + + if ($this->looksLikeOut($item['name']) && !isset($counterData[$fieldOut])) { + $counterData[$fieldOut] = $item['value']; + continue; + } + } + + foreach ($items as $item) { + if (!isset($counterData[$fieldIn])) { + $counterData[$fieldIn] = $item['value']; + continue; + } + + if (!isset($counterData[$fieldOut])) { + $counterData[$fieldOut] = $item['value']; + continue; + } + } + } + + private function DetectCounterType(array $counterData): string + { + $unit = isset($counterData['unit']) ? $this->normalizeUnit((string) $counterData['unit']) : ''; + + if (isset($counterData['voltage_value']) || isset($counterData['current_value']) || isset($counterData['frequency_value']) || isset($counterData['power_factor'])) { + return 'electricity'; + } + + if (isset($counterData['temperature_in_value']) || isset($counterData['temperature_out_value'])) { + return 'heat'; + } + + if ($unit === 'm3' || $unit === 'l') { + return 'water'; + } + + if ($unit === 'kwh' || $unit === 'mwh' || $unit === 'wh') { + if (isset($counterData['temperature_in_value']) || isset($counterData['flow_value'])) { + return 'heat'; + } + + return 'electricity'; + } + + return 'other'; + } + + private function inferDefaultUnit(array $counterData): string + { + if (isset($counterData['total_value'])) { + if ($counterData['type'] === 'water') { + return 'm3'; + } + + return 'kWh'; + } + + return ''; + } + + private function looksLikeIn(string $name): bool + { + return strpos($name, 'vorlauf') !== false + || strpos($name, 'vl') !== false + || strpos($name, 'flow') !== false + || strpos($name, 'ein') !== false + || strpos($name, 'in') !== false; + } + + private function looksLikeOut(string $name): bool + { + return strpos($name, 'ruecklauf') !== false + || strpos($name, 'rücklauf') !== false + || strpos($name, 'rl') !== false + || strpos($name, 'return') !== false + || strpos($name, 'aus') !== false + || strpos($name, 'out') !== false; + } + + private function normalizeUnit(string $unit): string + { + $unit = mb_strtolower(trim($unit)); + $unit = str_replace([' ', '°'], ['', '°'], $unit); + $unit = str_replace(['m³', '㎥'], 'm3', $unit); + return $unit; + } + + private function makeSlug(string $text): string + { + $text = mb_strtolower($text); + $text = str_replace(['ä', 'ö', 'ü', 'ß'], ['ae', 'oe', 'ue', 'ss'], $text); + $text = preg_replace('/[^a-z0-9]+/u', '_', $text); + $text = trim($text, '_'); + + if ($text === '') { + $text = 'counter_' . time(); + } + + return $text; + } +} +?> \ No newline at end of file diff --git a/CounterServer/form.json b/CounterServer/form.json new file mode 100644 index 0000000..b31e39f --- /dev/null +++ b/CounterServer/form.json @@ -0,0 +1,24 @@ +{ + "elements": [ + { + "type": "SelectInstance", + "name": "Connector", + "caption": "DB-Connector", + "validModules": ["{7B420C9A-F0FF-4C65-925D-6FEE0D8F1A55}"] + }, + { + "type": "SelectVariable", + "name": "SourceJSON", + "caption": "JSON-Datenquelle", + "validVariableTypes": [3] + }, + { + "type": "NumberSpinner", + "name": "UpdateTime", + "caption": "Aktualisierungszeit (Sekunden)", + "digits": 0 + } + ], + "actions": [], + "status": [] +} \ No newline at end of file diff --git a/CounterTable/module.json b/CounterServer/module.json similarity index 83% rename from CounterTable/module.json rename to CounterServer/module.json index 944f6a1..e1fd065 100644 --- a/CounterTable/module.json +++ b/CounterServer/module.json @@ -1,6 +1,6 @@ { "id": "{D0DA85AB-1BAD-C36C-A3FD-1C2660F96F1C}", - "name": "CounterTable", + "name": "CounterServer", "type": 3, "vendor": "J.H. Artworks", "aliases": @@ -10,5 +10,5 @@ "parentRequirements": [], "childRequirements": [], "implemented": [], - "prefix": "CT" + "prefix": "SECS" } \ No newline at end of file diff --git a/CounterServer/module.php b/CounterServer/module.php new file mode 100644 index 0000000..e8b0db7 --- /dev/null +++ b/CounterServer/module.php @@ -0,0 +1,166 @@ +RegisterPropertyInteger('Connector', 0); + $this->RegisterPropertyInteger('SourceJSON', 0); + $this->RegisterPropertyInteger('UpdateTime', 60); + + $this->RegisterTimer('Update', 0, 'SECT_checkTable($_IPS[\'TARGET\']);'); + } + + public function ApplyChanges() + { + parent::ApplyChanges(); + + $updateTime = $this->ReadPropertyInteger('UpdateTime'); + if ($updateTime < 1) { + $updateTime = 60; + } + + $this->SetTimerInterval('Update', $updateTime * 1000); + $this->checkTable(); + } + + public function checkTable() + { + $sqlId = $this->ReadPropertyInteger('Connector'); + $jsonVar = $this->ReadPropertyInteger('SourceJSON'); + + if ($sqlId <= 0 || $jsonVar <= 0) { + IPS_LogMessage('CounterTable', 'Config fehlt.'); + return; + } + + $jsonString = GetValueString($jsonVar); + if ($jsonString === '') { + return; + } + + $payload = json_decode($jsonString, true); + if (!is_array($payload)) { + IPS_LogMessage('CounterTable', 'JSON Fehler'); + return; + } + + $clientId = (string)$this->InstanceID; + $clientName = IPS_GetName($this->InstanceID); + + $timestamp = $payload['timestamp'] ?? date('Y-m-d H:i:s'); + $counters = $payload['counters'] ?? []; + + foreach ($counters as $c) { + + $counterId = $c['id'] ?? ''; + if ($counterId === '') continue; + + $counterName = $c['name'] ?? $counterId; + $type = $c['type'] ?? 'other'; + $unit = $c['unit'] ?? 'kWh'; + $projectId = isset($c['project_id']) ? (int)$c['project_id'] : 'NULL'; + + // ---------- UPSERT ---------- + $sql = " + INSERT INTO meter_devices + (project_id, client_id, counter_id, meter_uuid, meter_name, external_id, meter_type, billing_unit, description, is_active) + VALUES + ( + " . ($projectId === 'NULL' ? 'NULL' : $projectId) . ", + '" . $this->esc($clientId) . "', + '" . $this->esc($counterId) . "', + '" . $this->esc($clientId . '_' . $counterId) . "', + '" . $this->esc($counterName) . "', + '" . $this->esc($counterId) . "', + '" . $this->esc($type) . "', + '" . $this->esc($unit) . "', + '" . $this->esc('Client: ' . $clientName) . "', + 1 + ) + ON DUPLICATE KEY UPDATE + meter_name = VALUES(meter_name), + meter_type = VALUES(meter_type), + billing_unit = VALUES(billing_unit), + is_active = 1 + "; + + MySQL_ExecuteSimple($sqlId, $sql); + + // ---------- GET meter_id ---------- + $sqlGet = " + SELECT id FROM meter_devices + WHERE client_id = '" . $this->esc($clientId) . "' + AND counter_id = '" . $this->esc($counterId) . "' + LIMIT 1 + "; + + $res = MySQL_ExecuteSimple($sqlId, $sqlGet); + + if (!is_array($res) || count($res) === 0) { + IPS_LogMessage('CounterTable', 'Kein meter_id gefunden'); + continue; + } + + $meterId = (int)$res[0]['id']; + + // ---------- INSERT measurement ---------- + $sqlInsert = " + INSERT INTO meter_measurements + ( + meter_id, measured_at, + total_value, consumption_value, + power_value, flow_value, + current_value, voltage_value, + temperature_in_value, temperature_out_value, + pressure_in_value, pressure_out_value, + frequency_value, power_factor + ) + VALUES + ( + " . $meterId . ", + '" . $this->esc($timestamp) . "', + " . $this->f($c, 'total_value') . ", + " . $this->f($c, 'consumption_value') . ", + " . $this->f($c, 'power_value') . ", + " . $this->f($c, 'flow_value') . ", + " . $this->f($c, 'current_value') . ", + " . $this->f($c, 'voltage_value') . ", + " . $this->f($c, 'temperature_in_value') . ", + " . $this->f($c, 'temperature_out_value') . ", + " . $this->f($c, 'pressure_in_value') . ", + " . $this->f($c, 'pressure_out_value') . ", + " . $this->f($c, 'frequency_value') . ", + " . $this->f($c, 'power_factor') . " + ) + "; + + MySQL_ExecuteSimple($sqlId, $sqlInsert); + } + } + + private function f($arr, $key) + { + if (!isset($arr[$key]) || $arr[$key] === '' || $arr[$key] === null) { + return 'NULL'; + } + + if (is_numeric($arr[$key])) { + return str_replace(',', '.', (string)$arr[$key]); + } + + return 'NULL'; + } + + private function esc($v) + { + return str_replace( + ['\\', "'", '"'], + ['\\\\', "\\'", '\\"'], + $v + ); + } +} +?> \ No newline at end of file diff --git a/CounterTable/form.json b/CounterTable/form.json deleted file mode 100644 index 2344921..0000000 --- a/CounterTable/form.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "elements": - [ - { "type": "NumberSpinner", "Name": "Price", "caption": "Strompreis", "digits": 2}, - { - "type": "List", - "name": "Counters", - "caption": "Zählerliste", - "add": true, - "delete": true, - "sort": { - "column": "Name", - "direction": "ascending" - }, - "columns": [ - { - "caption": "Name", - "name": "Name", - "width": "100px", - "edit": { - "type": "ValidationTextBox" - }, - "add": "" - }, - { - "caption": "Gesamtverbrauch", - "name": "Total", - "width": "100px", - "edit": { - "type": "SelectVariable", - "validVariableTypes": [2] - }, - "add": 0 - }, - { - "caption": "Monatsverbrauch", - "name": "Monthly", - "width": "100px", - "edit": { - "type": "SelectVariable", - "validVariableTypes": [2] - }, - "add": 0 - }, - { - "caption": "Wochenverbrauch", - "name": "Weekly", - "width": "100px", - "edit": { - "type": "SelectVariable", - "validVariableTypes": [2] - }, - "add": 0 - }, - { - "caption": "Tagesverbrauch", - "name": "Daily", - "width": "100px", - "edit": { - "type": "SelectVariable", - "validVariableTypes": [2] - }, - "add": 0 - }, - { - "caption": "Limit", - "name": "Limit", - "width": "100px", - "edit": { - "type": "NumberSpinner", - "digits": 2 - }, - "add": 0.1 - } - - - ] - } - - ], - "actions": - [ - - ], - "status": - [ - - ] -} \ No newline at end of file diff --git a/CounterTable/module.php b/CounterTable/module.php deleted file mode 100644 index 219ad99..0000000 --- a/CounterTable/module.php +++ /dev/null @@ -1,190 +0,0 @@ -RegisterPropertyFloat("Price",0.23); - $this->RegisterPropertyString("Counters",""); - - $this->RegisterVariableString("htmlOutput", "Zählerübersicht",["PRESENTATION"=> VARIABLE_PRESENTATION_LEGACY, "PROFILE" => "~HTMLBox"],0); - - } - - // Überschreibt die intere IPS_ApplyChanges($id) Funktion - public function ApplyChanges() { - // Diese Zeile nicht löschen - parent::ApplyChanges(); - $this->checkTable(); - - - } - public function RequestAction($Ident, $Value) { - $varid = $this->GetIDForIdent($Ident); - SetValue($varid, $Value); - } - - - public function checkTable(){ - $price = $this->ReadPropertyFloat("Price"); - - - $arrString = $this->ReadPropertyString("Counters"); - $arr = json_decode($arrString); - - - $dataoutput = ""; - /// print_r($arr); - - if($arr != NULL){ - foreach($arr as $cnt){ - - $cost = 0.0; - $cost = $cnt->Monthly * $price; - $style =''; - if ($cnt->Monthly > $cnt->Limit){ - $style = 'style="background-color:yellow;"'; - } - - $dataoutput .= ' - - - - '.$cnt->Name.' - - - - '.$cnt->Total.' kWh - - - - '.$cnt->Monthly.' kWh - - - - '.$cnt->Weekly.' kWh - - - - '.$cnt->Daily.' kWh - - - - '.$cost.'€ - - - '; - - } - } - $this->createTable($dataoutput); - -/* - - Array - ( - [0] => stdClass Object - ( - [Name] => Blub - [Total] => 49294 - [Monthly] => 49294 - [Weekly] => 49294 - [Daily] => 49294 - [Limit] => 0.1 - ) - - ) - (Code: -32603) - */ - - - } - - public function createTable($data) - { - $htmlprefix =' - - - - - - - - - - - - - - - - - - - - - - - - - - - - '; - - $htmlsuffix = ' - - - - -
- Name: - - Gesamt: - - Monat: - - Woche: - - Tag: - - Kosten: -
- - '; - - SetValueString($this->GetIDForIdent("htmlOutput"),$htmlprefix.$data.$htmlsuffix); - } - - - -} - -?> \ No newline at end of file diff --git a/library.json b/library.json index 333c51f..fb27968 100644 --- a/library.json +++ b/library.json @@ -8,6 +8,6 @@ "date": 1491343200 }, "version": "1.0", - "build": 9, + "build": 0, "date": 0 } \ No newline at end of file