Skip to content
Draft
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
19 changes: 0 additions & 19 deletions docs/decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ from ovos_workshop.decorators import (
skill_api_method,
adds_context,
removes_context,
homescreen_app,
killable_intent,
killable_event,
)
Expand Down Expand Up @@ -368,24 +367,6 @@ def reset_everything(self, message):

---

## GUI / Homescreen Decorators

### `@homescreen_app`

`homescreen_app` — `ovos_workshop/decorators/__init__.py:149`

Register a method as a homescreen app launcher. The icon file must be inside the `gui/` subfolder of the skill.

```python
from ovos_workshop.decorators import homescreen_app

@homescreen_app(icon="my_app.png", name="My App")
def launch_app(self, message):
self.gui.show_page("main.qml")
```

---

## API Decorator

### `@skill_api_method`
Expand Down
22 changes: 17 additions & 5 deletions docs/ovos-skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,9 @@ Override these in your skill class:
4. Init GUI
5. Load resource files (`load_data_files`)
6. Register decorated intents (`_register_decorated`)
7. Register homescreen app if `@homescreen_app` used
8. Register resting screen if `@resting_screen_handler` used
9. Call `initialize()`
10. Check first run
11. Set status to `ready`
7. Call `initialize()`
8. Check first run
9. Set status to `ready`

### Shutdown Sequence (`default_shutdown`)

Expand Down Expand Up @@ -187,3 +185,17 @@ This is used by `SkillManager` to defer loading until the required connectivity
| `question:action.{skill_id}` | Common query callback |
| `homescreen.metadata.get` | Homescreen requesting metadata |
| `{skill_id}.public_api` | Skill API introspection |

## Spec conformance — OVOS-GUI-1 §6.9

The skill-side home/resting-screen API has been removed: the home/resting screen
is a GUI render-backend concern, never a skill. Removed surface:

- The `@resting_screen_handler` and `@homescreen_app` decorators.
- `OVOSSkill.register_resting_screen()` and `register_homescreen_app()`, plus the
startup hook that invoked them (`register_resting_screen` / `_register_app_launcher`,
now a no-op).
- The `IdleDisplaySkill` base class (`ovos_workshop/skills/idle_display_skill.py`).

This is a **breaking** change. Skills must no longer register or own a homescreen;
that responsibility moves to the ovos-gui / homescreen-manager layer.
37 changes: 0 additions & 37 deletions ovos_workshop/decorators/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from functools import wraps
from typing import Optional, Callable, List
from ovos_utils.log import log_deprecation

Check failure on line 3 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:3:28: F401 `ovos_utils.log.log_deprecation` imported but unused help: Remove unused import: `ovos_utils.log.log_deprecation`
import warnings

Check failure on line 4 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:4:8: F401 `warnings` imported but unused help: Remove unused import: `warnings`
from ovos_workshop.decorators.killable import killable_intent, killable_event

Check failure on line 5 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:5:64: F401 `ovos_workshop.decorators.killable.killable_event` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `killable_event as killable_event`

Check failure on line 5 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:5:47: F401 `ovos_workshop.decorators.killable.killable_intent` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `killable_intent as killable_intent`
from ovos_workshop.decorators.layers import enables_layer, \

Check failure on line 6 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:6:45: F401 `ovos_workshop.decorators.layers.enables_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `enables_layer as enables_layer`
disables_layer, layer_intent, removes_layer, resets_layers, replaces_layer

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:35: F401 `ovos_workshop.decorators.layers.removes_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `removes_layer as removes_layer`

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:21: F401 `ovos_workshop.decorators.layers.layer_intent` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `layer_intent as layer_intent`

Check failure on line 7 in ovos_workshop/decorators/__init__.py

View workflow job for this annotation

GitHub Actions / lint / lint

ruff (F401)

ovos_workshop/decorators/__init__.py:7:5: F401 `ovos_workshop.decorators.layers.disables_layer` imported but unused; consider removing, adding to `__all__`, or using a redundant alias help: Use an explicit re-export: `disables_layer as disables_layer`
from ovos_workshop.decorators.ocp import ocp_play, ocp_pause, ocp_resume, \
ocp_search, ocp_previous, ocp_featured_media

Expand Down Expand Up @@ -74,23 +74,6 @@
return real_decorator


def resting_screen_handler(name: str):
"""
Decorator for adding a method as a resting screen handler to optionally
be shown on screen when device enters idle mode.
@param name: Name of the restring screen to register
"""

def real_decorator(func):
# Store the resting information inside the function
# This will be used later in register_resting_screen
if not hasattr(func, 'resting_handler'):
func.resting_handler = name
return func

return real_decorator


def skill_api_method(func: callable):
"""
Decorator for adding a method to the skill's public api. Methods with this
Expand Down Expand Up @@ -161,23 +144,3 @@
return func

return real_decorator


def homescreen_app(icon: str, name: Optional[str] = None):
"""
Decorator for adding a method as a homescreen app

the icon file MUST be located under 'gui' subfolder

