Build a hybrid office space planning tool using pressure sensors, infrared motion sensors, or ultrasonic distance sensors deployed across desks, connected via MQTT to a time-series database, with analytics dashboards showing peak use hours and efficiency scores. This reveals actual desk usage patterns driving informed space optimization decisions rather than guesswork.
Hybrid Office Space Planning Tool for Facilities Managers: Optimizing Desk Use in 2026
Hybrid office space planning requires accurate data about how employees actually use physical workspace. Without real occupancy insights, facilities managers rely on guesswork for desk allocation, leading to either overcrowded spaces or wasted real estate. Building a desk use tracking system provides the data needed to optimize space allocation, reduce costs, and improve the employee experience. This guide covers the technical implementation of a hybrid office space planning tool—from sensor deployment to analytics dashboards.
Core Components of a Desk Use System
A practical desk use tracking system consists of four main layers: sensing hardware, data collection infrastructure, processing logic, and visualization interfaces. Each component plays a specific role in generating actionable occupancy data.
Sensor Options for Desk Detection
Choosing the right sensors depends on your deployment scale and budget. Three approaches work well for different scenarios.
Pressure-based sensors detect weight changes when someone sits at a desk. These are cost-effective and easy to install under desk mats or chair cushions. The main limitation is they cannot distinguish between a person and objects placed on the sensor.
Infrared motion sensors detect movement within a defined zone. Place them above each desk or in common areas to capture occupancy patterns. These sensors work best when combined with timeout logic—treating a desk as occupied for a set period after last detected motion.
Ultrasonic distance sensors measure the presence of objects below the desk surface. These provide higher accuracy than pressure sensors but require careful calibration to avoid false positives from bags or coats.
# Example: Reading occupancy from ultrasonic distance sensor
import RPi.GPIO as GPIO
import time
TRIG_PIN = 23
ECHO_PIN = 24
def measure_distance():
GPIO.output(TRIG_PIN, True)
time.sleep(0.00001)
GPIO.output(TRIG_PIN, False)
while GPIO.input(ECHO_PIN) == 0:
pulse_start = time.time()
while GPIO.input(ECHO_PIN) == 1:
pulse_end = time.time()
pulse_duration = pulse_end - pulse_start
distance = pulse_duration * 17150
return distance
def is_desk_occupied(distance_cm, threshold=40):
# Desk is occupied if object detected within threshold
return distance_cm < threshold
# Example usage
distance = measure_distance()
occupied = is_desk_occupied(distance)
print(f"Desk status: {'Occupied' if occupied else 'Available'}")
Data Collection Architecture
MQTT-Based Data Pipeline
Transmitting sensor data via MQTT provides a lightweight, reliable foundation for real-time occupancy tracking. Each sensor publishes its status to a hierarchical topic structure that helps filtering and aggregation.
// Node.js MQTT client for desk sensor data
const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://office-sensors.local');
const FLOOR = 'floor-3';
const ZONE = 'engineering';
client.on('connect', () => {
console.log('Connected to MQTT broker');
client.subscribe(`office/desks/${FLOOR}/${ZONE}/#`);
});
client.on('message', (topic, message) => {
const [, , floor, zone, deskId] = topic.split('/');
const payload = JSON.parse(message.toString());
const deskEvent = {
floor,
zone,
deskId,
occupied: payload.status === 'occupied',
timestamp: new Date().toISOString(),
sensorType: payload.sensor
};
// Store in time-series database
influxClient.writePoint({
measurement: 'desk_occupancy',
tags: {
floor: deskEvent.floor,
zone: deskEvent.zone,
deskId: deskEvent.deskId
},
fields: {
occupied: deskEvent.occupied ? 1 : 0
},
timestamp: deskEvent.timestamp
});
// Update Redis cache for real-time queries
redisClient.hSet(
`desk:${floor}:${zone}:${deskId}`,
'status',
deskEvent.occupied ? 'occupied' : 'available'
);
});
API Design for Space Management
Building a RESTful API enables integration with existing facilities management systems and custom dashboards. Structure endpoints around desks, floors, and time-based queries.
// Express.js API for desk utilization data
const express = require('express');
const app = express();
// Get current desk status for a floor
app.get('/api/floors/:floorId/desks', async (req, res) => {
const { floorId } = req.params;
const { zone } = req.query;
const filter = { floor: floorId };
if (zone) filter.zone = zone;
const desks = await redisClient.hGetAll(`floor:${floorId}:desks`);
const deskList = Object.entries(desks).map(([deskId, status]) => ({
deskId,
status,
floor: floorId
}));
res.json({ floor: floorId, desks: deskList });
});
// Get utilization metrics for a time range
app.get('/api/analytics/utilization', async (req, res) => {
const { floorId, startDate, endDate, interval = 'hour' } = req.query;
const query = `
SELECT mean(occupied) as utilization
FROM desk_occupancy
WHERE floor = $1 AND time >= $2 AND time <= $3
GROUP BY time(${interval}), deskId
`;
const results = await influxClient.query(query, [floorId, startDate, endDate]);
const utilizationByHour = results.reduce((acc, row) => {
const hour = new Date(row.time).getHours();
acc[hour] = (acc[hour] || 0) + row.utilization;
return acc;
}, {});
res.json({ floorId, period: { start: startDate, end: endDate }, utilization: utilizationByHour });
});
app.listen(3000, () => console.log('Desk API running on port 3000'));
Occupancy Analytics and Insights
Use Rate Calculations
Raw occupancy data becomes valuable when transformed into meaningful metrics. Calculate key performance indicators that drive space planning decisions.
# Python analytics for desk utilization metrics
from datetime import datetime, timedelta
from collections import defaultdict
def calculate_utilization_metrics(occupancy_data, total_desks):
"""
Calculate utilization metrics from raw occupancy records.
Args:
occupancy_data: List of dicts with 'desk_id', 'occupied', 'timestamp'
total_desks: Total number of desks in the analyzed area
Returns:
Dictionary with utilization metrics
"""
occupied_records = [r for r in occupancy_data if r['occupied']]
# Peak utilization: maximum concurrent desks in use
occupancy_by_minute = defaultdict(int)
for record in occupied_records:
minute = record['timestamp'].replace(second=0)
occupancy_by_minute[minute] += 1
peak_utilization = max(occupancy_by_minute.values()) if occupancy_by_minute else 0
# Average utilization rate
avg_utilization = (len(occupied_records) / len(occupancy_data)) * 100 if occupancy_data else 0
# Utilization efficiency: how well desk capacity matches demand
efficiency = (peak_utilization / total_desks) * 100
return {
'peak_utilization': peak_utilization,
'peak_percentage': (peak_utilization / total_desks) * 100,
'average_utilization': round(avg_utilization, 2),
'total_desks': total_desks,
'efficiency_score': round(efficiency, 2),
'recommendation': get_recommendation(efficiency)
}
def get_recommendation(efficiency):
if efficiency > 85:
return 'Add more desks or consider expansion'
elif efficiency > 60:
return 'Current capacity is well-utilized'
elif efficiency > 30:
return 'Consider hybrid scheduling to increase utilization'
else:
return 'Reduce desk count or repurpose space'
Heatmap Generation
Visualizing occupancy patterns reveals spatial trends that raw numbers miss. Generate heatmaps showing which areas experience high demand and when.
// JavaScript heatmap data preparation for visualization
function generateHeatmapData(occupancyRecords, floorPlan) {
const gridSize = 20; // pixels per grid cell
const heatmapData = [];
// Group occupancy by hour and grid position
const hourBuckets = {};
occupancyRecords.forEach(record => {
const hour = new Date(record.timestamp).getHours();
const gridX = Math.floor(record.x_position / gridSize);
const gridY = Math.floor(record.y_position / gridSize);
if (!hourBuckets[hour]) hourBuckets[hour] = {};
const key = `${gridX},${gridY}`;
hourBuckets[hour][key] = (hourBuckets[hour][key] || 0) + (record.occupied ? 1 : 0);
});
// Normalize and prepare for rendering
Object.keys(hourBuckets).forEach(hour => {
const gridData = hourBuckets[hour];
const maxCount = Math.max(...Object.values(gridData));
Object.entries(gridData).forEach(([key, count]) => {
const [x, y] = key.split(',').map(Number);
heatmapData.push({
x, y,
hour: parseInt(hour),
intensity: count / maxCount // normalized 0-1
});
});
});
return heatmapData;
}
Integration with Space Planning Tools
Export Formats for Facilities Software
Most facilities management platforms accept standard data formats. Export your use data in formats that integrate with industry tools.
// Export utilization data in COBie format for BIM integration
function exportToCOBie(utilizationData, floorInfo) {
const cobieExport = {
Zone: {
ZoneName: floorInfo.name,
ZoneCategory: 'Floor',
ZoneType: 'Office',
Description: `Floor ${floorInfo.id} - ${floorInfo.zone}`
},
Space: utilizationData.desks.map(desk => ({
SpaceName: desk.deskId,
SpaceType: 'Desk',
ZoneName: floorInfo.name,
Description: `Desk ${desk.deskId} in ${floorInfo.zone}`,
NominalFloorArea: floorInfo.deskAreaSqFt
}))
};
return cobieExport;
}
// Generate CSV report for Excel analysis
function generateUtilizationReport(utilizationMetrics) {
const headers = ['Floor', 'Zone', 'Total Desks', 'Peak Utilization', 'Avg Utilization %', 'Efficiency Score', 'Recommendation'];
const rows = utilizationMetrics.map(m => [
m.floorId, m.zone, m.totalDesks, m.peakUtilization,
m.averageUtilization, m.efficiencyScore, m.recommendation
]);
return [headers, ...rows].map(row => row.join(',')).join('\n');
}
Deployment Considerations
Network Infrastructure
Deploy sensors on a separate VLAN from general office traffic to ensure reliable connectivity. Use Power over Ethernet (PoE) switches to simplify cable management for permanent installations. Configure network redundancy so individual sensor failures don’t cascade.
Privacy and Compliance
Desk occupancy tracking involves employee privacy considerations. Anonymize data where possible, aggregate metrics before reporting, and establish clear policies about how use data gets used. Some jurisdictions require notice or consent for workplace monitoring systems.
Scaling Strategy
Start with a pilot floor covering 20-50 desks. Validate your sensor reliability, data pipeline stability, and analytics accuracy before expanding. Plan for horizontal scaling by designing your MQTT topic structure and database schema to accommodate additional floors without refactoring.
Related Articles
- Best Practice for Hybrid Office Kitchen and Shared Space
- Calculate pod count based on floor space and team size
- Async Capacity Planning Process for Remote Engineering — Managers
- Useful Thai search terms
- Air Quality Monitoring for Hybrid Office Spaces: A
Built by theluckystrike — More at zovo.one