├── .eslintrc.json
├── .gitignore
├── README.md
├── build.sh
├── chromecast
└── endpoints.txt
├── extension.js
├── icons
├── ceiling-light.svg
├── fan.svg
├── hass-blue.png
├── hass-symbolic.svg
├── media-player.svg
├── palette.svg
├── run.svg
├── script-text.svg
├── secondary.png
└── toggle-switch-outline.svg
├── metadata.json
├── po
├── de.po
├── es.po
├── fr.mo
├── fr.po
├── hass-gshell.pot
└── sk.po
├── prefs.js
├── schemas
├── gschemas.compiled
└── org.gnome.shell.extensions.hass-data.gschema.xml
├── screenshots
├── general_settings.png
├── hass_events.png
├── hass_events_40.png
├── panel_icons.png
├── panel_icons_40.png
├── panel_menu.png
├── panel_menu_40.png
├── preferences_menu.png
├── preferences_menu_up.png
└── togglable_settings.png
├── settings.js
├── stylesheet.css
└── utils.js
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true
5 | },
6 | "extends": "eslint:recommended",
7 | "parserOptions": {
8 | "ecmaVersion": "latest"
9 | },
10 | "rules": {
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | data/
2 | myextensions.code-workspace
3 | locale
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Simple Gnome Shell Extension for Home Assistant
2 |
3 | This is a simple gnome shell extension which allows you to control your home assistant setup from your gnome desktop.
4 |
5 | Currently, the extension supports temperature (and humidity) sensors, toggling lights and switches and turning on scenes and scripts.
6 | In addition, you can also use this extension in order to send `start`, `stop` or `close` events to your Home Assistant instance.
7 |
8 | ## Contents
9 |
10 | - [Installation](#installation)
11 | - [Direct Install (Recommended)](#direct-install)
12 | - [Installing from Source](#installing-from-source)
13 | - [Installing from Gnome Extensions](#installing-from-gnome-extensions)
14 | - [How to Use](#how-to-use)
15 | - [Manage your Preferences](#manage-your-preferences)
16 | - [Authentication](#authentication)
17 | - [Appearance](#appearance)
18 | - [Panel Appearance](#panel-appearance)
19 | - [Opening the Menu](#opening-the-menu)
20 | - [Preferences (Settings)](#preferences-settings)
21 | - [Changing the Togglables](#changing-the-togglables)
22 | - [Security](#security)
23 | - [Updating](#updating)
24 | - [Removing the Extension](#removing-the-extension)
25 | - [Feature Requests](#feature-requests)
26 | - [Notes](#notes)
27 | - [Credits](#credits)
28 |
29 |
30 | ## Installation
31 |
32 | ### Direct Install
33 |
34 | The script `build.sh` aims at helping you download and install the extension. Its default behavior will simply download the latest release corresponding to your gnome version. If you want to have the latest changes directly from the `master` branch then you can provide the `--latest` argument. Example usage is shown below:
35 |
36 | ```bash
37 | # Download build.sh and give execution rights
38 | wget https://raw.githubusercontent.com/geoph9/hass-gshell-extension/master/build.sh && chmod +x build.sh
39 |
40 | # Download and install hass-gshell@geoph9-on-github from the releases
41 | ./build.sh
42 |
43 | # # Download and install the latest version directly from master.
44 | # # This makes it a bit more possible to encounter some bug.
45 | # # If you do so, please make an issue on github!
46 | # ./build.sh --latest # or simply -l
47 |
48 | # # Get help message
49 | # ./build.sh -h
50 |
51 | # Delete the build.sh script since you no longer need it.
52 | rm ./build.sh
53 | ```
54 |
55 | After that, you will have to restart your session (e.g. `Alt+F2` -> `r+Enter` on Xorg or simply logout and re-login on Wayland) and then you will
56 | need to enable the extension. The enabling part can be done either from the terminal (`gnome-extensions enable hass-gshell@geoph9-on-github`) or
57 | from an app such as `Extensions` (available as a flatpak) or from the [`Gnome Extensions` website](https://extensions.gnome.org/).
58 |
59 | **NOTE:** The script simply downloads the extension's files either from github or fromt he realeases page. You can check that yourself. If you still don't trust running the script, then you follow the steps below.
60 |
61 | ### Installing from Source
62 |
63 | In order to install the extension you will have to clone this repository and move it under the directory where your other extensions are. The following commands should make it work:
64 |
65 | ```bash
66 | # Create the extensions directory in case it doesn't exist
67 | mkdir -p "$HOME"/.local/share/gnome-shell/extensions
68 | git clone https://github.com/geoph9/hass-gshell-extension.git "$HOME"/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
69 | ```
70 |
71 | Then open Gnome Tweaks (or the Extensions app on Gnome >=40) and enable the extension.
72 |
73 | **Note:** Ubuntu 21.04 does not ship with `Gnome 40` and so you will still need to use the `Gnome 3.38` version. You can install that by using the `build.sh` script above, with the default settings.
74 |
75 | ### Installing from Gnome Extensions
76 |
77 | The extension is also available at the [Gnome Extensions (e.g.o.)](https://extensions.gnome.org/extension/4170/home-assistant-extension/) website
78 | with the name *Home Assistant Extension*.
79 |
80 | The versions there will not be updated very often and so you may miss some features if you choose to use the e.g.o. website. That is why, the recommended [installation method is from the release page](#installing-from-releases).
81 |
82 | ## How to Use
83 |
84 | ### Manage your Preferences
85 |
86 | After installing the extension, you can use the preferences widget in order to customize it. In order to do that, you can open the panel menu by pressing on the home assistant tray icon and then press `Preferences`.
87 |
88 | ### Authentication
89 |
90 | In order to communicate with `Home Assistant` you will need to provide a `Long Live Access Token` which you can get from your *dashboard → profile (on the bottom left) → Long Live Access Tokens (on the bottom of the page)→ Create Token*.
91 |
92 | After that, copy the token and add it in the in the text box below the `Access Token:` entry on the preferences menu.
93 |
94 | In addition, you need to provide the url of your hass instance on the `URL` box.
95 |
96 | ## Appearance
97 |
98 | ### Panel Appearance
99 |
100 | The panel will contain the following 2 entries (after configuring the temperature). *Note: The red line is there just to emphasize the icons.*
101 |
102 | 
103 |
104 | By pressing the temperature buttons you can refresh the temperature.
105 |
106 | **Note: You can change the panel icon from the preferences menu. Currently only a blue and a white icon is supported. The white icon is the default.**
107 |
108 | ### Opening the Menu
109 |
110 | If you click the home assistant icon, you will get the following:
111 |
112 | 
113 |
114 | In this example, I have added 2 togglable entities that control my kitchen lights and my TV power. By pressing any of these buttons, its state will toggle. The names of these entries are taken from home assistant.
115 |
116 | **NOTE:** The menu can also be opened (toglled)by using the `Super+G` shortcut.This may make it easier for you to toggle something without using the mouse/touchpad. It is not possible (currently) to change this shortcut key (unless you change the schema file and re-compile it or use something like dconf). **UPDATE: This feature is removed now.**
117 |
118 |
119 | #### Home Assistant Events
120 |
121 | By pressing `Hass Events` a new sublist will appear:
122 |
123 |
124 | 
125 |
126 | ### Preferences (Settings)
127 |
128 | **NOTE:** This is for Gnome >=40. Gnome 3.38 has a different menu but with similar functionality.
129 |
130 | By pressing the `Preferences` button you will get the following:
131 |
132 |
133 | 
134 |
135 | Currently, there are four pages. Generic settings, togglables (lights/switches), runnables (scenes/scripts) and sensors.
136 | In the general settings, you are prompted to enter the URL and Long-Live Access Token of your Home Assistant instance.
137 |
138 | The rest of the options are self-describing. About the temperature and humidity id, they are only needed if the
139 | `Show Temperature/Humidity` and `Show Humidity` switches are on. Otherwise, you can still use the extension by
140 | using only the toggles or runnables. Theoretically, you can put any kind of sensor in these spots
141 | (but I haven't tested any other kind of sensor).
142 |
143 | **Note:** The `Hass White Icon` is the default Icon option and it is the classical home-assistant icon without any color. This integrates better with the rest of the icons in your panel. In the screenshots above I am using the `Hass Blue Icon`.
144 |
145 | **Note:** The Long Live Access Token can be obtained by going to your Home Assistant dashboard, then to your profile (on the bottom) and then go to the bottom of the page and create a new Long Live Access Token. More information about it [on the oficial Home Assistant website](https://developers.home-assistant.io/docs/auth_api/#long-lived-access-token).
146 |
147 | **Note:** The options to refresh the temperature/humidity statistics are currently not working.
148 |
149 | ### Changing the Togglables and Runnables
150 |
151 | If you click the `Togglables` page on the side you will get the following:
152 |
153 | 
154 |
155 | The extension will scan your home assistant instance in order to find all of the entities that are either switches or lights. These entities will be listed here. In my case, I only have
156 | two togglable entities and I use both of them. If I unchecked the switch.kitchen_lights entry then the option would also be removed from the extension's panel.
157 |
158 | By default, all togglables will appear. If you only want a subset of the switches, you can do that here.
159 |
160 | The same applies to the `Runnables` page with scene and script entities.
161 |
162 | ## Security
163 |
164 | I am using the `Secret` library in order to store the access token in the running secret service (like gnome-keyring or ksecretservice) [source](https://developer.gnome.org/libsecret/unstable/js-store-example.html). This makes it safer to use your access token since it is more difficult to have it stolen by a third party. So, your token is as safe as your current user (this means that if a third party knows your user password and has access to your machine then they can theoretically get the token, but if that is the case then you probably have more improtant things to worry about).
165 |
166 | In general, if you think that you have an exposed access token, then you should go to your profile and delete it. Pay attention to this especially if you are hosting your instance on the internet (and not locally).
167 |
168 | ## Updating
169 |
170 | If you installed the extension from the [release page](https://github.com/geoph9/hass-gshell-extension/releases), you should simply re-run the the `build.sh` script.
171 |
172 | If you installed from source, then you will have to pull the changes from the master branch as follows:
173 |
174 | ```bash
175 | cd $HOME/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github && git pull origin master
176 | ```
177 |
178 | If you installed from the [Gnome Extensions (e.g.o.)](https://extensions.gnome.org/extension/4170/home-assistant-extension/) website and there is an update available, then you will be prompted to update whenever you visit the website.
179 |
180 | ## Removing the Extension
181 |
182 | If you followed the installation instructions above then you can do the following:
183 |
184 | ```bash
185 | rm -rf $HOME/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
186 | ```
187 |
188 | You will also have to restart your session in order to have the panel buttons dissapear.
189 |
190 | ## Feature Requests
191 |
192 | For feature requests please create a new issue and describe what you want. I will be happy to try and implement it (or you can implement it yourself in then make a PR).
193 |
194 | ## Notes:
195 |
196 | 1. Before starting check the preferences page by opening the widget (pressing the home assistant button on the panel) and pressing `Preferences`. There you can add as many (valid) entities as you want.
197 | 2. On Gnome 3.38, the entities **MUST** include a dot (`.`) and at least one underscore (`_`). For example, an entity id could be: `switch.kitchen_lights_relay`.
198 | 3. On Gnome 3.38, changing the preferences doesn't update togglable entities and you will need to restart your session for the changes to take effect.
199 | - On `Xorg` you can do that by pressing `Alt+F2` and then `r`.
200 | - On `Wayland` you will have to logout and re-login.
201 |
202 | If you are unsure about whether you have `Xorg` or `Wayland` then simply try the `Xorg` option and see if it works.
203 |
204 |
205 | ## Credits
206 |
207 | My implementation is based on the following:
208 |
209 | - **Codeproject Tutorial**: [How to Create A GNOME Extension](https://www.codeproject.com/Articles/5271677/How-to-Create-A-GNOME-Extension)
210 | - **Github Repo**: [TV Switch - Gnome Shell Extension](https://github.com/geoph9/tv-switch-gnome-shell-extension).
211 | - **Caffeine Extension**: [Caffeine](https://github.com/eonpatapon/gnome-shell-extension-caffeine)
212 | - **GameMode Extension**: [GameMode](https://github.com/gicmo/gamemode-extension)
213 | - **Custom Hot Corners Extended (forked extension)**: [Custom Hot Corners Extended](https://github.com/G-dH/custom-hot-corners-extended)
214 |
215 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Get gnome version
4 | vers=$(gnome-shell --version | cut -d ' ' -f 3- | cut -d. -f1)
5 | if [[ $vers == 40* ]] ; then # e.g. convert 40.0 to 40
6 | vers="40";
7 | fi
8 | if [[ -z $vers ]] ; then
9 | # Are you not using gnome?
10 | echo "Could not identify gnome version. Aborting..."
11 | exit 1;
12 | fi
13 |
14 | function get_help() {
15 | echo "Usage: $0 [-h | --help] [-l | --latest] "
16 | echo
17 | echo "Arguments:"
18 | echo " -h | --help: Show this help message."
19 | echo " -l | --latest: Download the latest version directly from the 'master' branch on github."
20 | echo
21 | echo "The latter means that you will have the latest features but you may also encounter"
22 | echo "some bugs. Please make an issue on github if you encounter any bug!"
23 | echo
24 | echo "The version installed will depend on your gnome-shell version."
25 | echo "You can check the available versions from the releases page on github:"
26 | echo " https://github.com/geoph9/hass-gshell-extension/releases"
27 | echo
28 | }
29 |
30 | function check_releases() {
31 | echo "$0: Error: You either don't have 'wget' installed or the version $1 is invalid."
32 | echo "$0: Check the available versions from the releases page on github:"
33 | echo " https://github.com/geoph9/hass-gshell-extension/releases"
34 | exit 1;
35 | }
36 |
37 | function download_stable() {
38 | echo "$0: Downloading extension for version $vers..."
39 |
40 | # Download with wget (you can also download manually).
41 | wget -q https://github.com/geoph9/hass-gshell-extension/releases/download/"$vers"/hass-gshell@geoph9-on-github.shell-extension.zip || check_releases "$vers"
42 | # Unzip contents
43 | unzip hass-gshell@geoph9-on-github.shell-extension.zip -d hass-gshell@geoph9-on-github
44 | # Remove zip
45 | rm hass-gshell@geoph9-on-github.shell-extension.zip
46 | # Move directory to the extensions directory
47 | mkdir -p "$HOME"/.local/share/gnome-shell/extensions/
48 | rm -rf "$HOME"/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
49 | mv hass-gshell@geoph9-on-github "$HOME"/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
50 |
51 | # Enable extension (the session needs to be restarted before that)
52 | # gnome-extensions enable hass-gshell@geoph9-on-github
53 | exit 0;
54 | }
55 |
56 | function download_latest() {
57 | echo "$0: Downloading latest version directly from github. This may result in unstable behavior."
58 | mkdir -p "$HOME"/.local/share/gnome-shell/extensions
59 | ext_dir="$HOME"/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
60 | git clone https://github.com/geoph9/hass-gshell-extension.git "$ext_dir"
61 | # Delete the screenshots/README since they take up a lot of space.
62 | rm -rf "$ext_dir"/screenshots "$ext_dir"/README.md "$ext_dir"/chromecast
63 | echo
64 | # echo "$0: Trying to enable the extension. This may result in an error if you don't have the 'gnome-extension' cli installed but it is okay."
65 | # echo "$0: You will still need to restart your session in order to make it work."
66 | # gnome-extensions enable hass-gshell-local@geoph9-on-github
67 | exit 0;
68 | }
69 |
70 | while [[ $# -gt 0 ]] ; do
71 | key="$1"
72 |
73 | case $key in
74 | -h|--help)
75 | get_help
76 | exit 0
77 | shift
78 | ;;
79 | -l|--latest)
80 | download_latest
81 | shift
82 | ;;
83 | *) # unknown option
84 | echo "$0: Unknown option $1."
85 | get_help
86 | read -p "$0: Do you want to continue with the default behavior? [y|N]: " -n 1 -r
87 | echo
88 | if [[ "$REPLY" =~ ^[Yy]$ ]] ; then
89 | break
90 | else
91 | echo "$0: Aborting..."
92 | exit 1;
93 | fi
94 | shift # past argument
95 | ;;
96 | esac
97 | done
98 |
99 | download_stable
100 | glib-compile-schemas "$HOME"/.local/share/gnome-shell/extensions/hass-gshell@geoph9-on-github
101 |
102 | exit 0;
103 |
--------------------------------------------------------------------------------
/chromecast/endpoints.txt:
--------------------------------------------------------------------------------
1 | # Example:
2 | curl -X POST -d '{"entity_id": "media_player.tv_name"}' http://URL/api/services/media_player/media_play_pause
3 |
4 | media_play
5 | media_pause
6 | media_play_pause
7 | media_stop
8 | media_next_track
9 | media_previous_track
10 |
11 | media_seek (entity_id PLUS seek_position:int)
12 |
13 | select_sound_mode
14 |
15 | volume_up
16 | volume_down
17 | volume_set
18 | volume_mute
19 |
20 | turn_on
21 | turn_off
22 | toggle
23 |
24 |
25 |
--------------------------------------------------------------------------------
/extension.js:
--------------------------------------------------------------------------------
1 | import Gio from 'gi://Gio';
2 | import St from 'gi://St';
3 | import Clutter from 'gi://Clutter';
4 | import GObject from 'gi://GObject';
5 | import GLib from 'gi://GLib';
6 |
7 | import * as Main from 'resource:///org/gnome/shell/ui/main.js';
8 | import * as MessageTray from 'resource:///org/gnome/shell/ui/messageTray.js';
9 | import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
10 | import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
11 |
12 | import * as Utils from './utils.js';
13 | import * as Settings from './settings.js';
14 |
15 | import {Extension, gettext as _} from 'resource:///org/gnome/shell/extensions/extension.js';
16 |
17 | var HassPanelSensor = GObject.registerClass ({
18 | GTypeName: "HassPanelSensor"
19 | }, class HassPanelSensor extends St.Bin {
20 |
21 | _init(entity, force_reload=false) {
22 | super._init({
23 | style_class : "panel-button",
24 | reactive : true,
25 | can_focus : true,
26 | track_hover : true,
27 | height : 30,
28 | visible: true,
29 | });
30 | this.entity = entity;
31 |
32 | this.label = new St.Label({
33 | text : force_reload ? this.computePlaceholderText() : this.computeLabelText(),
34 | y_align: Clutter.ActorAlign.CENTER,
35 | style: "spacing: 2px",
36 | });
37 | this.set_child(this.label);
38 | this.connect("button-press-event", () => this.refresh(true));
39 |
40 | if (force_reload)
41 | this.refresh(true);
42 |
43 | this.build_tooltip();
44 |
45 | // Import settings to have access to its constants
46 | // Note: we can't do that globally.
47 | this.Settings = Settings;
48 | this.connectedSettingIds = Utils.connectSettings([this.Settings.HASS_ENTITIES_CACHE], this.refresh.bind(this));
49 | }
50 |
51 | build_tooltip() {
52 | this.tooltip = new St.Label({
53 | text: this.entity.name,
54 | visible: false,
55 | style_class: "hass-sensor-tooltip",
56 | });
57 | Main.layoutManager.uiGroup.add_child(this.tooltip);
58 | Main.layoutManager.uiGroup.set_child_above_sibling(this.tooltip, null);
59 |
60 | this.set_track_hover(true);
61 | this.connect('style-changed', (self) => {
62 | if(self.hover) {
63 | let [x, y] = this.get_transformed_position();
64 | let w = this.tooltip.get_width();
65 | let h = this.tooltip.get_height();
66 | this.tooltip.set_position(
67 | x + Math.round(this.get_width() / 2) - Math.round(w / 2),
68 | y + Math.round(1.3 * h)
69 | );
70 | this.tooltip.show();
71 | this.tooltip.ease({
72 | opacity: 200,
73 | duration: 300,
74 | mode: Clutter.AnimationMode.EASE_OUT_QUAD,
75 | });
76 | }
77 | else {
78 | this.tooltip.remove_all_transitions();
79 | this.tooltip.ease({
80 | opacity: 0,
81 | duration: 100,
82 | mode: Clutter.AnimationMode.EASE_OUT_QUAD,
83 | onComplete: () => this.tooltip.hide(),
84 | });
85 | }
86 | });
87 | }
88 |
89 | refresh(force_reload=false, entity=null) {
90 | if (entity) {
91 | this.entity = entity;
92 | this.label.text = this.computeLabelText();
93 | this.tooltip.text = this.entity.name;
94 | return;
95 | }
96 | Utils.getSensor(
97 | this.entity.entity_id,
98 | (entity) => this.refresh(false, entity),
99 | () => {
100 | Utils._log(
101 | 'Fail to load %s (%s) sensor state',
102 | [this.entity.name, this.entity.entity_id]
103 | );
104 | this.label.text = this.computePlaceholderText();
105 | },
106 | force_reload
107 | );
108 | }
109 |
110 | computeLabelText() {
111 | return `${this.entity.state} ${this.entity.unit}`;
112 | }
113 |
114 | computePlaceholderText() {
115 | return `- ${this.entity.unit}`;
116 | }
117 |
118 | destroy() {
119 | Utils.disconnectSettings(this.connectedSettingIds);
120 | this.label.destroy();
121 | this.tooltip.destroy();
122 | super.destroy();
123 | }
124 |
125 | });
126 |
127 | class TrayMenuItems {
128 | constructor(type, parentMenu) {
129 | if (type !== "togglable" && type !== "runnable")
130 | throw new Error(`Type ${type} is not supported in TrayMenuItems`)
131 |
132 | this.type = type;
133 | this.parentMenu = parentMenu;
134 | this.menuItems = [];
135 | this.separatorItem = null;
136 | }
137 |
138 | refresh(force_reload=false) {
139 | Utils._log(`refresh ${this.type} in tray menu...`);
140 |
141 | // Firstly delete previously created menu items
142 | this.delete();
143 |
144 | // Now get the entities and continue in callback
145 | Utils.getEntitiesByType(
146 | this.type,
147 | function(results) {
148 | Utils._log(`get enabled ${this.type}s, continue refreshing tray menu`);
149 | this.menuItems = [];
150 | for (let [idx, entity] of results.entries()) {
151 | if (entity.entity_id === "" || !entity.entity_id.includes("."))
152 | continue
153 | let pmItem = new PopupMenu.PopupImageMenuItem(
154 | _(this.type === "togglable" ? 'Toggle:': 'Run:') + ' ' + entity.name,
155 | Utils.getEntityIcon(entity.entity_id.split(".")[0]),
156 | );
157 | pmItem.connect('activate', () => {
158 | if (this.type === "togglable")
159 | Utils.toggleEntity(entity)
160 | else if (this.type === "runnable")
161 | Utils.turnOnEntity(entity)
162 | });
163 | this.menuItems.push({item: pmItem, entity: entity, index: idx});
164 |
165 | // We insert here the menu items with their index as position to put
166 | // them at the top of the popup menu
167 | this.parentMenu.addMenuItem(pmItem, idx);
168 | }
169 | // If we have at least one item in menu, add the separator
170 | if (this.menuItems.length) {
171 | this.separatorItem = new PopupMenu.PopupSeparatorMenuItem();
172 | this.parentMenu.addMenuItem(
173 | this.separatorItem,
174 | // use the count as position of the separator in the menu
175 | this.menuItems.length
176 | );
177 | }
178 | Utils._log(`${this.type}s in tray menu refreshed`);
179 | }.bind(this),
180 | // On error callback
181 | () => Utils._log(`fail to load enabled ${this.type}s`, null, true),
182 | true, // we want only enabled entities
183 | force_reload
184 | );
185 | }
186 |
187 | delete() {
188 | // Destroy the previously created togglable menu items
189 | for (let ptItem of this.menuItems)
190 | ptItem.item.destroy();
191 | this.menuItems = [];
192 | if (this.separatorItem) {
193 | this.separatorItem.destroy();
194 | this.separatorItem = null;
195 | }
196 | }
197 | }
198 |
199 | // Credits for organizing this class: https://github.com/vchlum/hue-lights/
200 | var HassMenu = GObject.registerClass ({
201 | GTypeName: "HassMenu"
202 | }, class HassMenu extends PanelMenu.Button {
203 | _init(metadata, settings, mainDir, openPref) {
204 | super._init(0.0, metadata.name, false);
205 | this.style_class = 'hass-menu';
206 | this._settings = settings;
207 | this.Settings = null;
208 | // this.shortcutId = "hass-shortcut";
209 | this._mainDir = mainDir;
210 | this._openPrefs = openPref;
211 |
212 | this.box = null;
213 |
214 | this.trayButton = null;
215 | this.trayIcon = null;
216 |
217 | this.togglablesMenuItems = new TrayMenuItems("togglable", this.menu);
218 | this.runnablesMenuItems = new TrayMenuItems("runnable", this.menu);
219 |
220 | this.panelSensorBox = null;
221 | this.panelSensors = [];
222 | this.refreshSensorsTimeout = null;
223 |
224 | this.connectedSettingIds = [];
225 | }
226 |
227 | enable() {
228 | Utils._log("enabling...");
229 |
230 | // Import settings to have access to its constants
231 | // Note: we can't do that globally.
232 | this.Settings = Settings;
233 |
234 | // Disable click event on the PopupMenu to handle it in the components it contains
235 | this.connect('event', (actor, event) => {
236 | if ((event.type() == Clutter.EventType.TOUCH_BEGIN ||
237 | event.type() == Clutter.EventType.BUTTON_PRESS))
238 | return Clutter.EVENT_STOP;
239 | return Clutter.EVENT_PROPAGATE;
240 | });
241 |
242 | // Create the main BoxLayout which will contain all compoments of the extension
243 | this.box = new St.BoxLayout({style: 'spacing: 2px'});
244 |
245 | // Load settings and build all compoments
246 | this._loadSettings();
247 | this._buildTrayMenu();
248 | this._buildPanelSensors();
249 | this._buildTrayIcon();
250 | // this._enableShortcut();
251 |
252 | // Add the main box as child of the PopupMenu
253 | this.add_child(this.box);
254 |
255 | // Connect the setting field that contain the HASS URL with the refresh() method with
256 | // force_reload argument equal to true
257 | this._connectSettings([this.Settings.HASS_URL], this.refresh, [true]);
258 |
259 | // Connect the setting field that contain the HASS entities state cache with the refresh()
260 | // method with force_reload argument equal to false (default)
261 | this._connectSettings([this.Settings.HASS_ENTITIES_CACHE], this.refresh);
262 | }
263 |
264 | disable() {
265 | Utils._log("disabling...");
266 | Utils.disconnectSettings(this.connectedSettingIds);
267 | this._deletePanelSensors();
268 | this._deleteTrayIcon();
269 | // this._disableShortcut();
270 | this._deleteMenuItems();
271 | if (this.panelSensorBox) this.panelSensorBox.destroy();
272 | if (this.box) this.box.destroy();
273 | if (this.refreshSensorsTimeout) {
274 | GLib.Source.remove(this.refreshSensorsTimeout);
275 | this.refreshSensorsTimeout = null;
276 | }
277 | }
278 |
279 | refresh(force_reload=false) {
280 | if (force_reload) {
281 | Utils.getEntities(
282 | // We do not have to trigger a refresh() here, because on success, cache setting
283 | // will be updated and a refresh will be automatically triggered. So no on-success
284 | // callback here.
285 | null,
286 | function () {
287 | Utils._log("fail to refresh entities cache, invalidate it.", null, true);
288 | Utils.invalidateEntitiesCache();
289 | }.bind(this),
290 | true // force refreshing cache
291 | );
292 | }
293 | else {
294 | this.togglablesMenuItems.refresh();
295 | this.runnablesMenuItems.refresh();
296 | this._refreshPanelSensors();
297 | }
298 | }
299 |
300 | /*
301 | **********************************************************************************************
302 | * Shortcut
303 | **********************************************************************************************
304 | */
305 | // TODO: Add proper support for shortcuts (in a similar manner as with hass-url for example)
306 | // _enableShortcut() {
307 | // Main.wm.addKeybinding(
308 | // this.shortcutId,
309 | // this._settings.get_strv('org.gnome.shell.extensions.hass-shortcut'),
310 | // Meta.KeyBindingFlags.NONE, // key binding flag
311 | // Shell.ActionMode.ALL, // binding mode
312 | // () => this.menu.toggle()
313 | // );
314 | // }
315 | // _disableShortcut() {
316 | // if (this.refreshSensorsTimeout) {
317 | // Mainloop.source_remove(this.refreshSensorsTimeout);
318 | // this.refreshSensorsTimeout = null;
319 | // }
320 | // Main.wm.removeKeybinding(this.shortcutId);
321 | // }
322 |
323 | /*
324 | **********************************************************************************************
325 | * Manage settings
326 | **********************************************************************************************
327 | */
328 |
329 | _loadSettings() {
330 | Utils._log("load settings");
331 | this.panelSensorIds = this._settings.get_strv(this.Settings.HASS_ENABLED_SENSOR_IDS);
332 | this.doRefresh = this._settings.get_boolean(this.Settings.DO_REFRESH);
333 | this.refreshSeconds = Number(this._settings.get_string(this.Settings.REFRESH_RATE));
334 | }
335 |
336 | _connectSettings(settings, callback, args=[]) {
337 | this.connectedSettingIds.push.apply(
338 | this.connectedSettingIds,
339 | Utils.connectSettings(
340 | settings,
341 | function() {
342 | this._loadSettings();
343 | callback.apply(this, args);
344 | }.bind(this)
345 | )
346 | );
347 | }
348 |
349 | /*
350 | **********************************************************************************************
351 | * Tray icon
352 | **********************************************************************************************
353 | */
354 |
355 | _getTrayIconPath() {
356 | let icon_path = this._settings.get_string(this.Settings.PANEL_ICON_PATH);
357 | // Make sure the path is valid
358 | if (!icon_path.startsWith("/"))
359 | icon_path = "/" + icon_path;
360 | return this._mainDir.get_path() + icon_path;
361 | }
362 |
363 | _buildTrayIcon() {
364 | this.trayButton = new St.Bin({
365 | style_class : "panel-button",
366 | reactive : true,
367 | can_focus : true,
368 | track_hover : true,
369 | height : 30,
370 | });
371 |
372 |
373 | this.trayIcon = new St.Icon({
374 | gicon : Gio.icon_new_for_string(this._getTrayIconPath()),
375 | style_class : 'system-status-icon',
376 | });
377 |
378 | this.trayButton.set_child(this.trayIcon);
379 | this.trayButton.connect("button-press-event", () => this.menu.toggle());
380 |
381 | this.box.add_child(this.trayButton);
382 |
383 | // Connect the setting field that contain the selected icon with the _updateTrayIcon()
384 | // method
385 | this._connectSettings([this.Settings.PANEL_ICON_PATH], this._updateTrayIcon);
386 | }
387 |
388 | _updateTrayIcon() {
389 | this.trayIcon.gicon = Gio.icon_new_for_string(this._getTrayIconPath());
390 | }
391 |
392 | _deleteTrayIcon() {
393 | if (this.trayIcon) {
394 | this.trayIcon.destroy();
395 | this.trayIcon = null;
396 | }
397 | if (this.trayButton) {
398 | this.trayButton.destroy();
399 | this.trayButton = null;
400 | }
401 | }
402 |
403 | /*
404 | **********************************************************************************************
405 | * Tray menu
406 | **********************************************************************************************
407 | */
408 |
409 | _buildTrayMenu() {
410 | Utils._log("build tray menu");
411 |
412 | // Build the submenu containing the HASS events
413 | let subItem = new PopupMenu.PopupSubMenuMenuItem(_('HASS Events'), true);
414 | subItem.icon.gicon = Gio.icon_new_for_string(this._mainDir.get_path() + '/icons/hass-symbolic.svg');
415 | this.menu.addMenuItem(subItem);
416 |
417 | let start_hass_item = new PopupMenu.PopupMenuItem(_('Start Home Assistant'));
418 | subItem.menu.addMenuItem(start_hass_item);
419 | start_hass_item.connect('activate', () => Utils.triggerHassEvent('start'));
420 |
421 | let stop_hass_item = new PopupMenu.PopupMenuItem(_('Stop Home Assistant'));
422 | subItem.menu.addMenuItem(stop_hass_item);
423 | stop_hass_item.connect('activate', () => Utils.triggerHassEvent('stop'));
424 |
425 | let close_hass_item = new PopupMenu.PopupMenuItem(_('Close Home Assistant'));
426 | subItem.menu.addMenuItem(close_hass_item, 2);
427 | close_hass_item.connect('activate', () => Utils.triggerHassEvent('close'));
428 |
429 | // Build the Refresh menu item
430 | let refreshMenuItem = new PopupMenu.PopupImageMenuItem(_("Refresh"), 'view-refresh');
431 | refreshMenuItem.connect('activate', () => {
432 | // Firstly close the menu to avoid artifact when it will partially be rebuiled
433 | this.menu.close();
434 | Utils._log("Refreshing...");
435 | this.refresh(true);
436 | });
437 | this.menu.addMenuItem(refreshMenuItem);
438 |
439 | // Build the Preferences menu item
440 | let prefsMenuItem = new PopupMenu.PopupImageMenuItem(
441 | _("Preferences"),
442 | 'security-high-symbolic',
443 | );
444 | prefsMenuItem.connect('activate', () => {
445 | Utils._log("opening Preferences...");
446 | this._openPrefs();
447 | });
448 | this.menu.addMenuItem(prefsMenuItem);
449 |
450 | // Connect the setting field that contain enabled togglable entities
451 | this._connectSettings([this.Settings.HASS_ENABLED_ENTITIES], this.togglablesMenuItems.refresh.bind(this.togglablesMenuItems));
452 |
453 | // Refresh togglable items a first time
454 | this.togglablesMenuItems.refresh();
455 |
456 | // Connect the setting field that contain enabled runnable entities
457 | this._connectSettings([this.Settings.HASS_ENABLED_RUNNABLES], this.runnablesMenuItems.refresh.bind(this.runnablesMenuItems));
458 |
459 | // Refresh runnable items a first time
460 | this.runnablesMenuItems.refresh();
461 |
462 | Utils._log("tray menu builded");
463 | }
464 |
465 | _deleteMenuItems() {
466 | // Delete all the menu items
467 | this.togglablesMenuItems.delete();
468 | this.runnablesMenuItems.delete();
469 | this.menu.removeAll();
470 | }
471 |
472 | /*
473 | **********************************************************************************************
474 | * Panel sensors
475 | **********************************************************************************************
476 | */
477 |
478 | _buildPanelSensors() {
479 | Utils._log("build sensors panel...");
480 |
481 | // Create a box for panel sensors and add it to main box
482 | this.panelSensorBox = new St.BoxLayout();
483 | this.box.add_child(this.panelSensorBox);
484 |
485 | // Rebuild panel sensors item a first time (with force reload enabled)
486 | this._rebuildPanelSensors(true);
487 |
488 | // Connect the setting field that contain enabled sensors with the _rebuildPanelSensors()
489 | // method
490 | this._connectSettings([this.Settings.HASS_ENABLED_SENSOR_IDS], this._rebuildPanelSensors);
491 |
492 | // Configure the refreshing of sensors panel
493 | this._configPanelSensorsRefresh();
494 |
495 | // Connect all setting fields that have impact on the refreshing of sensors panel with
496 | // the _configPanelSensorsRefresh() method
497 | this._connectSettings(
498 | [
499 | this.Settings.HASS_ENABLED_SENSOR_IDS,
500 | this.Settings.DO_REFRESH,
501 | this.Settings.REFRESH_RATE,
502 | ],
503 | this._configPanelSensorsRefresh
504 | );
505 |
506 | Utils._log("panel sensor builded...");
507 | }
508 |
509 | _rebuildPanelSensors(force_reload=false) {
510 | Utils._log("Rebuild panel sensors...");
511 | Utils.getSensors(
512 | function(panelSensors) {
513 | Utils._log("Get enabled sensors, rebuild panel...");
514 | let panelSensorsIds = panelSensors.map((p) => p.entity_id);
515 |
516 | // Firstly, remove all panel sensors from their box and destroy removed sensors
517 | for (let [panelSensorId, panelSensor] of Object.entries(this.panelSensors)) {
518 | this.panelSensorBox.remove_child(panelSensor);
519 | if (!panelSensorsIds.includes(panelSensorId)) {
520 | Utils._log("Remove sensor %s (%s) from panel", [panelSensor.entity.name, panelSensor.entity.entity_id]);
521 | panelSensor.destroy();
522 | delete this.panelSensors[panelSensorId];
523 | }
524 | }
525 |
526 | // Now refresh existing sensors, create new ones and put them in their box
527 | for (let [idx, panelSensor] of panelSensors.entries()) {
528 | if (panelSensor.entity_id in this.panelSensors) {
529 | Utils._log("Refresh sensor %s (%s) in panel", [panelSensor.name, panelSensor.entity_id]);
530 | this.panelSensors[panelSensor.entity_id].refresh(force_reload, panelSensor);
531 | }
532 | else {
533 | Utils._log("Add sensor %s (%s) to panel", [panelSensor.name, panelSensor.entity_id]);
534 | this.panelSensors[panelSensor.entity_id] = new HassPanelSensor(panelSensor, force_reload);
535 | }
536 | this.panelSensorBox.add_child(this.panelSensors[panelSensor.entity_id]);
537 | }
538 | Utils._log("Panel sensors rebuilded");
539 | }.bind(this),
540 | function() {
541 | Utils._log("fail to load enabled panel sensors, remove all", null, true);
542 | this._deletePanelSensors();
543 | }.bind(this),
544 | true, // we want only enabled sensors
545 | );
546 | }
547 |
548 | _configPanelSensorsRefresh() {
549 | // Firstly cancel previous configured timeout (if defined)
550 | if (this.refreshSensorsTimeout) {
551 | Utils._log("cancel previous sensors refresh timer...");
552 | GLib.Source.remove(this.refreshSensorsTimeout);
553 | this.refreshSensorsTimeout = null;
554 | }
555 |
556 | // Only continue if refreshing is enabled, refreshing rate is configured and we have at
557 | // least one panel sensors configured
558 | if (!this.doRefresh || !this.refreshSeconds || !this.panelSensorIds.length)
559 | return;
560 |
561 | // Schedule sensors panel refreshing every X seconds
562 | Utils._log(
563 | 'schedule refreshing sensors panel every %s seconds',
564 | [this.refreshSeconds]
565 | );
566 | this.refreshSensorsTimeout = GLib.timeout_add_seconds(
567 | GLib.PRIORITY_DEFAULT,
568 | this.refreshSeconds, () => {
569 | this._refreshPanelSensors(true);
570 | // We have to return true to keep the timer alive
571 | return true // return GLib.SOURCE_CONTINUE;
572 | }
573 | );
574 | }
575 |
576 | _refreshPanelSensors(force_reload=false) {
577 | for (let panelSensor of Object.values(this.panelSensors))
578 | panelSensor.refresh(force_reload);
579 | }
580 |
581 | _deletePanelSensors() {
582 | for (let [panelSensorId, panelSensor] of Object.entries(this.panelSensors)) {
583 | this.box.remove_child(panelSensor);
584 | panelSensor.destroy();
585 | delete this.panelSensors[panelSensorId];
586 | }
587 | }
588 |
589 | })
590 |
591 | export default class HassExtension extends Extension {
592 | enable() {
593 | this._settings = this.getSettings();
594 | Utils.init(
595 | this.metadata.uuid,
596 | this._settings,
597 | this.metadata,
598 | this.dir,
599 | _,
600 | MessageTray,
601 | Main
602 | );
603 | Utils._log("enabling...");
604 |
605 | this.popupMenu = new HassMenu(
606 | this.metadata,
607 | this._settings,
608 | this.dir,
609 | this.openPreferences.bind(this)
610 | );
611 | this.popupMenu.enable()
612 |
613 | Main.panel.addToStatusArea('hass-extension', this.popupMenu);
614 | }
615 |
616 | disable() {
617 | Utils._log("disabling...");
618 |
619 | this.popupMenu.disable();
620 | this.popupMenu.destroy();
621 | this.popupMenu = null;
622 | this._settings = null;
623 | Utils.disable();
624 | }
625 | }
626 |
627 |
--------------------------------------------------------------------------------
/icons/ceiling-light.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
15 |
34 |
36 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/icons/fan.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
15 |
34 |
36 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/icons/hass-blue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/icons/hass-blue.png
--------------------------------------------------------------------------------
/icons/hass-symbolic.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/icons/media-player.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icons/palette.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icons/run.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icons/script-text.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/icons/secondary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/icons/secondary.png
--------------------------------------------------------------------------------
/icons/toggle-switch-outline.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
15 |
34 |
36 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "_generated": "Generated by SweetTooth, do not edit",
3 | "description": "A simple gnome shell extension for Home Assistant. Check the README on github for additional help!\n\nMain points:\n- You need to provide the url of your hass, a long live access token obtained from your profile page (on your hass web instance) and the entity ids of the entities you want to have as togglable.\n- In order to add some local temperature/humidity sensor, you may also provide a temperature and/or a humidity entity id (which should match the corresponding ids of your hass instance).",
4 | "gettext-domain": "hass-gshell",
5 | "name": "Home Assistant Extension",
6 | "settings-schema": "org.gnome.shell.extensions.hass-data",
7 | "shell-version": [
8 | "45", "46", "47", "48"
9 | ],
10 | "url": "https://github.com/geoph9/hass-gshell-extension",
11 | "uuid": "hass-gshell@geoph9-on-github",
12 | "version": 26.0
13 | }
14 |
--------------------------------------------------------------------------------
/po/de.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: \n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2023-06-25 09:46+0200\n"
11 | "PO-Revision-Date: 2023-06-25 09:52+0200\n"
12 | "Last-Translator: Roy Meissner\n"
13 | "Language-Team: \n"
14 | "Language: de_DE\n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19 | "X-Generator: Poedit 3.2.2\n"
20 |
21 | #: extension.js:75
22 | msgid "Toggle:"
23 | msgstr "Umschalten:"
24 |
25 | #: extension.js:464
26 | msgid "Run:"
27 | msgstr "Ausführen:"
28 |
29 | #: extension.js:93
30 | msgid "HASS Events"
31 | msgstr "Home Assistant Ereignisse"
32 |
33 | #: extension.js:95
34 | msgid "Start Home Assistant"
35 | msgstr "Starte Home Assistant"
36 |
37 | #: extension.js:96
38 | msgid "Stop Home Assistant"
39 | msgstr "Stoppe Home Assistant"
40 |
41 | #: extension.js:97
42 | msgid "Close Home Assistant"
43 | msgstr "Home Assistant schließen"
44 |
45 | #: extension.js:113
46 | msgid "Preferences"
47 | msgstr "Einstellungen"
48 |
49 | #: prefs.js:48
50 | msgid "General Settings"
51 | msgstr "Allgemeine Einstellungen"
52 |
53 | #: prefs.js:51
54 | msgid "Togglables"
55 | msgstr "Schaltbares"
56 |
57 | #: prefs.js:172
58 | msgid "Runnables"
59 | msgstr "Ausführbares"
60 |
61 | #: prefs.js:56 schemas/org.gnome.shell.extensions.hass-data.gschema.xml:94
62 | msgid "Panel Sensors"
63 | msgstr "Panel-Sensoren"
64 |
65 | #: prefs.js:85
66 | msgid "Global options:"
67 | msgstr "Globale Optionen:"
68 |
69 | #: prefs.js:103
70 | msgid "Temperature/Humidity options:"
71 | msgstr "Optionen für Temperatur/Luftfeuchtigkeit:"
72 |
73 | #: prefs.js:132
74 | msgid "Panel Icon Options:"
75 | msgstr "Panel Icon Optionen:"
76 |
77 | #: prefs.js:149
78 | #, javascript-format
79 | msgid "%s Icon"
80 | msgstr "%s Icon"
81 |
82 | #: prefs.js:157
83 | msgid ""
84 | "You will need to restart your session in order for this change to take "
85 | "effect."
86 | msgstr ""
87 | "Sie müssen sich abmelden und neu anmelden, damit diese Änderung wirksam wird."
88 |
89 | #: prefs.js:158
90 | msgid ""
91 | "On Xorg, you can do that by Alt+F2 and then pressing 'r' and Enter. If this "
92 | "doesn't work (Wayland), you have to logout and re-login."
93 | msgstr ""
94 | "Bei Xorg können Sie das mit Alt+F2 und dann mit 'r' und Enter machen. Wenn "
95 | "das nicht funktioniert (Wayland), müssen Sie sich abmelden und neu anmelden."
96 |
97 | #: prefs.js:282
98 | msgid "Togglable Entities:"
99 | msgstr "Umschaltbare Entitäten:"
100 |
101 | #: prefs.js:287
102 | msgid "Choose Togglable Entities:"
103 | msgstr "Wählen Sie umschaltbare Entitäten:"
104 |
105 | #: prefs.js:310
106 | msgid "Group Switches"
107 | msgstr "Gruppen-Schalter"
108 |
109 | #: prefs.js:317
110 | msgid "Experimental"
111 | msgstr "Experimentell"
112 |
113 | #: prefs.js:318
114 | msgid "Does not currently work."
115 | msgstr "Funktioniert aktuell nicht."
116 |
117 | #: prefs.js:409
118 | msgid "Sensors:"
119 | msgstr "Sensoren:"
120 |
121 | #: prefs.js:414
122 | msgid "Choose Which Sensors Should Appear on the Panel:"
123 | msgstr "Wählen Sie aus, welche Sensoren im Panel angezeigt werden sollen:"
124 |
125 | #: prefs.js:176
126 | msgid "Choose which runnables should appear in the menu:"
127 | msgstr "Wählen Sie aus, welche ausführbare Entitäten im Panel angezeigt werden sollen:"
128 |
129 | #: prefs.js:586
130 | #, javascript-format
131 | msgid "Do you want to have the '%s' icon in your panel?"
132 | msgstr "Möchten Sie das '%s' Icon in Ihrem Panel?"
133 |
134 | #: prefs.js:588
135 | #, javascript-format
136 | msgid "Do you want to show %s in the tray menu?"
137 | msgstr "Möchten Sie anzeigen %s im Panel?"
138 |
139 | #: prefs.js:635
140 | msgid "Add"
141 | msgstr "Hinzufügen"
142 |
143 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:16
144 | msgid "Panel Icon."
145 | msgstr "Panel Icon."
146 |
147 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:17
148 | msgid "The default icon that will be visible in the panel."
149 | msgstr "Das Standard-Icon, das im Panel sichtbar sein wird."
150 |
151 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:22
152 | msgid "Complete list of valid panel icons for the extension."
153 | msgstr "Komplette Liste an möglichen Panel-Icons dieser Erweiterung."
154 |
155 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:23
156 | msgid ""
157 | "The paths have to be relative to the directory where the 'extension.js' file "
158 | "is located."
159 | msgstr ""
160 | "Die Pfade müssen relativ zum Verzeichnis sein, in dem die Datei 'extension."
161 | "js' liegt."
162 |
163 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:28
164 | msgid "Refresh Rate"
165 | msgstr "Aktualisierungsrate"
166 |
167 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:29
168 | msgid ""
169 | "(Currently Broken) The refresh rate for the weather statistics in seconds "
170 | "(works only if \"Refresh Temperature/Humidity Sensors\" is enabled)."
171 | msgstr ""
172 | "(Derzeit nicht verfügbar) Die Aktualisierungsrate für die Wetterstatistiken "
173 | "in Sekunden (funktioniert nur, wenn \"Temperatur-/Luftfeuchtigkeitssensoren "
174 | "aktualisieren\" aktiviert ist)."
175 |
176 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:34
177 | msgid "Refresh Temperature/Humidity Sensors"
178 | msgstr "Temperatur-/Luftfeuchtigkeitssensoren aktualisieren"
179 |
180 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:35
181 | msgid ""
182 | "(Currently Broken) Whether or not you want to refresh the temperature and/or "
183 | "humidity sensor values."
184 | msgstr ""
185 | "(Zurzeit nicht verfügbar) Gibt an, ob Sie die Werte der Temperatur- und/oder "
186 | "Luftfeuchtigkeitssensoren aktualisieren möchten oder nicht."
187 |
188 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:40
189 | msgid "Show notifications"
190 | msgstr "Benachrichtigungen anzeigen"
191 |
192 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:41
193 | msgid "Show notifications when enabled/disabled."
194 | msgstr "Benachrichtigungen anzeigen, wenn aktiviert/deaktiviert."
195 |
196 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:46
197 | msgid "Show Temperature/Humidity"
198 | msgstr "Temperatur/Luftfeuchtigkeit anzeigen"
199 |
200 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:47
201 | msgid ""
202 | "Whether to show the temperature and/or humidity values (assuming you have "
203 | "provided the corresponding entity ids)."
204 | msgstr ""
205 | "Ob die Temperatur- und/oder Luftfeuchtigkeitswerte angezeigt werden sollen "
206 | "(vorausgesetzt, Sie haben die entsprechenden Entity-IDs angegeben)."
207 |
208 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:52
209 | msgid "Show Humidity"
210 | msgstr "Luftfeuchtigkeit anzeigen"
211 |
212 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:53
213 | msgid "Whether to show humidity value or not."
214 | msgstr "Ob der Feuchtigkeitswert angezeigt werden soll oder nicht."
215 |
216 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:58
217 | msgid "Temperature ID"
218 | msgstr "Temperatur-ID"
219 |
220 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:59
221 | msgid "Your Home Assistant EntityID for the temperature sensor."
222 | msgstr "Ihre Home Assistant Entity-ID für den Temperatursensor."
223 |
224 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:64
225 | msgid "Humidity ID"
226 | msgstr "Luftfeuchtigkeits-ID"
227 |
228 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:65
229 | msgid "Your Home Assistant EntityID for the humidity sensor."
230 | msgstr "Ihre Home Assistant Entity-ID für den Feuchtigkeitssensor."
231 |
232 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:70
233 | msgid "Access Token"
234 | msgstr "Zugangs-Token"
235 |
236 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:71
237 | msgid ""
238 | "Long Live Access Token for Home Assistant (Go to Profile->Bottom-of-the-page-"
239 | ">Long-Live-Access-Tokens and create a new one)."
240 | msgstr ""
241 | "Langlebiger Zugangs-Token für Home Assistant (Gehen Sie zu Profil->Unten auf "
242 | "der Seite->Long-Live-Access-Tokens und erstellen Sie einen Neuen)."
243 |
244 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:76
245 | msgid "URL"
246 | msgstr "URL"
247 |
248 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:77
249 | msgid "The url where your hass instance is hosted."
250 | msgstr "Die URL, unter der Ihre Home Assistant-Instanz gehostet wird."
251 |
252 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:82
253 | msgid "Complete list of entity ids for home-assistant switches."
254 | msgstr "Vollständige Liste der Entity-IDs für Home-Assistant Schalter."
255 |
256 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:83
257 | msgid ""
258 | "Full list of home assistant switches. It is meant to be filled automatically "
259 | "after pressing the 'Scan' button in the Preferences menu."
260 | msgstr ""
261 | "Vollständige Liste der Home Assistant-Schalter. Sie sollte automatisch "
262 | "befüllt werden, nachdem Sie die Schaltfläche \"Scannen\" im Menü "
263 | "\"Einstellungen\" betätigt haben."
264 |
265 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:88
266 | msgid "Entity ids for home-assistant switches."
267 | msgstr "Entity-IDs für Home-Assistant-Schalter."
268 |
269 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:89
270 | msgid ""
271 | "Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay."
272 | msgstr ""
273 | "Entity-IDs für Home-Assistant-Schalter. z.B. switch.livingroom_lights_relay."
274 |
275 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:95
276 | msgid ""
277 | "List of entity ids for the sensor values that you want to show in the panel ."
278 | msgstr ""
279 | "Liste der Entity-IDs für die Sensorwerte, die Sie im Panel anzeigen möchten."
280 |
281 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:100
282 | msgid "Entity ids for home assistant sensors."
283 | msgstr "Entity-IDs der Home Assistant-Sensoren."
284 |
285 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:101
286 | msgid ""
287 | "Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature ."
288 | msgstr ""
289 | "Entitäts-IDs für Home Assistant-Sensoren. z.B. sensor."
290 | "livingroom_temperature ."
291 |
292 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:112
293 | msgid "Home Assistant Extension - Shortcut Key"
294 | msgstr "Home Assistant Erweiterung - Tastenkombination"
295 |
296 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:113
297 | msgid "Shortcut key on how to open the extension's menu from the tray."
298 | msgstr "Tastenkombination zum Öffnen des Menüs der Erweiterung über das Panel."
299 |
300 | #~ msgid "' icon in your panel?"
301 | #~ msgstr "' Icon in Ihrem Panel?"
302 |
303 | #~ msgid " in the tray menu?"
304 | #~ msgstr " im Panel?"
305 |
306 | #: prefs.js:195
307 | msgid "No runnables found. Please check your Home-Assistant connection settings."
308 | msgstr "Keine ausführbaren Entitäten gefunden. Bitte überprüfe die Home Assistant Verbindungseinstellungen."
309 |
310 | # utils.js:484
311 | msgid "Error turning on %s"
312 | msgid "Fehler beim Einschalten von %s"
313 |
314 | # utils.js:485
315 | msgid "Error occured trying to turn on %s."
316 | msgstr "Beim Einschalten von %s ist ein Fehler aufgetreten."
--------------------------------------------------------------------------------
/po/es.po:
--------------------------------------------------------------------------------
1 | # Spanish translation for Home Assistant Extension.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # Óscar Fernández Díaz , 2022.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: \n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2023-06-25 09:46+0200\n"
11 | "PO-Revision-Date: 2023-06-25 09:51+0200\n"
12 | "Last-Translator: Benjamin Renard\n"
13 | "Language-Team: \n"
14 | "Language: es\n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19 | "X-Generator: Poedit 3.2.2\n"
20 |
21 | #: extension.js:75
22 | msgid "Toggle:"
23 | msgstr "Conmutar:"
24 |
25 | #: extension.js:93
26 | msgid "HASS Events"
27 | msgstr "Eventos de HASS"
28 |
29 | #: extension.js:95
30 | msgid "Start Home Assistant"
31 | msgstr "Iniciar Home Assistant"
32 |
33 | #: extension.js:96
34 | msgid "Stop Home Assistant"
35 | msgstr "Detener Home Assistant"
36 |
37 | #: extension.js:97
38 | msgid "Close Home Assistant"
39 | msgstr "Cerrar Home Assistant"
40 |
41 | #: extension.js:113
42 | msgid "Preferences"
43 | msgstr "Preferencias"
44 |
45 | #: prefs.js:48
46 | msgid "General Settings"
47 | msgstr "Ajustes generales"
48 |
49 | #: prefs.js:51
50 | msgid "Togglables"
51 | msgstr "Conmutables"
52 |
53 | #: prefs.js:56 schemas/org.gnome.shell.extensions.hass-data.gschema.xml:94
54 | msgid "Panel Sensors"
55 | msgstr "Sensores del panel"
56 |
57 | #: prefs.js:85
58 | msgid "Global options:"
59 | msgstr "Opciones globales:"
60 |
61 | #: prefs.js:103
62 | msgid "Temperature/Humidity options:"
63 | msgstr "Opciones de temperatura/humedad:"
64 |
65 | #: prefs.js:132
66 | msgid "Panel Icon Options:"
67 | msgstr "Opciones de iconos del panel:"
68 |
69 | #: prefs.js:149
70 | #, javascript-format
71 | msgid "%s Icon"
72 | msgstr "Icono %s"
73 |
74 | #: prefs.js:157
75 | msgid ""
76 | "You will need to restart your session in order for this change to take "
77 | "effect."
78 | msgstr "Deberá reiniciar su sesión para que este cambio surta efecto."
79 |
80 | #: prefs.js:158
81 | msgid ""
82 | "On Xorg, you can do that by Alt+F2 and then pressing 'r' and Enter. If this "
83 | "doesn't work (Wayland), you have to logout and re-login."
84 | msgstr ""
85 | "En Xorg, puede hacerlo con Alt+F2 y luego pulsando 'r' y Enter. Si esto no "
86 | "funciona (Wayland), debe cerrar la sesión y volver a iniciarla."
87 |
88 | #: prefs.js:282
89 | msgid "Togglable Entities:"
90 | msgstr "Entidades conmutables:"
91 |
92 | #: prefs.js:287
93 | msgid "Choose Togglable Entities:"
94 | msgstr "Seleccionar entidades conmutables:"
95 |
96 | #: prefs.js:310
97 | msgid "Group Switches"
98 | msgstr "Interruptores de grupo"
99 |
100 | #: prefs.js:317
101 | msgid "Experimental"
102 | msgstr "Experimental"
103 |
104 | #: prefs.js:318
105 | msgid "Does not currently work."
106 | msgstr "No funciona actualmente."
107 |
108 | #: prefs.js:409
109 | msgid "Sensors:"
110 | msgstr "Sensores:"
111 |
112 | #: prefs.js:414
113 | msgid "Choose Which Sensors Should Appear on the Panel:"
114 | msgstr "Seleccione los sensores que deben aparecer en el panel:"
115 |
116 | #: prefs.js:586
117 | #, javascript-format
118 | msgid "Do you want to have the '%s' icon in your panel?"
119 | msgstr "¿Quiere tener el '%s' icono en su panel?"
120 |
121 | #: prefs.js:588
122 | #, javascript-format
123 | msgid "Do you want to show %s in the tray menu?"
124 | msgstr "¿Quiere mostrar %s en la bandeja del menú?"
125 |
126 | #: prefs.js:635
127 | msgid "Add"
128 | msgstr "Añadir"
129 |
130 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:16
131 | msgid "Panel Icon."
132 | msgstr "Icono del panel."
133 |
134 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:17
135 | msgid "The default icon that will be visible in the panel."
136 | msgstr "El icono predeterminado que será visible en el panel."
137 |
138 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:22
139 | msgid "Complete list of valid panel icons for the extension."
140 | msgstr "Lista completa de iconos del panel válidos para la extensión."
141 |
142 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:23
143 | msgid ""
144 | "The paths have to be relative to the directory where the 'extension.js' file "
145 | "is located."
146 | msgstr ""
147 | "Las rutas tienen que ser relativas al directorio donde se encuentra el "
148 | "archivo 'extension.js'."
149 |
150 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:28
151 | msgid "Refresh Rate"
152 | msgstr "Tasa de refresco"
153 |
154 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:29
155 | msgid ""
156 | "(Currently Broken) The refresh rate for the weather statistics in seconds "
157 | "(works only if \"Refresh Temperature/Humidity Sensors\" is enabled)."
158 | msgstr ""
159 | "(Actualmente roto) La frecuencia de actualización de las estadísticas "
160 | "meteorológicas en segundos (sólo funciona si está activada la opción "
161 | "\"Actualizar sensores de temperatura/humedad\")."
162 |
163 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:34
164 | msgid "Refresh Temperature/Humidity Sensors"
165 | msgstr "Actualizar los sensores de temperatura/humedad"
166 |
167 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:35
168 | msgid ""
169 | "(Currently Broken) Whether or not you want to refresh the temperature and/or "
170 | "humidity sensor values."
171 | msgstr ""
172 | "(Actualmente roto) Si desea o no refrescar los valores del sensor de "
173 | "temperatura y/o humedad."
174 |
175 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:40
176 | msgid "Show notifications"
177 | msgstr "Mostrar notificaciones"
178 |
179 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:41
180 | msgid "Show notifications when enabled/disabled."
181 | msgstr "Mostrar notificaciones cuando se activa/desactiva."
182 |
183 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:46
184 | msgid "Show Temperature/Humidity"
185 | msgstr "Mostrar temperatura/humedad"
186 |
187 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:47
188 | msgid ""
189 | "Whether to show the temperature and/or humidity values (assuming you have "
190 | "provided the corresponding entity ids)."
191 | msgstr ""
192 | "Si se muestran los valores de temperatura y/o humedad (suponiendo que se han "
193 | "proporcionado los identificadores de entidad correspondientes)."
194 |
195 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:52
196 | msgid "Show Humidity"
197 | msgstr "Mostrar humedad"
198 |
199 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:53
200 | msgid "Whether to show humidity value or not."
201 | msgstr "Mostrar o no el valor de la humedad."
202 |
203 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:58
204 | msgid "Temperature ID"
205 | msgstr "Identificador de temperatura"
206 |
207 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:59
208 | msgid "Your Home Assistant EntityID for the temperature sensor."
209 | msgstr "El EntityID de Home Assistant para el sensor de temperatura."
210 |
211 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:64
212 | msgid "Humidity ID"
213 | msgstr "Identificador de humedad"
214 |
215 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:65
216 | msgid "Your Home Assistant EntityID for the humidity sensor."
217 | msgstr "El EntityID de Home Assistant para el sensor de humedad."
218 |
219 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:70
220 | msgid "Access Token"
221 | msgstr "Testigo de acceso"
222 |
223 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:71
224 | msgid ""
225 | "Long Live Access Token for Home Assistant (Go to Profile->Bottom-of-the-page-"
226 | ">Long-Live-Access-Tokens and create a new one)."
227 | msgstr ""
228 | "Testigo de acceso de larga duración para Home Assistant (Vaya a Perfil-"
229 | ">Fondo de la página->Testigos de acceso de larga duración y cree uno nuevo)."
230 |
231 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:76
232 | msgid "URL"
233 | msgstr "URL"
234 |
235 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:77
236 | msgid "The url where your hass instance is hosted."
237 | msgstr "La url donde está alojada su instancia de hass."
238 |
239 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:82
240 | msgid "Complete list of entity ids for home-assistant switches."
241 | msgstr ""
242 | "Lista completa de identificadores de entidad para los interruptores de "
243 | "asistencia domiciliaria."
244 |
245 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:83
246 | msgid ""
247 | "Full list of home assistant switches. It is meant to be filled automatically "
248 | "after pressing the 'Scan' button in the Preferences menu."
249 | msgstr ""
250 | "Lista completa de interruptores del asistente doméstico. Está pensada para "
251 | "que se rellene automáticamente tras pulsar el botón \"Escanear\" en el menú "
252 | "de Preferencias."
253 |
254 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:88
255 | msgid "Entity ids for home-assistant switches."
256 | msgstr "Identificadores de entidad para los interruptores de Home Assistant."
257 |
258 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:89
259 | msgid ""
260 | "Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay."
261 | msgstr ""
262 | "Identificadores de entidad para los interruptores de asistencia al hogar. "
263 | "Por ejemplo, switch.livingroom_lights_relay."
264 |
265 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:95
266 | msgid ""
267 | "List of entity ids for the sensor values that you want to show in the panel ."
268 | msgstr ""
269 | "Lista de identificadores de entidades para los valores de los sensores que "
270 | "desea mostrar en el panel."
271 |
272 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:100
273 | msgid "Entity ids for home assistant sensors."
274 | msgstr "Identificadores de entidad para los sensores de Home Assistant."
275 |
276 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:101
277 | msgid ""
278 | "Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature ."
279 | msgstr ""
280 | "Identificadores de entidad para los sensores de asistencia al hogar. Por "
281 | "ejemplo, sensor.livingroom_temperature ."
282 |
283 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:112
284 | msgid "Home Assistant Extension - Shortcut Key"
285 | msgstr "Home Assistant Extension - Atajo del teclado"
286 |
287 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:113
288 | msgid "Shortcut key on how to open the extension's menu from the tray."
289 | msgstr "Atajo para abrir el menú de la extensión desde la bandeja."
290 |
291 | #~ msgid "' icon in your panel?"
292 | #~ msgstr "' icono en su panel?"
293 |
294 | #~ msgid " in the tray menu?"
295 | #~ msgstr " en la bandeja del menú?"
296 |
--------------------------------------------------------------------------------
/po/fr.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/po/fr.mo
--------------------------------------------------------------------------------
/po/fr.po:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | msgid ""
7 | msgstr ""
8 | "Project-Id-Version: \n"
9 | "Report-Msgid-Bugs-To: \n"
10 | "POT-Creation-Date: 2023-07-06 14:35+0200\n"
11 | "PO-Revision-Date: 2023-07-06 14:36+0200\n"
12 | "Last-Translator: Benjamin Renard \n"
13 | "Language-Team: \n"
14 | "Language: fr_FR\n"
15 | "MIME-Version: 1.0\n"
16 | "Content-Type: text/plain; charset=UTF-8\n"
17 | "Content-Transfer-Encoding: 8bit\n"
18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n"
19 | "X-Generator: Poedit 3.2.2\n"
20 |
21 | #: extension.js:230
22 | msgid "HASS Events"
23 | msgstr "Évènements HASS"
24 |
25 | #: extension.js:234
26 | msgid "Start Home Assistant"
27 | msgstr "Démarrer Home Assistant"
28 |
29 | #: extension.js:238
30 | msgid "Stop Home Assistant"
31 | msgstr "Arrêter Home Assistant"
32 |
33 | #: extension.js:242
34 | msgid "Close Home Assistant"
35 | msgstr "Fermer Home Assistant"
36 |
37 | #: extension.js:247
38 | msgid "Refresh"
39 | msgstr "Rafraîchir"
40 |
41 | #: extension.js:258
42 | msgid "Preferences"
43 | msgstr "Préférences"
44 |
45 | #: extension.js:289
46 | msgid "Toggle:"
47 | msgstr "Allumer/Éteindre :"
48 |
49 | #: prefs.js:37 prefs.js:41
50 | msgid "General Settings"
51 | msgstr "Paramètres générals"
52 |
53 | #: prefs.js:49 schemas/org.gnome.shell.extensions.hass-data.gschema.xml:34
54 | msgid "Refresh sensors"
55 | msgstr "Rafraîchir les capteurs"
56 |
57 | #: prefs.js:55
58 | msgid "Panel Icon Options:"
59 | msgstr "Options de l'icône de la barre des tâches :"
60 |
61 | #: prefs.js:79
62 | msgid "Togglables"
63 | msgstr "Interrupteurs"
64 |
65 | #: prefs.js:83
66 | msgid "Choose which togglables should appear in the menu:"
67 | msgstr "Sélectionner les interrupteurs qui apparaîtront dans le menu :"
68 |
69 | #: prefs.js:102
70 | msgid ""
71 | "No togglable found. Please check your Home-Assistant connection settings."
72 | msgstr ""
73 | "Aucun interrupteur trouvé. Merci de vérifier vos paramètres de connexion à "
74 | "Home-Assistant."
75 |
76 | #: prefs.js:167
77 | msgid "Sensors"
78 | msgstr "Capteurs"
79 |
80 | #: prefs.js:171
81 | msgid "Choose which sensors should appear on the panel:"
82 | msgstr "Sélectionner les capteurs qui apparaîtront dans la barre des tâches :"
83 |
84 | #: prefs.js:190
85 | msgid "No sensor found. Please check your Home-Assistant connection settings."
86 | msgstr ""
87 | "Aucun capteur trouvé. Merci de vérifier vos paramètres de connexion à Home-"
88 | "Assistant."
89 |
90 | #: prefs.js:290
91 | msgid "Access Token"
92 | msgstr "Jeton d'accès"
93 |
94 | #: utils.js:303 utils.js:325
95 | #, javascript-format
96 | msgid "Error toggling %s"
97 | msgstr "Erreur en allumant/éteignant %s"
98 |
99 | #: utils.js:304
100 | #, javascript-format
101 | msgid "Error occured trying to toggle %s: entity not found."
102 | msgstr "Une erreur est survenue en allumant/éteignant %s : entité non-trouvée."
103 |
104 | #: utils.js:309
105 | #, javascript-format
106 | msgid "%s toggled on"
107 | msgstr "%s allumé"
108 |
109 | #: utils.js:310
110 | #, javascript-format
111 | msgid "%s successfully toggled on."
112 | msgstr "%s a bien été allumé."
113 |
114 | #: utils.js:314
115 | #, javascript-format
116 | msgid "%s toggled off"
117 | msgstr "%s éteint"
118 |
119 | #: utils.js:315
120 | #, javascript-format
121 | msgid "%s successfully toggled off."
122 | msgstr "%s a bien été éteint."
123 |
124 | #: utils.js:319
125 | #, javascript-format
126 | msgid "%s toggled"
127 | msgstr "%s allumé/éteint"
128 |
129 | #: utils.js:320
130 | #, javascript-format
131 | msgid "%s successfully toggled."
132 | msgstr "%s a bien été allumé ou éteint."
133 |
134 | #: utils.js:326
135 | #, javascript-format
136 | msgid "Error occured trying to toggle %s."
137 | msgstr "Une erreur est survenue en allumant/éteignant %s."
138 |
139 | #: utils.js:344
140 | msgid "Home-Assistant start event triggered"
141 | msgstr "Démarrage d'Home-Assistant déclenché"
142 |
143 | #: utils.js:345
144 | msgid "Home-Assistant start event successfully triggered."
145 | msgstr "Le démarrage d'Home-Assistant a bien été déclenché."
146 |
147 | #: utils.js:349
148 | msgid "Home-Assistant stop event triggered"
149 | msgstr "Arrêt d'Home-Assistant déclenché"
150 |
151 | #: utils.js:350
152 | msgid "Home-Assistant stop event successfully triggered."
153 | msgstr "L'arrêt d'Home-Assistant a bien été déclenché."
154 |
155 | #: utils.js:354
156 | msgid "Home-Assistant close event triggered"
157 | msgstr "Fermeture d'Home-Assistant déclenché"
158 |
159 | #: utils.js:355
160 | msgid "Home-Assistant close event successfully triggered."
161 | msgstr "La fermeture d'Home-Assistant a bien été déclenchée."
162 |
163 | #: utils.js:359
164 | msgid "Home-Assistant event triggered"
165 | msgstr "Évènement Home-Assistant déclenché"
166 |
167 | #: utils.js:360
168 | msgid "Home-Assistant event successfully triggered."
169 | msgstr "L'évènement Home-Assistant a bien été déclenché."
170 |
171 | #: utils.js:366
172 | msgid "Error triggering Home-Assistant start event"
173 | msgstr "Erreur en déclenchant le démarrage d'Home-Assistant"
174 |
175 | #: utils.js:367
176 | msgid "Error occured triggering Home-Assistant start event."
177 | msgstr "Une erreur est survenue en déclenchant le démarrage d'Home-Assistant."
178 |
179 | #: utils.js:371
180 | msgid "Error triggering Home-Assistant stop event"
181 | msgstr "Erreur en déclenchant l'arrêt d'Home-Assistant"
182 |
183 | #: utils.js:372
184 | msgid "Error occured triggering Home-Assistant stop event."
185 | msgstr "Une erreur est survenue en déclenchant l'arrêt d'Home-Assistant."
186 |
187 | #: utils.js:376
188 | msgid "Error triggering Home-Assistant close event"
189 | msgstr "Erreur en déclenchant la fermeture d'Home-Assistant"
190 |
191 | #: utils.js:377
192 | msgid "Error occured triggering Home-Assistant close event."
193 | msgstr "Une erreur est survenue en déclenchant la fermeture d'Home-Assistant."
194 |
195 | #: utils.js:381
196 | msgid "Error triggering Home-Assistant event"
197 | msgstr "Erreur en déclenchant l'évènement Home-Assistant"
198 |
199 | #: utils.js:382
200 | msgid "Error occured triggering Home-Assistant event."
201 | msgstr "Une erreur est survenue en déclenchant l'évènement Home-Assistant."
202 |
203 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:16
204 | msgid "Panel Icon."
205 | msgstr "Icône de la barre des tâches."
206 |
207 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:17
208 | msgid "The default icon that will be visible in the panel."
209 | msgstr "L'icône par défaut qui sera visible dans la barre des tâches."
210 |
211 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:22
212 | msgid "Complete list of valid panel icons for the extension."
213 | msgstr ""
214 | "Liste complète des icônes de la barre des tâches valides pour l'extension."
215 |
216 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:23
217 | msgid ""
218 | "The paths have to be relative to the directory where the 'extension.js' file "
219 | "is located."
220 | msgstr ""
221 | "Les chemins doivent être relatifs au dossier contenant le fichier "
222 | "\"extention.js\"."
223 |
224 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:28
225 | msgid "Refresh Rate"
226 | msgstr "Fréquence de rafraîchissement"
227 |
228 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:29
229 | msgid ""
230 | "The refresh rate for the weather statistics in seconds (works only if "
231 | "\"Refresh Temperature/Humidity Sensors\" is enabled)."
232 | msgstr ""
233 | "Le taux de rafraîchissement des statistiques météo en secondes (fonctionne "
234 | "uniquement si \"Rafraîchir les capteurs de température/humidité\" est "
235 | "activé)."
236 |
237 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:35
238 | msgid ""
239 | "Whether or not you want to refresh the temperature and/or humidity sensor "
240 | "values."
241 | msgstr ""
242 | "Si vous souhaitez ou non actualiser les valeurs des capteurs de température "
243 | "et/ou d'humidité."
244 |
245 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:40
246 | msgid "Show notifications"
247 | msgstr "Afficher les notifications"
248 |
249 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:41
250 | msgid "Show notifications when enable/disable togglables or on HASS events."
251 | msgstr ""
252 | "Voir les notifications lorsqu'on allume/éteint les interrupteurs ou lors "
253 | "d'évènements HASS."
254 |
255 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:46
256 | msgid "URL"
257 | msgstr "URL"
258 |
259 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:47
260 | msgid "The url where your hass instance is hosted."
261 | msgstr "L'url où votre instance hass est hébergée."
262 |
263 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:52
264 | msgid "Home-Assistant entities's state cache."
265 | msgstr "Cache de l'état des entités d'Home-Assistant."
266 |
267 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:53
268 | msgid ""
269 | "Home-Assistant entities's state cache. It is meant to be filled "
270 | "automatically on start-up or after pressing the 'Refresh' button in the "
271 | "Preferences menu."
272 | msgstr ""
273 | "Cache de l'état des entités d'Home-Assistant. Elle est censé être remplie "
274 | "automatiquement au démarrage ou après avoir appuyé sur le bouton "
275 | "'Rafraîchir' dans le menu Préférences."
276 |
277 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:58
278 | msgid "Entity ids for home-assistant switches."
279 | msgstr "ID d'entité des interrupteurs d'Home Assistant."
280 |
281 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:59
282 | msgid ""
283 | "Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay."
284 | msgstr ""
285 | "ID d'entité des interrupteurs d'Home Assistant. Ex : switch."
286 | "livingroom_lights_relay."
287 |
288 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:64
289 | msgid "Entity ids for home assistant sensors."
290 | msgstr "ID d'entité des capteurs d'Home Assistant."
291 |
292 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:65
293 | msgid ""
294 | "Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature."
295 | msgstr ""
296 | "ID d'entité des capteurs d'Home Assistant.. Ex : sensor."
297 | "livingroom_temperature."
298 |
299 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:70
300 | msgid "Enable debug mode"
301 | msgstr "Activer le mode debug"
302 |
303 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:71
304 | msgid ""
305 | "When this mode is enabled, more logged messages could be seen in the journal."
306 | msgstr ""
307 | "Lorsque ce mode est activé, plus de messages sont visibles dans le journal."
308 |
309 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:82
310 | msgid "Home Assistant Extension - Shortcut Key"
311 | msgstr "Extension Home Assistant - Raccourci clavier"
312 |
313 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:83
314 | msgid "Shortcut key on how to open the extension's menu from the tray."
315 | msgstr ""
316 | "Raccourci clavier pour ouvrir le menu de l'extension à partir de la zone de "
317 | "notification."
318 |
319 | #~ msgid "Choose Togglable Entities:"
320 | #~ msgstr "Sélectionner les entités interrupteurs :"
321 |
322 | #~ msgid "Refreshing sensors"
323 | #~ msgstr "Rafraîchir les capteurs"
324 |
325 | #~ msgid "Panel Sensors"
326 | #~ msgstr "Capteurs de la barre des tâches"
327 |
328 | #~ msgid "Global options:"
329 | #~ msgstr "Options générales :"
330 |
331 | #~ msgid "Temperature/Humidity options:"
332 | #~ msgstr "Options de température/humidité :"
333 |
334 | #, javascript-format
335 | #~ msgid "%s Icon"
336 | #~ msgstr "Icône %s"
337 |
338 | #~ msgid "Togglable Entities:"
339 | #~ msgstr "Entités interrupteurs :"
340 |
341 | #~ msgid "Sensors:"
342 | #~ msgstr "Capteurs :"
343 |
344 | #~ msgid ""
345 | #~ "Long Live Access Token for Home Assistant (Go to Profile->Bottom-of-the-"
346 | #~ "page->Long-Live-Access-Tokens and create a new one)."
347 | #~ msgstr ""
348 | #~ "Jeton d'accès de longue durée pour Home Assistant (Allez dans Profil-> "
349 | #~ "Bas de la page-> Jetons d'accès de longue durée et créez-en un nouveau)."
350 |
351 | #, javascript-format
352 | #~ msgid "Do you want to have the '%s' icon in your panel?"
353 | #~ msgstr "Avez-vous l'icône \"%s\" dans la barre des tâches ?"
354 |
355 | #, javascript-format
356 | #~ msgid "Do you want to show %s in the tray menu?"
357 | #~ msgstr "Voulez-vous afficher %s dans la zone de notification ?"
358 |
359 | #~ msgid "Apply"
360 | #~ msgstr "Appliquer"
361 |
362 | #~ msgid "Refresh Temperature/Humidity Sensors"
363 | #~ msgstr "Rafraîchir les capteurs de température/humidité"
364 |
365 | #~ msgid "Show Temperature/Humidity"
366 | #~ msgstr "Afficher la température/l'humidité"
367 |
368 | #~ msgid ""
369 | #~ "Whether to show the temperature and/or humidity values (assuming you have "
370 | #~ "provided the corresponding entity ids)."
371 | #~ msgstr ""
372 | #~ "Indique s'il faut afficher les valeurs de température et/ou d'humidité "
373 | #~ "(en supposant que vous avez fourni les identifiants d'entité "
374 | #~ "correspondants)."
375 |
376 | #~ msgid "Show Humidity"
377 | #~ msgstr "Afficher l'humidité"
378 |
379 | #~ msgid "Whether to show humidity value or not."
380 | #~ msgstr "Afficher ou non la valeur d'humidité."
381 |
382 | #~ msgid "Temperature ID"
383 | #~ msgstr "Identifiant température"
384 |
385 | #~ msgid "Your Home Assistant EntityID for the temperature sensor."
386 | #~ msgstr ""
387 | #~ "Votre identifiant d'entité Home Assistant pour le capteur de température."
388 |
389 | #~ msgid "Humidity ID"
390 | #~ msgstr "Identifiant humidité"
391 |
392 | #~ msgid "Your Home Assistant EntityID for the humidity sensor."
393 | #~ msgstr ""
394 | #~ "Votre identifiant d'entité Home Assistant pour le capteur d'humidité."
395 |
396 | #~ msgid "Show notifications when enabled/disabled."
397 | #~ msgstr "Afficher les notifications lorsqu'elles sont activées/désactivées."
398 |
399 | #~ msgid ""
400 | #~ "You will need to restart your session in order for this change to take "
401 | #~ "effect."
402 | #~ msgstr ""
403 | #~ "Vous devrez redémarrer votre sessions pour que ce changement soit "
404 | #~ "effectif."
405 |
406 | #~ msgid ""
407 | #~ "On Xorg, you can do that by Alt+F2 and then pressing 'r' and Enter. If "
408 | #~ "this doesn't work (Wayland), you have to logout and re-login."
409 | #~ msgstr ""
410 | #~ "Sur Xorg, vous pouvez faire cela avec Alt+F2 puis taper \"r\" et Entrer. "
411 | #~ "Si cela ne fonctionne pas (Wayland), vous devez vous fermer votre session "
412 | #~ "puis la ré-ouvrir."
413 |
414 | #~ msgid "Add"
415 | #~ msgstr "Ajouter"
416 |
417 | #~ msgid "Group Switches"
418 | #~ msgstr "Groupes d'interrupteurs"
419 |
420 | #~ msgid "Experimental"
421 | #~ msgstr "Expérimental"
422 |
423 | #~ msgid "Does not currently work."
424 | #~ msgstr "Ne fonctionne pas actuellement."
425 |
426 | #~ msgid "Complete list of entity ids for home-assistant switches."
427 | #~ msgstr ""
428 | #~ "Liste complète des identifiants d'entité des interrupteurs d'Home "
429 | #~ "Assistant."
430 |
431 | #~ msgid ""
432 | #~ "List of entity ids for the sensor values that you want to show in the "
433 | #~ "panel ."
434 | #~ msgstr ""
435 | #~ "Liste des ID d'entité des capteurs dont vous souhaitez afficher la valeur "
436 | #~ "dans la barre des tâches."
437 |
438 | #~ msgid " Icon"
439 | #~ msgstr " Icône"
440 |
441 | #~ msgid "' icon in your panel?"
442 | #~ msgstr "\" dans la barre des tâches ?"
443 |
444 | #~ msgid " in the tray menu?"
445 | #~ msgstr " dans la barre des tâches ?"
446 |
--------------------------------------------------------------------------------
/po/hass-gshell.pot:
--------------------------------------------------------------------------------
1 | # SOME DESCRIPTIVE TITLE.
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # FIRST AUTHOR , YEAR.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: PACKAGE VERSION\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2023-07-06 14:35+0200\n"
12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "Language: \n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=CHARSET\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 |
20 | #: extension.js:230
21 | msgid "HASS Events"
22 | msgstr ""
23 |
24 | #: extension.js:234
25 | msgid "Start Home Assistant"
26 | msgstr ""
27 |
28 | #: extension.js:238
29 | msgid "Stop Home Assistant"
30 | msgstr ""
31 |
32 | #: extension.js:242
33 | msgid "Close Home Assistant"
34 | msgstr ""
35 |
36 | #: extension.js:247
37 | msgid "Refresh"
38 | msgstr ""
39 |
40 | #: extension.js:258
41 | msgid "Preferences"
42 | msgstr ""
43 |
44 | #: extension.js:289
45 | msgid "Toggle:"
46 | msgstr ""
47 |
48 | #: prefs.js:37 prefs.js:41
49 | msgid "General Settings"
50 | msgstr ""
51 |
52 | #: prefs.js:49 schemas/org.gnome.shell.extensions.hass-data.gschema.xml:34
53 | msgid "Refresh sensors"
54 | msgstr ""
55 |
56 | #: prefs.js:55
57 | msgid "Panel Icon Options:"
58 | msgstr ""
59 |
60 | #: prefs.js:79
61 | msgid "Togglables"
62 | msgstr ""
63 |
64 | #: prefs.js:83
65 | msgid "Choose which togglables should appear in the menu:"
66 | msgstr ""
67 |
68 | #: prefs.js:102
69 | msgid ""
70 | "No togglable found. Please check your Home-Assistant connection settings."
71 | msgstr ""
72 |
73 | #: prefs.js:167
74 | msgid "Sensors"
75 | msgstr ""
76 |
77 | #: prefs.js:171
78 | msgid "Choose which sensors should appear on the panel:"
79 | msgstr ""
80 |
81 | #: prefs.js:190
82 | msgid "No sensor found. Please check your Home-Assistant connection settings."
83 | msgstr ""
84 |
85 | #: prefs.js:290
86 | msgid "Access Token"
87 | msgstr ""
88 |
89 | #: utils.js:303 utils.js:325
90 | #, javascript-format
91 | msgid "Error toggling %s"
92 | msgstr ""
93 |
94 | #: utils.js:304
95 | #, javascript-format
96 | msgid "Error occured trying to toggle %s: entity not found."
97 | msgstr ""
98 |
99 | #: utils.js:309
100 | #, javascript-format
101 | msgid "%s toggled on"
102 | msgstr ""
103 |
104 | #: utils.js:310
105 | #, javascript-format
106 | msgid "%s successfully toggled on."
107 | msgstr ""
108 |
109 | #: utils.js:314
110 | #, javascript-format
111 | msgid "%s toggled off"
112 | msgstr ""
113 |
114 | #: utils.js:315
115 | #, javascript-format
116 | msgid "%s successfully toggled off."
117 | msgstr ""
118 |
119 | #: utils.js:319
120 | #, javascript-format
121 | msgid "%s toggled"
122 | msgstr ""
123 |
124 | #: utils.js:320
125 | #, javascript-format
126 | msgid "%s successfully toggled."
127 | msgstr ""
128 |
129 | #: utils.js:326
130 | #, javascript-format
131 | msgid "Error occured trying to toggle %s."
132 | msgstr ""
133 |
134 | #: utils.js:344
135 | msgid "Home-Assistant start event triggered"
136 | msgstr ""
137 |
138 | #: utils.js:345
139 | msgid "Home-Assistant start event successfully triggered."
140 | msgstr ""
141 |
142 | #: utils.js:349
143 | msgid "Home-Assistant stop event triggered"
144 | msgstr ""
145 |
146 | #: utils.js:350
147 | msgid "Home-Assistant stop event successfully triggered."
148 | msgstr ""
149 |
150 | #: utils.js:354
151 | msgid "Home-Assistant close event triggered"
152 | msgstr ""
153 |
154 | #: utils.js:355
155 | msgid "Home-Assistant close event successfully triggered."
156 | msgstr ""
157 |
158 | #: utils.js:359
159 | msgid "Home-Assistant event triggered"
160 | msgstr ""
161 |
162 | #: utils.js:360
163 | msgid "Home-Assistant event successfully triggered."
164 | msgstr ""
165 |
166 | #: utils.js:366
167 | msgid "Error triggering Home-Assistant start event"
168 | msgstr ""
169 |
170 | #: utils.js:367
171 | msgid "Error occured triggering Home-Assistant start event."
172 | msgstr ""
173 |
174 | #: utils.js:371
175 | msgid "Error triggering Home-Assistant stop event"
176 | msgstr ""
177 |
178 | #: utils.js:372
179 | msgid "Error occured triggering Home-Assistant stop event."
180 | msgstr ""
181 |
182 | #: utils.js:376
183 | msgid "Error triggering Home-Assistant close event"
184 | msgstr ""
185 |
186 | #: utils.js:377
187 | msgid "Error occured triggering Home-Assistant close event."
188 | msgstr ""
189 |
190 | #: utils.js:381
191 | msgid "Error triggering Home-Assistant event"
192 | msgstr ""
193 |
194 | #: utils.js:382
195 | msgid "Error occured triggering Home-Assistant event."
196 | msgstr ""
197 |
198 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:16
199 | msgid "Panel Icon."
200 | msgstr ""
201 |
202 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:17
203 | msgid "The default icon that will be visible in the panel."
204 | msgstr ""
205 |
206 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:22
207 | msgid "Complete list of valid panel icons for the extension."
208 | msgstr ""
209 |
210 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:23
211 | msgid ""
212 | "The paths have to be relative to the directory where the 'extension.js' file "
213 | "is located."
214 | msgstr ""
215 |
216 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:28
217 | msgid "Refresh Rate"
218 | msgstr ""
219 |
220 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:29
221 | msgid ""
222 | "The refresh rate for the weather statistics in seconds (works only if "
223 | "\"Refresh Temperature/Humidity Sensors\" is enabled)."
224 | msgstr ""
225 |
226 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:35
227 | msgid ""
228 | "Whether or not you want to refresh the temperature and/or humidity sensor "
229 | "values."
230 | msgstr ""
231 |
232 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:40
233 | msgid "Show notifications"
234 | msgstr ""
235 |
236 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:41
237 | msgid "Show notifications when enable/disable togglables or on HASS events."
238 | msgstr ""
239 |
240 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:46
241 | msgid "URL"
242 | msgstr ""
243 |
244 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:47
245 | msgid "The url where your hass instance is hosted."
246 | msgstr ""
247 |
248 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:52
249 | msgid "Home-Assistant entities's state cache."
250 | msgstr ""
251 |
252 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:53
253 | msgid ""
254 | "Home-Assistant entities's state cache. It is meant to be filled "
255 | "automatically on start-up or after pressing the 'Refresh' button in the "
256 | "Preferences menu."
257 | msgstr ""
258 |
259 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:58
260 | msgid "Entity ids for home-assistant switches."
261 | msgstr ""
262 |
263 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:59
264 | msgid ""
265 | "Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay."
266 | msgstr ""
267 |
268 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:64
269 | msgid "Entity ids for home assistant sensors."
270 | msgstr ""
271 |
272 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:65
273 | msgid ""
274 | "Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature."
275 | msgstr ""
276 |
277 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:70
278 | msgid "Enable debug mode"
279 | msgstr ""
280 |
281 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:71
282 | msgid ""
283 | "When this mode is enabled, more logged messages could be seen in the journal."
284 | msgstr ""
285 |
286 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:82
287 | msgid "Home Assistant Extension - Shortcut Key"
288 | msgstr ""
289 |
290 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:83
291 | msgid "Shortcut key on how to open the extension's menu from the tray."
292 | msgstr ""
293 |
--------------------------------------------------------------------------------
/po/sk.po:
--------------------------------------------------------------------------------
1 | # Slovak translation for Home Assistant Gnome Extension
2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3 | # This file is distributed under the same license as the PACKAGE package.
4 | # Jozef Gaal , 2024.
5 | #
6 | #, fuzzy
7 | msgid ""
8 | msgstr ""
9 | "Project-Id-Version: Home Assistant Gnome Extension v25 (G47)\n"
10 | "Report-Msgid-Bugs-To: \n"
11 | "POT-Creation-Date: 2023-07-06 14:35+0200\n"
12 | "PO-Revision-Date: 2024-11-02 00:08+0100\n"
13 | "Last-Translator: Jozef Gaal \n"
14 | "Language-Team: Jozef Gaal \n"
15 | "Language: sk_SK\n"
16 | "MIME-Version: 1.0\n"
17 | "Content-Type: text/plain; charset=UTF-8\n"
18 | "Content-Transfer-Encoding: 8bit\n"
19 | "X-Generator: Poedit 3.4.2\n"
20 |
21 | #: extension.js:230
22 | msgid "HASS Events"
23 | msgstr "HASS udalosti"
24 |
25 | #: extension.js:234
26 | msgid "Start Home Assistant"
27 | msgstr "Spustiť Home Assistant"
28 |
29 | #: extension.js:238
30 | msgid "Stop Home Assistant"
31 | msgstr "Zastaviť Home Assistant"
32 |
33 | #: extension.js:242
34 | msgid "Close Home Assistant"
35 | msgstr "Zavrieť Home Assistant"
36 |
37 | #: extension.js:247
38 | msgid "Refresh"
39 | msgstr "Obnoviť"
40 |
41 | #: extension.js:258
42 | msgid "Preferences"
43 | msgstr "Predvoľby"
44 |
45 | #: extension.js:289
46 | msgid "Toggle:"
47 | msgstr "Prepnúť:"
48 |
49 | #: prefs.js:37 prefs.js:41
50 | msgid "General Settings"
51 | msgstr "Všeobecné nastavenia"
52 |
53 | #: prefs.js:49 schemas/org.gnome.shell.extensions.hass-data.gschema.xml:34
54 | msgid "Refresh sensors"
55 | msgstr "Obnovovať senzory"
56 |
57 | #: prefs.js:55
58 | msgid "Panel Icon Options:"
59 | msgstr "Možnosti ikony panela:"
60 |
61 | #: prefs.js:79
62 | msgid "Togglables"
63 | msgstr "Prepínače"
64 |
65 | #: prefs.js:83
66 | msgid "Choose which togglables should appear in the menu:"
67 | msgstr "Vyberte, ktoré prepínače sa majú zobraziť v ponuke:"
68 |
69 | #: prefs.js:102
70 | msgid ""
71 | "No togglable found. Please check your Home-Assistant connection settings."
72 | msgstr ""
73 | "Nenašiel sa žiadny prepínač. Skontrolujte nastavenia pripojenia Home-"
74 | "Assistant."
75 |
76 | #: prefs.js:167
77 | msgid "Sensors"
78 | msgstr "Senzory"
79 |
80 | #: prefs.js:171
81 | msgid "Choose which sensors should appear on the panel:"
82 | msgstr "Vyberte, ktoré senzory sa majú zobraziť na paneli:"
83 |
84 | #: prefs.js:190
85 | msgid "No sensor found. Please check your Home-Assistant connection settings."
86 | msgstr ""
87 | "Nenašiel sa žiadny senzor. Skontrolujte nastavenia pripojenia Home-Assistant."
88 |
89 | #: prefs.js:290
90 | msgid "Access Token"
91 | msgstr "Prístupový token"
92 |
93 | #: utils.js:303 utils.js:325
94 | #, javascript-format
95 | msgid "Error toggling %s"
96 | msgstr "Chyba pri prepínaní %s"
97 |
98 | #: utils.js:304
99 | #, javascript-format
100 | msgid "Error occured trying to toggle %s: entity not found."
101 | msgstr "Pri pokuse o prepnutie %s došlo k chybe: entita nebola nájdená."
102 |
103 | #: utils.js:309
104 | #, javascript-format
105 | msgid "%s toggled on"
106 | msgstr "%s zapnuté"
107 |
108 | #: utils.js:310
109 | #, javascript-format
110 | msgid "%s successfully toggled on."
111 | msgstr "%s bolo úspešne zapnuté."
112 |
113 | #: utils.js:314
114 | #, javascript-format
115 | msgid "%s toggled off"
116 | msgstr "%s vypnuté"
117 |
118 | #: utils.js:315
119 | #, javascript-format
120 | msgid "%s successfully toggled off."
121 | msgstr "%s bolo úspešne vypnuté."
122 |
123 | #: utils.js:319
124 | #, javascript-format
125 | msgid "%s toggled"
126 | msgstr "%s prepnuté"
127 |
128 | #: utils.js:320
129 | #, javascript-format
130 | msgid "%s successfully toggled."
131 | msgstr "%s bolo úspešne prepnuté."
132 |
133 | #: utils.js:326
134 | #, javascript-format
135 | msgid "Error occured trying to toggle %s."
136 | msgstr "Pri pokuse o prepnutie %s došlo k chybe."
137 |
138 | #: utils.js:344
139 | msgid "Home-Assistant start event triggered"
140 | msgstr "Udalosť spustenia Home-Assistant bola spustená"
141 |
142 | #: utils.js:345
143 | msgid "Home-Assistant start event successfully triggered."
144 | msgstr "Udalosť spustenia Home-Assistant bola úspešne spustená."
145 |
146 | #: utils.js:349
147 | msgid "Home-Assistant stop event triggered"
148 | msgstr "Udalosť zastavenia Home-Assistant bola spustená"
149 |
150 | #: utils.js:350
151 | msgid "Home-Assistant stop event successfully triggered."
152 | msgstr "Udalosť zastavenia Home-Assistant bola úspešne spustená."
153 |
154 | #: utils.js:354
155 | msgid "Home-Assistant close event triggered"
156 | msgstr "Udalosť zatvorenia Home-Assistant bola spustená"
157 |
158 | #: utils.js:355
159 | msgid "Home-Assistant close event successfully triggered."
160 | msgstr "Udalosť zatvorenia Home-Assistant bola úspešne spustená."
161 |
162 | #: utils.js:359
163 | msgid "Home-Assistant event triggered"
164 | msgstr "Udalosť Home-Assistant bola spustená"
165 |
166 | #: utils.js:360
167 | msgid "Home-Assistant event successfully triggered."
168 | msgstr "Udalosť Home-Assistant bola úspešne spustená."
169 |
170 | #: utils.js:366
171 | msgid "Error triggering Home-Assistant start event"
172 | msgstr "Chyba pri spustení udalosti spustenia Home-Assistant"
173 |
174 | #: utils.js:367
175 | msgid "Error occured triggering Home-Assistant start event."
176 | msgstr "Pri spustení udalosti spustenia Home-Assistant došlo k chybe."
177 |
178 | #: utils.js:371
179 | msgid "Error triggering Home-Assistant stop event"
180 | msgstr "Chyba pri spustení udalosti zastavenia Home-Assistant"
181 |
182 | #: utils.js:372
183 | msgid "Error occured triggering Home-Assistant stop event."
184 | msgstr "Pri spustení udalosti zastavenia Home-Assistant došlo k chybe."
185 |
186 | #: utils.js:376
187 | msgid "Error triggering Home-Assistant close event"
188 | msgstr "Chyba pri spustení udalosti zatvorenia Home-Assistant"
189 |
190 | #: utils.js:377
191 | msgid "Error occured triggering Home-Assistant close event."
192 | msgstr "Pri spustení udalosti zatvorenia Home-Assistant došlo k chybe."
193 |
194 | #: utils.js:381
195 | msgid "Error triggering Home-Assistant event"
196 | msgstr "Chyba pri spustení udalosti Home-Assistant"
197 |
198 | #: utils.js:382
199 | msgid "Error occured triggering Home-Assistant event."
200 | msgstr "Pri spustení udalosti Home-Assistant došlo k chybe."
201 |
202 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:16
203 | msgid "Panel Icon."
204 | msgstr "Ikona panela."
205 |
206 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:17
207 | msgid "The default icon that will be visible in the panel."
208 | msgstr "Predvolená ikona, ktorá bude viditeľná na paneli."
209 |
210 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:22
211 | msgid "Complete list of valid panel icons for the extension."
212 | msgstr "Úplný zoznam platných ikon panelu pre rozšírenie."
213 |
214 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:23
215 | msgid ""
216 | "The paths have to be relative to the directory where the 'extension.js' file "
217 | "is located."
218 | msgstr ""
219 | "Cesty musia byť relatívne k adresáru, v ktorom sa nachádza súbor \"extension."
220 | "js\"."
221 |
222 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:28
223 | msgid "Refresh Rate"
224 | msgstr "Obnovovacia frekvencia"
225 |
226 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:29
227 | msgid ""
228 | "The refresh rate for the weather statistics in seconds (works only if "
229 | "\"Refresh Temperature/Humidity Sensors\" is enabled)."
230 | msgstr ""
231 | "Frekvencia obnovovania štatistík počasia v sekundách (funguje len vtedy, ak "
232 | "je povolená možnosť „Obnovovať snímače teploty/vlhkosti“)."
233 |
234 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:35
235 | msgid ""
236 | "Whether or not you want to refresh the temperature and/or humidity sensor "
237 | "values."
238 | msgstr ""
239 | "Či chcete alebo nechcete obnovovať hodnoty senzora teploty a/alebo vlhkosti."
240 |
241 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:40
242 | msgid "Show notifications"
243 | msgstr "Zobrazovať oznámenia"
244 |
245 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:41
246 | msgid "Show notifications when enable/disable togglables or on HASS events."
247 | msgstr ""
248 | "Zobraziť oznámenia pri zapnutí/vypnutí prepínačov alebo pri udalostiach HASS."
249 |
250 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:46
251 | msgid "URL"
252 | msgstr "URL adresa"
253 |
254 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:47
255 | msgid "The url where your hass instance is hosted."
256 | msgstr "Adresa url, na ktorej je umiestnená vaša inštalácia hass."
257 |
258 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:52
259 | msgid "Home-Assistant entities's state cache."
260 | msgstr "Vyrovnávacia pamäť stavu entít Home-Assistant."
261 |
262 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:53
263 | msgid ""
264 | "Home-Assistant entities's state cache. It is meant to be filled "
265 | "automatically on start-up or after pressing the 'Refresh' button in the "
266 | "Preferences menu."
267 | msgstr ""
268 | "Vyrovnávacia pamäť stavu entít Home-Assistant. Má sa vyplniť automaticky pri "
269 | "spustení alebo po stlačení tlačidla „Obnoviť“ v ponuke Predvoľby."
270 |
271 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:58
272 | msgid "Entity ids for home-assistant switches."
273 | msgstr "Identifikátory entít pre prepínače Home-assistant."
274 |
275 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:59
276 | msgid ""
277 | "Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay."
278 | msgstr ""
279 | "Identifikátory entít pre prepínače Home-assistant. Napr. prepinac."
280 | "obyvacka_svetlo."
281 |
282 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:64
283 | msgid "Entity ids for home assistant sensors."
284 | msgstr "Identifikátory entít pre senzory Home Assistant."
285 |
286 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:65
287 | msgid ""
288 | "Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature."
289 | msgstr ""
290 | "Identifikátory entít pre senzory Home Assistant. Napr. senzor."
291 | "obyvacka_teplota."
292 |
293 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:70
294 | msgid "Enable debug mode"
295 | msgstr "Povoliť režim ladenia"
296 |
297 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:71
298 | msgid ""
299 | "When this mode is enabled, more logged messages could be seen in the journal."
300 | msgstr ""
301 | "Keď je tento režim povolený, v protokole sa zobrazí viac zaznamenaných správ."
302 |
303 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:82
304 | msgid "Home Assistant Extension - Shortcut Key"
305 | msgstr "Rozšírenie Home Assistant - klávesová skratka"
306 |
307 | #: schemas/org.gnome.shell.extensions.hass-data.gschema.xml:83
308 | msgid "Shortcut key on how to open the extension's menu from the tray."
309 | msgstr "Klávesová skratka na otvorenie ponuky rozšírenia z panela."
310 |
--------------------------------------------------------------------------------
/prefs.js:
--------------------------------------------------------------------------------
1 | import Adw from 'gi://Adw';
2 | import Gio from 'gi://Gio';
3 | import Gtk from 'gi://Gtk';
4 | import Secret from 'gi://Secret';
5 |
6 | import * as Utils from './utils.js';
7 | import * as Settings from './settings.js';
8 |
9 | import {ExtensionPreferences, gettext as _} from 'resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js';
10 |
11 |
12 | class SettingsPage {
13 | constructor(type, window, mscOptions) {
14 | if (type !== "togglable" && type !== "runnable" && type !== "sensor")
15 | throw new Error(`Type ${type} is not supported in SettingsPage`)
16 |
17 | this.type = type;
18 | this.window = window;
19 | this._mscOptions = mscOptions;
20 | this.page = null;
21 | this.group = null;
22 | this.rows = [];
23 | }
24 |
25 | get pageConfig() {
26 | let title;
27 | let iconName;
28 | switch (this.type) {
29 | case "togglable":
30 | title = _('Togglables');
31 | iconName = "system-shutdown-symbolic";
32 | break;
33 | case "runnable":
34 | title = _('Runnables');
35 | iconName = "system-shutdown-symbolic";
36 | break;
37 | case "sensor":
38 | title = _('Sensors');
39 | iconName = "weather-clear-symbolic";
40 | break;
41 | }
42 | return {
43 | title: title,
44 | iconName: iconName
45 | }
46 | }
47 |
48 | build() {
49 | this.page = new Adw.PreferencesPage({
50 | title: this.pageConfig.title,
51 | icon_name: this.pageConfig.iconName,
52 | });
53 |
54 | this.group = new Adw.PreferencesGroup({ title: _(`Choose which ${this.type}s should appear in the menu:`)});
55 | this.page.add(this.group);
56 | this.window.add(this.page);
57 | Utils.connectSettings([Settings.HASS_ENTITIES_CACHE], this.refresh.bind(this));
58 | this.refresh();
59 | }
60 |
61 | refresh(entries=null) {
62 | this.deleteRows();
63 | if (!entries) {
64 | Utils.getEntitiesByType(
65 | this.type,
66 | (results) => this.refresh(results),
67 | () => this.refresh([])
68 | );
69 | return;
70 | }
71 |
72 | if (!entries.length) {
73 | let row = SettingsPage.createTextRow(
74 | _(`No ${this.type} found. Please check your Home-Assistant connection settings.`)
75 | );
76 | this.rows.push(row);
77 | this.group.add(row);
78 | return;
79 | }
80 |
81 | let enabledEntities = this._mscOptions.getEnabledByType(this.type);
82 | for (let entry of entries) {
83 | let row = SettingsPage.createEntityRow(
84 | entry,
85 | enabledEntities.includes(entry.entity_id),
86 | (rowEntry, checked) => {
87 | Utils._log(
88 | "%s %s (%s) as panel entry",
89 | [checked ? "Check" : "Uncheck", rowEntry.name, rowEntry.entity_id]
90 | );
91 | let currentEntities = this._mscOptions.getEnabledByType(this.type);
92 | let index = currentEntities.indexOf(rowEntry.entity_id);
93 | if (index > -1 && !checked) { // then it exists and so we pop
94 | Utils._log(
95 | "Entry %s (%s) currently present, remove it",
96 | [rowEntry.name, rowEntry.entity_id]
97 | );
98 | currentEntities.splice(index, 1);
99 | }
100 | else if (index <= -1 && checked) {
101 | Utils._log(
102 | "Entry %s (%s) not currently present, add it",
103 | [rowEntry.name, rowEntry.entity_id]
104 | );
105 | currentEntities.push(rowEntry.entity_id);
106 | }
107 | else {
108 | Utils._log(
109 | "Entry %s (%s) currently %s, no change",
110 | [rowEntry.name, rowEntry.entity_id, checked ? "present" : "not present"]
111 | );
112 | return;
113 | }
114 | this._mscOptions.setEnabledByType(this.type, entries.map(
115 | ent => ent.entity_id
116 | ).filter(
117 | ent => currentEntities.includes(ent)
118 | ));
119 | Utils._log(
120 | "%s entries enabled: %s",
121 | [this._mscOptions.getEnabledByType(this.type).length, this._mscOptions.getEnabledByType(this.type).join(', ')]
122 | );
123 | }
124 | );
125 | this.rows.push(row);
126 | this.group.add(row);
127 | }
128 | }
129 |
130 | deleteRows() {
131 | // Remove previously created rows
132 | for (let row of this.rows)
133 | this.group.remove(row);
134 | this.rows = [];
135 | }
136 |
137 | static createEntityRow(entity, checked, on_toggle) {
138 | let row = new Adw.ActionRow({
139 | title: "%s (%s)".format(entity.name, entity.entity_id),
140 | });
141 |
142 | // Create a switch and bind its value to the `show-indicator` key
143 | let toggle = new Gtk.CheckButton({
144 | active: checked,
145 | valign: Gtk.Align.CENTER,
146 | });
147 |
148 | // Add the switch to the row
149 | row.add_suffix(toggle);
150 | row.activatable_widget = toggle;
151 |
152 | toggle.connect('notify::active', () => {
153 | on_toggle(entity, toggle.active);
154 | });
155 |
156 | return row;
157 | }
158 |
159 | static createTextRow(text) {
160 | return new Adw.ActionRow({
161 | title: text,
162 | });
163 | }
164 |
165 | destroy() {
166 | this.page = null;
167 | this.group = null;
168 | this.rows = [];
169 | }
170 | }
171 |
172 | export default class HassPrefs extends ExtensionPreferences {
173 | // constructor(window) {
174 | // }
175 |
176 | fillPreferencesWindow(window) {
177 | this.window = window;
178 | this._settings = this.getSettings();
179 | this.window._settings = this._settings;
180 | this._mscOptions = new Settings.MscOptions(
181 | this.metadata,
182 | this.dir
183 | );
184 |
185 | this.togglablesPage = new SettingsPage("togglable", this.window, this._mscOptions);
186 | this.runnablesPage = new SettingsPage("runnable", this.window, this._mscOptions);
187 | this.sensorsPage = new SettingsPage("sensor", this.window, this._mscOptions);
188 | Utils.init(
189 | this.metadata.uuid,
190 | this._settings,
191 | this.metadata,
192 | this.dir,
193 | _
194 | );
195 | this.build();
196 | this.window.connect('close-request', () => {
197 | this._settings = null;
198 | this._mscOptions.destroy();
199 | this._mscOptions = null;
200 | this.togglablesPage.destroy();
201 | this.togglablesPage = null;
202 | this.runnablesPage.destroy();
203 | this.runnablesPage = null;
204 | this.sensorsPage.destroy();
205 | this.sensorsPage = null;
206 | Utils.disable();
207 | });
208 | }
209 |
210 | build() {
211 | this.buildGeneralSettingsPage();
212 |
213 | this.togglablesPage.build();
214 | this.runnablesPage.build();
215 | this.sensorsPage.build();
216 |
217 | // Enable search on settings
218 | this.window.search_enabled = true;
219 | }
220 |
221 | buildGeneralSettingsPage() {
222 | let page = new Adw.PreferencesPage({
223 | title: _('General Settings'),
224 | icon_name: "preferences-other-symbolic",
225 | });
226 |
227 | const general_group = new Adw.PreferencesGroup({ title: _('General Settings')});
228 | page.add(general_group);
229 |
230 | general_group.add(this.createStringSettingRow(Settings.HASS_URL));
231 | general_group.add(this.createAccessTokenSettingRow());
232 | general_group.add(this.createBooleanSettingRow(Settings.SHOW_NOTIFICATIONS_KEY));
233 | general_group.add(this.createBooleanSettingRow(Settings.DEBUG_MODE));
234 |
235 | const refresh_group = new Adw.PreferencesGroup({ title: _('Refresh sensors')});
236 | page.add(refresh_group);
237 |
238 | refresh_group.add(this.createBooleanSettingRow(Settings.DO_REFRESH));
239 | refresh_group.add(this.createStringSettingRow(Settings.REFRESH_RATE));
240 |
241 | const icon_group = new Adw.PreferencesGroup({ title: _('Panel Icon Options:')});
242 | page.add(icon_group);
243 |
244 | let validIcons = this._mscOptions.validIcons;
245 | let currentIcon = this._mscOptions.panelIcon;
246 | let iconGroup = new Gtk.CheckButton();
247 | for (let icon of validIcons) {
248 | icon_group.add(
249 | this.createIconRow(
250 | icon,
251 | icon == currentIcon,
252 | iconGroup,
253 | (icon) => {
254 | this._mscOptions.panelIcon = icon;
255 | }
256 | )
257 | );
258 | }
259 |
260 | this.window.add(page);
261 | }
262 |
263 | createBooleanSettingRow(name) {
264 | let key = this._settings.settings_schema.get_key(name);
265 | let row = new Adw.ActionRow({
266 | title: _(key.get_summary()),
267 | subtitle: _(key.get_description()),
268 | });
269 |
270 | // Create a switch and bind its value to the `show-indicator` key
271 | let toggle = new Gtk.Switch({
272 | active: this._settings.get_boolean(name),
273 | valign: Gtk.Align.CENTER,
274 | });
275 | this._settings.bind(name, toggle, 'active', Gio.SettingsBindFlags.DEFAULT);
276 |
277 | // Add the switch to the row
278 | row.add_suffix(toggle);
279 | row.activatable_widget = toggle;
280 |
281 | return row;
282 | }
283 |
284 | createStringSettingRow(name) {
285 | let key = this._settings.settings_schema.get_key(name);
286 | let row = new Adw.EntryRow({
287 | title: _(key.get_summary()),
288 | text: this._settings.get_string(name),
289 | show_apply_button: true,
290 | });
291 |
292 | row.connect('apply', () => {
293 | this._settings.set_string(name, row.get_text())
294 | });
295 |
296 | return row;
297 | }
298 |
299 | createAccessTokenSettingRow() {
300 | let row = new Adw.PasswordEntryRow({
301 | title: _("Access Token"),
302 | show_apply_button: true,
303 | });
304 |
305 | row.connect('apply', () => {
306 | Utils._log('Access token changed: "%s"', [row.get_text()]);
307 | let new_value = row.get_text();
308 | if (!new_value) return;
309 | Secret.password_store(
310 | Utils.getTokenSchema(),
311 | {"token_string": "user_token"},
312 | Secret.COLLECTION_DEFAULT,
313 | "long_live_access_token",
314 | row.get_text(),
315 | null,
316 | (source, result) => {
317 | Secret.password_store_finish(result);
318 | // Always force reload entities cache in case of HASS Token change and invalidate it in case
319 | // of error
320 | Utils.getEntities(null, () => Utils.invalidateEntitiesCache(), true);
321 | }
322 | );
323 | });
324 |
325 | return row;
326 | }
327 |
328 | createIconRow(icon, checked, icon_group, on_toggle) {
329 | let label = icon.split("/")[icon.split("/").length-1]
330 | .split(".")[0]
331 | .split("-")
332 | .map(word => word.charAt(0).toUpperCase() + word.slice(1))
333 | .join(" ");
334 | let row = new Adw.ActionRow({
335 | title: label,
336 | });
337 |
338 | // Create a switch and bind its value to the `show-indicator` key
339 | let toggle = new Gtk.CheckButton({
340 | active: checked,
341 | valign: Gtk.Align.CENTER,
342 | group: icon_group,
343 | });
344 |
345 | // Add the switch to the row
346 | row.add_suffix(toggle);
347 | row.activatable_widget = toggle;
348 |
349 | toggle.connect('notify::active', () => {
350 | on_toggle(icon, toggle.active);
351 | });
352 |
353 | return row;
354 | }
355 | }
356 |
--------------------------------------------------------------------------------
/schemas/gschemas.compiled:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/schemas/gschemas.compiled
--------------------------------------------------------------------------------
/schemas/org.gnome.shell.extensions.hass-data.gschema.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
13 |
14 |
15 | "/icons/hass-symbolic.svg"
16 | Panel Icon.
17 | The default icon that will be visible in the panel.
18 |
19 |
20 |
21 | ['/icons/hass-symbolic.svg', '/icons/hass-blue.png']
22 | Complete list of valid panel icons for the extension.
23 | The paths have to be relative to the directory where the 'extension.js' file is located.
24 |
25 |
26 |
27 | "60"
28 | Refresh Rate
29 | The refresh rate for the weather statistics in seconds (works only if "Refresh Temperature/Humidity Sensors" is enabled).
30 |
31 |
32 |
33 | false
34 | Refresh sensors
35 | Whether or not you want to refresh the temperature and/or humidity sensor values.
36 |
37 |
38 |
39 | false
40 | Show notifications
41 | Show notifications when enable/disable togglables or on HASS events.
42 |
43 |
44 |
45 | ""
46 | URL
47 | The url where your hass instance is hosted.
48 |
49 |
50 |
51 | []
52 | Home-Assistant entities's state cache.
53 | Home-Assistant entities's state cache. It is meant to be filled automatically on start-up or after pressing the 'Refresh' button in the Preferences menu.
54 |
55 |
56 |
57 | []
58 | Entity ids for home-assistant switches.
59 | Entity ids for home-assistant switches. E.g. switch.livingroom_lights_relay.
60 |
61 |
62 |
63 | []
64 | Entity ids for home-assistant runnables (scenes and scripts).
65 | Entity ids for home-assistant runnables (scenes and scripts). E.g. script.run_me.
66 |
67 |
68 |
69 | []
70 | Entity ids for home assistant sensors.
71 | Entity ids for home-assistant sensors. E.g. sensor.livingroom_temperature.
72 |
73 |
74 |
75 | false
76 | Enable debug mode
77 | When this mode is enabled, more logged messages could be seen in the journal.
78 |
79 |
80 |
81 |
82 |
85 |
86 |
87 | g']]]>
88 | Home Assistant Extension - Shortcut Key
89 |
90 | Shortcut key on how to open the extension's menu from the tray.
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/screenshots/general_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/general_settings.png
--------------------------------------------------------------------------------
/screenshots/hass_events.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/hass_events.png
--------------------------------------------------------------------------------
/screenshots/hass_events_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/hass_events_40.png
--------------------------------------------------------------------------------
/screenshots/panel_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/panel_icons.png
--------------------------------------------------------------------------------
/screenshots/panel_icons_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/panel_icons_40.png
--------------------------------------------------------------------------------
/screenshots/panel_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/panel_menu.png
--------------------------------------------------------------------------------
/screenshots/panel_menu_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/panel_menu_40.png
--------------------------------------------------------------------------------
/screenshots/preferences_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/preferences_menu.png
--------------------------------------------------------------------------------
/screenshots/preferences_menu_up.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/preferences_menu_up.png
--------------------------------------------------------------------------------
/screenshots/togglable_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoph9/hass-gshell-extension/c2e3509ec07e8d23c1f796acd117f1799a0ad8d6/screenshots/togglable_settings.png
--------------------------------------------------------------------------------
/settings.js:
--------------------------------------------------------------------------------
1 | import Gio from 'gi://Gio';
2 |
3 | export const PANEL_ICON_PATH = 'default-panel-icon';
4 | export const VALID_PANEL_ICONS = 'valid-panel-icons';
5 | export const HASS_URL = 'hass-url';
6 | export const HASS_ENTITIES_CACHE = 'hass-entities-cache';
7 | export const HASS_ENABLED_ENTITIES = 'hass-enabled-entities';
8 | export const HASS_ENABLED_RUNNABLES = 'hass-enabled-runnables';
9 | export const HASS_ENABLED_SENSOR_IDS = 'hass-enabled-sensor-ids';
10 | export const SHOW_NOTIFICATIONS_KEY = 'show-notifications';
11 | export const DO_REFRESH = 'sensors-refresh';
12 | export const REFRESH_RATE = 'sensors-refresh-seconds';
13 | export const DEBUG_MODE = 'debug-mode';
14 |
15 | export var MscOptions = class MscOptions {
16 | constructor(metadata, mainDir) {
17 | this._metadata = metadata;
18 | this._mainDir = mainDir;
19 | this._gsettings = this._getSettings();
20 | this._connectionIds = [];
21 | }
22 |
23 | /**
24 | * A helper function to get the Gio.Settings object for the extension
25 | * @param {String} schema_name
26 | * @return {Gio.Settings} The settings corresponding to the input schema
27 | */
28 | _getSettings(schema=null) {
29 | schema = schema ? schema : this._metadata['settings-schema'];
30 | const schemaDir = this._mainDir.get_child('schemas');
31 | let schemaSource;
32 | if (schemaDir.query_exists(null)) {
33 | schemaSource = Gio.SettingsSchemaSource.new_from_directory(
34 | schemaDir.get_path(),
35 | Gio.SettingsSchemaSource.get_default(),
36 | false
37 | );
38 | } else {
39 | schemaSource = Gio.SettingsSchemaSource.get_default();
40 | }
41 |
42 | const schemaObj = schemaSource.lookup(schema, true);
43 | if (!schemaObj) {
44 | throw new Error(
45 | 'Schema' + schema + ' could not be found for extension ' +
46 | this._metadata.uuid + '. Please check your installation.'
47 | );
48 | }
49 |
50 | const args = { settings_schema: schemaObj };
51 | // let path = schema.replace('.', '/');
52 | // if (path) {
53 | // args.path = path;
54 | // }
55 |
56 | return new Gio.Settings(args);
57 | }
58 |
59 | connect(name, callback) {
60 | const id = this._gsettings.connect(name, callback);
61 | this._connectionIds.push(id);
62 | return id;
63 | }
64 |
65 | destroy() {
66 | this._connectionIds.forEach(id => this._gsettings.disconnect(id));
67 | this._gsettings = null;
68 | }
69 |
70 | // Panel Icons
71 | get panelIcon() {
72 | return this._gsettings.get_string(PANEL_ICON_PATH);
73 | }
74 | set panelIcon(icon_path) {
75 | this._gsettings.set_string(PANEL_ICON_PATH, icon_path);
76 | }
77 | get validIcons() {
78 | return this._gsettings.get_strv(VALID_PANEL_ICONS);
79 | }
80 | set validIcons(icon_paths) {
81 | this._gsettings.set_strv(VALID_PANEL_ICONS, icon_paths);
82 | }
83 |
84 | // General Settings
85 | get hassUrl() {
86 | return this._gsettings.get_string(HASS_URL);
87 | }
88 | set hassUrl(bool_val) {
89 | this._gsettings.set_string(HASS_URL, bool_val);
90 | }
91 |
92 | get doRefresh() {
93 | return this._gsettings.get_boolean(DO_REFRESH);
94 | }
95 | set doRefresh(bool_val) {
96 | this._gsettings.set_boolean(DO_REFRESH, bool_val);
97 | }
98 |
99 | get refreshRate() {
100 | return this._gsettings.get_string(REFRESH_RATE);
101 | }
102 | set refreshRate(rate) {
103 | this._gsettings.set_string(REFRESH_RATE, rate);
104 | }
105 |
106 | // Entities cache
107 | get entitiesCache() {
108 | return this._gsettings.get_strv(HASS_ENTITIES_CACHE).map(ent => JSON.parse(ent));
109 | }
110 | set entitiesCache(entities) {
111 | this._gsettings.set_strv(HASS_ENTITIES_CACHE, entities.map(ent => JSON.stringify(ent)));
112 | }
113 |
114 | // Togglable entities of menu
115 | get enabledEntities() {
116 | return this._gsettings.get_strv(HASS_ENABLED_ENTITIES);
117 | }
118 | set enabledEntities(entities) {
119 | this._gsettings.set_strv(HASS_ENABLED_ENTITIES, entities);
120 | }
121 |
122 | // Runnable entities of menu (script and scene domains)
123 | get enabledRunnables() {
124 | return this._gsettings.get_strv(HASS_ENABLED_RUNNABLES);
125 | }
126 | set enabledRunnables(entities) {
127 | this._gsettings.set_strv(HASS_ENABLED_RUNNABLES, entities);
128 | }
129 |
130 | // Panel extra sensors
131 | get enabledSensors() {
132 | return this._gsettings.get_strv(HASS_ENABLED_SENSOR_IDS);
133 | }
134 | set enabledSensors(entities) {
135 | this._gsettings.set_strv(HASS_ENABLED_SENSOR_IDS, entities);
136 | }
137 |
138 | // abstraction layer for togglables, runnables and sensors
139 | getEnabledByType(type) {
140 | if (type === "runnable") {
141 | return this.enabledRunnables; // calls the getter
142 | } else if (type === "togglable") {
143 | return this.enabledEntities; // calls the getter
144 | } else if (type === "sensor") {
145 | return this.enabledSensors; // calls the getter
146 | }
147 | }
148 | setEnabledByType(type, enabledEntities) {
149 | if (type === "runnable") {
150 | this.enabledRunnables = enabledEntities; // calls the setter
151 | } else if (type === "togglable") {
152 | this.enabledEntities = enabledEntities; // calls the setter
153 | } else if (type === "sensor") {
154 | this.enabledSensors = enabledEntities; // calls the setter
155 | }
156 | }
157 |
158 | // Debug mode
159 | get debugMode() {
160 | return this._gsettings.get_boolean(DEBUG_MODE);
161 | }
162 | set debugMode(bool_val) {
163 | this._gsettings.set_boolean(DEBUG_MODE, bool_val);
164 | }
165 |
166 | // Show notifications
167 | get showNotifications() {
168 | return this._gsettings.get_boolean(SHOW_NOTIFICATIONS_KEY);
169 | }
170 | set showNotifications(bool_val) {
171 | this._gsettings.set_boolean(SHOW_NOTIFICATIONS_KEY, bool_val);
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/stylesheet.css:
--------------------------------------------------------------------------------
1 | .bg-color {
2 | background-color : gold;
3 | }
4 |
5 | .hass-sensor-tooltip {
6 | background-color : black;
7 | color: white;
8 | padding: 0.4em;
9 | border-radius: 0.5em;
10 | font-size: 0.9em;
11 | font-weight: 600;
12 | opacity: 0;
13 | }
14 |
--------------------------------------------------------------------------------
/utils.js:
--------------------------------------------------------------------------------
1 | import Soup from 'gi://Soup';
2 | import Gio from 'gi://Gio';
3 | import GLib from 'gi://GLib';
4 | import Secret from 'gi://Secret';
5 |
6 | import * as Settings from './settings.js';
7 |
8 | let MyUUID = null; // "hass-gshell@geoph9-on-github";
9 |
10 | let mscOptions = null;
11 | let _settings = null;
12 | let _metadata = null;
13 | let _mainDir = null;
14 | let _ = null;
15 | let MessageTray = null;
16 | let Main = null;
17 |
18 | let TOKEN_SCHEMA;
19 |
20 | export function init(
21 | uuid,
22 | settings,
23 | metadata,
24 | mainDir,
25 | gettext_func,
26 | messageTray_class=null, // not mandatory (only used for notifications in extensions.js)
27 | main_class=null // not mandatory (only used for notifications in extensions.js)
28 | ) {
29 | if (MyUUID === null) MyUUID = uuid;
30 | if (_settings === null) _settings = settings;
31 | if (_metadata === null) _metadata = metadata;
32 | if (_mainDir === null) _mainDir = mainDir;
33 | if (_ === null) _ = gettext_func;
34 | if (mscOptions === null) mscOptions = new Settings.MscOptions(_metadata, _mainDir);
35 | if (MessageTray === null && messageTray_class !== null) MessageTray = messageTray_class;
36 | if (Main === null && main_class !== null) Main = main_class;
37 | }
38 |
39 | export function disable() {
40 | if (mscOptions !== null) {
41 | mscOptions.destroy();
42 | mscOptions = null;
43 | }
44 | if (_settings !== null) _settings = null;
45 | if (MyUUID !== null) MyUUID = null;
46 | _settings = null;
47 | }
48 |
49 | export function getTokenSchema() {
50 | if (!TOKEN_SCHEMA) {
51 | TOKEN_SCHEMA = Secret.Schema.new(
52 | "org.gnome.hass-data.Password",
53 | /** DONT_MATCH_NAME is used as a workaround for a bug in gnome-keyring
54 | * which prevents cold keyrings from being searched (and hence does not prompt for unlocking)
55 | * see https://gitlab.gnome.org/GNOME/gnome-keyring/-/issues/89 and
56 | * https://gitlab.gnome.org/GNOME/libsecret/-/issues/7 for more information
57 | */
58 | Secret.SchemaFlags.DONT_MATCH_NAME,
59 | {
60 | "token_string": Secret.SchemaAttributeType.STRING,
61 | }
62 | );
63 | }
64 | return TOKEN_SCHEMA;
65 | }
66 |
67 | const VALID_TOGGLABLES = ['switch.', 'light.', 'fan.', 'input_boolean.', 'media_player.'];
68 | const VALID_RUNNABLES = ['scene.', 'script.'];
69 |
70 | /**
71 | *
72 | * @param {String} type Request type.
73 | * @param {String} url Url of the request.
74 | * @param {Object} data Data of the request (null if no data)
75 | * @param {Function} callback The callback to run with resulting message
76 | * @param {Function} on_error The callback to run on error (optional)
77 | * @return {Soup.Message} A soup message with the requested parameters.
78 | */
79 | function forge_async_message(type, url, data, callback, on_error=null) {
80 | // Encode data to JSON (if provided)
81 | if (data != null) data = JSON.stringify(data);
82 | _log(
83 | "Forge a %s message for %s (%s): firstly retrieve the API Long-Live Token...",
84 | [type, url, data?"with data=%s".format(data):"without data"]
85 | );
86 | Secret.password_lookup(
87 | getTokenSchema(),
88 | {"token_string": "user_token"},
89 | null,
90 | (source, result) => {
91 | let token = Secret.password_lookup_finish(result);
92 | if (!token) {
93 | _log(
94 | "Fail to retreive API Long-Live Token from configuration, "
95 | + "can't construct API message", null, false
96 | );
97 | if (on_error) on_error();
98 | return;
99 | }
100 | _log("API Long-Live Token retreived, forge message...");
101 | // Initialize message and set the required headers
102 | let message = Soup.Message.new(type, url);
103 | message.request_headers.append('Authorization', `Bearer ${token}`);
104 | message.request_headers.set_content_type("application/json", null);
105 | if (data !== null){
106 | let bytes2 = GLib.Bytes.new(data);
107 | message.set_request_body_from_bytes('application/json', bytes2);
108 | }
109 | callback(message);
110 | }
111 | );
112 | }
113 |
114 | /**
115 | *
116 | * @param {String} url The url which you want to request
117 | * @param {String} type Request type (e.g. 'GET', 'POST', default: GET)
118 | * @param {Object} data Data that you want to send with the request (optional, must be in json format, default: null)
119 | * @param {Function} callback The callback for request result (optional)
120 | * @param {Function} on_error The callback to run on request error (optional)
121 | * @return {Object} The response of the request (returns false if the request was unsuccessful)
122 | */
123 | function send_async_request(url, type, data, callback=null, on_error=null) {
124 | forge_async_message(
125 | type ? type : 'GET',
126 | url,
127 | data,
128 | (message) => {
129 | // Initialize session
130 | let session = Soup.Session.new();
131 | session.set_timeout(5);
132 |
133 | try {
134 | _log("Sending %s request on %s...", [type, url]);
135 | let result = session.send_and_read_async(
136 | message,
137 | GLib.PRIORITY_DEFAULT,
138 | null,
139 | (session, result) => {
140 | _log(
141 | "Handling result of %s request on %s (status: %s)...",
142 | [type, url, Soup.Status.get_phrase(message.get_status())]
143 | );
144 | if (message.get_status() == Soup.Status.OK) {
145 | result = session.send_and_read_finish(result);
146 | if (!callback) {
147 | _log("%s request on %s: success", [type, url]);
148 | return;
149 | }
150 | try {
151 | _log("Decoding result of %s request on %s...", [type, url]);
152 | let decoder = new TextDecoder('utf-8');
153 | let response = decoder.decode(result.get_data());
154 | _log(
155 | "Result of %s request on %s (%s): %s", [
156 | type,
157 | url,
158 | data?"with data=%s".format(JSON.stringify(data)):"without data",
159 | response
160 | ]);
161 | _log("Run callback for %s request on %s", [type, url]);
162 | callback(JSON.parse(response));
163 | } catch (error) {
164 | logError(error, `${MyUUID}: fail to decode result of request on ${url}.`);
165 | if (on_error) on_error();
166 | }
167 | }
168 | else {
169 | _log(
170 | "Invalid return of request on %s (status: %s)",
171 | [url, Soup.Status.get_phrase(message.get_status())], false
172 | );
173 | if (on_error) on_error();
174 | }
175 | }
176 | );
177 | } catch (error) {
178 | logError(error, `${MyUUID}: error durring request on ${url}: ${error}`);
179 | if (on_error) on_error();
180 | }
181 | },
182 | () => {
183 | _log("Fail to build message for %s request on %s", [type, url]);
184 | if (on_error) on_error();
185 | return;
186 | }
187 | );
188 | }
189 |
190 |
191 | // Compute HASS URL
192 | function computeURL(path, hass_url=null) {
193 | let url = hass_url ? hass_url : mscOptions.hassUrl;
194 | if (!RegExp('^https?://').exec(url))
195 | url = `http://${url}` // use http:// by default
196 | if (!path)
197 | return url
198 | if (!url.endsWith("/")) url += "/"; // needs a trailing slash
199 | return url + path
200 | }
201 |
202 | /**
203 | * Map an entity object
204 | *
205 | * @param {Object} entity The raw entity state object, as return by HASS API
206 | * @returns {Object}
207 | */
208 | function mapEntity(ent) {
209 | return {
210 | 'entity_id': ent.entity_id,
211 | 'name': ent.attributes.friendly_name,
212 | 'attributes': ent.attributes,
213 | 'state': ent.state,
214 | }
215 | }
216 |
217 | /**
218 | * Get entities
219 | *
220 | * @param {Function} callback The callback to run with the result
221 | * @param {Function} on_error The callback to run on error
222 | * @param {Boolean} force_reload Force reloading cache (optional, default: false)
223 | *
224 | */
225 | export function getEntities(callback=null, on_error=null, force_reload=false) {
226 | let entities = mscOptions.entitiesCache;
227 | if (entities.length == 0 || force_reload) {
228 | _log("get entities from API");
229 | send_async_request(
230 | computeURL('api/states'), 'GET', null,
231 | function (response) {
232 | if (Array.isArray(response)) {
233 | let entities = response.map(mapEntity);
234 | _log("%s entities retreived, sort it by name", [entities.length]);
235 | entities = entities.sort((a,b) => (a.name > b.name) ? 1 : ((b.name > a.name) ? -1 : 0));
236 | _log("update entities cache");
237 | mscOptions.entitiesCache = entities;
238 | if (callback)
239 | callback(entities);
240 | }
241 | else if (on_error) {
242 | on_error();
243 | }
244 | }.bind(this),
245 | on_error
246 | );
247 | }
248 | else {
249 | _log("get entities from cache");
250 | if (callback) callback(entities);
251 | }
252 | }
253 |
254 | /**
255 | * Get one entity
256 | *
257 | * @param {String} entity_id The requested entity ID
258 | * @param {Function} callback The callback to run with the result
259 | * @param {Function} on_error The callback to run on error
260 | * @param {Boolean} force_reload Force reloading cache (optional, default: false)
261 | *
262 | */
263 | function getEntity(entity_id, callback=null, on_error=null, force_reload=false) {
264 | let entity = mscOptions.entitiesCache.filter(ent => ent.entity_id == entity_id);
265 | if (entity.length == 0 || force_reload) {
266 | _log("get entity %s from API", [entity_id]);
267 | send_async_request(
268 | computeURL(`api/states/${entity_id}`), 'GET', null,
269 | function (response) {
270 | if (typeof response === "object") {
271 | if (callback)
272 | callback(mapEntity(response));
273 | }
274 | else if (on_error) {
275 | on_error();
276 | }
277 | }.bind(this),
278 | on_error
279 | );
280 | }
281 | else {
282 | _log("get entity %s from cache", [entity_id]);
283 | if (callback) callback(entity[0]);
284 | }
285 | }
286 |
287 | /**
288 | * Invalidate entities cache
289 | */
290 | export function invalidateEntitiesCache() {
291 | _log("invalidate entities cache");
292 | mscOptions.entitiesCache = [];
293 | }
294 |
295 | /**
296 | * Get entities by type
297 | *
298 | * @param {string} type The type of the entities to get ("runnable", "togglable", or "sensor") // TODO sensors!
299 | * @param {Function} callback The callback to run with the result
300 | * @param {Function} on_error The callback to run on error
301 | * @param {Boolean} only_enabled Filter on enabled runnables (optional, default: false)
302 | * @param {Boolean} force_reload Force reloading cache (optional, default: false)
303 | */
304 | export function getEntitiesByType(type, callback, on_error=null, only_enabled=false, force_reload=false) {
305 | getEntities(
306 | function(entities) {
307 | let results = [];
308 | for (let ent of entities) {
309 | if (only_enabled && !mscOptions.getEnabledByType(type).includes(ent.entity_id))
310 | continue;
311 |
312 | if (type === "sensor") {
313 | if (!isSensor(ent))
314 | continue;
315 | results.push(mapSensor(ent));
316 | } else {
317 |
318 | let entitySupported = false;
319 | if (type === "togglable") entitySupported = isEntitySupported(ent, VALID_TOGGLABLES)
320 | else if (type === "runnable") entitySupported = isEntitySupported(ent, VALID_RUNNABLES)
321 |
322 | if (entitySupported)
323 | results.push({ 'entity_id': ent.entity_id, 'name': ent.name });
324 |
325 | }
326 |
327 | }
328 | _log("%s entities found", [results.length]);
329 | callback(results);
330 | },
331 | on_error,
332 | force_reload
333 | );
334 | }
335 |
336 | /**
337 | * Map a sensor entity object
338 | *
339 | * @param {Object} entity The raw entity object, as returned by getEntity()/getEntities()
340 | * @return {Object}
341 | */
342 | function mapSensor(entity) {
343 | return {
344 | 'entity_id': entity.entity_id,
345 | 'name': entity.name,
346 | 'unit': entity.attributes.unit_of_measurement,
347 | 'state': entity.state,
348 | }
349 | }
350 |
351 | /**
352 | * Check it's a sensor
353 | *
354 | * @param {Object} entity The entity object
355 | * @return {Boolean}
356 | */
357 | function isSensor(entity) {
358 | return (
359 | entity.entity_id.startsWith('sensor.')
360 | && entity.state
361 | && entity.attributes.unit_of_measurement
362 | && entity.state !== "unknown"
363 | && entity.state !== "unavailable"
364 | );
365 | }
366 |
367 | /**
368 | * Check if entity supported
369 | *
370 | * @param {Object} entity The entity object
371 | * @param {Array} valid_domains Array of valid domain names
372 | * @return {Boolean}
373 | */
374 | function isEntitySupported(entity, valid_domains) {
375 |
376 | // Not supported if not in valid domains:
377 | if (valid_domains.filter(domain => entity.entity_id.startsWith(domain)).length == 0) return false
378 |
379 | // Custom check for media players:
380 | if (entity.entity_id.startsWith('media_player.')) {
381 |
382 | /** Check if media_player supports toggle service.
383 | * It can be read from the supported_features attribute, TURN_OFF and TURN_ON features have to be supported
384 | * Decimal values for these features are 128 and 256 respectively:
385 | * https://github.com/home-assistant/core/blob/dev/homeassistant/components/media_player/const.py
386 | *
387 | * To check, supported_features have to be converted to binary,
388 | * than check if the 8th and 9th position from the right is 1.
389 | */
390 |
391 | if (!entity.attributes.supported_features) return false
392 |
393 | // Convert supported_features to binary:
394 | let supported_features_bin = parseInt(entity.attributes.supported_features).toString(2)
395 |
396 | return (
397 | // Check if enough digits:
398 | supported_features_bin.length > 8
399 | // 8th digit from the right is TURN_ON:
400 | && supported_features_bin.charAt(supported_features_bin.length - 8) === '1'
401 | // 9th digit from the right is TURN_OFF:
402 | && supported_features_bin.charAt(supported_features_bin.length - 9) === '1'
403 | );
404 | } else {
405 | return true
406 | }
407 | }
408 |
409 |
410 | /**
411 | * Get sensors
412 | *
413 | * @param {Function} callback The callback to run with the result
414 | * @param {Function} on_error The callback to run on error
415 | * @param {Boolean} only_enabled Filter on enabled togglables (optional, default: false)
416 | * @param {Boolean} force_reload Force reloading cache (optional, default: false)
417 | *
418 | */
419 | export function getSensors(callback, on_error=null, only_enabled=false, force_reload=false) {
420 | getEntities(
421 | function(entities) {
422 | let sensors = [];
423 | for (let ent of entities) {
424 | if (only_enabled && !mscOptions.enabledSensors.includes(ent.entity_id))
425 | continue;
426 | if (!isSensor(ent))
427 | continue;
428 | sensors.push(mapSensor(ent));
429 | }
430 | _log("%s %ssensor entities found", [sensors.length, only_enabled?'enabled ':'']);
431 | callback(sensors);
432 | },
433 | on_error,
434 | force_reload
435 | );
436 | }
437 |
438 | /**
439 | * Get a sensor by its id
440 | *
441 | * @param {String} sensor_id The expected sensor ID
442 | * @param {Function} callback The callback to run with the result
443 | * @param {Function} on_not_found The callback to run if sensor is not found (or on error)
444 | * @param {Boolean} force_reload Force reloading cache (optional, default: false)
445 | *
446 | */
447 | export function getSensor(sensor_id, callback, on_not_found=null, force_reload=false) {
448 | getEntity(
449 | sensor_id,
450 | function(entity) {
451 | if (isSensor(entity)) {
452 | callback(mapSensor(entity));
453 | return;
454 | }
455 | _log('getSensor(%s): is not a sensor (%s)', [sensor_id, JSON.stringify(entity)]);
456 | if (on_not_found) on_not_found();
457 | },
458 | on_not_found,
459 | force_reload
460 | );
461 | }
462 |
463 | /**
464 | * Toggle an entity in Home-Assistant
465 | * @param {String} entityId The entity ID
466 | */
467 | export function toggleEntity(entity) {
468 | let data = { "entity_id": entity.entity_id };
469 | let domain = entity.entity_id.split(".")[0]; // e.g. light.mylight => light
470 | send_async_request(
471 | computeURL(`api/services/${domain}/toggle`),
472 | 'POST',
473 | data,
474 | function(response) {
475 | _log(
476 | 'HA result toggling %s (%s): %s',
477 | [entity.name, entity.entity_id, JSON.stringify(response)]
478 | );
479 |
480 | // HA do not return new entity state in each case and not only the one we requested
481 | let state = null;
482 | for (let ent of response) {
483 | if (ent.entity_id == entity.entity_id) {
484 | state = ent.state;
485 | break;
486 | }
487 | }
488 |
489 | if (state == 'on')
490 | notify(
491 | _('%s toggled on').format(entity.name),
492 | _('%s successfully toggled on.').format(entity.name)
493 | );
494 | else if (state == 'off')
495 | notify(
496 | _('%s toggled off').format(entity.name),
497 | _('%s successfully toggled off.').format(entity.name)
498 | );
499 | else
500 | notify(
501 | _('%s toggled').format(entity.name),
502 | _('%s successfully toggled.').format(entity.name)
503 | );
504 | },
505 | function() {
506 | notify(
507 | _('Error toggling %s').format(entity.name),
508 | _('Error occured trying to toggle %s.').format(entity.name),
509 | )
510 | }
511 | );
512 | }
513 |
514 | /**
515 | * Turns an entity on in Home-Assistant
516 | * @param {String} entityId The entity ID
517 | */
518 | export function turnOnEntity(entity) {
519 | let data = { "entity_id": entity.entity_id };
520 | let domain = entity.entity_id.split(".")[0]; // e.g. script.run_me => script
521 | send_async_request(
522 | computeURL(`api/services/${domain}/turn_on`),
523 | 'POST',
524 | data,
525 | function(response) {
526 | _log(
527 | 'HA result turning on %s (%s): %s',
528 | [entity.name, entity.entity_id, JSON.stringify(response)]
529 | );
530 |
531 | // HA does respond with a timestamp as a new scenes/scripts state,
532 | // so there is no use in computing the response here
533 | },
534 | function() {
535 | notify(
536 | _('Error turning on %s').format(entity.name),
537 | _('Error occured trying to turn on %s.').format(entity.name),
538 | )
539 | }
540 | );
541 | }
542 |
543 | /**
544 | * Trigger Home-Assistant event by name
545 | * @param {String} eventName The HA event name (start/stop/restart)
546 | */
547 | export function triggerHassEvent(eventName, callback=null, on_error=null) {
548 | send_async_request(
549 | computeURL(`api/events/homeassistant_${eventName}`),
550 | 'POST',
551 | null,
552 | function() {
553 | if (eventName == 'start')
554 | notify(
555 | _('Home-Assistant start event triggered'),
556 | _('Home-Assistant start event successfully triggered.')
557 | );
558 | else if (eventName == 'stop')
559 | notify(
560 | _('Home-Assistant stop event triggered'),
561 | _('Home-Assistant stop event successfully triggered.')
562 | );
563 | else if (eventName == 'close')
564 | notify(
565 | _('Home-Assistant close event triggered'),
566 | _('Home-Assistant close event successfully triggered.')
567 | );
568 | else
569 | notify(
570 | _('Home-Assistant event triggered'),
571 | _('Home-Assistant event successfully triggered.')
572 | );
573 | },
574 | function() {
575 | if (eventName == 'start')
576 | notify(
577 | _('Error triggering Home-Assistant start event'),
578 | _('Error occured triggering Home-Assistant start event.'),
579 | );
580 | else if (eventName == 'stop')
581 | notify(
582 | _('Error triggering Home-Assistant stop event'),
583 | _('Error occured triggering Home-Assistant stop event.'),
584 | );
585 | else if (eventName == 'close')
586 | notify(
587 | _('Error triggering Home-Assistant close event'),
588 | _('Error occured triggering Home-Assistant close event.'),
589 | );
590 | else
591 | notify(
592 | _('Error triggering Home-Assistant event'),
593 | _('Error occured triggering Home-Assistant event.'),
594 | );
595 |
596 | }
597 | );
598 | }
599 |
600 | /**
601 | * Check equality of elements of two arrays
602 | * @param {Array} a Array 1
603 | * @param {Array} b Array 2
604 | * @return {Boolean} true if the two arrays have the same elements. false otherwise.
605 | */
606 | export function arraysEqual(a, b) {
607 | if (a === b) return true;
608 | if (a == null || b == null) return false;
609 | if (a.length !== b.length) return false;
610 |
611 | // If you don't care about the order of the elements inside
612 | // the array, you should sort both arrays here.
613 | // Please note that calling sort on an array will modify that array.
614 | // you might want to clone your array first.
615 |
616 | for (let i = 0; i < a.length; ++i) {
617 | if (a[i] !== b[i]) return false;
618 | }
619 | return true;
620 | }
621 |
622 |
623 | // const getMethods = (obj) => {
624 | // let properties = new Set()
625 | // let currentObj = obj
626 | // do {
627 | // Object.getOwnPropertyNames(currentObj).map(item => properties.add(item))
628 | // } while ((currentObj = Object.getPrototypeOf(currentObj)))
629 | // return [...properties.keys()].filter(item => typeof obj[item] === 'function')
630 | // }
631 |
632 | export function getEntityIcon(domain) {
633 | let icon_path = _mainDir.get_path();
634 | switch (domain) {
635 | case "scene":
636 | icon_path += '/icons/palette.svg';
637 | break;
638 | case "script":
639 | icon_path += '/icons/script-text.svg';
640 | break;
641 | case "light":
642 | icon_path += '/icons/ceiling-light.svg';
643 | break;
644 | case "fan":
645 | icon_path += '/icons/fan.svg';
646 | break;
647 | case "media_player":
648 | icon_path += '/icons/media-player.svg';
649 | break;
650 | case "switch":
651 | case "input_boolean":
652 | icon_path += '/icons/toggle-switch-outline.svg';
653 | break;
654 | default:
655 | // no need for a default as these are all the supported domains by the plugin, but log anyways
656 | _log(`Received unexpected domain in getEntityIcon: ${domain}`)
657 | }
658 | return Gio.icon_new_for_string(icon_path);
659 | }
660 |
661 | /**
662 | * Log a message
663 | * @param {String} msg The message
664 | * @param {[Mixed]} [args=null] If array provided, it will be used to format the mesage
665 | * using it format() method (optional, default: null)
666 | * @param {Boolean} [debug=true] If true, consider message as debugging one and logged it
667 | * only if the debug mode is enabled (optional, default: true)
668 | */
669 | export function _log(msg, args=null, debug=true) {
670 | if (debug && !mscOptions.debugMode) return;
671 | if (args) msg = msg.format.apply(msg, args);
672 | log(`${MyUUID}: ${msg}`);
673 | }
674 |
675 | export function notify(msg, details) {
676 | if (!mscOptions.showNotifications) return;
677 | // let Main = imports.ui.main;
678 | // let MessageTray = imports.ui.messageTray;
679 | let source = new MessageTray.Source(_metadata.name);
680 | Main.messageTray.add(source);
681 | let notification = new MessageTray.Notification(
682 | source, msg, details,
683 | {gicon: Gio.icon_new_for_string(_mainDir.get_path() + '/icons/hass-symbolic.svg')}
684 | );
685 | notification.setTransient(true);
686 | source.showNotification(notification);
687 | }
688 |
689 | /**
690 | * Connect specified settings changes to provided callback
691 | * @param {Array} settings List of settings
692 | * @param {Function} callback The callback to run on change
693 | * @param {Array} [args=[]] Optional arguments to pass to callback
694 | */
695 | export function connectSettings(settings, callback, args=[]) {
696 | let connectedSettingIds = [];
697 | for (let setting of settings) {
698 | connectedSettingIds.push(
699 | _settings.connect(
700 | "changed::" + setting,
701 | () => callback.apply(this, args)
702 | )
703 | );
704 | }
705 | return connectedSettingIds;
706 | }
707 |
708 | /**
709 | * Disconnect connected settings by ID
710 | * @param {Array} connectedSettingIds List of connected setting IDs
711 | */
712 | export function disconnectSettings(connectedSettingIds) {
713 | connectedSettingIds.forEach(id => _settings.disconnect(id));
714 | }
715 |
--------------------------------------------------------------------------------