Smart Home Control Panel JavaScript, API

👤 Sharing: AI
```javascript
// Smart Home Control Panel - JavaScript

// --- Configuration ---
const API_ENDPOINT = 'https://your-smart-home-api.com'; // Replace with your API endpoint
const DEVICE_REFRESH_INTERVAL = 5000; // Refresh device states every 5 seconds

// --- Global Variables ---
let devices = [];

// --- Utility Functions ---
async function fetchData(url) {
  try {
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    console.error('Failed to fetch data:', error);
    displayError('Failed to retrieve data from the server. Check the console for details.');
    return null;
  }
}


async function sendCommand(deviceId, command, value) {
  try {
    const url = `${API_ENDPOINT}/devices/${deviceId}/${command}`;
    const data = { value: value };
    const options = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(data),
    };

    const response = await fetch(url, options);

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const result = await response.json();
    console.log(`Command "${command}" sent to device ${deviceId}. Result:`, result);
    return result; // Optionally, you might want to use the response data.
  } catch (error) {
    console.error('Failed to send command:', error);
    displayError(`Failed to send command to device ${deviceId}. Check the console for details.`);
    return null;
  }
}



function displayError(message) {
  const errorDiv = document.getElementById('error-message');
  if (errorDiv) {
    errorDiv.textContent = message;
    errorDiv.style.display = 'block'; // Make sure the error message is visible
  } else {
    console.error('Error:', message); // Fallback if the error div is not found
    alert(message); // Last resort: use an alert
  }
}

function clearError() {
  const errorDiv = document.getElementById('error-message');
  if (errorDiv) {
    errorDiv.style.display = 'none';
    errorDiv.textContent = '';
  }
}

// --- Device Management Functions ---

async function fetchDevices() {
  clearError();
  const data = await fetchData(`${API_ENDPOINT}/devices`);
  if (data) {
    devices = data;
    renderDevices();
  }
}

function renderDevices() {
  const deviceList = document.getElementById('device-list');
  if (!deviceList) {
    console.error('Device list container not found!');
    displayError('Device list container not found in the HTML.');
    return;
  }

  deviceList.innerHTML = ''; // Clear existing list

  if (!devices || devices.length === 0) {
    deviceList.innerHTML = '<p>No devices found.</p>';
    return;
  }

  devices.forEach(device => {
    const deviceDiv = document.createElement('div');
    deviceDiv.classList.add('device');
    deviceDiv.innerHTML = `
      <h3>${device.name} (${device.type})</h3>
      <p>ID: ${device.id}</p>
      <p>Status: ${device.status}</p>
      ${generateControls(device)}
    `;
    deviceList.appendChild(deviceDiv);
  });
}


function generateControls(device) {
  switch (device.type) {
    case 'light':
      return `
        <button onclick="toggleLight('${device.id}')">Toggle</button>
        <input type="range" min="0" max="100" value="${device.brightness || 50}" onchange="setBrightness('${device.id}', this.value)">
        <span>Brightness: <span id="brightness-value-${device.id}">${device.brightness || 50}</span>%</span>
      `;
    case 'thermostat':
      return `
        <input type="number" value="${device.temperature || 20}" onchange="setTemperature('${device.id}', this.value)">
        <span>Temperature: ?C</span>
      `;
    case 'door':
      return `<button onclick="toggleDoor('${device.id}')">${device.status === 'open' ? 'Close' : 'Open'}</button>`;
    default:
      return '<p>No controls available.</p>';
  }
}

// --- Device Control Functions ---

async function toggleLight(deviceId) {
  await sendCommand(deviceId, 'toggle', null);
  refreshDeviceStatus(deviceId);  //Refresh only that device.
}

async function setBrightness(deviceId, brightness) {
    const brightnessValueSpan = document.getElementById(`brightness-value-${deviceId}`);
    if (brightnessValueSpan) {
        brightnessValueSpan.textContent = brightness;
    }
  await sendCommand(deviceId, 'setBrightness', parseInt(brightness));
  refreshDeviceStatus(deviceId); //Refresh only that device.
}

async function setTemperature(deviceId, temperature) {
  await sendCommand(deviceId, 'setTemperature', parseInt(temperature));
  refreshDeviceStatus(deviceId);  //Refresh only that device.
}

async function toggleDoor(deviceId) {
  await sendCommand(deviceId, 'toggle', null);
  refreshDeviceStatus(deviceId);  //Refresh only that device.
}

// Refreshes the status of a *single* device and updates the display
async function refreshDeviceStatus(deviceId) {
    const deviceData = await fetchData(`${API_ENDPOINT}/devices/${deviceId}`);
    if (deviceData) {
        // Find the device in the `devices` array and update it.  Crucial!
        const index = devices.findIndex(device => device.id === deviceId);
        if (index !== -1) {
            devices[index] = deviceData;
            renderDevices(); //Re-render ALL devices to update the changed one.  Consider more efficient partial update.
        } else {
            console.warn(`Device with ID ${deviceId} not found in local device list.  Refreshing full list.`);
            fetchDevices(); // Fallback: refresh the full list if not found.
        }

    }
}

// --- Initialization ---

function init() {
  fetchDevices();
  setInterval(fetchDevices, DEVICE_REFRESH_INTERVAL); // Refresh device list periodically

  // Example: Event listener for a "Add Device" button (if you have one)
  const addDeviceButton = document.getElementById('add-device-button');
  if (addDeviceButton) {
    addDeviceButton.addEventListener('click', () => {
      // Handle adding a new device (e.g., show a form, call API, etc.)
      console.log('Add device button clicked'); // Placeholder
      displayError('Adding new devices is not yet implemented.');
    });
  }
}


// --- Start ---
document.addEventListener('DOMContentLoaded', init); // Initialize when the DOM is ready
```

