Remote Work Tools

Hybrid Office Locker System for Employees Who Hot Desk

When employees hot desk, they need secure storage for personal belongings, equipment, and valuables throughout the workday. A well-designed locker system integrates with existing badge access, provides real-time availability tracking, and offers programmatic control for custom workplace workflows. This guide covers the technical implementation of a hybrid office locker system built for hot-desking environments.

Why Hot-Desking Requires Smart Locker Systems

Traditional lockers with combination locks or physical keys don’t work in hot-desking scenarios. Employees can’t remember codes, keys get lost, and there’s no way to track which lockers are available or who currently has which locker assigned. Smart locker systems solve these problems by providing badge-controlled access, automatic assignment, and integration with desk booking platforms.

Modern locker systems connect to your identity provider, automatically assigning lockers when employees book desks and releasing them when reservations end. This automation removes friction from the hot-desking experience while providing security teams with audit logs of every access event.

Core Locker System Architecture

A smart locker system consists of four primary components: the locker controller hardware, the backend API service, the integration layer, and the user-facing applications. Understanding how these components interact helps you design a system that scales across multiple office locations.

Locker Controller Hardware

The controller hardware manages individual locker doors and communicates with the central system. Most commercial smart locker solutions use one of three architectures:

Networked Controllers: Each locker has a network-connected controller that communicates over Ethernet or WiFi. These controllers accept commands from a central server and report status changes. Examples include systems from Essential Robotics and OpenSpaces.

Bus-Based Systems: Lockers connect to a central controller via a communication bus (RS-485 or CAN bus). This architecture reduces wiring complexity but requires longer cable runs. Bosch Smart Locker and Stanley Access Control use this approach.

Standalone Smart Locks: Individual battery-powered smart locks retrofit onto existing lockers. August Home and SmartRent offer locks that connect via Zigbee, Z-Wave, or WiFi. These work well for organizations wanting to upgrade existing furniture without full system replacement.

For new installations, networked controllers provide the most reliable performance and easiest integration. Here’s a typical controller specification:

# Locker controller specification
class LockerController:
    def __init__(self, ip_address, locker_count):
        self.ip_address = ip_address
        self.locker_count = locker_count
        self.status = [None] * locker_count  # None=available, str=user_id=assigned

    def get_status(self):
        """Poll controller for current locker states."""
        # Returns list of dicts with locker_id, status, last_access, battery_level
        pass

    def open_locker(self, locker_id, user_id, duration_seconds=3600):
        """Unlock specific locker for timed access."""
        pass

    def assign_locker(self, locker_id, user_id, reservation_id):
        """Permanently assign locker for reservation duration."""
        pass

Building the Locker API Service

The backend API handles all business logic: user authentication, locker assignment, access logging, and integration with other workplace systems. Here’s a Flask-based implementation of the core locker service:

from flask import Flask, request, jsonify
from datetime import datetime, timedelta
from functools import wraps

app = Flask(__name__)

# In-memory storage (replace with database in production)
lockers = {}
reservations = {}
access_logs = []

class Locker:
    def __init__(self, locker_id, location, size='medium'):
        self.id = locker_id
        self.location = location  # e.g., "floor-3-north"
        self.size = size  # small, medium, large
        self.status = 'available'
        self.current_user = None
        self.current_reservation = None

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization')
        if not token or not token.startswith('Bearer '):
            return jsonify({'error': 'Unauthorized'}), 401
        # Verify token against identity provider
        # In production, validate JWT or check against SSO
        return f(*args, **kwargs)
    return decorated

@app.route('/api/lockers', methods=['GET'])
@require_auth
def list_lockers():
    """List all lockers with availability status."""
    location = request.args.get('location')
    size = request.args.get('size')

    available_lockers = [
        {
            'id': locker.id,
            'location': locker.location,
            'size': locker.size,
            'status': locker.status
        }
        for locker in lockers.values()
        if (not location or locker.location == location)
        and (not size or locker.size == size)
    ]

    return jsonify({'lockers': available_lockers})

