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