; Behavision installer (Inno Setup 6). ; ; Built by installer\build.ps1, which stages dist\Behavision first. Compile it ; by hand with: ; ISCC.exe /DMyAppVersion=0.1.0 installer\behavision.iss ; ; Three decisions worth reading before changing anything here: ; ; 1. ADMIN AT INSTALL TIME, NEVER AT RUN TIME. The files go under Program ; Files, so installing needs elevation. Running does not: the app is a ; normal user-session process that spawns the engine as a child, which is ; also why it can draw a tray icon at all - a Windows service runs in ; session 0 and cannot. ; ; 2. NO MODELS IN THE PACKAGE. They are ~200 MB and `setup-models` downloads ; them resumably into the state root on first run. Bundling them would ; quadruple this file and force a re-sign for a model change. ; ; 3. NOTHING WRITABLE UNDER Program Files. The database, logs, camera list, ; downloaded models and agent config all live in %PROGRAMDATA%\Behavision, ; which is what makes an upgrade a file copy rather than a migration. That ; split is behavision/paths.py and agent/pkg/paths, and this file must not ; contradict it. #define MyAppName "Behavision" #ifndef MyAppVersion #define MyAppVersion "0.1.0" #endif #define MyAppPublisher "Loyaly" #define MyAppURL "https://platform.loyaly.ai" #define MyAppExeName "Behavision.exe" [Setup] ; The Loyaly mark, on the installer and in Add/Remove Programs. SetupIconFile=..\brand\loyaly.ico AppId={{7C4B9E2A-3F51-4C86-9D0A-B1E7A2F65D11} AppName={#MyAppName} AppVersion={#MyAppVersion} AppPublisher={#MyAppPublisher} AppPublisherURL={#MyAppURL} DefaultDirName={autopf}\{#MyAppName} DefaultGroupName={#MyAppName} DisableProgramGroupPage=yes OutputDir=..\dist OutputBaseFilename=Behavision-Setup-{#MyAppVersion} Compression=lzma2/max SolidCompression=yes WizardStyle=modern ; Admin, because Program Files is. See decision 1 above. PrivilegesRequired=admin ArchitecturesAllowed=x64compatible ArchitecturesInstallIn64BitMode=x64compatible UninstallDisplayIcon={app}\{#MyAppExeName} ; The engine folder alone is ~400 MB unpacked; saying so up front beats a ; wizard that stops halfway on a small shop PC. ExtraDiskSpaceRequired=450000000 ; Ask Windows' Restart Manager to close a running copy instead of writing DLLs ; underneath it. The engine holds the SQLite WAL and the camera, so a half ; replaced install fails on the NEXT start - long after anyone would connect ; the two events. RestartApplications=no because the [Run] section starts the ; app again itself, and twice is one process too many for one webcam. CloseApplications=yes RestartApplications=no [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "Create a &desktop shortcut"; GroupDescription: "Shortcuts:" ; Ticked by default: the product is meaningless if it is not watching. A shop ; PC reboots after a power cut at 3am with nobody there to open anything. Name: "startup"; Description: "Start Behavision when this PC starts"; GroupDescription: "Startup:" [Files] Source: "..\dist\Behavision\Behavision.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "..\dist\Behavision\behavision-agent.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "..\dist\Behavision\engine\*"; DestDir: "{app}\engine"; Flags: ignoreversion recursesubdirs createallsubdirs ; ~2 MB bootstrapper, downloaded at build time by build.ps1. The app is a ; WebView2 window; without the runtime it opens BLANK - not an error, just an ; empty white rectangle, which is the single worst failure mode to hand a shop. ; Present on Windows 11 and recent Windows 10, absent on plenty of older ; machines, and a shop PC is exactly where an older machine lives. Source: "vendor\MicrosoftEdgeWebview2Setup.exe"; DestDir: "{tmp}"; \ Flags: deleteafterinstall; Check: WebView2Missing [Icons] Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon ; Per-user, not HKLM\Run: the app draws a tray icon and a window, so it has to ; start in an interactive session. A machine-wide entry would try before anyone ; has logged in. Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startup ; Named in the installer's own text when somebody skips the download, so it has ; to exist. Console window on purpose: it is a 200 MB download with a progress ; line, and a silent one looks like nothing happened. Name: "{group}\Download models"; Filename: "{app}\engine\behavision.exe"; \ Parameters: "setup-models"; Comment: "Download the face recognition models" Name: "{group}\Where is my data"; Filename: "{app}\engine\behavision.exe"; \ Parameters: "paths"; Comment: "Print the installed layout" [Dirs] ; Created here so a first run does not have to, and ACLed to Administrators ; plus the installing user rather than Everyone: it holds face templates, which ; are biometric personal data, and the engine's generated API password. Name: "{commonappdata}\Behavision"; Permissions: admins-full users-modify [Run] Filename: "{tmp}\MicrosoftEdgeWebview2Setup.exe"; Parameters: "/silent /install"; \ StatusMsg: "Installing Microsoft Edge WebView2 (required to show the app)..."; \ Flags: waituntilterminated; Check: WebView2Missing ; ~200 MB over the network, so it is offered rather than forced, and it is ; resumable: a killed download leaves a .part file and the next run continues. Filename: "{app}\engine\behavision.exe"; Parameters: "setup-models"; \ StatusMsg: "Downloading face recognition models (about 200 MB)..."; \ Flags: runhidden waituntilterminated; Check: WantModels Filename: "{app}\{#MyAppExeName}"; Description: "Start {#MyAppName} now"; \ Flags: nowait postinstall skipifsilent [UninstallDelete] ; The unpacked engine writes nothing here, but PyInstaller leaves stray ; __pycache__ directories that would keep {app} alive after an uninstall. Type: filesandordirs; Name: "{app}\engine" [Code] var ModelsPage: TInputOptionWizardPage; procedure InitializeWizard; begin ModelsPage := CreateInputOptionPage(wpSelectTasks, 'Face recognition models', 'Behavision needs about 200 MB of model files to recognise faces.', 'These are downloaded once. If this PC has no internet connection now, ' + 'skip this and run "Download models" from the Start menu later - the app ' + 'will not recognise anyone until they are present.', True, False); ModelsPage.Add('Download the models now (recommended)'); ModelsPage.Values[0] := True; end; function WantModels: Boolean; begin Result := ModelsPage.Values[0]; end; // The WebView2 runtime registers itself under EdgeUpdate with a non-empty // version. Checked in three places because the runtime can be installed // per-machine (both registry views on 64-bit) or per-user. function WebView2Missing: Boolean; const CLIENT = '{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}'; var pv: string; begin Result := True; if RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then Result := False else if RegQueryStringValue(HKLM, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then Result := False else if RegQueryStringValue(HKCU, 'SOFTWARE\Microsoft\EdgeUpdate\Clients\' + CLIENT, 'pv', pv) and (pv <> '') then Result := False; end;