@app.route('/api/lockers/assign', methods=['POST'])
@require_auth
def assign_locker():
    """Assign a locker to user for their desk booking."""
    data = request.json
    user_id = data.get('user_id')
    desk_booking_id = data.get('desk_booking_id')
    desired_location = data.get('preferred_location')

    # Find available locker in preferred location
    available = [
        locker for locker in lockers.values()
        if locker.status == 'available'
        and (not desired_location or locker.location == desired_location)
    ]

    if not available:
        return jsonify({'error': 'No lockers available'}), 409

    locker = available[0]
    locker.status = 'assigned'
    locker.current_user = user_id
    locker.current_reservation = desk_booking_id

    reservation = {
        'id': desk_booking_id,
        'locker_id': locker.id,
        'user_id': user_id,
        'assigned_at': datetime.utcnow().isoformat(),
        'expires_at': (datetime.utcnow() + timedelta(hours=10)).isoformat()
    }
    reservations[desk_booking_id] = reservation

    return jsonify({
        'locker_id': locker.id,
        'location': locker.location,
        'reservation': reservation
    })

@app.route('/api/lockers/<locker_id>/access', methods=['POST'])
@require_auth
def access_locker(locker_id):
    """Record locker access event and trigger hardware unlock."""
    if locker_id not in lockers:
        return jsonify({'error': 'Locker not found'}), 404

    locker = lockers[locker_id]
    user_id = request.json.get('user_id')

    # Verify user has active reservation for this locker
    active_reservation = None
    for res in reservations.values():
        if res['locker_id'] == locker_id and res['user_id'] == user_id:
            active_reservation = res
            break

    if not active_reservation:
        return jsonify({'error': 'No active reservation'}), 403

    # Log access event
    access_log = {
        'timestamp': datetime.utcnow().isoformat(),
        'locker_id': locker_id,
        'user_id': user_id,
        'action': 'unlock'
    }
    access_logs.append(access_log)

    # Trigger hardware unlock (communicate with controller)
    # controller.open_locker(locker_id, user_id)

    return jsonify({'status': 'unlocked', 'log': access_log})

@app.route('/api/lockers/<locker_id>/release', methods=['POST'])
@require_auth
def release_locker(locker_id):
    """Release locker when desk booking ends."""
    if locker_id not in lockers:
        return jsonify({'error': 'Locker not found'}), 404

    locker = lockers[locker_id]

    if locker.status == 'available':
        return jsonify({'error': 'Locker already released'}), 400

    # Log release event
    access_logs.append({
        'timestamp': datetime.utcnow().isoformat(),
        'locker_id': locker_id,
        'user_id': locker.current_user,
        'action': 'release'
    })

    # Clear reservation and make available
    if locker.current_reservation in reservations:
        del reservations[locker.current_reservation]

    locker.status = 'available'
    locker.current_user = None
    locker.current_reservation = None

    return jsonify({'status': 'released'})

Integrating with Desk Booking Systems

The locker system achieves its full potential when integrated with desk booking platforms. When an employee books a desk, the system automatically assigns an available locker nearby. When the booking ends, the locker automatically releases for the next employee.

Here’s how to integrate with a desk booking API:

import requests
from datetime import datetime

class DeskBookingIntegration:
    def __init__(self, desk_api_url, locker_api_url, api_key):
        self.desk_api = desk_api_url
        self.locker_api = locker_api_url
        self.api_key = api_key
        self.headers = {'Authorization': f'Bearer {api_key}'}

    def on_desk_booked(self, booking_event):
        """Handle desk booking creation event."""
        user_id = booking_event['user_id']
        booking_id = booking_event['booking_id']
        floor = booking_event['floor']

        # Find locker on same floor as booked desk
        location = f'floor-{floor}'

        # Request locker assignment
        response = requests.post(
            f'{self.locker_api}/lockers/assign',
            json={
                'user_id': user_id,
                'desk_booking_id': booking_id,
                'preferred_location': location
            },
            headers=self.headers
        )

        if response.status_code == 200:
            locker = response.json()
            print(f"Assigned locker {locker['locker_id']} to user {user_id}")
            # Optionally notify user via email or Slack
            return locker
        else:
            print(f"No lockers available for booking {booking_id}")
            return None

    def on_desk_released(self, booking_event):
        """Handle desk booking cancellation or end event."""
        booking_id = booking_event['booking_id']

        # Find associated locker
        response = requests.get(
            f'{self.locker_api}/reservations/{booking_id}',
            headers=self.headers
        )

        if response.status_code == 200:
            reservation = response.json()
            locker_id = reservation['locker_id']

            # Release the locker
            requests.post(
                f'{self.locker_api}/lockers/{locker_id}/release',
                headers=self.headers
            )
            print(f"Released locker {locker_id} from booking {booking_id}")

