def generate_future_timesteps(data1, data2, data3, data4, num_timesteps):
    # Use the last `num_timesteps` data points as input

    max_length = max(len(data1), len(data2), len(data3), len(data4))
    future_input = np.zeros((4, max_length, 1))
    future_input[0, :len(data1), :] = np.array(data1).reshape(-1, 1)
    future_input[1, :len(data2), :] = np.array(data2).reshape(-1, 1)
    future_input[2, :len(data3), :] = np.array(data3).reshape(-1, 1)
    future_input[3, :len(data4), :] = np.array(data4).reshape(-1, 1)

    # Reshape the input data to match the model's input shape
    future_input = future_input[:, :num_timesteps, :]
    future_input = np.swapaxes(future_input, 0, 1)

    return future_input


def create_dataset(dataset, look_back=1):
    dataX, dataY = [], []
    for i in range(len(dataset) - look_back - 1):
        a = dataset[i:(i + look_back), 0]
        dataX.append(a)
        dataY.append(dataset[i + look_back, :])
    return np.array(dataX), np.array(dataY)

def create_rnn_model(trainX, trainY, testX, testY, rnn_units, name='RNN Model'):
    # Define the RNN model
    model = Sequential()
    model.add(SimpleRNN(rnn_units[0], activation='tanh', input_shape=(1, 1), return_sequences=True))
    model.add(SimpleRNN(rnn_units[1], activation='tanh'))
    model.add(Dense(rnn_units[2]))
    model.add(Dense(4))

    # Compile the model
    model.compile(optimizer='adam', loss='mae')

    # Train the model on the training data
    history = model.fit(trainX, trainY, epochs=25, batch_size=16, verbose=1, validation_split=0.2)

    # Evaluate the model on the testing data
    loss = model.evaluate(testX, testY, batch_size=64)
    print(f'{name} loss: {loss}')

    return model

def create_lstm_model(trainX, trainY, testX, testY, lstm_units, name='LSTM Model'):
    # Define the LSTM autoencoder model
    model = Sequential()
    model.add(LSTM(lstm_units[0], activation='relu', input_shape = (1, 1), return_sequences = True))
    model.add(LSTM(lstm_units[1], activation='relu', return_sequences = True))
    model.add(LSTM(lstm_units[2], activation='relu'))
    model.add(Dense(lstm_units[3]))
    model.add(Dense(4))

    # Compile the model
    model.compile(optimizer='adam', loss ='mae')

    # Train the model on the training data
    history = model.fit(trainX, trainY, epochs=25, batch_size=16, verbose=1, validation_split=0.2)

    # Evaluate the model on the testing data
    loss = model.evaluate(testX, testY, batch_size=64)
    print(f'{name} loss: {loss}')

    return model


