Quick Start Guide

Get started building TelemetryOS applications in minutes

Quick Start Guide

Get started building TelemetryOS applications in minutes.

Prerequisites

  • Node.js 18+
  • npm or pnpm
  • Basic knowledge of JavaScript/TypeScript and web development

Installation

CLI Tool Installation

The TelemetryOS CLI scaffolds and runs applications locally. CLI installation enables rapid application generation with best practices built in:

# Install CLI globally
npm install -g @telemetryos/cli

# Create new application
tos init my-weather-app

# Navigate to project
cd my-weather-app

# Install dependencies
npm install

# Start local development server
tos serve

Manual SDK Installation

The SDK integrates into existing projects through npm installation:

npm install @telemetryos/sdk

Your First Application

Project Structure

A TelemetryOS application typically has this structure:

my-weather-app/
├── src/
│   ├── views/
│   │   ├── Render.tsx      # Display content (runs on devices)
│   │   └── Settings.tsx    # Configuration UI (runs in admin)
│   ├── App.tsx             # Routing between views
│   └── index.tsx           # Entry point
├── telemetry.config.json   # Application configuration
├── package.json
├── vite.config.ts
└── tsconfig.json

Configuration File

Create telemetry.config.json in your project root:

{
  "name": "my-weather-app",
  "version": "1.0.0",
  "mountPoints": {
    "render": "/render",
    "settings": "/settings"
  },
  "devServer": {
    "runCommand": "vite --port 3000",
    "url": "http://localhost:3000"
  }
}

Key Fields:

  • name - Application identifier (must match configure() call)
  • version - Application version (semver format)
  • mountPoints - URL paths for render and settings views
  • devServer - Local development server configuration

Settings View

Create the configuration UI that runs in the admin portal:

src/views/Settings.tsx:

import { useEffect, useState } from 'react';
import { configure, store } from '@telemetryos/sdk';

configure('my-weather-app');

export function Settings() {
  const [city, setCity] = useState('');

  // Load current city on mount
  useEffect(() => {
    store().instance.get<string>('city').then(savedCity => {
      if (savedCity) setCity(savedCity);
    });
  }, []);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    await store().instance.set('city', city);
    alert('Settings saved!');
  };

  return (
    <div className="settings">
      <h2>Weather App Settings</h2>
      <form onSubmit={handleSubmit}>
        <label>
          City:
          <input
            type="text"
            value={city}
            onChange={(e) => setCity(e.target.value)}
            placeholder="Enter city name"
          />
        </label>
        <button type="submit">Save</button>
      </form>
    </div>
  );
}

Render View

Create the display content that runs on devices:

src/views/Render.tsx:

import { useEffect, useState } from 'react';
import { configure, store } from '@telemetryos/sdk';

configure('my-weather-app');

export function Render() {
  const [city, setCity] = useState<string | null>(null);
  const [weather, setWeather] = useState<any>(null);

  useEffect(() => {
    // Subscribe to city changes
    const handler = (newCity: string) => {
      setCity(newCity);
      fetchWeather(newCity);
    };

    store().instance.subscribe('city', handler);

    // Load initial value
    store().instance.get<string>('city').then(savedCity => {
      if (savedCity) {
        setCity(savedCity);
        fetchWeather(savedCity);
      }
    });

    return () => {
      store().instance.unsubscribe('city', handler);
    };
  }, []);

  const fetchWeather = async (cityName: string) => {
    // Fetch weather data for the city
    // Implementation details omitted for brevity
    setWeather({ temp: '72°F', condition: 'Sunny' });
  };

  if (!city) {
    return <div className="render">Please configure a city in settings</div>;
  }

  return (
    <div className="render">
      <h1>{city}</h1>
      {weather && (
        <>
          <div className="temp">{weather.temp}</div>
          <div className="condition">{weather.condition}</div>
        </>
      )}
    </div>
  );
}

Routing

Set up routing between views based on URL path:

src/App.tsx:

import { Settings } from './views/Settings';
import { Render } from './views/Render';

export function App() {
  const path = window.location.pathname;

  if (path === '/settings') {
    return <Settings />;
  }

  if (path === '/render') {
    return <Render />;
  }

  return <div>404 - Not Found</div>;
}

src/index.tsx:

import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>
);

Local Development

Development Server

