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

Developer App

Download the TelemetryOS Developer App from Studio. The Developer App includes the tos CLI for scaffolding, developing, and publishing applications.

# Create new application
tos init my-weather-app

# Or create an application with web mount point support
tos init my-menu-app --template vite-react-typescript-web

# Navigate to project
cd my-weather-app

# Open in Developer App
tos dev

Manual SDK Installation

The SDK integrates into existing projects through npm installation:

npm install @telemetryos/sdk

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

Applications with a web mount point (created with --template vite-react-typescript-web) include an additional view:

my-menu-app/
├── src/
│   ├── views/
│   │   ├── Render.tsx      # Display content (runs on devices)
│   │   ├── Settings.tsx    # Configuration UI (runs in admin)
│   │   └── Web.tsx         # Management interface (runs in any browser)
│   ├── 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 the project root:

{
  "name": "my-weather-app",
  "version": "2026.1.0",
  "mountPoints": {
    "render": "/render",
    "settings": "/settings"
  },
  "devServer": {
    "runCommand": "vite --port $PORT",
    "url": "http://localhost:$PORT",
    "readyPattern": "VITE.*ready in"
  }
}

The name field is the application identifier used by the platform. The version field uses CalVer format (YYYY.M.PATCH). The mountPoints object maps view names to URL paths for your render and settings views. The devServer object tells the Developer App how to start your local development server — $PORT is replaced at runtime with an available port, and readyPattern detects when the server is ready from its stdout output.

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();

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();

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 tos dev command (alias: tos serve) opens your project in the Developer App:

tos dev

The Developer App reads your telemetry.config.json, starts your dev server (typically Vite), and provides a simulated TelemetryOS environment so you can test SDK features without connecting to the live platform.

Mount Point Testing

Mount points are displayed within the Developer App:

  • Render: Shown on the main canvas with adjustable aspect ratios and backgrounds
  • Settings: Shown in the settings sidebar panel
  • Web: Shown in the Web tab (if mountPoints.web is configured)

The Developer App provides mock SDK responses, enabling testing without TelemetryOS platform connection.

SDK Initialization

Always call configure() early in the application lifecycle:

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

configure();

Important: Call configure() once 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

In Studio, use the Applications interface to connect your GitHub repository. Once connected, the platform establishes secure access to your source code. Select a branch to determine which commits trigger builds, and the platform will automatically build and deploy on every push.

Archive Upload

Archive upload supports manual deployment when Git is unavailable:

# Build the application
npm run build

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

In Studio, use the Applications interface to upload your zip archive. The platform extracts the archive contents, executes your build scripts using the configured parameters, and deploys the application after a successful build. Note that 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. Push your code to your hosting service, then use the generic Git repository option in Studio to provide the repository URL. Deploy keys provide secure repository access, and webhook configuration enables automatic builds on push.

Post-Deployment

After successful deployment, your application appears in the Studio Applications list. From there you can add it to playlists as a resizable region, access its settings interface by selecting the application within a playlist, and deploy it to devices by assigning the playlist.

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: The configure() call must happen before using any other SDK functions.

Timeout Errors: Network connectivity issues cause SDK operation timeouts (30-second default). Implement retry logic to handle transient failures for critical operations, and provide fallback content so displays never show blank states.

Storage Persistence Issues: The storage scope you choose (application, instance, device, or shared) determines data visibility across components. A successful set() response confirms data persistence, so if data appears missing, check for scope mismatches between the writing and reading components.

Mount Point Loading Issues: Mount point paths in telemetry.config.json must match your application's routing configuration. If mount points fail to load, check the browser console for specific routing or configuration errors.

Development Best Practices

Call configure() at application startup before using any other SDK features. This ensures the SDK is properly initialized when your components begin rendering.

Use the subscription pattern for real-time updates rather than polling. Subscriptions are efficient and ensure your render views reflect configuration changes immediately, whereas polling wastes resources and introduces unnecessary latency.

Wrap all async SDK operations in error handling. Every call to store(), media(), or proxy() can fail due to network issues or timeouts, so error handling ensures your application degrades gracefully instead of crashing.

Use tos dev for rapid iteration. The Developer App provides a simulated TelemetryOS environment locally, so you can test SDK interactions without deploying to the platform.

Take advantage of TypeScript definitions throughout your application. The SDK ships with full type coverage, which catches configuration mistakes and API usage errors at development time rather than in production.


What’s Next