Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
site
/.next
/.source
/.vscode
.cache
node_modules
docs/.vitepress/dist
docs/.vitepress/cache
21 changes: 17 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,22 @@ RUN pnpm run build

FROM node:${NODE_VERSION}-alpine${ALPINE_VERSION} AS runtime

EXPOSE 80
ARG PNPM_VERSION

WORKDIR /docs

RUN wget -qO /bin/pnpm "https://github.com/pnpm/pnpm/releases/download/v${PNPM_VERSION}/pnpm-linuxstatic-x64" && chmod +x /bin/pnpm
Copy link

Copilot AI Jan 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dockerfile downloads and executes the pnpm-linuxstatic-x64 binary directly from GitHub Releases via wget without any checksum or signature verification (RUN wget -qO /bin/pnpm ... && chmod +x /bin/pnpm). If that download endpoint, TLS, or DNS is ever compromised, an attacker could deliver a malicious binary that runs with full privileges inside your build/runtime container and modify build outputs or exfiltrate secrets. To reduce supply-chain risk, fetch pnpm via the official package manager or add an integrity check (e.g., pinned checksum/signature) before marking the binary executable and using it in the image.

Copilot uses AI. Check for mistakes.

ENV NODE_ENV=production

COPY package.json .
COPY pnpm-lock.yaml .
RUN pnpm install --prod --frozen-lockfile --strict-peer-dependencies

COPY --from=build /docs/.next ./.next
COPY --from=build /docs/public ./public
COPY --from=build /docs/next.config.mjs ./next.config.mjs

# Copy release artifacts (static HTML, JS, CSS)
COPY --from=build /docs/docs/.vitepress/dist /usr/share/nginx/html
EXPOSE 3000