The CLI development server (tos serve) provides local application testing:

tos serve

The development server:

  • Reads telemetry.config.json for configuration
  • Executes devServer.runCommand (typically Vite or webpack-dev-server)
  • Opens development UI at the configured URL
  • Provides simulated TelemetryOS environment for testing

Mount Point Testing

Mount points are accessible at configured URLs:

The development server provides mock SDK responses, enabling testing without TelemetryOS platform connection.

SDK Initialization

Always call configure() early in your application lifecycle:

import { configure } from '@telemetryos/sdk';

// Must match name in telemetry.config.json
configure('my-weather-app');

Important: Call configure() once per mount point before using other SDK features.

Communication Between Views

Settings and render views share the same instance storage scope:

// In Settings - save configuration
await store().instance.set('city', 'New York');

// In Render - subscribe to changes
store().instance.subscribe('city', (newCity) => {
  console.log('City changed to:', newCity);
});

This pattern enables real-time updates when configuration changes.

Error Handling

All SDK operations can throw errors or timeout (30 seconds):

try {
  await store().instance.set('key', 'value');
} catch (error) {
  console.error('Failed to save:', error);
  // Show user-friendly error message
}

Deployment Methods

TelemetryOS supports three deployment methods. GitHub integration provides automated Git-to-Screen deployment and is recommended for production workflows.

GitHub Integration

GitHub integration enables automated deployment through repository connection:

# Initialize git and push to GitHub
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/your-org/weather-widget.git
git push -u origin main

Studio repository connection:

  • Applications interface provides GitHub integration option
  • Repository connection establishes secure access to source code
  • Branch selection determines which commits trigger builds
  • Platform automatically builds and deploys on every push

Archive Upload

Archive upload supports manual deployment when Git is unavailable:

# Build your application
npm run build

# Create zip archive
cd dist
zip -r ../my-weather-app.zip .

Archive upload workflow:

  • Applications interface provides archive upload option
  • Platform extracts archive contents
  • Build scripts execute using configured parameters
  • Applications deploy after successful builds

Archive uploads require manual updates for code changes; automatic builds are not available.

Generic Git Repository

Generic Git repository support accommodates GitLab, Bitbucket, or self-hosted Git services:

  • Code pushes to Git hosting service
  • Studio generic Git repository option accepts repository URLs
  • Deploy keys provide secure repository access
  • Webhook configuration enables automatic builds

Post-Deployment

After successful deployment:

  • Applications appear in Studio Applications list
  • Applications add to playlists as resizable regions
  • Settings interface accessible through playlist application selection
  • Playlist assignment deploys applications to devices

Next Steps

Common Patterns

Loading Initial Data

useEffect(() => {
  store().instance.get<string>('setting').then(value => {
    if (value) setSettingValue(value);
  });
}, []);

Real-Time Updates

useEffect(() => {
  const handler = (newValue: any) => {
    setMyState(newValue);
  };

  store().instance.subscribe('key', handler);

  return () => {
    store().instance.unsubscribe('key', handler);
  };
}, []);

Error Recovery

try {
  const data = await media().getAllByTag('marketing');
  displayMedia(data);
} catch (error) {
  // Provide fallback
  displayPlaceholder();
}

Troubleshooting

SDK Configuration Errors:

  • configure('app-name') must be called before using SDK functions
  • Application name must match telemetry.config.json name field

Timeout Errors:

  • Network connectivity issues cause SDK operation timeouts
  • Retry logic handles transient failures for critical operations
  • Fallback content ensures displays never show blank states

Storage Persistence Issues:

  • Storage scope selection (application/instance/device/shared) determines data visibility
  • Successful set() responses confirm data persistence
  • Scope mismatches prevent data access across components

Mount Point Loading Issues:

  • Mount point paths in telemetry.config.json must match application routing
  • Browser console errors indicate routing or configuration problems

Development Best Practices

Early Initialization: configure() calls occur at application startup before SDK usage.

Subscription Pattern: Subscriptions provide real-time updates; polling is unnecessary and inefficient.

Error Handling: All async SDK operations can fail; error handling ensures graceful degradation.

Local Testing: CLI development server (tos serve) enables rapid iteration without platform deployment.

Type Safety: TypeScript definitions catch configuration and API usage errors during development.


What’s Next