Badge Access Integration

Most hybrid offices already have badge access systems. Integrating locker access with existing badges simplifies the user experience—no additional credentials needed. Here’s how to connect with common access control platforms:

class BadgeAccessIntegration:
    def __init__(self, access_provider='honeywell'):
        self.provider = access_provider
        # Provider-specific configuration
        self.api_endpoints = {
            'honeywell': 'https://api.honeywell-buildings.com/v1',
            'assa': 'https://api.assaabloy.com/v2',
            'bosch': 'https://api.bosch-security.com/v1'
        }

    def sync_user_credentials(self, user_id, badge_id):
        """Sync user badge to locker system for access control."""
        # This ensures the user's badge can open their assigned locker
        # Implementation varies by locker hardware vendor
        pass

    def check_access_permission(self, user_id, locker_id):
        """Verify user has permission to access specific locker."""
        # Query access control system for user permissions
        # Return True if user has access, False otherwise
        pass

    def log_access_event(self, badge_id, locker_id, timestamp, action):
        """Send access event to central security system."""
        event_data = {
            'badge_id': badge_id,
            'door_id': f'locker-{locker_id}',
            'timestamp': timestamp,
            'action': action
        }
        # Send to security logging system
        pass

Locker Fleet Management

When managing hundreds of lockers across multiple floors and buildings, fleet management becomes critical. Here’s a dashboard-ready data structure for monitoring:

# Locker fleet status aggregation
def get_fleet_status(lockers):
    """Generate fleet-wide statistics for dashboard display."""
    total = len(lockers)
    available = sum(1 for l in lockers if l.status == 'available')
    assigned = sum(1 for l in lockers if l.status == 'assigned')
    maintenance = sum(1 for l in lockers if l.status == 'maintenance')

    # Group by location
    by_location = {}
    for locker in lockers:
        if locker.location not in by_location:
            by_location[locker.location] = {'total': 0, 'available': 0}
        by_location[locker.location]['total'] += 1
        if locker.status == 'available':
            by_location[locker.location]['available'] += 1

    return {
        'summary': {
            'total': total,
            'available': available,
            'assigned': assigned,
            'maintenance': maintenance,
            'utilization_rate': (assigned / total * 100) if total > 0 else 0
        },
        'by_location': by_location,
        'last_updated': datetime.utcnow().isoformat()
    }

Deployment Considerations

When deploying smart lockers in hybrid offices, several practical factors affect success:

Power and Network: Networked lockers require both power and Ethernet connectivity to each controller. Plan cable routes during office construction. For retrofit installations, consider PoE (Power over Ethernet) to reduce electrical work. Battery-powered smart locks work for wireless scenarios but require regular battery replacement.

Location Strategy: Place lockers near high-traffic areas like elevator banks and stairwells. Consider zoning—lockers for each floor or department reduce congestion. Provide a mix of sizes: small for wallets and phones, medium for bags and laptops, large for coats and equipment.

Maintenance Access: Build in maintenance modes for battery replacement, hardware repairs, and firmware updates. The API should support temporarily taking individual lockers offline without affecting the rest of the fleet.

User Communication: Set clear expectations about what can and cannot be stored. Most systems prohibit valuables, perishables, and prohibited items. Display policies on locker doors and include in employee onboarding.

A well-integrated locker system removes one of the friction points in hot-desking, making it effortless for employees to store belongings securely while they work from any desk in the office.

Built by theluckystrike — More at zovo.one