@param icon: icon file to use in app drawer (relative to "gui" folder)
@param name: short name to show under the icon in app drawer
"""

def real_decorator(func):
# Store the icon inside the function
# This will be used later to call register_homescreen_app
func.homescreen_app_icon = icon
func.homescreen_app_name = name
return func

return real_decorator
1 change: 0 additions & 1 deletion ovos_workshop/skills/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from ovos_workshop.decorators.layers import IntentLayers
from ovos_workshop.skills.ovos import OVOSSkill
from ovos_workshop.skills.idle_display_skill import IdleDisplaySkill
from ovos_workshop.skills.fallback import FallbackSkill
from ovos_workshop.skills.common_play import OVOSCommonPlaybackSkill
93 changes: 0 additions & 93 deletions ovos_workshop/skills/idle_display_skill.py

This file was deleted.

84 changes: 3 additions & 81 deletions ovos_workshop/skills/ovos.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import datetime
import os
import re
import shutil
import sys
import time
import traceback
Expand All @@ -37,7 +36,6 @@
from ovos_bus_client.session import SessionManager, Session
from ovos_bus_client.util import get_message_lang
from ovos_config.config import Configuration
from ovos_config.locations import get_xdg_cache_save_path
from ovos_config.locations import get_xdg_config_save_path
from ovos_number_parser import pronounce_number
from ovos_option_matcher_fuzzy import FuzzyOptionMatcherPlugin
Expand Down Expand Up @@ -721,7 +719,6 @@ def _startup(self, bus: MessageBusClient, skill_id: str = ""):
self._register_skill_json()
self._register_decorated()
self._register_app_launcher()
self.register_resting_screen()

self.status.set_started()
# run skill developer initialization code
Expand Down Expand Up @@ -754,25 +751,9 @@ def _register_skill_json(self, root_directory: Optional[str] = None):
{"skill_id": self.skill_id, "utterances": utts, "lang": lang}))

def _register_app_launcher(self):
# register app launcher if registered via decorator
for attr_name in get_non_properties(self):
method = getattr(self, attr_name)
if hasattr(method, 'homescreen_app_icon'):
name = getattr(method, 'homescreen_app_name')
event = f"{self.skill_id}.{name or method.__name__}.homescreen.app"
icon = getattr(method, 'homescreen_app_icon')
name = name or self.__skill_id2name
LOG.debug(f"homescreen app registered: {name} - '{event}'")
self.register_homescreen_app(icon=icon,
name=name or self.skill_id,
event=event)
self.add_event(event, method, speak_errors=False)

@property
def __skill_id2name(self) -> str:
"""helper to make a nice string out of a skill_id"""
return (self.skill_id.split(".")[0].replace("_", " ").
replace("-", " ").replace("skill", "").title().strip())
# NOTE: app launchers / homescreen apps are a GUI render-backend concern
# and are no longer registered from skills (OVOS-GUI-1 §6.9).
pass

def _init_settings(self):
"""
Expand Down Expand Up @@ -821,65 +802,6 @@ def _init_skill_gui(self):
self.gui = SkillGUI(self)
self.gui.setup_default_handlers()

def register_homescreen_app(self, icon: str, name: str, event: str):
"""the icon file MUST be located under 'gui' subfolder"""
# this path is hardcoded in ovos_gui.constants and follows XDG spec
# we use it to ensure resource availability between containers
# it is the only path assured to be accessible both by skills and GUI
GUI_CACHE_PATH = get_xdg_cache_save_path('ovos_gui')

full_icon_path = f"{self.res_dir}/gui/{icon}"
if not os.path.isfile(full_icon_path):
self.log.error(f"failed to register homescreen app, icon does not exist: {full_icon_path}")
return
os.makedirs(f"{GUI_CACHE_PATH}/{self.skill_id}", exist_ok=True)
shared_path = f"{GUI_CACHE_PATH}/{self.skill_id}/{icon}"
shutil.copy(full_icon_path, shared_path)

self.bus.emit(Message("homescreen.register.app",
{"skill_id": self.skill_id,
"icon": shared_path,
"name": name,
"event": event}))

def register_resting_screen(self):
"""
Registers resting screen from the resting_screen_handler decorator.

This only allows one screen and if two is registered only one
will be used.
"""
for attr_name in get_non_properties(self):
handler = getattr(self, attr_name)
if hasattr(handler, 'resting_handler'):
resting_name = handler.resting_handler
LOG.debug(f"{get_handler_name(handler)} is a resting screen, name: {resting_name}")

def register(message=None, name=resting_name):
self.log.info(f'Registering resting screen {name} for {self.skill_id}.')
self.bus.emit(Message("homescreen.manager.add",
{"name": name, "id": self.skill_id}))

register() # initial registering

self.add_event("homescreen.manager.reload.list", register, speak_errors=False)

def wrapper(message, cb=handler):
if message.data["homescreen_id"] == self.skill_id:
LOG.debug(f"triggering resting_handler: {get_handler_name(cb)}")
cb(message)

self.add_event("homescreen.manager.activate.display", wrapper, speak_errors=False)

def shutdown_handler(message):
if message.data["id"] == self.skill_id:
msg = message.forward("homescreen.manager.remove",
{"id": self.skill_id})
self.bus.emit(msg)

self.add_event("mycroft.skills.shutdown", shutdown_handler, speak_errors=False)
break # TODO - if multiple decorators are used what do? this is not deterministic

def _start_filewatcher(self):
"""
Start watching settings for file changes if settings file exists and
Expand Down
25 changes: 0 additions & 25 deletions test/unittests/skills/test_idle_display_skill.py

This file was deleted.

Loading