diff --git a/packages/protodevice/src/device/Device.ts b/packages/protodevice/src/device/Device.ts
index 2dca02765..9bd285d1f 100644
--- a/packages/protodevice/src/device/Device.ts
+++ b/packages/protodevice/src/device/Device.ts
@@ -22,16 +22,23 @@ class Device {
deviceComponents.esphome.name = deviceName
deviceComponents.protofy = { credentials: this.credentials }
- this.components?.forEach((component, i) => {
- console.log("🚀 ~ file: Device.ts:62 ~ Device ~ this.components?.forEach ~ component:", component)
- if(component){
- deviceComponents = component.attach(
- !isNaN(parseInt(this.pinTable[i]))
- ? parseInt(this.pinTable[i])
- : this.pinTable[i], deviceComponents, this.components
- );
- }
+ this.components?.map((component, i) => ({
+ component,
+ pin: !isNaN(parseInt(this.pinTable[i])) ? parseInt(this.pinTable[i]) : this.pinTable[i]
+ }))
+ // Sort to ensure "sd_card_component" types are attached last
+ .sort((a, b) => {
+ if (a.component?.type === "sd_card_component") return 1;
+ if (b.component?.type === "sd_card_component") return -1;
+ return 0;
})
+ // Attach each component in the sorted order with its associated pin
+ .forEach(({ component, pin }) => {
+ if (component) {
+ deviceComponents = component.attach(pin, deviceComponents, this.components);
+ }
+ });
+
delete deviceComponents.protofy
//console.log("🚀 ~ file: Device.ts:120 ~ Device ~ getComponents ~ deviceComponents:", deviceComponents)
this.componentsTree = deviceComponents
@@ -50,6 +57,12 @@ class Device {
}
}
})
+
+
+
+
+
+
this.subsystemsTree = this.subsystemsTree.sort((a,b)=>{
if(a.name=="mqtt"){
return -1
diff --git a/packages/protodevice/src/device/SdOfflineLogger.ts b/packages/protodevice/src/device/SdOfflineLogger.ts
new file mode 100644
index 000000000..c71dc22e7
--- /dev/null
+++ b/packages/protodevice/src/device/SdOfflineLogger.ts
@@ -0,0 +1,79 @@
+import { device } from "./Device";
+
+class SdOfflineLogger {
+ name;
+ type;
+ timeId;
+ jsonFileName;
+ intervalSeconds;
+ publishDataWhenOnline;
+ publishDataTopic;
+ sensorsArray;
+
+ constructor(name, timeId, jsonFileName, intervalSeconds, publishDataWhenOnline, publishDataTopic) {
+ this.name = name;
+ this.type = "sd_card_component";
+ this.timeId = timeId;
+ this.jsonFileName = jsonFileName;
+ this.intervalSeconds = intervalSeconds;
+ this.publishDataWhenOnline = publishDataWhenOnline;
+ this.publishDataTopic = publishDataTopic;
+ this.sensorsArray = [];
+ }
+
+ attach(pin, deviceComponents) {
+ deviceComponents?.sensor?.forEach((sensor) => {
+ if (sensor.name){
+ this.sensorsArray.push(sensor.name);
+ } else if (sensor.id){
+ this.sensorsArray.push(sensor.id);
+ }
+ });
+
+ const componentObjects = [
+ {
+ name: "external_components",
+ config: {
+ //@ts-ignore
+ source: "github://Protofy-xyz/esphome-components",
+ refresh: "10s",
+ components: ["sd_card_component"]
+ }
+ },
+ {
+ name: "sd_card_component",
+ config: {
+ id: this.name,
+ cs_pin: pin,
+ time_id: this.timeId,
+ json_file_name: this.jsonFileName,
+ interval_seconds: this.intervalSeconds,
+ publish_data_when_online: this.publishDataWhenOnline,
+ publish_data_topic: deviceComponents.mqtt?.topic_prefix+this.publishDataTopic,
+ sensors: this.sensorsArray,
+ },
+ },
+ ];
+
+ componentObjects.forEach((element) => {
+ if (!deviceComponents[element.name]) {
+ deviceComponents[element.name] = element.config;
+ } else {
+ if (!Array.isArray(deviceComponents[element.name])) {
+ deviceComponents[element.name] = [deviceComponents[element.name]];
+ }
+ deviceComponents[element.name] = [...deviceComponents[element.name], element.config];
+ }
+ });
+
+ return deviceComponents;
+ }
+
+ getSubsystem() {
+ return {}
+ }
+}
+
+export function sdOfflineLogger(name, timeId, jsonFileName, intervalSeconds, publishDataWhenOnline, publishDataTopic) {
+ return new SdOfflineLogger(name, timeId, jsonFileName, intervalSeconds, publishDataWhenOnline, publishDataTopic);
+}
diff --git a/packages/protodevice/src/device/index.ts b/packages/protodevice/src/device/index.ts
index d3feee90d..21c17214e 100644
--- a/packages/protodevice/src/device/index.ts
+++ b/packages/protodevice/src/device/index.ts
@@ -50,4 +50,5 @@ export * from './MKSServo42D'
export * from './Msa3xx'
export * from './ODrive'
export * from './INA226'
-export * from './SntpTime'
\ No newline at end of file
+export * from './SntpTime'
+export * from './SdOfflineLogger'
\ No newline at end of file
diff --git a/packages/protodevice/src/nodes/SdOfflineLogger.tsx b/packages/protodevice/src/nodes/SdOfflineLogger.tsx
new file mode 100644
index 000000000..12ce392dd
--- /dev/null
+++ b/packages/protodevice/src/nodes/SdOfflineLogger.tsx
@@ -0,0 +1,46 @@
+import React from "react";
+import { Node, Field, HandleOutput, NodeParams } from "protoflow";
+import { getColor } from ".";
+
+const SdOfflineLogger = ({ node = {}, nodeData = {}, children, color }: any) => {
+ const nameErrorMsg = 'Reserved name'
+ const [name, setName] = React.useState(nodeData['param-1'])
+ const intervalErrorMsg = 'Add units h/m/s/ms'
+ const nodeParams: Field[] = [
+ {
+ label: 'Name', static: true, field: 'param-1', type: 'input', onBlur: () => { setName(nodeData['param-1']) },
+ error: nodeData['param-1']?.value?.replace(/['"]+/g, '') == 'sd_card_component' ? nameErrorMsg : null
+ },
+ { label: 'Time ID', static: true, field: 'param-2', type: 'input', },
+ { label: 'JSON File Name', static: true, field: 'param-3', type: 'input' },
+ {
+ label: 'Interval Seconds', static: true, field: 'param-4', type: 'input',
+ error: !['h', 'm', 's', 'ms'].includes(nodeData['param-4']?.value?.replace(/['"0-9]+/g, '')) ? intervalErrorMsg : null
+ },
+ { label: 'Publish Data When Online', static: true, field: 'param-5', type: 'boolean' },
+ { label: 'Publish Data Topic', static: true, field: 'param-6', type: 'input' },
+
+ ] as Field[]
+ return (
+
+
+
+ )
+}
+
+export default {
+ id: 'SdOfflineLogger',
+ type: 'CallExpression',
+ category: "Utils",
+ keywords: ["sd", "log", "offline"],
+ check: (node, nodeData) => node.type == "CallExpression" && nodeData.to?.startsWith('sdOfflineLogger'),
+ getComponent: (node, nodeData, children) => ,
+ getInitialData: () => { return { to: 'sdOfflineLogger',
+ "param-1": { value: "", kind: "StringLiteral" },
+ "param-2": { value: "", kind: "StringLiteral" },
+ "param-3": { value: "/offline_data.json", kind: "StringLiteral" },
+ "param-4": { value: "30s", kind: "StringLiteral" },
+ "param-5": { value: true, kind: "FalseKeyword" },
+ "param-6": { value: "/ofline_data", kind: "StringLiteral" },
+ } }
+}
diff --git a/packages/protodevice/src/nodes/index.tsx b/packages/protodevice/src/nodes/index.tsx
index 863cb50a6..2d27bfeb9 100644
--- a/packages/protodevice/src/nodes/index.tsx
+++ b/packages/protodevice/src/nodes/index.tsx
@@ -39,6 +39,7 @@ import ODrive from './ODrive';
import INA226 from './INA226';
import LEDCOutput from './LEDCOutput';
import SntpTime from './SntpTime';
+import SdOfflineLogger from './SdOfflineLogger';
const deviceMasks = [
@@ -81,6 +82,7 @@ const deviceMasks = [
INA226,
LEDCOutput,
SntpTime,
+ SdOfflineLogger,
]
const masksLength = deviceMasks.length