diff --git a/.DS_Store b/.DS_Store index 15607a8..0b0a16d 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/api/v2/get/catalog.php b/api/v2/get/catalog.php index 56d1126..97a1b6d 100644 --- a/api/v2/get/catalog.php +++ b/api/v2/get/catalog.php @@ -1,6 +1,10 @@ $item['version'], 'config_setting' => $item['config'], - 'main_option_for_display' => $item['measurement'], + 'main_option_for_display' => $item['measurement'] ?? '', 'configurations' => [] ]; } @@ -170,6 +174,64 @@ removeKeysRecursive($catalog,$keys_to_remove); //------------------------------------------ $messages = processProductCollection($catalog); +//------------------------------------------ +//check for METAfeed request +//------------------------------------------ +if (isset($criterias['meta'])){ + //------------------------------------------ + // Meta Feed Configuration + //------------------------------------------ + $meta_config = [ + 'base_url' => 'https://www.morvalwatches.com', // Product page URL + 'image_base_url' => 'https://cloud.soveliti.nl', + 'brand' => 'Morval Watches', + 'currency' => 'EUR', + 'condition' => 'new', + 'availability' => 'in stock', + 'google_product_category' => 'Apparel & Accessories > Jewelry > Watches', + 'output_format' => 'json' // Options: 'csv', 'xml', 'json' + ]; + + //------------------------------------------ + // Product Code Based Descriptions + // Keys can be: exact code (MWTH2NB) or pattern (MWTH1*, MWTH2*) + //------------------------------------------ + $meta_descriptions = [ + // Pattern based (will match any product starting with this) + 'MWTH1' => 'The Thomas-I exudes elegance and sophistication. Classic dimensions combined with subtle details in the dial make it an special automatic watch that can be worn on all occasions.', + 'MWTH2' => 'The Thomas-II provides a view of the beating heart of the Swiss timepiece. It marks the precision and perfection with which the time is displayed.', + 'MWABR' => 'Handmade Italian calf leather bracelet', + ]; + + //------------------------------------------ + //Include meta functions + //------------------------------------------ + include_once './assets/functions_meta.php'; + + $meta_feed = catalogToMetaFeed($messages, $meta_config); + //------------------------------------------ + // Output based on format parameter + //------------------------------------------ + if ($criterias['meta'] === true) { + $format = $meta_config['output_format']; + } else { + $format = $criterias['meta']; + } + + switch ($format) { + case 'xml': + $messages = outputMetaFeedXML($meta_feed); + break; + case 'csv': + $messages = outputMetaFeedCSV($meta_feed); + break; + case 'json': + default: + $messages = outputMetaFeedJSON($meta_feed); + break; + } + exit(); +} //------------------------------------------ //JSON_ENCODE //------------------------------------------ diff --git a/assets/functions_meta.php b/assets/functions_meta.php new file mode 100644 index 0000000..b8c11a1 --- /dev/null +++ b/assets/functions_meta.php @@ -0,0 +1,256 @@ + $product_id, + 'title' => $product_name, + 'description' => $description, + 'price' => $base_price, + 'link' => $product_url, + 'image_link' => $main_image, + ], $config); + + } else { + // Product with variants + $variants = extractVariants($product, $base_price); + + foreach ($variants as $variant) { + // Combine meta description with variant suffix + $variant_description = $description . ' ' . $variant['description_suffix']; + + $meta_feed[] = buildMetaProduct([ + 'id' => $variant['id'], + 'item_group_id' => $product_id, + 'title' => $variant['title'], + 'description' => $variant_description, + 'price' => $variant['price'], + 'link' => $product_url, + 'image_link' => $variant['image'], + 'additional_image_link' => $variant['additional_image'] ?? '', + ], $config); + } + } + } + + return $meta_feed; +} + +//------------------------------------------ +// Extract variants from configurations +//------------------------------------------ +function extractVariants($product, $base_price) { + $variants = []; + $product_id = $product['productcode']; + $product_name = $product['productname']; + $image_base_url = $GLOBALS['meta_config']['image_base_url']; + + $configurations = $product['configurations'] ?? []; + + foreach ($configurations as $config) { + if ($config['type'] === 'group') { + $group_name = $config['assignment_name'] ?? 'Option'; + $attributes = $config['attributes'] ?? []; + + foreach ($attributes as $attr) { + // Calculate price + $attr_price = floatval($attr['price'] ?? 0); + $attr_modifier = intval($attr['price_modifier'] ?? 1); + $final_price = calculateVariantPrice($base_price, $attr_price, $attr_modifier); + + // Get images (using image_base_url for images) + $variant_image = buildImageUrl($attr['alternative_media_full_path'] ?? '', $image_base_url); + $additional_image = buildImageUrl($attr['full_path'] ?? '', $image_base_url); + + // Fallback to main product image if no variant image + if (empty($variant_image)) { + $variant_image = buildImageUrl($product['full_path'] ?? '', $image_base_url); + } + + // Extract option name from item_name + $item_name = $attr['item_name'] ?? ''; + $option_name = formatAttributeName($item_name); + + // Build variant + $variants[] = [ + 'id' => $product_id . '_' . ($attr['attribute_id'] ?? ''), + 'title' => $product_name . ' - ' . $option_name, + 'description_suffix' => 'Available with ' . $option_name . ' ' . strtolower($group_name) . '.', + 'price' => $final_price, + 'image' => $variant_image, + 'additional_image' => $additional_image, + ]; + } + } + } + + return $variants; +} + +//------------------------------------------ +// Build Meta product row +//------------------------------------------ +function buildMetaProduct($data, $config) { + $product = [ + 'id' => $data['id'], + 'title' => $data['title'], + 'description' => $data['description'], + 'availability' => $config['availability'], + 'condition' => $config['condition'], + 'price' => formatPrice($data['price'], $config['currency']), + 'link' => $data['link'], + 'image_link' => $data['image_link'], + 'brand' => $config['brand'], + 'google_product_category' => $config['google_product_category'], + ]; + + // Add optional fields if present + if (!empty($data['item_group_id'])) { + $product['item_group_id'] = $data['item_group_id']; + } + + if (!empty($data['additional_image_link'])) { + $product['additional_image_link'] = $data['additional_image_link']; + } + + return $product; +} + +//------------------------------------------ +// Helper Functions +//------------------------------------------ +function getMetaDescription($product_code, $fallback_description = '') { + global $meta_descriptions; + + // First check for exact match + if (isset($meta_descriptions[$product_code])) { + return $meta_descriptions[$product_code]; + } + + // Then check for pattern match (starts with) + foreach ($meta_descriptions as $pattern => $description) { + if (strpos($product_code, $pattern) === 0) { + return $description; + } + } + + // Return fallback if no match + return $fallback_description; +} + +function calculateVariantPrice($base_price, $modifier_price, $modifier_type) { + if ($modifier_price <= 0) { + return $base_price; + } + + switch ($modifier_type) { + case 1: // Addition + return $base_price + $modifier_price; + case 2: // Multiplication + return $base_price * $modifier_price; + default: + return $base_price; + } +} + +function buildImageUrl($path, $base_url) { + if (empty($path)) return ''; + return rtrim($base_url, '/') . '/' . ltrim($path, '/'); +} + +function buildProductUrl($slug, $base_url) { + if (empty($slug)) return ''; + return rtrim($base_url, '/') . '/product/' . $slug; +} + +function formatPrice($price, $currency) { + return number_format($price, 2, '.', '') . ' ' . $currency; +} + +function cleanText($text) { + $text = strip_tags($text); + $text = preg_replace('/\s+/', ' ', $text); + return trim($text); +} + +function formatAttributeName($name) { + // Remove prefix like "bracelet_" + $name = preg_replace('/^bracelet_/', '', $name); + // Replace underscores with spaces + $name = str_replace('_', ' ', $name); + // Capitalize words + return ucwords($name); +} + +//------------------------------------------ +// Output Functions +//------------------------------------------ +function outputMetaFeedCSV($meta_feed) { + header('Content-Type: text/csv; charset=utf-8'); + header('Content-Disposition: attachment; filename="meta_product_feed.csv"'); + + $output = fopen('php://output', 'w'); + + // Write headers + if (!empty($meta_feed)) { + fputcsv($output, array_keys($meta_feed[0])); + + // Write data + foreach ($meta_feed as $row) { + fputcsv($output, $row); + } + } + + fclose($output); +} + +function outputMetaFeedXML($meta_feed) { + header('Content-Type: application/xml; charset=utf-8'); + + $xml = new SimpleXMLElement(''); + $channel = $xml->addChild('channel'); + $channel->addChild('title', 'Morval Product Feed'); + $channel->addChild('link', $GLOBALS['meta_config']['base_url']); + $channel->addChild('description', 'Product feed for Meta catalog'); + + foreach ($meta_feed as $product) { + $item = $channel->addChild('item'); + + foreach ($product as $key => $value) { + $xml_key = 'g:' . $key; + $item->addChild($xml_key, htmlspecialchars($value)); + } + } + + echo $xml->asXML(); +} + +function outputMetaFeedJSON($meta_feed) { + //header('Content-Type: application/json; charset=utf-8'); + echo json_encode($meta_feed, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); +} + +?> \ No newline at end of file diff --git a/assets/readdevice.js b/assets/readdevice.js index 379b221..8e19e5c 100644 --- a/assets/readdevice.js +++ b/assets/readdevice.js @@ -1,7 +1,7 @@ var port, textEncoder, writableStreamClosed, writer, historyIndex = -1; const lineHistory = []; -maintenanceRun = 0; -handshakeComplete = false; +let maintenanceRun = 0; +let handshakeComplete = false; // Function to log communication to API async function logCommunication(data, direction) { @@ -102,6 +102,7 @@ async function connectSerial() { // Log connection failure details await logCommunication(`Serial connection failed: ${error.message || 'Unknown error'}`, 'disconnected'); alert("Serial Connection Failed"); + window.location.reload(); } } @@ -580,7 +581,7 @@ async function closePort(){ maintenanceRun = 0; // reset maintenanceRun // Refresh the page - window.location.reload(); + setTimeout(() => {window.location.reload();}, 500); } async function closePortCarTest(){ @@ -736,7 +737,7 @@ async function updateHistory(){ body: testdetails }); - const historyresult = await response.json; + const historyresult = await response.json(); setTimeout(maintenanceTest,5000); @@ -805,5 +806,6 @@ async function updateHistory(){ catch(e) { alert(e.message || e); + window.location.reload(); } } diff --git a/assets/scripts.js b/assets/scripts.js index 12d0961..8cb1139 100644 --- a/assets/scripts.js +++ b/assets/scripts.js @@ -1,5 +1,10 @@ const serialResultsDiv = document.getElementById("serialResults"); +let port, textEncoder, writableStreamClosed, writer; +let readableStreamClosed, reader, openPort = 0; +let x, serial, fw, hw, sw, commitCode; +let firmwarelocation, upgraded_version, hex_fw; + // Buffer for accumulating received data before logging let receivedDataBuffer = ''; @@ -113,13 +118,15 @@ async function connectDevice() { // Log connection failure details await logCommunication(`Serial connection failed: ${error.message || 'Unknown error'}`, 'disconnected'); - if (openPort = 1){ + if (openPort === 1){ closePort(); console.log("Closing port"); alert("System is still trying to close the serial port. If this message continues to come up please refresh this page."); + window.location.reload(); } else{ alert("Your browser does not support this functionality. Please use latest Chrome or Edge browser."); + window.location.reload(); } } } @@ -161,6 +168,7 @@ async function listenToPort() { async function appendToTerminal(newStuff) { serialResultsDiv.innerHTML += newStuff; + receivedDataBuffer += newStuff; // Log received data await logCommunication(newStuff.trim(), 'received'); diff --git a/firmwaretool.php b/firmwaretool.php index 835017e..81d81d9 100644 --- a/firmwaretool.php +++ b/firmwaretool.php @@ -100,7 +100,7 @@ echo ' ';