Revert "feat(valve): Implement obstacle detection with configurable thresholds"

This reverts commit 3c2235733b.
This commit is contained in:
Eduard Iten 2025-07-11 00:35:19 +02:00
parent 3c2235733b
commit b937c52bcc
6 changed files with 8 additions and 152 deletions

View File

@ -51,8 +51,6 @@ Alle Register sind in einer einzigen, durchgehenden Liste pro Register-Typ (`Inp
| **0x0002** | `MAX_CLOSING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. | | **0x0002** | `MAX_CLOSING_TIME_S` | Ventil | Sicherheits-Timeout in Sekunden für den Schliessen-Vorgang. |
| **0x0003** | `END_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Öffnen. | | **0x0003** | `END_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Öffnen. |
| **0x0004** | `END_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Schliessen. | | **0x0004** | `END_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Minimaler Stromschwellenwert in mA zur Endlagenerkennung beim Schliessen. |
| **0x0005** | `OBSTACLE_CURRENT_THRESHOLD_OPEN_MA` | Ventil | Stromschwellenwert in mA zur Hinderniserkennung beim Öffnen. |
| **0x0006** | `OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA` | Ventil | Stromschwellenwert in mA zur Hinderniserkennung beim Schliessen. |
| **0x0010** | `DIGITAL_OUTPUTS_STATE` | Ausgänge | Bitmaske zum Lesen und Schreiben der Ausgänge. Bit 0: Ausgang 1, Bit 1: Ausgang 2. `1`=AN, `0`=AUS. | | **0x0010** | `DIGITAL_OUTPUTS_STATE` | Ausgänge | Bitmaske zum Lesen und Schreiben der Ausgänge. Bit 0: Ausgang 1, Bit 1: Ausgang 2. `1`=AN, `0`=AUS. |
| **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout des Fail-Safe-Watchdogs in Sekunden. `0`=Deaktiviert. | | **0x00F0** | `WATCHDOG_TIMEOUT_S` | System | Timeout des Fail-Safe-Watchdogs in Sekunden. `0`=Deaktiviert. |
| **0x00F1** | `DEVICE_RESET` | System | Schreibt `1` um das Gerät neu zu starten. | | **0x00F1** | `DEVICE_RESET` | System | Schreibt `1` um das Gerät neu zu starten. |

View File

@ -89,21 +89,9 @@ enum {
*/ */
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002, REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002,
/** /**
* @brief Minimum current threshold in mA for end-position detection during opening. * @brief Minimum current threshold in mA for end-position detection.
*/ */
REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003, REG_HOLDING_VALVE_END_CURRENT_THRESHOLD_MA = 0x0003,
/**
* @brief Minimum current threshold in mA for end-position detection during closing.
*/
REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004,
/**
* @brief Obstacle current threshold in mA for opening.
*/
REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA = 0x0005,
/**
* @brief Obstacle current threshold in mA for closing.
*/
REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA = 0x0006,
/** /**
* @brief Bitmask for reading and writing digital outputs. Bit 0: Output 1, * @brief Bitmask for reading and writing digital outputs. Bit 0: Output 1,
* Bit 1: Output 2. 1=ON, 0=OFF. * Bit 1: Output 2. 1=ON, 0=OFF.

View File

@ -32,8 +32,7 @@ enum valve_movement {
VALVE_MOVEMENT_IDLE, /**< The valve is not moving. */ VALVE_MOVEMENT_IDLE, /**< The valve is not moving. */
VALVE_MOVEMENT_OPENING, /**< The valve is currently opening. */ VALVE_MOVEMENT_OPENING, /**< The valve is currently opening. */
VALVE_MOVEMENT_CLOSING, /**< The valve is currently closing. */ VALVE_MOVEMENT_CLOSING, /**< The valve is currently closing. */
VALVE_MOVEMENT_ERROR, /**< An error occurred during movement. */ VALVE_MOVEMENT_ERROR /**< An error occurred during movement. */
VALVE_MOVEMENT_OBSTACLE /**< An obstacle was detected during movement. */
}; };
/** /**
@ -114,20 +113,6 @@ void valve_set_end_current_threshold_open(uint16_t current_ma);
*/ */
void valve_set_end_current_threshold_close(uint16_t current_ma); void valve_set_end_current_threshold_close(uint16_t current_ma);
/**
* @brief Sets the current threshold for obstacle detection during opening.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_obstacle_current_threshold_open(uint16_t current_ma);
/**
* @brief Sets the current threshold for obstacle detection during closing.
*
* @param current_ma The current threshold in milliamps.
*/
void valve_set_obstacle_current_threshold_close(uint16_t current_ma);
/** /**
* @brief Gets the current threshold for end-position detection during opening. * @brief Gets the current threshold for end-position detection during opening.
* *
@ -156,20 +141,6 @@ uint16_t valve_get_max_open_time(void);
*/ */
uint16_t valve_get_max_close_time(void); uint16_t valve_get_max_close_time(void);
/**
* @brief Gets the current threshold for obstacle detection during opening.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_obstacle_current_threshold_open(void);
/**
* @brief Gets the current threshold for obstacle detection during closing.
*
* @return The current threshold in milliamps.
*/
uint16_t valve_get_obstacle_current_threshold_close(void);
/** /**
* @brief Gets the current drawn by the valve motor during opening. * @brief Gets the current drawn by the valve motor during opening.
* *

View File

@ -55,32 +55,6 @@ static int cmd_valve_set_end_curr_close(const struct shell *sh, size_t argc, cha
return 0; return 0;
} }
static int cmd_valve_set_obs_curr_open(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_obs_curr_open <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_obstacle_current_threshold_open(current_ma);
shell_print(sh, "Obstacle current threshold (open) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_set_obs_curr_close(const struct shell *sh, size_t argc, char **argv)
{
if (argc != 2) {
shell_print(sh, "Usage: valve set_obs_curr_close <milliamps>");
return -EINVAL;
}
uint16_t current_ma = (uint16_t)atoi(argv[1]);
valve_set_obstacle_current_threshold_close(current_ma);
shell_print(sh, "Obstacle current threshold (close) set to %u mA.", current_ma);
return 0;
}
static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv) static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv)
{ {
const int label_width = 30; const int label_width = 30;
@ -98,16 +72,6 @@ static int cmd_valve_show(const struct shell *sh, size_t argc, char **argv)
label_width, label_width,
"End Current Threshold (Close):", "End Current Threshold (Close):",
valve_get_end_current_threshold_close()); valve_get_end_current_threshold_close());
shell_print(sh,
"%*s %u mA",
label_width,
"Obstacle Current Threshold (Open):",
valve_get_obstacle_current_threshold_open());
shell_print(sh,
"%*s %u mA",
label_width,
"Obstacle Current Threshold (Close):",
valve_get_obstacle_current_threshold_close());
return 0; return 0;
} }
@ -122,14 +86,6 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_valve_settings,
NULL, NULL,
"Set end current threshold for closing (mA)", "Set end current threshold for closing (mA)",
cmd_valve_set_end_curr_close), cmd_valve_set_end_curr_close),
SHELL_CMD(set_obs_curr_open,
NULL,
"Set obstacle current threshold for opening (mA)",
cmd_valve_set_obs_curr_open),
SHELL_CMD(set_obs_curr_close,
NULL,
"Set obstacle current threshold for closing (mA)",
cmd_valve_set_obs_curr_close),
SHELL_CMD(show, NULL, "Show valve configuration", cmd_valve_show), SHELL_CMD(show, NULL, "Show valve configuration", cmd_valve_show),
SHELL_SUBCMD_SET_END); SHELL_SUBCMD_SET_END);

View File

@ -29,8 +29,6 @@ static uint16_t max_opening_time_s = 10;
static uint16_t max_closing_time_s = 10; static uint16_t max_closing_time_s = 10;
static uint16_t end_current_threshold_open_ma = 10; // Default value for open static uint16_t end_current_threshold_open_ma = 10; // Default value for open
static uint16_t end_current_threshold_close_ma = 10; // Default value for close static uint16_t end_current_threshold_close_ma = 10; // Default value for close
static uint16_t obstacle_current_threshold_open_ma = 600; // Default value for open
static uint16_t obstacle_current_threshold_close_ma = 600; // Default value for close
static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts static struct k_work_delayable valve_work; // Work item for scheduling valve movement timeouts
static struct k_timer movement_timer; static struct k_timer movement_timer;
@ -49,11 +47,6 @@ static void valve_work_handler(struct k_work *work)
if (current_movement == VALVE_MOVEMENT_OPENING) { if (current_movement == VALVE_MOVEMENT_OPENING) {
vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, &current_ma); vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_OPEN, &current_ma);
if (current_ma > obstacle_current_threshold_open_ma) {
LOG_ERR("Obstacle detected during opening! Current: %d mA", current_ma);
current_movement = VALVE_MOVEMENT_OBSTACLE;
goto work_handler_finish;
}
if (current_ma > end_current_threshold_open_ma) { if (current_ma > end_current_threshold_open_ma) {
k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL);
return; return;
@ -61,11 +54,6 @@ static void valve_work_handler(struct k_work *work)
LOG_INF("Valve finished opening"); LOG_INF("Valve finished opening");
} else if (current_movement == VALVE_MOVEMENT_CLOSING) { } else if (current_movement == VALVE_MOVEMENT_CLOSING) {
vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, &current_ma); vnd7050aj_read_load_current(vnd7050aj_dev, VALVE_CHANNEL_CLOSE, &current_ma);
if (current_ma > obstacle_current_threshold_close_ma) {
LOG_ERR("Obstacle detected during closing! Current: %d mA", current_ma);
current_movement = VALVE_MOVEMENT_OBSTACLE;
goto work_handler_finish;
}
if (current_ma > end_current_threshold_close_ma) { if (current_ma > end_current_threshold_close_ma) {
k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL); k_work_schedule(&valve_work, VALVE_ENDPOSITION_CHECK_INTERVAL);
return; return;
@ -75,7 +63,6 @@ static void valve_work_handler(struct k_work *work)
} }
current_movement = VALVE_MOVEMENT_IDLE; current_movement = VALVE_MOVEMENT_IDLE;
work_handler_finish:
// Reset the movement timer // Reset the movement timer
k_timer_stop(&movement_timer); k_timer_stop(&movement_timer);
@ -117,21 +104,13 @@ int valve_init(void)
settings_load_one("valve/end_current_threshold_close", settings_load_one("valve/end_current_threshold_close",
&end_current_threshold_close_ma, &end_current_threshold_close_ma,
sizeof(end_current_threshold_close_ma)); sizeof(end_current_threshold_close_ma));
settings_load_one("valve/obstacle_current_threshold_open",
&obstacle_current_threshold_open_ma,
sizeof(obstacle_current_threshold_open_ma));
settings_load_one("valve/obstacle_current_threshold_close",
&obstacle_current_threshold_close_ma,
sizeof(obstacle_current_threshold_close_ma));
LOG_INF("Valve initialized: max_open=%us, max_close=%us, end_curr_open=%umA, " LOG_INF("Valve initialized: max_open=%us, max_close=%us, end_curr_open=%umA, "
"end_curr_close=%umA, obs_curr_open=%umA, obs_curr_close=%umA", "end_curr_close=%umA",
max_opening_time_s, max_opening_time_s,
max_closing_time_s, max_closing_time_s,
end_current_threshold_open_ma, end_current_threshold_open_ma,
end_current_threshold_close_ma, end_current_threshold_close_ma);
obstacle_current_threshold_open_ma,
obstacle_current_threshold_close_ma);
valve_close(); valve_close();
return 0; return 0;
} }
@ -211,22 +190,6 @@ void valve_set_end_current_threshold_close(uint16_t current_ma)
sizeof(end_current_threshold_close_ma)); sizeof(end_current_threshold_close_ma));
} }
void valve_set_obstacle_current_threshold_open(uint16_t current_ma)
{
obstacle_current_threshold_open_ma = current_ma;
settings_save_one("valve/obstacle_current_threshold_open",
&obstacle_current_threshold_open_ma,
sizeof(obstacle_current_threshold_open_ma));
}
void valve_set_obstacle_current_threshold_close(uint16_t current_ma)
{
obstacle_current_threshold_close_ma = current_ma;
settings_save_one("valve/obstacle_current_threshold_close",
&obstacle_current_threshold_close_ma,
sizeof(obstacle_current_threshold_close_ma));
}
uint16_t valve_get_max_open_time(void) uint16_t valve_get_max_open_time(void)
{ {
return max_opening_time_s; return max_opening_time_s;
@ -246,16 +209,6 @@ uint16_t valve_get_end_current_threshold_close(void)
return end_current_threshold_close_ma; return end_current_threshold_close_ma;
} }
uint16_t valve_get_obstacle_current_threshold_open(void)
{
return obstacle_current_threshold_open_ma;
}
uint16_t valve_get_obstacle_current_threshold_close(void)
{
return obstacle_current_threshold_close_ma;
}
int32_t valve_get_opening_current(void) int32_t valve_get_opening_current(void)
{ {
int32_t current; int32_t current;

View File

@ -27,8 +27,6 @@ REG_HOLDING_MAX_OPENING_TIME_S = 0x0001
REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002 REG_HOLDING_MAX_CLOSING_TIME_S = 0x0002
REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003 REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA = 0x0003
REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004 REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA = 0x0004
REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA = 0x0005
REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA = 0x0006
REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010 REG_HOLDING_DIGITAL_OUTPUTS_STATE = 0x0010
REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0 REG_HOLDING_WATCHDOG_TIMEOUT_S = 0x00F0
REG_HOLDING_DEVICE_RESET = 0x00F1 REG_HOLDING_DEVICE_RESET = 0x00F1
@ -92,7 +90,7 @@ def poll_status(slave_id, interval):
ir_current = client.read_input_registers(REG_INPUT_MOTOR_OPEN_CURRENT_MA, count=2, slave=slave_id) ir_current = client.read_input_registers(REG_INPUT_MOTOR_OPEN_CURRENT_MA, count=2, slave=slave_id)
ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id) ir_dig = client.read_input_registers(REG_INPUT_DIGITAL_INPUTS_STATE, count=2, slave=slave_id)
ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=6, slave=slave_id) ir_sys = client.read_input_registers(REG_INPUT_FIRMWARE_VERSION_MAJOR_MINOR, count=6, slave=slave_id)
hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=6, slave=slave_id) hr_valve = client.read_holding_registers(REG_HOLDING_MAX_OPENING_TIME_S, count=4, slave=slave_id)
hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id) hr_dig = client.read_holding_registers(REG_HOLDING_DIGITAL_OUTPUTS_STATE, count=1, slave=slave_id)
hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id) hr_sys = client.read_holding_registers(REG_HOLDING_WATCHDOG_TIMEOUT_S, count=1, slave=slave_id)
@ -101,7 +99,7 @@ def poll_status(slave_id, interval):
raise ModbusException(str(res)) raise ModbusException(str(res))
valve_state_raw = ir_valve.registers[0] valve_state_raw = ir_valve.registers[0]
movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error", 4: "Obstacle"} movement_map = {0: "Idle", 1: "Opening", 2: "Closing", 3: "Error"}
state_map = {0: "Closed", 1: "Open"} state_map = {0: "Closed", 1: "Open"}
new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown') new_data["movement"] = movement_map.get(valve_state_raw >> 8, 'Unknown')
new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown') new_data["state"] = state_map.get(valve_state_raw & 0xFF, 'Unknown')
@ -111,8 +109,6 @@ def poll_status(slave_id, interval):
new_data["close_time"] = f"{hr_valve.registers[1]}s" new_data["close_time"] = f"{hr_valve.registers[1]}s"
new_data["end_curr_open"] = f"{hr_valve.registers[2]}mA" new_data["end_curr_open"] = f"{hr_valve.registers[2]}mA"
new_data["end_curr_close"] = f"{hr_valve.registers[3]}mA" new_data["end_curr_close"] = f"{hr_valve.registers[3]}mA"
new_data["obs_curr_open"] = f"{hr_valve.registers[4]}mA"
new_data["obs_curr_close"] = f"{hr_valve.registers[5]}mA"
new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}" new_data["digital_inputs"] = f"0x{ir_dig.registers[0]:04X}"
new_data["button_events"] = f"0x{ir_dig.registers[1]:04X}" new_data["button_events"] = f"0x{ir_dig.registers[1]:04X}"
new_data["digital_outputs"] = f"0x{hr_dig.registers[0]:04X}" new_data["digital_outputs"] = f"0x{hr_dig.registers[0]:04X}"
@ -238,7 +234,7 @@ def main_menu(stdscr, slave_id):
stdscr.bkgd(' ', curses.color_pair(1)) stdscr.bkgd(' ', curses.color_pair(1))
menu = ["Open Valve", "Close Valve", "Stop Valve", "Toggle Output 1", "Toggle Output 2", "Settings", "Reset Node", "Firmware Update", "Exit"] menu = ["Open Valve", "Close Valve", "Stop Valve", "Toggle Output 1", "Toggle Output 2", "Settings", "Reset Node", "Firmware Update", "Exit"]
settings_menu = ["Set Max Open Time", "Set Max Close Time", "Set End Current Open", "Set End Current Close", "Set Obstacle Current Open", "Set Obstacle Current Close", "Set Watchdog", "Back"] settings_menu = ["Set Max Open Time", "Set Max Close Time", "Set End Current Open", "Set End Current Close", "Set Watchdog", "Back"]
current_menu = menu current_menu = menu
current_row_idx = 0 current_row_idx = 0
message, message_time = "", 0 message, message_time = "", 0
@ -289,10 +285,6 @@ def main_menu(stdscr, slave_id):
input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Open (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Open (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_OPEN_MA
elif selected_option == "Set End Current Close": elif selected_option == "Set End Current Close":
input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Close (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA input_mode, input_prompt, input_target_reg = True, "Enter End Current Threshold Close (mA): ", REG_HOLDING_END_CURRENT_THRESHOLD_CLOSE_MA
elif selected_option == "Set Obstacle Current Open":
input_mode, input_prompt, input_target_reg = True, "Enter Obstacle Current Threshold Open (mA): ", REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_OPEN_MA
elif selected_option == "Set Obstacle Current Close":
input_mode, input_prompt, input_target_reg = True, "Enter Obstacle Current Threshold Close (mA): ", REG_HOLDING_OBSTACLE_CURRENT_THRESHOLD_CLOSE_MA
elif selected_option == "Set Watchdog": elif selected_option == "Set Watchdog":
input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S input_mode, input_prompt, input_target_reg = True, "Enter Watchdog Timeout (s): ", REG_HOLDING_WATCHDOG_TIMEOUT_S
elif selected_option == "Reset Node": elif selected_option == "Reset Node":
@ -332,8 +324,6 @@ def main_menu(stdscr, slave_id):
stdscr.addstr(3, col3, "Watchdog:", bold); stdscr.addstr(3, col3 + 16, str(current_data.get('watchdog', 'N/A')), normal) stdscr.addstr(3, col3, "Watchdog:", bold); stdscr.addstr(3, col3 + 16, str(current_data.get('watchdog', 'N/A')), normal)
stdscr.addstr(4, col3, "End Curr Open:", bold); stdscr.addstr(4, col3 + 16, str(current_data.get('end_curr_open', 'N/A')), normal) stdscr.addstr(4, col3, "End Curr Open:", bold); stdscr.addstr(4, col3 + 16, str(current_data.get('end_curr_open', 'N/A')), normal)
stdscr.addstr(5, col3, "End Curr Close:", bold); stdscr.addstr(5, col3 + 16, str(current_data.get('end_curr_close', 'N/A')), normal) stdscr.addstr(5, col3, "End Curr Close:", bold); stdscr.addstr(5, col3 + 16, str(current_data.get('end_curr_close', 'N/A')), normal)
stdscr.addstr(6, col3, "Obs Curr Open:", bold); stdscr.addstr(6, col3 + 16, str(current_data.get('obs_curr_open', 'N/A')), normal)
stdscr.addstr(7, col3, "Obs Curr Close:", bold); stdscr.addstr(7, col3 + 16, str(current_data.get('obs_curr_close', 'N/A')), normal)
stdscr.addstr(1, col4, "Firmware:", bold); stdscr.addstr(1, col4 + 14, str(current_data.get('firmware', 'N/A')), normal) stdscr.addstr(1, col4, "Firmware:", bold); stdscr.addstr(1, col4 + 14, str(current_data.get('firmware', 'N/A')), normal)
stdscr.addstr(2, col4, "Uptime:", bold); stdscr.addstr(2, col4 + 14, str(current_data.get('uptime', 'N/A')), normal) stdscr.addstr(2, col4, "Uptime:", bold); stdscr.addstr(2, col4 + 14, str(current_data.get('uptime', 'N/A')), normal)
stdscr.addstr(3, col4, "Dev. Status:", bold); stdscr.addstr(3, col4 + 14, str(current_data.get('device_status', 'N/A')), normal) stdscr.addstr(3, col4, "Dev. Status:", bold); stdscr.addstr(3, col4 + 14, str(current_data.get('device_status', 'N/A')), normal)