def ensemble_models(data1, data2, data3, data4, future_timesteps):
    min_length = min(len(data1), len(data2), len(data3), len(data4))

    # Slice each array to the minimum length
    data1 = data1[len(data1) - min_length:]
    data2 = data2[len(data2) - min_length:]
    data3 = data3[len(data3) - min_length:]
    data4 = data4[len(data4) - min_length:]

    # Pad the sequences with zeros so that they all have the same length
    max_length = max(len(data1), len(data2), len(data3), len(data4))
    all_data = np.zeros((4, max_length, 1))
    for i, data in enumerate([data1, data2, data3, data4]):
        all_data[i, :len(data), :] = np.array(data).reshape(-1, 1)

    d = {'hum': data1, 'light': data2, 'temp': data3, 'mois': data4}
    dataset = pandas.DataFrame(d)
    dataset = dataset.values
    dataset = dataset.astype("float32")


    train_size = int(len(dataset) * 0.7)
    test_size = len(dataset) - train_size
    train, test = dataset[0:train_size, :], dataset[train_size:len(dataset), :]


    trainX, trainY = create_dataset(train)
    testX, testY = create_dataset(test)

    trainX = np.reshape(trainX, (trainX.shape[0], 1, trainX.shape[1]))
    testX = np.reshape(testX, (testX.shape[0], 1, testX.shape[1]))

    # Create the first LSTM model
    lstm1 = create_lstm_model(trainX, trainY, testX, testY, lstm_units=[128, 64, 32, 64], name='LSTM Model 1')

    # Create the second LSTM model with a different architecture
    #lstm2 = create_lstm_model(trainX, trainY, testX, testY, lstm_units=[64, 32, 16, 32], name='LSTM Model 2')
    rnn = create_rnn_model(trainX, trainY, testX, testY, rnn_units=[128, 64, 32, 64], name='RNN Model')

    # Get predictions from both models
    predictions1 = lstm1.predict(testX)
    predictions2 = rnn.predict(testX)

    # Combine the predictions using a weighted average
    ensemble_weight_1 = 0.5
    ensemble_weight_2 = 1.0 - ensemble_weight_1
    ensemble_predictions = ensemble_weight_1 * predictions1 + ensemble_weight_2 * predictions2

    # Evaluate the ensemble model’s performance
    mae = np.mean(np.abs(ensemble_predictions - testY), axis=0)
    print(f'Ensemble MAE: {mae}')

    max_humidity = 100  # Set the maximum possible value for humidity
    max_light = 100  # Set the maximum possible value for light
    max_temperature = 50  # Set the maximum possible value for temperature
    max_moisture = 100  # Set the maximum possible value for moisture

    mae_humidity = np.mean(np.abs(ensemble_predictions[:, 0] - testY[:, 0]))
    mae_light = np.mean(np.abs(ensemble_predictions[:, 1] - testY[:, 1]))
    mae_temperature = np.mean(np.abs(ensemble_predictions[:, 2] - testY[:, 2]))
    mae_moisture = np.mean(np.abs(ensemble_predictions[:, 3] - testY[:, 3]))

    percentage_error_humidity = (mae_humidity / max_humidity) * 100
    percentage_error_light = (mae_light / max_light) * 100
    percentage_error_temperature = (mae_temperature / max_temperature) * 100
    percentage_error_moisture = (mae_moisture / max_moisture) * 100

    print(f'Humidity Percentage Error: {round(percentage_error_humidity, 2)}%')
    print(f'Light Percentage Error: {round(percentage_error_light, 2)}%')
    print(f'Temperature Percentage Error: {round(percentage_error_temperature, 2)}%')
    print(f'Moisture Percentage Error: {round(percentage_error_moisture, 2)}%')

    print(f'Mean error: {round(np.mean([percentage_error_light, percentage_error_humidity, percentage_error_moisture, percentage_error_temperature]), 2)}%')

    # Reshape the data for future predictions

    future_input = generate_future_timesteps(data1, data2, data3, data4, 500)

    # Get predictions for future timesteps
    future_predictions1 = lstm1.predict(future_input)
    future_predictions2 = rnn.predict(future_input)

    # Combine the predictions using a weighted average
    future_ensemble_predictions = (ensemble_weight_1 * future_predictions1 + ensemble_weight_2 * future_predictions2)

    # Reshape array2 to match the first dimension of array1
    array2_reshaped = np.repeat(future_ensemble_predictions, ensemble_predictions.shape[0] // future_ensemble_predictions.shape[0], axis=0)

    # Concatenate the reshaped arrays along axis 0
    future_ensemble_predictions = np.concatenate((ensemble_predictions, array2_reshaped), axis=0)

    # (Optional) Visualization code

    plt.figure(figsize=(12, 6))

    #plt.plot(ensemble_predictions[:, 0], label='Humidity Predictions', linestyle ='-')
    #plt.plot(ensemble_predictions[:, 1], label='Light Predictions', linestyle ='-')
    #plt.plot(ensemble_predictions[:, 2], label='Temperature Predictions', linestyle ='-')
    #plt.plot(ensemble_predictions[:, 3], label='Moisture Predictions', linestyle ='-')

    plt.plot(future_ensemble_predictions[:, 0], label='Humidity Predictions', linestyle='-')
    plt.plot(future_ensemble_predictions[:, 1], label='Light Predictions', linestyle='-')
    plt.plot(future_ensemble_predictions[:, 2], label='Temperature Predictions', linestyle='-')
    plt.plot(future_ensemble_predictions[:, 3], label='Moisture Predictions', linestyle='-')

    plt.plot(testY[:, 0], label='Humidity Real', alpha=0.7, linestyle='dashed')
    plt.plot(testY[:, 1], label='Light Real', alpha=0.7, linestyle='dashed')
    plt.plot(testY[:, 2], label='Temperature Real', alpha=0.7, linestyle='dashed')
    plt.plot(testY[:, 3], label='Moisture Real', alpha=0.7, linestyle='dashed')

    plt.xlabel('Time Steps')
    plt.ylabel('Sensor Values')

    plt.legend()
    plt.show()

    return lstm1, rnn

What is wrong with my code the future timesteps prediction is not working at all, giving me weird results and also not the number of future timesteps that I specify