# Leave startup as-is.
CMD ["pnpm", "start"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

## Development

This documentation site is built with [VitePress](https://vitepress.dev).
This documentation site is built with [Fumadocs](https://fumadocs.dev) on top of Next.js.

### Local preview

Expand Down
45 changes: 45 additions & 0 deletions app/(docs)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from 'fumadocs-ui/page';
import { getMDXComponents } from '@/mdx-components';
import { source } from '@/lib/source';

type PageParams = {
slug?: string[];
};

export default async function Page({ params }: { params: PageParams }) {
const page = source.getPage(params.slug);
if (!page) notFound();

const MDX = page.data.body;

return (
<DocsPage
toc={page.data.toc}
full={page.data.full}
tableOfContent={{ enabled: false }}
tableOfContentPopover={{ enabled: false }}
>
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
<MDX components={getMDXComponents()} />
</DocsBody>
</DocsPage>
);
}

export async function generateStaticParams() {
return source.generateParams();
}

export async function generateMetadata({ params }: { params: PageParams }): Promise<Metadata> {
const page = source.getPage(params.slug);
if (!page) notFound();

return {
title: page.data.title,
description: page.data.description,
};
}
12 changes: 12 additions & 0 deletions app/(docs)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ReactNode } from 'react';
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
import { baseOptions } from '@/lib/layout.shared';
import { source } from '@/lib/source';

export default function DocsRootLayout({ children }: { children: ReactNode }) {
return (
<DocsLayout tree={source.getPageTree()} {...baseOptions()}>
{children}
</DocsLayout>
);
}
3 changes: 3 additions & 0 deletions app/global.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import 'tailwindcss';
@import 'fumadocs-ui/css/neutral.css';
@import 'fumadocs-ui/css/preset.css';
18 changes: 18 additions & 0 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ReactNode } from 'react';
import { RootProvider } from 'fumadocs-ui/provider/next';
import { Inter } from 'next/font/google';
import './global.css';

const inter = Inter({
subsets: ['latin'],
});

export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" className={inter.className} suppressHydrationWarning>
<body className="flex min-h-screen flex-col">
<RootProvider>{children}</RootProvider>
</body>
</html>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ git clone https://github.com/OpenShock/Firmware.git
```
Open the folder you just downloaded with VSCode. Allow time for PlatformIO to initialize the IDE. Once it has completed, pick the project environment based on the board you would like to compile for under the new PlatformIO icon.

![An image showing where to find the icon for 'Pick project environment'](../../static/diy/software/compiling/platformio.png)
![An image showing where to find the icon for 'Pick project environment'](/static/diy/software/compiling/platformio.png)

First, run the `PlatformIO > Project Tasks > General > Upload` task, then run `Platform > Upload Filesystem Image`. These tasks auto-build the latest changes and then upload the code to a connected micro-controller. This may require pressing the reset button on your micro-controller, refer to the documentation for your specific board for more information.
File renamed without changes.
2 changes: 1 addition & 1 deletion docs/dev/index.md → content/docs/dev/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tags:

## Backend Architecture

![Backend Architecture Diagram](../static/developer/backend-architecture-overview.svg)
![Backend Architecture Diagram](/static/developer/backend-architecture-overview.svg)


## OpenAPI Documentation
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Or, you can re-enable the Captive Portal on the website under the Hub's "..." me


:::
![Example Pinout](../../static/diy/pinout.png)
![Example Pinout](/static/diy/pinout.png)

::: warning Logic level compatibility
Please note that all ESP32s operate at 3.3V logic levels. To avoid overvolting your ESP's IO pins, it is recommended to either: connect your transmitter's power input to a 3V supply pin on the ESP's board, or use a logic-level shifter if your transmitter ***really* requires** more than 3V power to operate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ You will need to connect the button and resistor in a pullup or pulldown configu

Either pullup or pulldown will work use which ever is easier for your board.

![Image "Image"](../../static/diy/pullupexample.png)
![Image "Image"](../../static/diy/pulldownexample.png)
![Image "Image"](/static/diy/pullupexample.png)
![Image "Image"](/static/diy/pulldownexample.png)

*Most* of the GPIO pins on any given ESP32 board should work for E-Stop, however some pins are reserved for special usage and will return an error if you attempt to use them during setup. If you run into that error, adjust your setup to use a different pin with a higher number.

Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ The electricity could flow through your heart.
1. Plug your hub in and ensure it has power.
2. On your phone, search for a Wi-Fi network named similar to ``OpenShock-XX:XX:XX:XX:XX:XX`` and connect to it.
::: details Images (click to expand)
![Image "image"](../../static/guides/first-setup/WiFioverview.png)
![Image "image"](/static/guides/first-setup/WiFioverview.png)
:::
2. Connect to the hub via the network:
1. Open your phone's browser and type in ``10.10.10.10`` *or* ``openshock.local`` this should open up a web-interface for the hub.
2. Find your router's Wi-Fi network in the web-interface.
3. Press the green button next to it and type in your Wi-Fi password, then press submit.
*A green pop up should appear if it's connected successfully*
::: details Images (click to expand)
![Image "image"](../../static/guides/first-setup/ESPWebGUI.png)
![Image "image"](/static/guides/first-setup/ESPWebGUI.png)
:::
3. Set the RF TX Pin if needed

Expand All @@ -55,22 +55,22 @@ If you do not know how to do this, you can also re-enable the captive portal (ho
3. Type in a name, *(your name for example)* into the name field.
4. Press save.
::: details Images (click to expand)
![Image "image"](../../static/guides/first-setup/findaddbutton3.png)
![Image "image"](../../static/guides/first-setup/find_device_context_menu.png)
![Image "image"](../../static/guides/first-setup/edit_device.png)
![Image "image"](/static/guides/first-setup/findaddbutton3.png)
![Image "image"](/static/guides/first-setup/find_device_context_menu.png)
![Image "image"](/static/guides/first-setup/edit_device.png)
:::
2. Pair the hub:
1. Open the context menu of your hub again.
2. Select **pair** and press **get pair code**, this will generate a new pair code.
3. On your phone type the code into the account Linking field of the hub's web-interface, then press **pair**.
- After you linked the hub to your account it should shut down it's own Wi-Fi network.
::: details Images (click to expand)
![Image "image"](../../static/guides/first-setup/findpaircode.png)
![Image "image"](../../static/guides/first-setup/paircodeexample.png)
![Image "image"](/static/guides/first-setup/findpaircode.png)
![Image "image"](/static/guides/first-setup/paircodeexample.png)
:::
3. The hub is now connected!
- If everything went well, it should show a **green icon** next to the device name on the website.
![Image "image"](../../static/guides/first-setup/checkifonline.png)
![Image "image"](/static/guides/first-setup/checkifonline.png)

## Pairing shockers

Expand All @@ -86,16 +86,16 @@ If you do not know how to do this, you can also re-enable the captive portal (ho
7. Select the **model** of shocker.
8. Click **Create**
::: details Images (click to expand)
![image](../../static/guides/first-setup/Create_shocker_green_plus.png)
![image](../../static/guides/first-setup/create_shocker.png)
![image](/static/guides/first-setup/Create_shocker_green_plus.png)
![image](/static/guides/first-setup/create_shocker.png)
:::
4. Pair your Shocker.
1. Grab your shocker and turn it on. (Press the power button once. it should beep one time.)
2. Hold the power button again until it beeps and the LED flashes fast. *This means the pair mode is active*
3. On the website click the ***speaker icon*** of your shocker, if your shocker beeps in response, the pairing was successful.
4. You must click the icon before the shocker's pairing mode times out (while the shocker's LED is flashing quickly).
::: details Images (click to expand)
![image](../../static/guides/first-setup/find_sound_button.png)
![image](/static/guides/first-setup/find_sound_button.png)
:::
**Everything should work now, have fun!** 🎉

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ If you received your hub from an OpenShock hardware vendor, you can likely **ski
2. Open the [Flashtool](https://next.openshock.app/flashtool).
3. Click "Select Device" and select your hub in the Popup window.
If your hub is not showing up click on "Install Drivers", after that repeat this step.
![Connect Controller](../../static/guides/how-to-flash/Connect_Device_Flashtoolguide.png)
![Connect Controller](/static/guides/how-to-flash/Connect_Device_Flashtoolguide.png)
4. Ensure you have the "Stable" channel selected.
5. Ensure the correct [board](../../hardware/boards/index.md) is selected.
![Settings](../../static/guides/how-to-flash/settings.png)
![Settings](/static/guides/how-to-flash/settings.png)
6. Press Flash and let it do it's thing, keep the window open and it will tell you when it's done.
7. When everything went well you board will restart and you should be able to run through the [First Setup](../openshock/first-setup.md) steps to configure your hub and link it to your shocker etc.
8. (Optional) If you have issues after flashing, try again with "Erase everything before flashing" enabled.
Expand Down Expand Up @@ -61,8 +61,8 @@ On some boards without firmware, you won't see a Serial port until you enter the

Example pins for the Wemos D1 Mini

![Wemos D1 Mini GND Figure](../../static/boards/wemos-d1-mini-esp32/gnd-figure.webp)
![Wemos D1 Mini GND Figure 2](../../static/boards/wemos-d1-mini-esp32/gnd-figure2.webp)
![Wemos D1 Mini GND Figure](/static/boards/wemos-d1-mini-esp32/gnd-figure.webp)
![Wemos D1 Mini GND Figure 2](/static/boards/wemos-d1-mini-esp32/gnd-figure2.webp)

#### Manually flash using `esptool.py`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
2. Connect your hub to a power source and make sure it appears as online in the Device section.
3. Open the context menu of your hub.
4. Select "OTA Update".
![Open OTA](../../static/guides/how-to-update/update-Open-OTA.png)
![Open OTA](/static/guides/how-to-update/update-Open-OTA.png)
5. Now you can see 3 different branches of firmware, these are "Develop", "Stable" and "Beta". **We recommend that you only use the Stable branch if you don't know what you're doing.**
6. If your firmware version is older than the one displayed on the "Stable" button, you should update.
![Update Window](../../static/guides/how-to-update/update-Window.png)
![Update Window](/static/guides/how-to-update/update-Window.png)
7. Click the "Stable" button.
8. Confirm the update.
9. Your hub should now update automatically, don't close the website during this.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ You can also decode this ID yourself using a 433 Mhz Receiver module with an ESP
3. Set the Shocker RfId field to the **Offline Remote ID**
4. Save
::: details Images (click to expand)
![Image "image"](../../static/guides/offline-remote-setup/shockercontextmenu.png)
![Image "image"](../../static/guides/offline-remote-setup/shockerrfidfield.png)
![Image "image"](/static/guides/offline-remote-setup/shockercontextmenu.png)
![Image "image"](/static/guides/offline-remote-setup/shockerrfidfield.png)
:::
4. RePair the Shocker
**Everything should work now, have fun!** 🎉
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ Shares are permanent until unshared/deleted by the owner of the shocker.
3. Click on the **green plus icon**, that will generate a new share code.
4. Send this code to a person you trust.
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_CreateCode.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_FindCode.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_CreateCode.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_FindCode.png)
:::

## Use a share code
Expand All @@ -36,9 +36,9 @@ Shares are permanent until unshared/deleted by the owner of the shocker.

**Now the shocker is linked to your account and you can control it.** 🎉
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_FindAddCode.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_AddCode.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_Added.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_FindAddCode.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_AddCode.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_Added.png)
:::
::: tip
You can find all shockers you added with a share code on the same page in your account under **Shockers** -> [**Shared**](https://openshock.app/#/dashboard/shockers/shared)
Expand All @@ -61,10 +61,10 @@ For this step to work, someone has to [use your share code](#use-a-share-code) f
5. Set max ***intensity*** and ***duration*** and also select what kind of ***commands*** the person can send.
6. Press **Save**, you are done. 🎉
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_SharedContextMneu.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_EditLimit.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_SharedContextMneu.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_EditLimit.png)
:::

## Pause/Unpause a share code
Expand All @@ -82,8 +82,8 @@ For this step to work, someone has to [use your share code](#use-a-share-code) f
- *Press the **play icon** next to the persons name to unpause the code again.*
4. You are done. 🎉
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
:::

## Unshare/Delete a share code
Expand All @@ -100,9 +100,9 @@ For this step to work, someone has to [use your share code](#use-a-share-code) f
4. Select **Unshare**
5. You are done. 🎉
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
![Image "Image"](../../static/guides/how-to-sharecodes/ShareCode_SharedContextMneu.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_ContextMenuShocker.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_LinkedList.png)
![Image "Image"](/static/guides/how-to-sharecodes/ShareCode_SharedContextMneu.png)
:::
::: tip
You can also pause a specific share to temporarily stop the person from using this shocker.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ Share links are a great way to give people control of your shockers without the
5. Press **Create**
- Your new share link should popup as a new entry on the page.
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharelinks/findshocklinks.png)
![Image "Image"](../../static/guides/how-to-sharelinks/addnewsharelink.png)
![Image "Image"](../../static/guides/how-to-sharelinks/createshocklink.png)
![Image "Image"](../../static/guides/how-to-sharelinks/sharelinkcreated.png)
![Image "Image"](/static/guides/how-to-sharelinks/findshocklinks.png)
![Image "Image"](/static/guides/how-to-sharelinks/addnewsharelink.png)
![Image "Image"](/static/guides/how-to-sharelinks/createshocklink.png)
![Image "Image"](/static/guides/how-to-sharelinks/sharelinkcreated.png)
:::
2. Add a Shocker to the Link:
1. Click on the newly created link.
Expand All @@ -32,8 +32,8 @@ Share links are a great way to give people control of your shockers without the
5. Press **Add** *(repeat that to add more shockers)*
- You should be able to see the shockers controls now.
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharelinks/addshockertosharelink.png)
![Image "Image"](../../static/guides/how-to-sharelinks/addshockertosharelink2.png)
![Image "Image"](/static/guides/how-to-sharelinks/addshockertosharelink.png)
![Image "Image"](/static/guides/how-to-sharelinks/addshockertosharelink2.png)
:::
**That's it.**
Everyone you send the share link to can now control your shocker. 🎉
Expand All @@ -58,8 +58,8 @@ You can also **Pause** the link so nobody can send commands with this link.
3. Set the maximum ***intensity***, ***duration*** and choose what kind of ***command*** can be send.
4. To exit the Edit Mode open the share links context menu and select **Edit Mode** again. This will return the controls to their normal color.
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharelinks/editlinkllimits.png)
![Image "Image"](../../static/guides/how-to-sharelinks/editinterface.png)
![Image "Image"](/static/guides/how-to-sharelinks/editlinkllimits.png)
![Image "Image"](/static/guides/how-to-sharelinks/editinterface.png)
:::
**That's it.** 🎉

Expand All @@ -74,6 +74,6 @@ A paused link will not accept any commands.
- It should now ***blur*** the shocker controls telling you it's paused.
2. To un-pause the share link again simply click on the ``Play Icon``.
::: details Images (click to expand)
![Image "Image"](../../static/guides/how-to-sharelinks/pauseshocker.png)
![Image "Image"](../../static/guides/how-to-sharelinks/pausedlink.png)
![Image "Image"](/static/guides/how-to-sharelinks/pauseshocker.png)
![Image "Image"](/static/guides/how-to-sharelinks/pausedlink.png)
:::
Loading
Loading