aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPinapelz <yukais@pinapelz.com>2025-01-14 00:13:38 -0800
committerPinapelz <yukais@pinapelz.com>2025-01-14 00:13:38 -0800
commitbba427d31081f99b7dc5e724308ca6151753ccd1 (patch)
treec77350108472acab71d0300417f2964ab0a56788
initial commit1.0
-rw-r--r--.gitignore462
-rw-r--r--brokenithm-evolved-umi.py96
-rw-r--r--key_config.py65
-rw-r--r--server/Brokenithm-Evolved-iOS.sln25
-rw-r--r--server/Brokenithm-Evolved-iOS/App.config6
-rw-r--r--server/Brokenithm-Evolved-iOS/Brokenithm-Evolved-iOS.csproj108
-rw-r--r--server/Brokenithm-Evolved-iOS/Brokenithm.icobin0 -> 12862 bytes
-rw-r--r--server/Brokenithm-Evolved-iOS/Program.cs343
-rw-r--r--server/Brokenithm-Evolved-iOS/Properties/AssemblyInfo.cs36
-rw-r--r--server/Brokenithm-Evolved-iOS/packages.config4
-rw-r--r--umi_led.py203
11 files changed, 1348 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7030cbd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,462 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+#uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
+.pdm.toml
+.pdm-python
+.pdm-build/
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+# PyPI configuration file
+.pypirc
+*.spec
+.vs
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+**/Properties/launchSettings.json
+
+# VS Code
+.vscode/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Typescript v1 declaration files
+typings/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs \ No newline at end of file
diff --git a/brokenithm-evolved-umi.py b/brokenithm-evolved-umi.py
new file mode 100644
index 0000000..b1b72b1
--- /dev/null
+++ b/brokenithm-evolved-umi.py
@@ -0,0 +1,96 @@
+import mmap
+import ctypes
+import time
+import key_config
+import umi_led
+import threading
+import keyboard
+
+class SharedMemoryData(ctypes.Structure):
+ _fields_ = [
+ ("airIoStatus", ctypes.c_uint8 * 6),
+ ("sliderIoStatus", ctypes.c_uint8 * 32),
+ ("ledRgbData", ctypes.c_uint8 * 96),
+ ("reserved", ctypes.c_uint8 * 4)
+ ]
+
+# Shared memory details
+SHARED_MEMORY_NAME = "Local\\BROKENITHM_SHARED_BUFFER"
+SHARED_MEMORY_SIZE = ctypes.sizeof(ctypes.c_uint8) * ctypes.sizeof(SharedMemoryData)
+
+
+def initialize_shared_mem():
+ try:
+ shared_memory = mmap.mmap(-1, SHARED_MEMORY_SIZE, tagname=SHARED_MEMORY_NAME, access=mmap.ACCESS_WRITE)
+ slider_status = [0] * 32 # Default: All slider zones inactive
+ air_status = [0] * 6 # Default: All air zones inactive
+ shared_memory.seek(0)
+ shared_memory.write(bytearray(air_status))
+ print("[Initialization] Resetting Air Note Status")
+ shared_memory.seek(6)
+ shared_memory.write(bytearray(slider_status))
+ print("[Initialization] Resetting Slider Status")
+ shared_memory.seek(6 + 32)
+ print("[Initialization] Setting Default Slider LED state")
+ shared_memory.write(bytearray(umi_led.DEFAULT_LED_STATE))
+ print("[Initialization] Success. Complete")
+ return shared_memory
+ except Exception:
+ print("[Error] A Fatal Error occured while trying to write to the Shared Memory while initializing")
+ return None
+
+def monitor_key_presses_and_air(controller: key_config.InputConfig):
+ try:
+ shared_memory = mmap.mmap(-1, SHARED_MEMORY_SIZE, tagname=SHARED_MEMORY_NAME, access=mmap.ACCESS_READ)
+ key_states = {zone: False for zone in controller.get_keyzone_layout().keys()}
+ air_states = {zone: False for zone in controller.get_airzone_layout().keys()}
+ print("Now Monitoring for Inputs")
+ while True:
+ shared_memory.seek(0)
+ current_air_status = list(shared_memory.read(6))
+ shared_memory.seek(6)
+ current_slider_status = list(shared_memory.read(32))
+ for zone, key in controller.get_keyzone_layout().items():
+ is_pressed = current_slider_status[zone] == 128
+ if is_pressed != key_states[zone]:
+ if is_pressed:
+ keyboard.press(key)
+ print(f"Key {key} pressed")
+ else:
+ keyboard.release(key)
+ print(f"Key {key} released")
+ key_states[zone] = is_pressed
+ for zone, key in controller.get_airzone_layout().items():
+ is_active = current_air_status[zone] == 128
+ if is_active != air_states[zone]:
+ if is_active:
+ keyboard.press(key)
+ print(f"Air {key} activated")
+ else:
+ keyboard.release(key)
+ print(f"Air {key} deactivated")
+ air_states[zone] = is_active
+ time.sleep(0.01)
+ except Exception:
+ print("[Error] A Fatal Error occured while monitoring and translating inputs")
+ except KeyboardInterrupt:
+ for key in controller.get_keyzone_layout().values():
+ keyboard.release(key)
+ for key in controller.get_airzone_layout().values():
+ keyboard.release(key)
+ print("Stopping monitoring.")
+
+
+if __name__ == "__main__":
+ shared_memory = initialize_shared_mem()
+ controller = key_config.Umiguri32KeyZone()
+ if shared_memory is not None:
+ stop_event = threading.Event()
+ server_thread = threading.Thread(target=umi_led.start_umiguri_websocket_server, args=(shared_memory, stop_event))
+ server_thread.start()
+ monitor_key_presses_and_air(controller)
+ print("Shutting down...")
+ stop_event.set()
+ shared_memory.close()
+ exit()
+
diff --git a/key_config.py b/key_config.py
new file mode 100644
index 0000000..eaa2be2
--- /dev/null
+++ b/key_config.py
@@ -0,0 +1,65 @@
+from abc import ABC, abstractmethod
+
+
+class InputConfig(ABC):
+ @abstractmethod
+ def get_keyzone_layout():
+ pass
+
+ @abstractmethod
+ def get_airzone_layout():
+ pass
+
+
+class Umiguri32KeyZone(InputConfig):
+ def __init__(self):
+ super().__init__()
+ self._UMIGIRI_32_KEYZONE_LAYOUT = {
+ 31: "a",
+ 30: "1",
+ 29: "z",
+ 28: "q",
+ 27: "s",
+ 26: "2",
+ 25: "x",
+ 24: "w",
+ 23: "d",
+ 22: "3",
+ 21: "c",
+ 20: "e",
+ 19: "f",
+ 18: "4",
+ 17: "v",
+ 16: "r",
+ 15: "g",
+ 14: "5",
+ 13: "b",
+ 12: "t",
+ 11: "h",
+ 10: "6",
+ 9: "n",
+ 8: "y",
+ 7: "j",
+ 6: "7",
+ 5: "m",
+ 4: "u",
+ 3: "k",
+ 2: "8",
+ 1: "9",
+ 0: "i",
+ }
+
+ self._UMIGIRI_32_AIRZONE_LAYOUT = {
+ 0: "o",
+ 1: "0",
+ 2: "p",
+ 3: "l",
+ 4: ",",
+ 5: ".",
+ }
+
+ def get_keyzone_layout(self):
+ return self._UMIGIRI_32_KEYZONE_LAYOUT
+
+ def get_airzone_layout(self):
+ return self._UMIGIRI_32_AIRZONE_LAYOUT
diff --git a/server/Brokenithm-Evolved-iOS.sln b/server/Brokenithm-Evolved-iOS.sln
new file mode 100644
index 0000000..4747dfe
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29709.97
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Brokenithm-Evolved-iOS", "Brokenithm-Evolved-iOS\Brokenithm-Evolved-iOS.csproj", "{D127D9DF-1FB2-48F1-A8ED-F50D9852732A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {D127D9DF-1FB2-48F1-A8ED-F50D9852732A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D127D9DF-1FB2-48F1-A8ED-F50D9852732A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D127D9DF-1FB2-48F1-A8ED-F50D9852732A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D127D9DF-1FB2-48F1-A8ED-F50D9852732A}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C3AA92C4-7827-465C-9827-FB95FDFF4C29}
+ EndGlobalSection
+EndGlobal
diff --git a/server/Brokenithm-Evolved-iOS/App.config b/server/Brokenithm-Evolved-iOS/App.config
new file mode 100644
index 0000000..ecdcf8a
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/App.config
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <startup>
+ <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
+ </startup>
+</configuration>
diff --git a/server/Brokenithm-Evolved-iOS/Brokenithm-Evolved-iOS.csproj b/server/Brokenithm-Evolved-iOS/Brokenithm-Evolved-iOS.csproj
new file mode 100644
index 0000000..9f5fe70
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/Brokenithm-Evolved-iOS.csproj
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{D127D9DF-1FB2-48F1-A8ED-F50D9852732A}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>Brokenithm_Evolved_iOS_Umi</RootNamespace>
+ <AssemblyName>Brokenithm-Evolved-iOS-Umi</AssemblyName>
+ <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+ <Deterministic>true</Deterministic>
+ <TargetFrameworkProfile />
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
+ <IsWebBootstrapper>false</IsWebBootstrapper>
+ <PublishUrl>publish\</PublishUrl>
+ <Install>true</Install>
+ <InstallFrom>Disk</InstallFrom>
+ <UpdateEnabled>false</UpdateEnabled>
+ <UpdateMode>Foreground</UpdateMode>
+ <UpdateInterval>7</UpdateInterval>
+ <UpdateIntervalUnits>Days</UpdateIntervalUnits>
+ <UpdatePeriodically>false</UpdatePeriodically>
+ <UpdateRequired>false</UpdateRequired>
+ <MapFileExtensions>true</MapFileExtensions>
+ <AutorunEnabled>true</AutorunEnabled>
+ <ApplicationRevision>1</ApplicationRevision>
+ <ApplicationVersion>1.0.0.%2a</ApplicationVersion>
+ <UseApplicationTrust>false</UseApplicationTrust>
+ <PublishWizardCompleted>true</PublishWizardCompleted>
+ <BootstrapperEnabled>true</BootstrapperEnabled>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <PlatformTarget>AnyCPU</PlatformTarget>
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup>
+ <GenerateManifests>true</GenerateManifests>
+ </PropertyGroup>
+ <PropertyGroup>
+ <SignManifests>true</SignManifests>
+ </PropertyGroup>
+ <PropertyGroup>
+ <ApplicationIcon>Brokenithm.ico</ApplicationIcon>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="iMobileDevice-net, Version=1.2.0.0, Culture=neutral, PublicKeyToken=040ae19651fac98a, processorArchitecture=MSIL">
+ <HintPath>..\packages\iMobileDevice-net.1.2.186\lib\net45\iMobileDevice-net.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Program.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="App.config" />
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
+ <Visible>False</Visible>
+ <ProductName>Microsoft .NET Framework 4.7.2 %28x86 和 x64%29</ProductName>
+ <Install>true</Install>
+ </BootstrapperPackage>
+ <BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
+ <Visible>False</Visible>
+ <ProductName>.NET Framework 3.5 SP1</ProductName>
+ <Install>false</Install>
+ </BootstrapperPackage>
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="Brokenithm.ico" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="..\packages\iMobileDevice-net.1.2.186\build\net45\iMobileDevice-net.targets" Condition="Exists('..\packages\iMobileDevice-net.1.2.186\build\net45\iMobileDevice-net.targets')" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\packages\iMobileDevice-net.1.2.186\build\net45\iMobileDevice-net.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\iMobileDevice-net.1.2.186\build\net45\iMobileDevice-net.targets'))" />
+ </Target>
+</Project> \ No newline at end of file
diff --git a/server/Brokenithm-Evolved-iOS/Brokenithm.ico b/server/Brokenithm-Evolved-iOS/Brokenithm.ico
new file mode 100644
index 0000000..8033b43
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/Brokenithm.ico
Binary files differ
diff --git a/server/Brokenithm-Evolved-iOS/Program.cs b/server/Brokenithm-Evolved-iOS/Program.cs
new file mode 100644
index 0000000..f6785cb
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/Program.cs
@@ -0,0 +1,343 @@
+using iMobileDevice;
+using iMobileDevice.iDevice;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO.MemoryMappedFiles;
+using System.Linq;
+using System.Security.Principal;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Brokenithm_Evolved_iOS
+{
+ class Program
+ {
+ public static Dictionary<string, iDeviceHandle> device_map = new Dictionary<string, iDeviceHandle>();
+ public static Dictionary<string, Process> process_map = new Dictionary<string, Process>();
+ public static Dictionary<string, iDeviceConnectionHandle> connection_map = new Dictionary<string, iDeviceConnectionHandle>();
+ public static IiDeviceApi idevice;
+ public static MemoryMappedFile sharedBuffer;
+ public static MemoryMappedViewAccessor sharedBufferAccessor;
+ public static bool exiting = false;
+ public static iDeviceEventCallBack _eventCallback;
+
+ static void Main(string[] args)
+ {
+ Console.Title = "Brokenithm-Evolved-iOS";
+ Console.WriteLine("=================================================");
+ Console.WriteLine("= Brokenithm-Evolved-iOS (Umi) =");
+ Console.WriteLine("= Brokenithm with full IO and USB connection =");
+ Console.WriteLine("= v0.3 by esterTion =");
+ Console.WriteLine("= Modified for Umiguri usage by Pinapelz =");
+ Console.WriteLine("= Original: thebit.link =");
+ Console.WriteLine("=================================================");
+ Console.WriteLine("");
+
+ NativeLibraries.Load();
+ idevice = LibiMobileDevice.Instance.iDevice;
+ iDeviceError status;
+ _eventCallback = new iDeviceEventCallBack(eventCallback);
+ status = LibiMobileDevice.Instance.iDevice.idevice_event_subscribe(_eventCallback, IntPtr.Zero);
+
+ MemoryMappedFileSecurity CustomSecurity = new MemoryMappedFileSecurity();
+ SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
+ var acct = sid.Translate(typeof(NTAccount)) as NTAccount;
+ CustomSecurity.AddAccessRule(new System.Security.AccessControl.AccessRule<MemoryMappedFileRights>(acct.ToString(), MemoryMappedFileRights.FullControl, System.Security.AccessControl.AccessControlType.Allow));
+ sharedBuffer = MemoryMappedFile.CreateOrOpen("Local\\BROKENITHM_SHARED_BUFFER", 1024, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, CustomSecurity, System.IO.HandleInheritability.Inheritable);
+ sharedBufferAccessor = sharedBuffer.CreateViewAccessor();
+
+ {
+ Thread ledThread = new Thread(new ThreadStart(outputLed));
+ ledThread.Start();
+ }
+
+ Console.WriteLine("Waiting for device...");
+ while (Console.ReadKey().Key != ConsoleKey.Q) { }
+ exiting = true;
+ }
+
+ public static void eventCallback(ref iDeviceEvent e, IntPtr user_data)
+ {
+ iDeviceError status;
+ string udid = e.udidString;
+ switch (e.@event)
+ {
+ case iDeviceEventType.DeviceAdd:
+ {
+ Console.WriteLine(string.Format("device add\tudid: {0}", udid));
+ if (device_map.ContainsKey(udid))
+ {
+ return;
+ }
+ iDeviceHandle device;
+ status = idevice.idevice_new(out device, e.udidString);
+ if (status != iDeviceError.Success)
+ {
+ Console.WriteLine("Create device failed: {0}", status);
+ return;
+ }
+ device_map[udid] = device;
+
+ Process process = new Process();
+ process.StartInfo.FileName = @"umi-evolved\brokenithm-evolved-umi.exe";
+ try
+ {
+ process.Start();
+
+ }
+ catch(Exception ex){
+ Console.WriteLine("Unable to launch brokenithm-evolved-umi server. Is the .exe there?");
+ Console.WriteLine("Current Directory: " + Environment.CurrentDirectory);
+ Console.WriteLine("Press Enter to continue...");
+ Console.ReadLine();
+ Environment.Exit(0);
+ }
+ process_map[udid] = process;
+
+ Thread thread = new Thread(new ParameterizedThreadStart(connectDevice));
+ thread.Start(udid);
+ break;
+ }
+ case iDeviceEventType.DevicePaired:
+ {
+ Console.WriteLine(string.Format("device paired\tudid: {0}", udid));
+ break;
+ }
+ case iDeviceEventType.DeviceRemove:
+ {
+ Console.WriteLine(string.Format("device remove\tudid: {0}", udid));
+ if (device_map.ContainsKey(udid))
+ {
+ iDeviceHandle device = device_map[udid];
+ device.Dispose();
+ device_map.Remove(udid);
+ }
+ if (process_map.ContainsKey(udid))
+ {
+ try
+ {
+ Process processToKill = process_map[udid];
+ if (!processToKill.HasExited)
+ {
+ processToKill.Kill();
+ }
+ processToKill.Dispose();
+ process_map.Remove(udid);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error killing process for device {udid}: {ex.Message}");
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ public static void connectDevice(object arg)
+ {
+ string udid = (string)arg;
+ if (!device_map.ContainsKey(udid))
+ {
+ return;
+ }
+ iDeviceHandle device = device_map[udid];
+ iDeviceConnectionHandle conn;
+ iDeviceError status;
+ status = idevice.idevice_connect(device, 24864, out conn);
+ if (status != iDeviceError.Success)
+ {
+ //Console.WriteLine(string.Format("connect failed: {0}", status));
+ Thread.Sleep(1000);
+ Thread thread = new Thread(new ParameterizedThreadStart(connectDevice));
+ thread.Start(udid);
+ return;
+ }
+
+ byte[] buf = new byte[256];
+ uint read = 0;
+ status = idevice.idevice_connection_receive(conn, buf, 4, ref read);
+ if (status != iDeviceError.Success)
+ {
+ Console.WriteLine(string.Format("receive data failed: {0}", status));
+ return;
+ }
+ if (
+ buf[0] != 3 ||
+ buf[1] != 'W' ||
+ buf[2] != 'E' ||
+ buf[3] != 'L'
+ )
+ {
+ // welcome message
+ Console.WriteLine("received invalid data");
+ conn.Dispose();
+ return;
+ }
+ Console.WriteLine("connected to device");
+ connection_map[udid] = conn;
+ {
+ Thread thread = new Thread(new ParameterizedThreadStart(readInputFromDevice));
+ thread.Start(udid);
+ }
+ return;
+ }
+ public static void readInputFromDevice(object arg)
+ {
+ string udid = (string)arg;
+ if (!connection_map.ContainsKey(udid))
+ {
+ return;
+ }
+ iDeviceConnectionHandle conn = connection_map[udid];
+ iDeviceError status;
+ bool airEnabled = true;
+
+ byte[] buf = new byte[256];
+ uint read = 0;
+ while (true)
+ {
+ if (exiting) break;
+ status = idevice.idevice_connection_receive_timeout(conn, buf, 1, ref read, 5);
+ if (status != iDeviceError.Success)
+ {
+ if (status == iDeviceError.Timeout)
+ {
+ continue;
+ }
+ break;
+ }
+ byte len = buf[0];
+ status = idevice.idevice_connection_receive_timeout(conn, buf, len, ref read, 5);
+ if (status != iDeviceError.Success)
+ {
+ break;
+ }
+ if (len >= 3+6+32 &&
+ buf[0] == 'I' &&
+ buf[1] == 'N' &&
+ buf[2] == 'P')
+ {
+ // key input
+ if (airEnabled)
+ {
+ sharedBufferAccessor.WriteArray<byte>(0, buf, 3, 6 + 32);
+ }
+ else
+ {
+ sharedBufferAccessor.WriteArray<byte>(6, buf, 3 + 6, 32);
+ }
+ if (len > 3 + 6 + 32)
+ {
+ sharedBufferAccessor.WriteArray<byte>(6+32+96, buf, 3+6+32, len - (3 + 6 + 32));
+ }
+ }
+ else if (len >= 4 &&
+ buf[0] == 'A' &&
+ buf[1] == 'I' &&
+ buf[2] == 'R')
+ {
+ // air input control
+ airEnabled = buf[3] != 0;
+ Console.WriteLine(string.Format("Air input {0}", airEnabled ? "enabled" : "disabled"));
+ }
+ else if (len >= 4 &&
+ buf[0] == 'F' &&
+ buf[1] == 'N' &&
+ buf[2] == 'C')
+ {
+ // function key
+ if (buf[3] == (byte)BNI_FUNCTION_KEYS.COIN)
+ {
+ sharedBufferAccessor.Write(6 + 32 + 96 + 2, 1);
+ }
+ else if (buf[3] == (byte)BNI_FUNCTION_KEYS.CARD)
+ {
+ sharedBufferAccessor.Write(6 + 32 + 96 + 3, 1);
+ }
+ }
+ else
+ {
+ if (len >= 3)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.Append((char)buf[0]);
+ sb.Append((char)buf[1]);
+ sb.Append((char)buf[2]);
+ Console.WriteLine(string.Format("unknown packet: {0}", sb.ToString()));
+ }
+ else
+ {
+ Console.WriteLine("unknown packet");
+ }
+ }
+ }
+ conn.Dispose();
+ connection_map.Remove(udid);
+ Console.WriteLine("disconnected");
+ if (exiting) return;
+ Thread.Sleep(1000);
+ {
+ Thread thread = new Thread(new ParameterizedThreadStart(connectDevice));
+ thread.Start(udid);
+ }
+ }
+
+ enum BNI_FUNCTION_KEYS {
+ COIN = 1,
+ CARD
+ };
+
+ private static byte[] prevLedRgb = new byte[32 * 3];
+ private static int skipCount = 0;
+ public static void outputLed()
+ {
+ while (true)
+ {
+ if (exiting) break;
+ byte[] ledRgb = new byte[32 * 3];
+ sharedBufferAccessor.ReadArray<byte>(6 + 32, ledRgb, 0, 32 * 3);
+ bool same = true;
+ for (int i = 0; i < 32 * 3; i++)
+ {
+ if (ledRgb[i] != prevLedRgb[i])
+ {
+ same = false;
+ break;
+ }
+ }
+ if (!same)
+ {
+ BroadcastLEDStatus(ledRgb);
+ prevLedRgb = ledRgb;
+ skipCount = 0;
+ }
+ else
+ {
+ if (++skipCount > 50)
+ {
+ BroadcastLEDStatus(prevLedRgb);
+ skipCount = 0;
+ }
+ }
+ Thread.Sleep(10);
+ }
+ }
+ public static void BroadcastLEDStatus(byte[] led)
+ {
+ uint sent = 0;
+ byte[] head = { 99, (byte)'L', (byte)'E', (byte)'D' };
+ foreach (var conn in connection_map)
+ {
+ try
+ {
+ idevice.idevice_connection_send(conn.Value, head, 4, ref sent);
+ idevice.idevice_connection_send(conn.Value, led, 96, ref sent);
+ }
+ catch (Exception e) { }
+ }
+ }
+ }
+}
diff --git a/server/Brokenithm-Evolved-iOS/Properties/AssemblyInfo.cs b/server/Brokenithm-Evolved-iOS/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..0e57bf6
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("Brokenithm-Evolved-iOS")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Brokenithm-Evolved-iOS")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("d127d9df-1fb2-48f1-a8ed-f50d9852732a")]
+
+// 程序集的版本信息由下列四个值组成:
+//
+// 主版本
+// 次版本
+// 生成号
+// 修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/server/Brokenithm-Evolved-iOS/packages.config b/server/Brokenithm-Evolved-iOS/packages.config
new file mode 100644
index 0000000..4266474
--- /dev/null
+++ b/server/Brokenithm-Evolved-iOS/packages.config
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="iMobileDevice-net" version="1.2.186" targetFramework="net472" />
+</packages> \ No newline at end of file
diff --git a/umi_led.py b/umi_led.py
new file mode 100644
index 0000000..e5718ff
--- /dev/null
+++ b/umi_led.py
@@ -0,0 +1,203 @@
+"""
+Umiguri LED Controller Protocol. Some commands have been stubbed
+Thanks inonote for the great resources on the protocol
+https://gist.github.com/inonote/00251fed881a82c9df1e505eef1722bc
+
+The implementation below is largely derived from
+https://github.com/inonote/UmiguriSampleLedServer/blob/master/src/App.cpp
+"""
+
+import asyncio
+import websockets
+
+kLedServerPort = 7124
+kLedServerName = "BrokenithmLedServer"
+kLedServerVersion = (123, 456)
+kLedServerHardwareName = "Brokenithm"
+kLedServerHardwareVersion = (987, 654)
+
+kLedProtocolVersion = 0x01
+
+ULED_COMMAND_SET_LED = 0x10
+ULED_COMMAND_INITIALIZE = 0x11
+ULED_COMMAND_READY = ULED_COMMAND_INITIALIZE | 0x08 # 0x19
+ULED_COMMAND_PING = 0x12
+ULED_COMMAND_PONG = ULED_COMMAND_PING | 0x08 # 0x1A
+ULED_COMMAND_REQUEST_SERVER_INFO = 0xD0
+ULED_COMMAND_REPORT_SERVER_INFO = ULED_COMMAND_REQUEST_SERVER_INFO | 0x08 # 0xD8
+
+DEFAULT_LED_STATE = [
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255, 0, 0, 0,
+ 255, 255, 255
+]
+
+SHOW_LOG = False
+
+def log_message(message: str):
+ if not SHOW_LOG:
+ return
+ print(message)
+
+def validate_message(payload: bytes) -> bool:
+ """
+ Checks if the incoming payload conforms to the protocol:
+ 1. At least 3 bytes: [protocolVersion, command, length].
+ 2. The length byte must match (payload.size() - 3).
+ 3. For known commands, check expected payload size if needed.
+ """
+ if len(payload) < 3:
+ return False
+
+ if payload[0] != kLedProtocolVersion:
+ return False
+
+ length_byte = payload[2]
+ if length_byte != (len(payload) - 3):
+ return False
+
+ command = payload[1]
+ if command == ULED_COMMAND_SET_LED:
+ if len(payload) != 106: # 103 bytes of LED data + 3 header bytes
+ return False
+ elif command == ULED_COMMAND_PING:
+ if len(payload) != 7: # 4 bytes payload + 3 header bytes
+ return False
+
+ return True
+
+def get_brokenithm_led_array(payload):
+ led_state = []
+ land_colors = []
+ border_colors = []
+ for i in range(16):
+ offset = 1 + i * 3
+ land_colors.append(tuple(payload[offset:offset + 3]))
+
+ for i in range(15):
+ offset = 49 + i * 3
+ border_colors.append(tuple(payload[offset:offset + 3]))
+ land_colors = list(reversed(land_colors))
+ border_colors = list(reversed(border_colors))
+ try:
+ for i in range(15):
+ led_state.append(land_colors[i][2])
+ led_state.append(land_colors[i][0])
+ led_state.append(land_colors[i][1])
+ led_state.append(border_colors[i][2])
+ led_state.append(border_colors[i][0])
+ led_state.append(border_colors[i][1])
+ led_state.append(land_colors[15][2])
+ led_state.append(land_colors[15][0])
+ led_state.append(land_colors[15][1])
+ except Exception:
+ log_message("[LEDServer] Invalid LED State received ignoring")
+ return None
+ return led_state
+
+# R,G,B -> B,R,G
+
+async def handle_message(websocket, payload: bytes, shared_memory):
+ """
+ Process incoming (validated) payload and respond if needed.
+ """
+ command = payload[1]
+ log_message(f"Received raw data ({len(payload)} bytes): {payload}")
+
+ if command == ULED_COMMAND_PING:
+ log_message("-> Handling PING command")
+ custom_data = payload[3:7]
+ response = bytearray([kLedProtocolVersion, ULED_COMMAND_PONG, 6]) + custom_data + bytearray([0x51, 0xED])
+ await websocket.send(response)
+
+ elif command == ULED_COMMAND_INITIALIZE:
+ log_message("-> Handling INITIALIZE command")
+ response = bytearray([kLedProtocolVersion, ULED_COMMAND_READY, 0])
+ await websocket.send(response)
+
+ elif command == ULED_COMMAND_REQUEST_SERVER_INFO:
+ log_message("-> Handling REQUEST_SERVER_INFO command")
+ buf = bytearray(3 + 44)
+ buf[0] = kLedProtocolVersion
+ buf[1] = ULED_COMMAND_REPORT_SERVER_INFO
+ buf[2] = 44
+
+ name_bytes = kLedServerName.encode('ascii')[:16]
+ buf[3:3+len(name_bytes)] = name_bytes
+
+ ver0, ver1 = kLedServerVersion
+ buf[3+16] = ver0 & 0xFF
+ buf[3+17] = (ver0 >> 8) & 0xFF
+ buf[3+18] = ver1 & 0xFF
+ buf[3+19] = (ver1 >> 8) & 0xFF
+
+ hw_bytes = kLedServerHardwareName.encode('ascii')[:16]
+ buf[3+22:3+22+len(hw_bytes)] = hw_bytes
+
+ hwver0, hwver1 = kLedServerHardwareVersion
+ buf[3+38] = hwver0 & 0xFF
+ buf[3+39] = (hwver0 >> 8) & 0xFF
+ buf[3+40] = hwver1 & 0xFF
+ buf[3+41] = (hwver1 >> 8) & 0xFF
+
+ await websocket.send(buf)
+
+ elif command == ULED_COMMAND_SET_LED:
+ log_message("-> Handling SET_LED command")
+ brightness = payload[3]
+ led_data = payload[3:]
+ log_message(f" Brightness: {brightness}")
+ log_message(f" LED color data length: {len(led_data)}")
+ led_arr = get_brokenithm_led_array(led_data)
+ if led_arr is None:
+ return
+ shared_memory.seek(6 + 32)
+ shared_memory.write(bytearray(led_arr))
+
+ else:
+ log_message(f"-> Unknown command 0x{command:02X}")
+
+async def handle_client(websocket, shared_memory):
+ """
+ Main entry point for each connected client.
+ Listens for messages, validates them, and calls handle_message.
+ """
+ print(f"[LEDServer] Client Detected from: {websocket.remote_address}")
+ try:
+ async for raw_message in websocket:
+ if isinstance(raw_message, bytes):
+ if validate_message(raw_message):
+ await handle_message(websocket, raw_message, shared_memory)
+ else:
+ log_message(f"Invalid message received: {raw_message}")
+ else:
+ log_message(f"Received text message (ignored): {raw_message}")
+ except websockets.exceptions.ConnectionClosed as e:
+ log_message(f"Client disconnected: {e.reason}")
+
+async def run_websocket_server(shared_memory, stop_event):
+ print(f"[LEDerver] Starting WebSocket server on port {kLedServerPort}...")
+ async with websockets.serve(lambda ws: handle_client(ws, shared_memory), "0.0.0.0", kLedServerPort):
+ print("[LEDServer] LEDServer started. Awaiting connections...")
+ while not stop_event.is_set():
+ await asyncio.sleep(0.1)
+ print("[LEDServer] Websocket Server Shutting Down")
+
+def start_umiguri_websocket_server(shared_memory, stop_event):
+ try:
+ asyncio.run(run_websocket_server(shared_memory, stop_event))
+ except KeyboardInterrupt:
+ log_message("Keyboard Interrupt Received - Server stopped.")
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage