Developer & Open Source Guide
MediaSignage provides extensive open-source tools and SDKs for developers who want to customize, extend, or white-label digital signage solutions. This guide covers the development ecosystem, from simple component creation to full platform customization.
Development Ecosystem Overview
Available Open Source Projects
| Project | License | Purpose | Technologies |
|---|---|---|---|
| StudioLite | GPL V3 | Lightweight CMS | Angular, TypeScript, RxJS |
| Pepper SDK | GPL V3 | Core signage library | JavaScript, HTML5 |
| msGetStarted | GPL V3 | Account creation wizard | Bootstrap, Backbone.js |
| Component SDK | GPL V3 | Custom component dev | HTML5, CSS, JavaScript |
| StudioDashboard | GPL V3 | Enterprise dashboard | Angular, Redux |
Development Stack
┌─────────────────────────────────────────────────────────────────────────────┐
│ Digital Signage Development Stack │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Frontend (Studio/CMS) │
│ ├── Angular Framework │
│ ├── TypeScript │
│ ├── RxJS (Reactive Programming) │
│ ├── NgRx (State Management) │
│ └── Bootstrap / Custom CSS │
│ │
│ Player Runtime │
│ ├── HTML5 / CSS3 │
│ ├── JavaScript (ES6+) │
│ ├── Web Components │
│ └── Canvas / WebGL (animations) │
│ │
│ Backend Services │
│ ├── Node.js │
│ ├── RESTful APIs │
│ ├── WebSocket (real-time) │
│ └── SignService (local player API) │
│ │
│ SDKs & Libraries │
│ ├── RedPepper SDK (authentication) │
│ ├── YellowPepper SDK (server communication) │
│ └── Component SDK (custom widgets) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
StudioLite - Open Source CMS
Overview
StudioLite is a fully open-source digital signage content management system built with Angular. It provides approximately 90% of SignStudio's functionality and can be freely forked, customized, and rebranded.
Repository: github.com/born2net/studioDashboard
Getting Started
# Clone the repository
git clone https://github.com/born2net/studioDashboard.git
cd studioDashboard
# Install dependencies
npm install
# Start development server
npm start
# Build for production
npm run build
# Run tests
npm test
Project Structure
studioDashboard/
├── src/
│ ├── app/
│ │ ├── campaigns/ # Campaign management
│ │ ├── scenes/ # Scene editor
│ │ ├── resources/ # Media library
│ │ ├── timeline/ # Timeline editor
│ │ ├── stations/ # Player management
│ │ ├── settings/ # User settings
│ │ └── shared/ # Shared components
│ ├── assets/
│ │ ├── images/
│ │ └── styles/
│ └── environments/
│ ├── environment.ts
│ └── environment.prod.ts
├── angular.json
├── package.json
└── README.md
Customization Examples
Branding / White-Label:
// src/environments/environment.ts
export const environment = {
production: false,
appName: 'Your Signage Platform',
companyName: 'Your Company',
logo: 'assets/images/your-logo.png',
primaryColor: '#3498db',
secondaryColor: '#2ecc71',
apiBaseUrl: 'https://your-api-server.com',
supportEmail: 'support@yourcompany.com'
};
Custom Theme:
// src/styles/custom-theme.scss
$primary-color: #3498db;
$secondary-color: #2ecc71;
$background-color: #1a1a2e;
$text-color: #ffffff;
// Override default styles
.navbar {
background-color: $primary-color;
}
.btn-primary {
background-color: $secondary-color;
&:hover {
background-color: darken($secondary-color, 10%);
}
}
Adding Custom Features:
// src/app/custom-feature/custom-feature.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { CustomFeatureComponent } from './custom-feature.component';
import { CustomFeatureRoutingModule } from './custom-feature-routing.module';
@NgModule({
declarations: [CustomFeatureComponent],
imports: [
CommonModule,
CustomFeatureRoutingModule
]
})
export class CustomFeatureModule { }
Component SDK
Overview
The Component SDK enables creation of custom widgets and components for both SignStudio (configuration UI) and SignPlayer (runtime display).
Component Architecture
custom-component/
├── player/ # SignPlayer runtime
│ ├── index.html # Component HTML
│ ├── styles.css # Component styles
│ └── script.js # Component logic
├── studio/ # SignStudio configuration
│ ├── properties.html # Properties panel
│ └── properties.js # Properties logic
├── icon.png # Component icon (64x64)
├── manifest.json # Component metadata
└── README.md
Creating a Custom Component
Step 1: Create manifest.json
{
"name": "Weather Widget",
"id": "com.yourcompany.weather",
"version": "1.0.0",
"description": "Displays current weather conditions",
"author": "Your Company",
"website": "https://yourcompany.com",
"icon": "icon.png",
"category": "data",
"tags": ["weather", "data", "api"],
"properties": {
"apiKey": {
"type": "string",
"label": "API Key",
"required": true
},
"location": {
"type": "string",
"label": "Location",
"default": "New York, US"
},
"units": {
"type": "select",
"label": "Units",
"options": ["imperial", "metric"],
"default": "imperial"
},
"refreshInterval": {
"type": "number",
"label": "Refresh (minutes)",
"default": 15,
"min": 5,
"max": 60
}
}
}
Step 2: Create Player Runtime (player/index.html)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="weather-widget">
<div class="weather-icon">
<img id="condition-icon" src="" alt="">
</div>
<div class="weather-info">
<div id="temperature" class="temp"></div>
<div id="condition" class="condition"></div>
<div id="location" class="location"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Step 3: Create Player Logic (player/script.js)
(function() {
// Get component properties from SignPlayer
const props = window.componentProperties || {};
const API_KEY = props.apiKey;
const LOCATION = props.location || 'New York, US';
const UNITS = props.units || 'imperial';
const REFRESH_INTERVAL = (props.refreshInterval || 15) * 60 * 1000;
async function fetchWeather() {
try {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${encodeURIComponent(LOCATION)}&appid=${API_KEY}&units=${UNITS}`
);
if (!response.ok) throw new Error('Weather API error');
const data = await response.json();
updateDisplay(data);
} catch (error) {
console.error('Weather fetch failed:', error);
showError();
}
}
function updateDisplay(data) {
const tempUnit = UNITS === 'imperial' ? '°F' : '°C';
document.getElementById('temperature').textContent =
Math.round(data.main.temp) + tempUnit;
document.getElementById('condition').textContent =
data.weather[0].description;
document.getElementById('location').textContent =
data.name;
document.getElementById('condition-icon').src =
`https://openweathermap.org/img/wn/${data.weather[0].icon}@2x.png`;
}
function showError() {
document.getElementById('temperature').textContent = '--';
document.getElementById('condition').textContent = 'Unable to load';
}
// Initial fetch
fetchWeather();
// Refresh on interval
setInterval(fetchWeather, REFRESH_INTERVAL);
// Listen for SignPlayer events
window.addEventListener('signplayer:refresh', fetchWeather);
window.addEventListener('signplayer:pause', () => {
// Handle pause if needed
});
})();
Step 4: Create Properties Panel (studio/properties.html)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5/dist/css/bootstrap.min.css">
<style>
body { padding: 15px; }
.form-group { margin-bottom: 15px; }
</style>
</head>
<body>
<form id="properties-form">
<div class="form-group">
<label for="apiKey">OpenWeatherMap API Key</label>
<input type="text" class="form-control" id="apiKey" required>
<small class="text-muted">Get free API key at openweathermap.org</small>
</div>
<div class="form-group">
<label for="location">Location</label>
<input type="text" class="form-control" id="location" value="New York, US">
</div>
<div class="form-group">
<label for="units">Temperature Units</label>
<select class="form-control" id="units">
<option value="imperial">Fahrenheit (°F)</option>
<option value="metric">Celsius (°C)</option>
</select>
</div>
<div class="form-group">
<label for="refreshInterval">Refresh Interval (minutes)</label>
<input type="number" class="form-control" id="refreshInterval"
value="15" min="5" max="60">
</div>
</form>
<script src="properties.js"></script>
</body>
</html>
Step 5: Create Properties Logic (studio/properties.js)
(function() {
// Communication with SignStudio
const StudioBridge = {
// Get current property values
getProperties: function() {
return window.parent.postMessage({
type: 'getProperties'
}, '*');
},
// Save property values
setProperties: function(props) {
window.parent.postMessage({
type: 'setProperties',
properties: props
}, '*');
}
};
// Listen for property updates from SignStudio
window.addEventListener('message', function(event) {
if (event.data.type === 'propertiesLoaded') {
loadProperties(event.data.properties);
}
});
// Load properties into form
function loadProperties(props) {
if (props.apiKey) document.getElementById('apiKey').value = props.apiKey;
if (props.location) document.getElementById('location').value = props.location;
if (props.units) document.getElementById('units').value = props.units;
if (props.refreshInterval) document.getElementById('refreshInterval').value = props.refreshInterval;
}
// Save on input change
document.getElementById('properties-form').addEventListener('change', function() {
StudioBridge.setProperties({
apiKey: document.getElementById('apiKey').value,
location: document.getElementById('location').value,
units: document.getElementById('units').value,
refreshInterval: parseInt(document.getElementById('refreshInterval').value)
});
});
// Request initial properties
StudioBridge.getProperties();
})();
Testing Components Locally
# Start local development server
npx http-server ./custom-component -p 8080
# Access component directly
open http://localhost:8080/player/index.html
# Simulate SignPlayer properties
# In browser console:
window.componentProperties = {
apiKey: 'your_test_api_key',
location: 'London, UK',
units: 'metric',
refreshInterval: 5
};
location.reload();
Packaging and Distribution
# Create component package
cd custom-component
zip -r weather-widget-1.0.0.zip *
# Package structure:
# weather-widget-1.0.0.zip
# ├── manifest.json
# ├── icon.png
# ├── player/
# │ ├── index.html
# │ ├── styles.css
# │ └── script.js
# └── studio/
# ├── properties.html
# └── properties.js
Pepper SDK
RedPepper SDK (Authentication)
// Authentication with RedPepper SDK
import { RedPepper } from '@mediasignage/pepper-sdk';
const pepper = new RedPepper({
serverUrl: 'https://your-server.com',
domain: 'your-domain'
});
// Login
async function login(username, password) {
try {
const session = await pepper.authenticate(username, password);
console.log('Logged in:', session.user);
return session;
} catch (error) {
console.error('Login failed:', error);
throw error;
}
}
// Logout
async function logout() {
await pepper.logout();
}
// Check session
function isAuthenticated() {
return pepper.isAuthenticated();
}
YellowPepper SDK (Server Communication)
// Server communication with YellowPepper SDK
import { YellowPepper } from '@mediasignage/pepper-sdk';
const yellow = new YellowPepper({
serverUrl: 'https://your-server.com',
authToken: sessionToken
});
// Get campaigns
async function getCampaigns() {
const campaigns = await yellow.campaigns.list();
return campaigns;
}
// Create resource
async function uploadMedia(file) {
const resource = await yellow.resources.upload(file, {
name: file.name,
type: 'image'
});
return resource;
}
// Update scene
async function updateScene(sceneId, sceneData) {
const scene = await yellow.scenes.update(sceneId, sceneData);
return scene;
}
// Subscribe to real-time updates
yellow.subscribe('campaign.updated', (data) => {
console.log('Campaign updated:', data);
});
msGetStarted - Account Creation Wizard
Overview
msGetStarted is an open-source account creation wizard that can be customized for white-label deployments.
Repository: github.com/born2net/msgetstarted
Features
- Studio selection (Lite/Pro)
- Player installer selection
- Branded account creation
- Password management
- Bootstrap responsive layout
Customization
// config.js - Branding configuration
const config = {
branding: {
companyName: 'Your Company',
logo: 'assets/your-logo.png',
primaryColor: '#3498db',
supportEmail: 'support@yourcompany.com',
termsUrl: 'https://yourcompany.com/terms',
privacyUrl: 'https://yourcompany.com/privacy'
},
features: {
showStudioSelection: true,
defaultStudio: 'lite',
showPlayerOptions: true,
allowSocialLogin: false,
requireEmailVerification: true
},
plans: {
showFreePlan: true,
showProPlan: true,
showEnterprisePlan: true,
defaultPlan: 'free'
}
};
Enterprise Dashboard
Features
The Enterprise StudioDashboard provides centralized management for large signage networks:
- User Control: Manage users, roles, permissions
- Server Status: Live monitoring of all servers
- Network Health: Reports and alerts
- Application Management: Deploy and manage apps
- User Activity: Audit logs and analytics
- Geo Maps: Visualize screen locations
- Remote Management: Control players remotely
Deployment
# Clone dashboard repository
git clone https://github.com/born2net/studioDashboard.git
cd studioDashboard
# Configure environment
cp .env.example .env
# Edit .env with your settings
# Build and deploy
npm install
npm run build
# Serve with nginx or similar
cp -r dist/* /var/www/dashboard/
API Integration
REST API Overview
// Base API endpoints
const API_BASE = 'https://your-server.com/api/v1';
const endpoints = {
// Authentication
auth: {
login: `${API_BASE}/auth/login`,
logout: `${API_BASE}/auth/logout`,
refresh: `${API_BASE}/auth/refresh`
},
// Campaigns
campaigns: {
list: `${API_BASE}/campaigns`,
create: `${API_BASE}/campaigns`,
get: (id) => `${API_BASE}/campaigns/${id}`,
update: (id) => `${API_BASE}/campaigns/${id}`,
delete: (id) => `${API_BASE}/campaigns/${id}`
},
// Resources
resources: {
list: `${API_BASE}/resources`,
upload: `${API_BASE}/resources/upload`,
get: (id) => `${API_BASE}/resources/${id}`,
delete: (id) => `${API_BASE}/resources/${id}`
},
// Stations (Players)
stations: {
list: `${API_BASE}/stations`,
get: (id) => `${API_BASE}/stations/${id}`,
update: (id) => `${API_BASE}/stations/${id}`,
command: (id) => `${API_BASE}/stations/${id}/command`
}
};
WebSocket Real-Time Updates
// Real-time connection for live updates
const ws = new WebSocket('wss://your-server.com/ws');
ws.onopen = () => {
// Authenticate WebSocket connection
ws.send(JSON.stringify({
type: 'auth',
token: authToken
}));
// Subscribe to updates
ws.send(JSON.stringify({
type: 'subscribe',
channels: ['stations', 'campaigns']
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'station.status':
handleStationUpdate(data.payload);
break;
case 'campaign.updated':
handleCampaignUpdate(data.payload);
break;
case 'resource.uploaded':
handleResourceUpdate(data.payload);
break;
}
};
Frequently Asked Questions
The open-source ecosystem enables developers to build customized digital signage solutions. For enterprise development support and additional resources, contact MediaSignage or visit the API documentation.