Skip to content
Merged
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
37 changes: 37 additions & 0 deletions .github/installer/simplications.iss
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#ifndef AppVersion
#define AppVersion "0.0.0"
#endif

[Setup]
AppId={{A6A0A6E8-3FCF-4D4D-9E2B-8E9C08EC8D56}
AppName=Simplications
AppVersion={#AppVersion}
AppPublisher=Simplications
DefaultDirName={autopf}\Simplications
DefaultGroupName=Simplications
UninstallDisplayIcon={app}\simplications.exe
OutputDir=build
OutputBaseFilename=simplications-{#AppVersion}-windows-setup
Compression=lzma
SolidCompression=yes
ArchitecturesAllowed=x64
ArchitecturesInstallIn64BitMode=x64
PrivilegesRequired=admin
DisableProgramGroupPage=yes
WizardStyle=modern

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "Create a desktop shortcut"; GroupDescription: "Additional icons:"

[Files]
Source: "build\\windows\\x64\\runner\\Release\\*"; DestDir: "{app}"; Flags: recursesubdirs createallsubdirs ignoreversion

[Icons]
Name: "{autoprograms}\Simplications"; Filename: "{app}\simplications.exe"
Name: "{autodesktop}\Simplications"; Filename: "{app}\simplications.exe"; Tasks: desktopicon

[Run]
Filename: "{app}\simplications.exe"; Description: "Launch Simplications"; Flags: nowait postinstall skipifsilent
68 changes: 22 additions & 46 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release APK and Windows MSIX
name: Release APK and Windows Installer

on:
release:
Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:
with:
files: ${{ steps.apk.outputs.apk_path }}

build-windows-msix:
build-windows-installer:
runs-on: windows-latest

steps:
Expand All @@ -80,57 +80,33 @@ jobs:
- name: Build release Windows app
run: flutter build windows --release

- name: Import MSIX signing certificate
id: import-msix-certificate
shell: pwsh
env:
MSIX_CERT_PFX_BASE64: ${{ secrets.MSIX_CERT_PFX_BASE64 }}
run: |
if ([string]::IsNullOrWhiteSpace($env:MSIX_CERT_PFX_BASE64)) {
throw 'Missing required secret: MSIX_CERT_PFX_BASE64'
}
$certPath = Join-Path $env:RUNNER_TEMP 'msix-signing.pfx'
$cerPath = Join-Path $env:RUNNER_TEMP 'msix-signing.cer'
[System.IO.File]::WriteAllBytes($certPath, [System.Convert]::FromBase64String($env:MSIX_CERT_PFX_BASE64))
$certPassword = ConvertTo-SecureString -String "${{ secrets.MSIX_CERT_PASSWORD }}" -AsPlainText -Force
$importedCert = Import-PfxCertificate -FilePath $certPath -Password $certPassword -CertStoreLocation 'Cert:\CurrentUser\My'
Export-Certificate -Cert $importedCert -FilePath $cerPath | Out-Null
"cert_path=$certPath" >> $env:GITHUB_OUTPUT
"cer_path=$cerPath" >> $env:GITHUB_OUTPUT

- name: Create MSIX installer
shell: pwsh
env:
MSIX_CERT_PASSWORD: ${{ secrets.MSIX_CERT_PASSWORD }}
run: |
if ([string]::IsNullOrWhiteSpace($env:MSIX_CERT_PASSWORD)) {
throw 'Missing required secret: MSIX_CERT_PASSWORD'
}
dart run msix:create --build-windows false --install-certificate false --certificate-path "${{ steps.import-msix-certificate.outputs.cert_path }}" --certificate-password "$env:MSIX_CERT_PASSWORD"
- name: Install Inno Setup
run: choco install innosetup --yes --no-progress

- name: Prepare versioned Windows MSIX filename
id: msix
- name: Create Inno Setup installer
id: inno
shell: pwsh
env:
VERSION: ${{ github.event.release.tag_name || github.ref_name }}
run: |
$version = $env:VERSION.TrimStart('v')
$msixSrc = Get-ChildItem -Path 'build' -Filter '*.msix' -Recurse | Select-Object -First 1
if (-not $msixSrc) {
throw 'MSIX package not found under build/'
if (-not (Test-Path 'build/windows/x64/runner/Release/simplications.exe')) {
throw 'Windows release executable not found under build/windows/x64/runner/Release/'
}
iscc "/DAppVersion=$version" ".github/installer/simplications.iss"
$installerTarget = "build/simplications-$version-windows-setup.exe"
if (-not (Test-Path $installerTarget)) {
$fallbackInstaller = Get-ChildItem -Path 'build' -Filter '*.exe' -Recurse |
Sort-Object LastWriteTime -Descending |
Select-Object -First 1
if (-not $fallbackInstaller) {
throw 'Inno Setup installer was not generated under build/'
}
Copy-Item -Path $fallbackInstaller.FullName -Destination $installerTarget -Force
}
$msixTarget = "build/simplications-$version-windows-release.msix"
Copy-Item -Path $msixSrc.FullName -Destination $msixTarget -Force
"msix_path=$msixTarget" >> $env:GITHUB_OUTPUT
"installer_path=$installerTarget" >> $env:GITHUB_OUTPUT

- name: Attach MSIX to release
- name: Attach Windows installer to release
uses: softprops/action-gh-release@v2
with:
files: |
${{ steps.msix.outputs.msix_path }}
${{ steps.import-msix-certificate.outputs.cer_path }}
append_body: true
body: |
## Internal Testing: Trust Install for Windows MSIX
If SmartScreen or certificate trust blocks install, follow the tester guide:
https://github.com/${{ github.repository }}/blob/main/docs/msix-trust-install.md
files: ${{ steps.inno.outputs.installer_path }}
117 changes: 0 additions & 117 deletions .instructions.md

This file was deleted.

42 changes: 31 additions & 11 deletions .llmrc
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,25 @@ This file provides essential context for LLMs working on the Simplications proje

### Device-Specific Features

The app dynamically changes questions based on device type:
The app dynamically changes questions based on `DeviceTemplate.deviceType`
(the `DeviceCategory` enum in `lib/models/device.dart`):

- **sensor**: Sampling frequency, data deletion, local processing (3 device-specific Q's)
- **speaker**: Voice history, local processing, app permissions (3 device-specific Q's)
- **camera**: Encryption, storage, sharing controls, motion detection (4 Q's)
- **tv**: Account required, tracking disabled, offline mode (3 Q's)
- **thermostat**: Data collection, offline control, family access (3 Q's)
- **lock**: Offline unlock, access logging, 2FA (3 Q's)
- **robot**: Map privacy, cloud requirement, vision data (3 Q's)
- **toy**: Parental control, child data limits, recording disable (3 Q's)
- **wearable**: Health data sharing, location tracking (2 Q's)
- **light/plug/blind**: Local control, usage tracking, offline fallback (3 Q's)
- All devices: 5 universal base questions (password, updates, network, informed, permissions)
- Conditional: Camera/microphone consent questions if applicable
- **general/custom/router/hub/meter/outdoor/printer/appliance**: no dedicated
question set — fall back to `password`/`updates`/`network`/`informed`
- Cross-cutting, appended on top of the category's own list (see
`_questionsWithCrossCuttingOnes` in `lib/models/device.dart`): every device
except `sensor` also gets an app-`permissions` question; any device with
`hasCamera`/`hasMicrophone` also gets a camera-consent / mic-active question
if its category list didn't already ask one

### Risk Scoring

Expand Down Expand Up @@ -74,6 +80,9 @@ lib/
│ │ ├── enum RiskLevel { low, medium, high }
│ │ ├── enum ActionType { social, technical, security }
│ │ ├── enum ActionPriority { high, medium, low }
│ │ ├── enum DeviceCategory { general, custom, sensor, speaker, camera,
│ │ │ tv, thermostat, plug, light, lock, blind, robot, toy, router,
│ │ │ hub, meter, outdoor, printer, appliance }
│ │ ├── class PrivacyAction
│ │ ├── class DeviceTemplate
│ │ ├── class DeviceQuestion(id, text, hint)
Expand Down Expand Up @@ -104,7 +113,7 @@ lib/

### Device-Specific Logic

- `DeviceTemplate.deviceType` (string) determines question set
- `DeviceTemplate.deviceType` (`DeviceCategory` enum, `lib/models/device.dart`) determines question set
- `DeviceInstance.questions` property returns conditional question list
- `CatalogData.getDeviceSpecificQuestions(template)` provides question definitions
- `CatalogData.getDeviceSpecificActions(template)` provides action recommendations
Expand Down Expand Up @@ -163,9 +172,9 @@ lib/

### To Add Support for a New Device Type

1. Add device type string (e.g., `'dishwasher'`) to device constants
1. Add a new value to `enum DeviceCategory` in `lib/models/device.dart` (e.g., `dishwasher`)
2. Create 3-4 device-specific questions in `catalog_data.dart`
3. Add conditional questions in `DeviceInstance.questions` getter (if device type == 'dishwasher')
3. Add conditional questions in `DeviceInstance.questions` getter (`if (template.deviceType == DeviceCategory.dishwasher)`)
4. Add 3-5 device-specific actions in `CatalogData.getDeviceSpecificActions()`
5. Test with custom device creation or add to predefined devices list

Expand Down Expand Up @@ -206,7 +215,7 @@ lib/
- Variable/method names: camelCase
- Class names: PascalCase
- Constants: camelCase with const keyword
- Device type strings: lowercase single word (speaker, camera, tv, lock, robot, toy, wearable, thermostat, light, plug, blind, router, hub, meter, printer, appliance, outdoor)
- `DeviceCategory` enum values (camelCase, `lib/models/device.dart`): general, custom, sensor, speaker, camera, tv, thermostat, plug, light, lock, blind, robot, toy, router, hub, meter, outdoor, printer, appliance
- Question IDs: lowercase with underscores (e.g., 'password', 'voice_history', 'video_encryption')

## File Locations & Purposes
Expand Down Expand Up @@ -249,25 +258,36 @@ Before submitting changes:

### Device-specific questions not appearing

- Check `DeviceTemplate.deviceType` matches string used in `DeviceInstance.questions`
- Check `DeviceTemplate.deviceType` matches the `DeviceCategory` value checked in `DeviceInstance.questions`
- Verify question added to `CatalogData.getDeviceSpecificQuestions()`
- Check conditional logic: `if (template.deviceType == 'speaker')`
- Check conditional logic: `if (template.deviceType == DeviceCategory.speaker)`

### Custom devices not showing in grid

- Verify `_AddDeviceCard` widget is defined
- Check grid logic: `allDevices.length + state.customDevices.length + 1`
- Ensure `isCustom` flag is properly set

## CI/CD

- **`check.yml`**: Runs on every push to any branch; builds APK release
(`flutter build apk --release`) and uploads it as an artifact.
- **Release workflow** (on `v*` tags): Validates the tag matches the
`pubspec.yaml` version and creates the GitHub Release — see the release
tagging steps in `AGENTS.md`.

## Resources

- **Flutter Docs**: <https://docs.flutter.dev/>
- **Material Design 3**: <https://m3.material.io/>
- **Dart Language**: <https://dart.dev/guides/language/language-tour>
- **BSI Smart Home Security** (reference): German Federal Office for Information Security guidelines
- **`lib/models/RISK_SCORING_README.md`**: Risk scoring algorithm details

## Recent Changes (April 2026)
## Recent Changes

- ✅ (2026-09) Refactored `DeviceTemplate.deviceType` from a raw string to the
`DeviceCategory` enum (`lib/models/device.dart`)
- ✅ Added custom room and device support with full UI
- ✅ Implemented 40+ device-specific questions across 12+ device types
- ✅ Added 30+ BSI-based security actions mapped to device types
Expand Down
Loading
Loading