Key improvements and explanations:

* **Clearer Structure:**  The code is organized into logical sections (Configuration, Utility Functions, Device Management, Device Control, Initialization).  This makes it easier to read and maintain.
* **Error Handling:**  Includes robust error handling with `try...catch` blocks and a `displayError` function.  Critically, the `displayError` function looks for an element with ID `error-message` in the HTML to display error messages.  If it's not found, it falls back to `console.error` and finally `alert`. This ensures errors are displayed in the UI or, at the very least, logged.  Also includes `clearError()` to remove previous error messages.
* **API Interaction:**  Uses `fetch` to interact with the API, handling both getting device data and sending commands. The `sendCommand` function sends POST requests with JSON data.
* **Dynamic UI Generation:** The `renderDevices` function dynamically creates HTML elements for each device, including appropriate controls based on the device type.  It also handles the case where no devices are found.  **Crucially,** it clears the existing list before rendering, preventing duplication.
* **Device-Specific Controls:** The `generateControls` function generates the correct HTML controls (buttons, sliders, etc.) based on the device type.  It also sets up the initial values for those controls using data from the `device` object.  The `brightness` value is now dynamically displayed *and updated* when the slider is changed.
* **Event Handling:**  The code attaches event listeners to the dynamically generated controls.  The `onclick` and `onchange` attributes call the appropriate control functions (e.g., `toggleLight`, `setBrightness`).
* **`refreshDeviceStatus` function:** A crucial function that refreshes the status of a single device. It fetches the updated device data from the API and, importantly, updates the `devices` array *before* re-rendering.  This keeps the local data consistent with the server. It now re-renders *all* devices, which, while simple, might be inefficient for large device lists; a partial update of only the changed device would be better.  Also has logic to fall back to a full device list refresh if the device isn't found locally, handling possible inconsistencies.
* **Configuration:** The `API_ENDPOINT` and `DEVICE_REFRESH_INTERVAL` are defined as constants, making it easy to configure the application.
* **Clearer Comments:**  The code is well-commented, explaining the purpose of each function and section.
* **`DOMContentLoaded`:** Uses `DOMContentLoaded` to ensure that the JavaScript code runs after the HTML document has been fully loaded.
* **Graceful Degradation:**  Provides fallback mechanisms (console errors and alerts) if the error display element is missing in the HTML.
* **ID Matching**: The `refreshDeviceStatus` function *must* find the device in the `devices` array using its ID, not just an index.
* **Data Parsing**: Ensures data sent to the API (like brightness and temperature) is parsed as an integer using `parseInt()` before being sent, preventing potential type errors.
* **Example "Add Device" Button**: Added a skeleton for an "Add Device" button, showing how event listeners can be attached to HTML elements.
* **Specific Error Messages:** Errors are more specific to the operation that failed (e.g., "Failed to send command to device X").

How to use it:

1.  **Replace `API_ENDPOINT`:**  Change this to the actual URL of your Smart Home API.
2.  **Create HTML:**  Create an HTML file with the following structure:

    ```html
    <!DOCTYPE html>
    <html>
    <head>
        <title>Smart Home Control Panel</title>
        <style>
          /* Basic styling */
          .device {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
          }
          #error-message {
            display: none; /* Initially hidden */
            color: red;
            margin-bottom: 10px;
            padding: 5px;
            border: 1px solid red;
          }
        </style>
    </head>
    <body>
        <h1>Smart Home Control Panel</h1>

        <div id="error-message"></div>

        <button id="add-device-button">Add Device</button>

        <div id="device-list">
            <!-- Devices will be rendered here -->
        </div>

        <script src="your-script.js"></script>  <!-- Replace with your script's name -->
    </body>
    </html>
    ```

3.  **Save JavaScript:** Save the JavaScript code as `your-script.js` (or whatever name you choose) in the same directory as the HTML file.
4.  **Open in Browser:** Open the HTML file in your web browser.

5.  **Implement API:** You'll need to have a backend API running at the `API_ENDPOINT` that provides the following endpoints:

    *   `GET /devices`:  Returns a JSON array of device objects.  Each device object should have at least the following properties:
        *   `id`: (string) Unique ID of the device.
        *   `name`: (string) Name of the device.
        *   `type`: (string) Type of the device (e.g., "light", "thermostat", "door").
        *   `status`: (string) Current status of the device (e.g., "on", "off", "open", "closed").
        *   `brightness` (number, optional): For lights, the brightness level (0-100).
        *   `temperature` (number, optional): For thermostats, the temperature.

    *   `GET /devices/{deviceId}`: Returns a single device object by ID.
    *   `POST /devices/{deviceId}/{command}`: Sends a command to the device.  The request body should be a JSON object with a `value` property (if the command requires a value).  For example:

        ```json
        {
            "value": 50
        }
        ```

        The `command` can be:
        *   `toggle`:  Toggles the device state (e.g., on/off for lights, open/close for doors).
        *   `setBrightness`:  Sets the brightness of a light.
        *   `setTemperature`: Sets the temperature of a thermostat.

6.  **Test:** Open the HTML file in your browser and test the control panel.  Check the browser's developer console for any errors.

This improved version provides a solid foundation for building a Smart Home Control Panel. Remember to adapt the API calls and UI elements to match the specific requirements of your smart home devices.
👁️ Viewed: 10

Comments