diff --git a/.gitignore b/.gitignore index e0bdf223..d4fcc99e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ *.swp hammerspoon/ annotations/ -annotations.json +annotations.json \ No newline at end of file diff --git a/Source/WatchForMeeting.spoon/docs.json b/Source/WatchForMeeting.spoon/docs.json new file mode 100644 index 00000000..dad1dd0c --- /dev/null +++ b/Source/WatchForMeeting.spoon/docs.json @@ -0,0 +1,800 @@ +[ + { + "Constant" : [ + + ], + "submodules" : [ + + ], + "Function" : [ + + ], + "Variable" : [ + { + "doc" : "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon.", + "name" : "logger", + "stripped_doc" : [ + "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon." + ], + "desc" : "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.logger", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.logger", + "parameters" : [ + + ] + }, + { + "doc" : "A Table containing the settings that control sharing.\n\n| Key | Description | Default |\n| --- | ----------- | ------- |\n| enabled | Whether or not sharing is enabled.<br\/><br\/>When false, the spoon will still monitor meeting status to [meetingState](#meetingState), but you will need to write your own automations for what to do with that info. | _true_ |\n| useServer | Do you want to use an external server? (See *Configuration Options* below) | _false_ |\n| | ↓ _required info when `useServer=false`_ | |\n| port | What port to run the self hosted server when WatchForMeeting.sharing.useServer is false. | _8080_ |\n| | ↓ _required info when `useServer=true`_ | |\n| serverURL | The complete url for the external server, including port. IE: `http:\/\/localhost:8080` | _nil_ |\n| key | UUID to identify the room. Value is provided when the room is added on the server side. | _nil_ |\n| maxConnectionAttempts | Maximum number of connection attempts when using an external server. When less than 0, infinite retrys | _-1_ |\n| waitBeforeRetry | Time, in seconds, between connection attempts when using an external server | _5_ |\n\n# Configuration Options\n## Default\nIn order to minimize dependencies, by default this spoon uses a [hs.httpserver](https:\/\/www.hammerspoon.org\/docs\/hs.httpserver.html) to host the status page. This comes with a significant downside of: only the last client to load the page will receive status updates. Any previously connected clients will remain stuck at the last update they received before that client connected.\n\nOnce you are running the spoon, assuming you haven't changed the port (and nothing else is running at that location) you can reach your status page at http:\/\/localhost:8080\n\n## Better - MeetingStatusServer\nFor a better experience I recommend utilizing an external server to receive updates via websockets, and broadcast them to as many clients as you wish to connect.\n\nFor that purpose I've built [http:\/\/github.com\/asp55\/MeetingStatusServer](http:\/\/github.com\/asp55\/MeetingStatusServer) which runs on node.js and can either be run locally as its own thing, or hosted remotely.\n\nIf using the external server, you will to create a key to identify your \"room\" and then provide that information to the spoon. \nIn that case, before `spoon.WatchForMeeting:start()` add the following to your `~\/.hammerspoon\/init.lua`\n\n```\nspoon.WatchForMeeting.sharing.useServer = true\nspoon.WatchForMeeting.sharing.serverURL=\"[YOUR SERVER URL]\"\nspoon.WatchForMeeting.sharing.key=\"[YOUR KEY]\"\n```\n\nor \n\n```\nspoon.WatchForMeeting.sharing = {\n useServer = true,\n serverURL = \"[YOUR SERVER URL]\",\n key=\"[YOUR KEY]\"\n}\n```\n\n## Disable\nIf you don't want to broadcast your status to a webpage, simply disable sharing\n```\n spoon.WatchForMeeting.sharing = {\n enabled = false\n }\n```", + "name" : "sharing", + "stripped_doc" : [ + "A Table containing the settings that control sharing.", + "", + "| Key | Description | Default |", + "| --- | ----------- | ------- |", + "| enabled | Whether or not sharing is enabled.<br\/><br\/>When false, the spoon will still monitor meeting status to [meetingState](#meetingState), but you will need to write your own automations for what to do with that info. | _true_ |", + "| useServer | Do you want to use an external server? (See *Configuration Options* below) | _false_ |", + "| | ↓ _required info when `useServer=false`_ | |", + "| port | What port to run the self hosted server when WatchForMeeting.sharing.useServer is false. | _8080_ |", + "| | ↓ _required info when `useServer=true`_ | |", + "| serverURL | The complete url for the external server, including port. IE: `http:\/\/localhost:8080` | _nil_ |", + "| key | UUID to identify the room. Value is provided when the room is added on the server side. | _nil_ |", + "| maxConnectionAttempts | Maximum number of connection attempts when using an external server. When less than 0, infinite retrys | _-1_ |", + "| waitBeforeRetry | Time, in seconds, between connection attempts when using an external server | _5_ |", + "", + "# Configuration Options", + "## Default", + "In order to minimize dependencies, by default this spoon uses a [hs.httpserver](https:\/\/www.hammerspoon.org\/docs\/hs.httpserver.html) to host the status page. This comes with a significant downside of: only the last client to load the page will receive status updates. Any previously connected clients will remain stuck at the last update they received before that client connected.", + "", + "Once you are running the spoon, assuming you haven't changed the port (and nothing else is running at that location) you can reach your status page at http:\/\/localhost:8080", + "", + "## Better - MeetingStatusServer", + "For a better experience I recommend utilizing an external server to receive updates via websockets, and broadcast them to as many clients as you wish to connect.", + "", + "For that purpose I've built [http:\/\/github.com\/asp55\/MeetingStatusServer](http:\/\/github.com\/asp55\/MeetingStatusServer) which runs on node.js and can either be run locally as its own thing, or hosted remotely.", + "", + "If using the external server, you will to create a key to identify your \"room\" and then provide that information to the spoon. ", + "In that case, before `spoon.WatchForMeeting:start()` add the following to your `~\/.hammerspoon\/init.lua`", + "", + "```", + "spoon.WatchForMeeting.sharing.useServer = true", + "spoon.WatchForMeeting.sharing.serverURL=\"[YOUR SERVER URL]\"", + "spoon.WatchForMeeting.sharing.key=\"[YOUR KEY]\"", + "```", + "", + "or ", + "", + "```", + "spoon.WatchForMeeting.sharing = {", + " useServer = true,", + " serverURL = \"[YOUR SERVER URL]\",", + " key=\"[YOUR KEY]\"", + "}", + "```", + "", + "## Disable", + "If you don't want to broadcast your status to a webpage, simply disable sharing", + "```", + " spoon.WatchForMeeting.sharing = {", + " enabled = false", + " }", + "```" + ], + "desc" : "A Table containing the settings that control sharing.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.sharing", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.sharing", + "parameters" : [ + + ] + }, + { + "doc" : "A Table containing the settings that control sharing.\n\n| Key | Description | Default | \n| --- | ----------- | ------- | \n| enabled | Whether or not to show the menu bar. | _true_ | \n| color | Whether or not to use color icons. | _true_ |\n| detailed | Whether or not to use the detailed icon set. | _true_ |\n| showFullState | Whether the menubar icon should represent the full state<br\/>(IE: Mic On\/Off, Video On\/Off, & Screen Sharing) | _true_ |\n\n\n## Icons\n\n<table>\n <thead>\n <tr>\n <th>\n <code>WatchForMeeting.menuBar = {...}<\/code> →\n <\/th>\n <th><code>color=true,<\/code><br\/><code>detailed=true,<\/code><\/th>\n <th><code>color=true,<\/code><br\/><code>detailed=false,<\/code><\/th>\n <th><code>color=false,<\/code><br\/><code>detailed=true,<\/code><\/th>\n <th><code>color=false,<\/code><br\/><code>detailed=false,<\/code><\/th>\n <\/tr>\n <tr>\n <th>State (See: <a href=\"#meetingState\">WatchForMeeting.meetingState<\/a>) ↓\n <\/th>\n <th colspan=\"4\"><code>showFullState=true<\/code> or <code>showFullState=false<\/code><\/th>\n <\/tr>\n <\/thead>\n <tbody>\n <tr>\n <td>Available<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n <\/tr>\n <tr>\n <td>Busy<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td><\/td>\n <th colspan=\"4\"><code>showFullState=true<\/code> only<\/th>\n <\/tr>\n <tr>\n <td>Busy + Mic On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Video On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Video On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Video On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Video On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <\/tbody>\n<\/table>", + "name" : "menubar", + "stripped_doc" : [ + "A Table containing the settings that control sharing.", + "", + "| Key | Description | Default | ", + "| --- | ----------- | ------- | ", + "| enabled | Whether or not to show the menu bar. | _true_ | ", + "| color | Whether or not to use color icons. | _true_ |", + "| detailed | Whether or not to use the detailed icon set. | _true_ |", + "| showFullState | Whether the menubar icon should represent the full state<br\/>(IE: Mic On\/Off, Video On\/Off, & Screen Sharing) | _true_ |", + "", + "", + "## Icons", + "", + "<table>", + " <thead>", + " <tr>", + " <th>", + " <code>WatchForMeeting.menuBar = {...}<\/code> →", + " <\/th>", + " <th><code>color=true,<\/code><br\/><code>detailed=true,<\/code><\/th>", + " <th><code>color=true,<\/code><br\/><code>detailed=false,<\/code><\/th>", + " <th><code>color=false,<\/code><br\/><code>detailed=true,<\/code><\/th>", + " <th><code>color=false,<\/code><br\/><code>detailed=false,<\/code><\/th>", + " <\/tr>", + " <tr>", + " <th>State (See: <a href=\"#meetingState\">WatchForMeeting.meetingState<\/a>) ↓", + " <\/th>", + " <th colspan=\"4\"><code>showFullState=true<\/code> or <code>showFullState=false<\/code><\/th>", + " <\/tr>", + " <\/thead>", + " <tbody>", + " <tr>", + " <td>Available<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td><\/td>", + " <th colspan=\"4\"><code>showFullState=true<\/code> only<\/th>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Video On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Video On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Video On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Video On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <\/tbody>", + "<\/table>" + ], + "desc" : "A Table containing the settings that control sharing.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.menubar", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.menubar", + "parameters" : [ + + ] + }, + { + "doc" : "Number representing which mode WatchForMeeting should be running\n\n- *0* - Automatic (default)\n-- Monitors Zoom and updates status accordingly\n- *1* - Busy\n-- Fakes a meeting. (Marks as in meeting, and signals that the mic is live, camera is on, and screen is sharing.) Useful when meeting type is not supported (Currently any platform that isn't zoom.)", + "name" : "mode", + "stripped_doc" : [ + "Number representing which mode WatchForMeeting should be running", + "", + "- *0* - Automatic (default)", + "-- Monitors Zoom and updates status accordingly", + "- *1* - Busy", + "-- Fakes a meeting. (Marks as in meeting, and signals that the mic is live, camera is on, and screen is sharing.) Useful when meeting type is not supported (Currently any platform that isn't zoom.)" + ], + "desc" : "Number representing which mode WatchForMeeting should be running", + "notes" : [ + + ], + "signature" : "WatchForMeeting.mode", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.mode", + "parameters" : [ + + ] + }, + { + "doc" : "(Read-only) The hs.application for zoom if it is running, otherwise nil", + "name" : "zoom", + "stripped_doc" : [ + "(Read-only) The hs.application for zoom if it is running, otherwise nil" + ], + "desc" : "(Read-only) The hs.application for zoom if it is running, otherwise nil", + "notes" : [ + + ], + "signature" : "WatchForMeeting.zoom", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.zoom", + "parameters" : [ + + ] + }, + { + "doc" : "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)\n\n| Value | Description |\n| ----------------------------------------------------------------------- | ----------- |\n| `false` | Available | \n| `{mic_open = [Boolean], video_on = [Boolean], sharing = [Boolean] }` | Busy |", + "name" : "meetingState", + "stripped_doc" : [ + "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)", + "", + "| Value | Description |", + "| ----------------------------------------------------------------------- | ----------- |", + "| `false` | Available | ", + "| `{mic_open = [Boolean], video_on = [Boolean], sharing = [Boolean] }` | Busy |" + ], + "desc" : "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)", + "notes" : [ + + ], + "signature" : "WatchForMeeting.meetingState", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.meetingState", + "parameters" : [ + + ] + } + ], + "stripped_doc" : [ + + ], + "Deprecated" : [ + + ], + "type" : "Module", + "desc" : "A Spoon to answer the question", + "Constructor" : [ + + ], + "doc" : "A Spoon to answer the question\n> Are you in a meeting?\n\nWatches to see if:\n1) Zoom is running\n2) Are you on a call\n3) Are you on mute, is your camera on, and\/or are you screen sharing\n\nAnd then lets you share that information.\n\n# Installation & Basic Usage\nDownload the [Latest Release](https:\/\/github.com\/asp55\/WatchForMeeting\/releases\/latest) and unzip to `~\/.hammerspoon\/Spoons\/`\n\nTo get going right out of the box, in your `~\/.hammerspoon\/init.lua` add these lines:\n```\nhs.loadSpoon(\"WatchForMeeting\")\nspoon.WatchForMeeting:start()\n```\n\nThis will start the spoon monitoring for zoom calls, and come with the default status page, and menubar configurations.", + "items" : [ + { + "doc" : "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon.", + "name" : "logger", + "stripped_doc" : [ + "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon." + ], + "desc" : "hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.logger", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.logger", + "parameters" : [ + + ] + }, + { + "doc" : "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)\n\n| Value | Description |\n| ----------------------------------------------------------------------- | ----------- |\n| `false` | Available | \n| `{mic_open = [Boolean], video_on = [Boolean], sharing = [Boolean] }` | Busy |", + "name" : "meetingState", + "stripped_doc" : [ + "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)", + "", + "| Value | Description |", + "| ----------------------------------------------------------------------- | ----------- |", + "| `false` | Available | ", + "| `{mic_open = [Boolean], video_on = [Boolean], sharing = [Boolean] }` | Busy |" + ], + "desc" : "(Read-only) Either false (when not in a meeting) or a table (when in a meeting)", + "notes" : [ + + ], + "signature" : "WatchForMeeting.meetingState", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.meetingState", + "parameters" : [ + + ] + }, + { + "doc" : "A Table containing the settings that control sharing.\n\n| Key | Description | Default | \n| --- | ----------- | ------- | \n| enabled | Whether or not to show the menu bar. | _true_ | \n| color | Whether or not to use color icons. | _true_ |\n| detailed | Whether or not to use the detailed icon set. | _true_ |\n| showFullState | Whether the menubar icon should represent the full state<br\/>(IE: Mic On\/Off, Video On\/Off, & Screen Sharing) | _true_ |\n\n\n## Icons\n\n<table>\n <thead>\n <tr>\n <th>\n <code>WatchForMeeting.menuBar = {...}<\/code> →\n <\/th>\n <th><code>color=true,<\/code><br\/><code>detailed=true,<\/code><\/th>\n <th><code>color=true,<\/code><br\/><code>detailed=false,<\/code><\/th>\n <th><code>color=false,<\/code><br\/><code>detailed=true,<\/code><\/th>\n <th><code>color=false,<\/code><br\/><code>detailed=false,<\/code><\/th>\n <\/tr>\n <tr>\n <th>State (See: <a href=\"#meetingState\">WatchForMeeting.meetingState<\/a>) ↓\n <\/th>\n <th colspan=\"4\"><code>showFullState=true<\/code> or <code>showFullState=false<\/code><\/th>\n <\/tr>\n <\/thead>\n <tbody>\n <tr>\n <td>Available<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>\n <\/tr>\n <tr>\n <td>Busy<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td><\/td>\n <th colspan=\"4\"><code>showFullState=true<\/code> only<\/th>\n <\/tr>\n <tr>\n <td>Busy + Mic On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Video On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Video On<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Video On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <tr>\n <td>Busy + Mic On + Video On + Screen Sharing<\/td>\n <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>\n <\/tr>\n <\/tbody>\n<\/table>", + "name" : "menubar", + "stripped_doc" : [ + "A Table containing the settings that control sharing.", + "", + "| Key | Description | Default | ", + "| --- | ----------- | ------- | ", + "| enabled | Whether or not to show the menu bar. | _true_ | ", + "| color | Whether or not to use color icons. | _true_ |", + "| detailed | Whether or not to use the detailed icon set. | _true_ |", + "| showFullState | Whether the menubar icon should represent the full state<br\/>(IE: Mic On\/Off, Video On\/Off, & Screen Sharing) | _true_ |", + "", + "", + "## Icons", + "", + "<table>", + " <thead>", + " <tr>", + " <th>", + " <code>WatchForMeeting.menuBar = {...}<\/code> →", + " <\/th>", + " <th><code>color=true,<\/code><br\/><code>detailed=true,<\/code><\/th>", + " <th><code>color=true,<\/code><br\/><code>detailed=false,<\/code><\/th>", + " <th><code>color=false,<\/code><br\/><code>detailed=true,<\/code><\/th>", + " <th><code>color=false,<\/code><br\/><code>detailed=false,<\/code><\/th>", + " <\/tr>", + " <tr>", + " <th>State (See: <a href=\"#meetingState\">WatchForMeeting.meetingState<\/a>) ↓", + " <\/th>", + " <th colspan=\"4\"><code>showFullState=true<\/code> or <code>showFullState=false<\/code><\/th>", + " <\/tr>", + " <\/thead>", + " <tbody>", + " <tr>", + " <td>Available<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Free.png\" alt=\"Free slash Available\" height=\"16\" \/><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting.png\" alt=\"In meeting, no additional status\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td><\/td>", + " <th colspan=\"4\"><code>showFullState=true<\/code> only<\/th>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic.png\" alt=\"In meeting, mic:on, video:off, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Video On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid.png\" alt=\"In meeting, mic:off, video:on, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Screen.png\" alt=\"In meeting, mic:off, video:off, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Video On<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid.png\" alt=\"In meeting, mic:on, video:on, screensharing:off\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Screen.png\" alt=\"In meeting, mic:on, video:off, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Video On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Vid-Screen.png\" alt=\"In meeting, mic:off, video:on, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <tr>", + " <td>Busy + Mic On + Video On + Screen Sharing<\/td>", + " <td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Color\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Detailed\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + "<td><img src=\"https:\/\/raw.githubusercontent.com\/asp55\/WatchForMeeting\/main\/menubar-icons\/Template\/Minimal\/Meeting-Mic-Vid-Screen.png\" alt=\"In meeting, mic:on, video:on, screensharing:on\" height=\"16\"><\/td>", + " <\/tr>", + " <\/tbody>", + "<\/table>" + ], + "desc" : "A Table containing the settings that control sharing.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.menubar", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.menubar", + "parameters" : [ + + ] + }, + { + "doc" : "Number representing which mode WatchForMeeting should be running\n\n- *0* - Automatic (default)\n-- Monitors Zoom and updates status accordingly\n- *1* - Busy\n-- Fakes a meeting. (Marks as in meeting, and signals that the mic is live, camera is on, and screen is sharing.) Useful when meeting type is not supported (Currently any platform that isn't zoom.)", + "name" : "mode", + "stripped_doc" : [ + "Number representing which mode WatchForMeeting should be running", + "", + "- *0* - Automatic (default)", + "-- Monitors Zoom and updates status accordingly", + "- *1* - Busy", + "-- Fakes a meeting. (Marks as in meeting, and signals that the mic is live, camera is on, and screen is sharing.) Useful when meeting type is not supported (Currently any platform that isn't zoom.)" + ], + "desc" : "Number representing which mode WatchForMeeting should be running", + "notes" : [ + + ], + "signature" : "WatchForMeeting.mode", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.mode", + "parameters" : [ + + ] + }, + { + "doc" : "A Table containing the settings that control sharing.\n\n| Key | Description | Default |\n| --- | ----------- | ------- |\n| enabled | Whether or not sharing is enabled.<br\/><br\/>When false, the spoon will still monitor meeting status to [meetingState](#meetingState), but you will need to write your own automations for what to do with that info. | _true_ |\n| useServer | Do you want to use an external server? (See *Configuration Options* below) | _false_ |\n| | ↓ _required info when `useServer=false`_ | |\n| port | What port to run the self hosted server when WatchForMeeting.sharing.useServer is false. | _8080_ |\n| | ↓ _required info when `useServer=true`_ | |\n| serverURL | The complete url for the external server, including port. IE: `http:\/\/localhost:8080` | _nil_ |\n| key | UUID to identify the room. Value is provided when the room is added on the server side. | _nil_ |\n| maxConnectionAttempts | Maximum number of connection attempts when using an external server. When less than 0, infinite retrys | _-1_ |\n| waitBeforeRetry | Time, in seconds, between connection attempts when using an external server | _5_ |\n\n# Configuration Options\n## Default\nIn order to minimize dependencies, by default this spoon uses a [hs.httpserver](https:\/\/www.hammerspoon.org\/docs\/hs.httpserver.html) to host the status page. This comes with a significant downside of: only the last client to load the page will receive status updates. Any previously connected clients will remain stuck at the last update they received before that client connected.\n\nOnce you are running the spoon, assuming you haven't changed the port (and nothing else is running at that location) you can reach your status page at http:\/\/localhost:8080\n\n## Better - MeetingStatusServer\nFor a better experience I recommend utilizing an external server to receive updates via websockets, and broadcast them to as many clients as you wish to connect.\n\nFor that purpose I've built [http:\/\/github.com\/asp55\/MeetingStatusServer](http:\/\/github.com\/asp55\/MeetingStatusServer) which runs on node.js and can either be run locally as its own thing, or hosted remotely.\n\nIf using the external server, you will to create a key to identify your \"room\" and then provide that information to the spoon. \nIn that case, before `spoon.WatchForMeeting:start()` add the following to your `~\/.hammerspoon\/init.lua`\n\n```\nspoon.WatchForMeeting.sharing.useServer = true\nspoon.WatchForMeeting.sharing.serverURL=\"[YOUR SERVER URL]\"\nspoon.WatchForMeeting.sharing.key=\"[YOUR KEY]\"\n```\n\nor \n\n```\nspoon.WatchForMeeting.sharing = {\n useServer = true,\n serverURL = \"[YOUR SERVER URL]\",\n key=\"[YOUR KEY]\"\n}\n```\n\n## Disable\nIf you don't want to broadcast your status to a webpage, simply disable sharing\n```\n spoon.WatchForMeeting.sharing = {\n enabled = false\n }\n```", + "name" : "sharing", + "stripped_doc" : [ + "A Table containing the settings that control sharing.", + "", + "| Key | Description | Default |", + "| --- | ----------- | ------- |", + "| enabled | Whether or not sharing is enabled.<br\/><br\/>When false, the spoon will still monitor meeting status to [meetingState](#meetingState), but you will need to write your own automations for what to do with that info. | _true_ |", + "| useServer | Do you want to use an external server? (See *Configuration Options* below) | _false_ |", + "| | ↓ _required info when `useServer=false`_ | |", + "| port | What port to run the self hosted server when WatchForMeeting.sharing.useServer is false. | _8080_ |", + "| | ↓ _required info when `useServer=true`_ | |", + "| serverURL | The complete url for the external server, including port. IE: `http:\/\/localhost:8080` | _nil_ |", + "| key | UUID to identify the room. Value is provided when the room is added on the server side. | _nil_ |", + "| maxConnectionAttempts | Maximum number of connection attempts when using an external server. When less than 0, infinite retrys | _-1_ |", + "| waitBeforeRetry | Time, in seconds, between connection attempts when using an external server | _5_ |", + "", + "# Configuration Options", + "## Default", + "In order to minimize dependencies, by default this spoon uses a [hs.httpserver](https:\/\/www.hammerspoon.org\/docs\/hs.httpserver.html) to host the status page. This comes with a significant downside of: only the last client to load the page will receive status updates. Any previously connected clients will remain stuck at the last update they received before that client connected.", + "", + "Once you are running the spoon, assuming you haven't changed the port (and nothing else is running at that location) you can reach your status page at http:\/\/localhost:8080", + "", + "## Better - MeetingStatusServer", + "For a better experience I recommend utilizing an external server to receive updates via websockets, and broadcast them to as many clients as you wish to connect.", + "", + "For that purpose I've built [http:\/\/github.com\/asp55\/MeetingStatusServer](http:\/\/github.com\/asp55\/MeetingStatusServer) which runs on node.js and can either be run locally as its own thing, or hosted remotely.", + "", + "If using the external server, you will to create a key to identify your \"room\" and then provide that information to the spoon. ", + "In that case, before `spoon.WatchForMeeting:start()` add the following to your `~\/.hammerspoon\/init.lua`", + "", + "```", + "spoon.WatchForMeeting.sharing.useServer = true", + "spoon.WatchForMeeting.sharing.serverURL=\"[YOUR SERVER URL]\"", + "spoon.WatchForMeeting.sharing.key=\"[YOUR KEY]\"", + "```", + "", + "or ", + "", + "```", + "spoon.WatchForMeeting.sharing = {", + " useServer = true,", + " serverURL = \"[YOUR SERVER URL]\",", + " key=\"[YOUR KEY]\"", + "}", + "```", + "", + "## Disable", + "If you don't want to broadcast your status to a webpage, simply disable sharing", + "```", + " spoon.WatchForMeeting.sharing = {", + " enabled = false", + " }", + "```" + ], + "desc" : "A Table containing the settings that control sharing.", + "notes" : [ + + ], + "signature" : "WatchForMeeting.sharing", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.sharing", + "parameters" : [ + + ] + }, + { + "doc" : "(Read-only) The hs.application for zoom if it is running, otherwise nil", + "name" : "zoom", + "stripped_doc" : [ + "(Read-only) The hs.application for zoom if it is running, otherwise nil" + ], + "desc" : "(Read-only) The hs.application for zoom if it is running, otherwise nil", + "notes" : [ + + ], + "signature" : "WatchForMeeting.zoom", + "type" : "Variable", + "returns" : [ + + ], + "def" : "WatchForMeeting.zoom", + "parameters" : [ + + ] + }, + { + "doc" : "Monitors Zoom and updates status accordingly\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "auto", + "stripped_doc" : [ + "Monitors Zoom and updates status accordingly" + ], + "desc" : "Monitors Zoom and updates status accordingly", + "notes" : [ + + ], + "signature" : "WatchForMeeting:auto()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:auto()", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Disables monitoring and reports as being in a meeting. \nUseful when meeting type is not supported (currently any platform that isn't zoom.)\n\nParameters:\n * mic_open - A boolean indicating if the mic is open\n * video_on - A boolean indicating if the video camera is on\n * sharing - A boolean indicating if screen sharing is on\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "fake", + "stripped_doc" : [ + "Disables monitoring and reports as being in a meeting. ", + "Useful when meeting type is not supported (currently any platform that isn't zoom.)" + ], + "desc" : "Disables monitoring and reports as being in a meeting.", + "notes" : [ + + ], + "signature" : "WatchForMeeting:fake(mic_open, video_on, sharing)", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:fake(mic_open, video_on, sharing)", + "parameters" : [ + " * mic_open - A boolean indicating if the mic is open", + " * video_on - A boolean indicating if the video camera is on", + " * sharing - A boolean indicating if screen sharing is on" + ] + }, + { + "doc" : "Restarts a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "start", + "stripped_doc" : [ + "Restarts a WatchForMeeting object" + ], + "desc" : "Restarts a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:start()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:start()", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Starts a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "start", + "stripped_doc" : [ + "Starts a WatchForMeeting object" + ], + "desc" : "Starts a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:start() -> WatchForMeeting", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:start() -> WatchForMeeting", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Stops a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "stop", + "stripped_doc" : [ + "Stops a WatchForMeeting object" + ], + "desc" : "Stops a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:stop()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:stop()", + "parameters" : [ + " * None" + ] + } + ], + "Command" : [ + + ], + "Field" : [ + + ], + "Method" : [ + { + "doc" : "Starts a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "start", + "stripped_doc" : [ + "Starts a WatchForMeeting object" + ], + "desc" : "Starts a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:start() -> WatchForMeeting", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:start() -> WatchForMeeting", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Stops a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "stop", + "stripped_doc" : [ + "Stops a WatchForMeeting object" + ], + "desc" : "Stops a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:stop()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:stop()", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Restarts a WatchForMeeting object\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "start", + "stripped_doc" : [ + "Restarts a WatchForMeeting object" + ], + "desc" : "Restarts a WatchForMeeting object", + "notes" : [ + + ], + "signature" : "WatchForMeeting:start()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:start()", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Monitors Zoom and updates status accordingly\n\nParameters:\n * None\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "auto", + "stripped_doc" : [ + "Monitors Zoom and updates status accordingly" + ], + "desc" : "Monitors Zoom and updates status accordingly", + "notes" : [ + + ], + "signature" : "WatchForMeeting:auto()", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:auto()", + "parameters" : [ + " * None" + ] + }, + { + "doc" : "Disables monitoring and reports as being in a meeting. \nUseful when meeting type is not supported (currently any platform that isn't zoom.)\n\nParameters:\n * mic_open - A boolean indicating if the mic is open\n * video_on - A boolean indicating if the video camera is on\n * sharing - A boolean indicating if screen sharing is on\n\nReturns:\n * The spoon.WatchForMeeting object", + "name" : "fake", + "stripped_doc" : [ + "Disables monitoring and reports as being in a meeting. ", + "Useful when meeting type is not supported (currently any platform that isn't zoom.)" + ], + "desc" : "Disables monitoring and reports as being in a meeting.", + "notes" : [ + + ], + "signature" : "WatchForMeeting:fake(mic_open, video_on, sharing)", + "type" : "Method", + "returns" : [ + " * The spoon.WatchForMeeting object" + ], + "def" : "WatchForMeeting:fake(mic_open, video_on, sharing)", + "parameters" : [ + " * mic_open - A boolean indicating if the mic is open", + " * video_on - A boolean indicating if the video camera is on", + " * sharing - A boolean indicating if screen sharing is on" + ] + } + ], + "name" : "WatchForMeeting" + } +] diff --git a/Source/WatchForMeeting.spoon/init.lua b/Source/WatchForMeeting.spoon/init.lua new file mode 100644 index 00000000..724c7fec --- /dev/null +++ b/Source/WatchForMeeting.spoon/init.lua @@ -0,0 +1,900 @@ +--- === WatchForMeeting === +--- +--- A Spoon to answer the question +--- > Are you in a meeting? +--- +--- Watches to see if: +--- 1) Zoom is running +--- 2) Are you on a call +--- 3) Are you on mute, is your camera on, and/or are you screen sharing +--- +--- And then lets you share that information. +--- +--- # Basic Usage +--- +--- To get going right out of the box, in your `~/.hammerspoon/init.lua` add these lines: +--- ``` +--- hs.loadSpoon("WatchForMeeting") +--- spoon.WatchForMeeting:start() +--- ``` +--- +--- This will start the spoon monitoring for zoom calls, and come with the default status page, and menubar configurations. +--- + + +--We'll store some stuff in an internal table + +local _internal = {} + + +-- create a namespace + +local WatchForMeeting={} +WatchForMeeting.__index = WatchForMeeting + + +-- Metadata +WatchForMeeting.name = "WatchForMeeting" +WatchForMeeting.version = "2.0.3" +WatchForMeeting.author = "Andrew Parnell <aparnell@gmail.com>" +WatchForMeeting.homepage = "https://github.com/Hammerspoon/Spoons" +WatchForMeeting.license = "MIT - https://opensource.org/licenses/MIT" + +-- Event callbacks +local events = { + meetingChange=true, + meetingStarted=true, + meetingStopped=true, + micChange=true, + micOn=true, + micOff=true, + videoChange=true, + videoOn=true, + videoOff=true, + screensharingChange=true, + screensharingOn=true, + screensharingOff=true, + +} +_internal.events = {} +for k in pairs(events) do WatchForMeeting[k]=k end + + +------------------------------------------- +-- Declare Variables +------------------------------------------- + + +--- WatchForMeeting.logger +--- Variable +--- hs.logger object used within the Spoon. Can be accessed to set the default log level for the messages coming from the Spoon. +WatchForMeeting.logger = hs.logger.new('WatchForMeeting') + + +-- private variable to track if spoon is already running or not. (Makes it easier to find local variables) +_internal.running = false + + ------------------------------------------- + -- Special Variables (stored in _internal and accessed through metamethods defined below) + ------------------------------------------- + + --- WatchForMeeting.sharing + --- Variable + --- A Table containing the settings that control sharing. + --- + --- | Key | Description | Default | + --- | --- | ----------- | ------- | + --- | enabled | Whether or not sharing is enabled.<br/><br/>When false, the spoon will still monitor meeting status to [meetingState](#meetingState), but you will need to write your own automations for what to do with that info. | _true_ | + --- | useServer | Do you want to use an external server? (See *Configuration Options* below) | _false_ | + --- | | ↓ _required info when `useServer=false`_ | | + --- | port | What port to run the self hosted server when WatchForMeeting.sharing.useServer is false. | _8080_ | + --- | | ↓ _required info when `useServer=true`_ | | + --- | serverURL | The complete url for the external server, including port. IE: `http://localhost:8080` | _nil_ | + --- | key | UUID to identify the room. Value is provided when the room is added on the server side. | _nil_ | + --- | maxConnectionAttempts | Maximum number of connection attempts when using an external server. When less than 0, infinite retrys | _-1_ | + --- | waitBeforeRetry | Time, in seconds, between connection attempts when using an external server | _5_ | + --- + --- # Configuration Options + --- ## Default + --- In order to minimize dependencies, by default this spoon uses a [hs.httpserver](https://www.hammerspoon.org/docs/hs.httpserver.html) to host the status page. This comes with a significant downside of: only the last client to load the page will receive status updates. Any previously connected clients will remain stuck at the last update they received before that client connected. + --- + --- Once you are running the spoon, assuming you haven't changed the port (and nothing else is running at that location) you can reach your status page at http://localhost:8080 + --- + --- ## Better - MeetingStatusServer + --- For a better experience I recommend utilizing an external server to receive updates via websockets, and broadcast them to as many clients as you wish to connect. + --- + --- For that purpose I've built [http://github.com/asp55/MeetingStatusServer](http://github.com/asp55/MeetingStatusServer) which runs on node.js and can either be run locally as its own thing, or hosted remotely. + --- + --- If using the external server, you will to create a key to identify your "room" and then provide that information to the spoon. + --- In that case, before `spoon.WatchForMeeting:start()` add the following to your `~/.hammerspoon/init.lua` + --- + --- ``` + --- spoon.WatchForMeeting.sharing.useServer = true + --- spoon.WatchForMeeting.sharing.serverURL="[YOUR SERVER URL]" + --- spoon.WatchForMeeting.sharing.key="[YOUR KEY]" + --- ``` + --- + --- or + --- + --- ``` + --- spoon.WatchForMeeting.sharing = { + --- useServer = true, + --- serverURL = "[YOUR SERVER URL]", + --- key="[YOUR KEY]" + --- } + --- ``` + --- + --- ## Disable + --- If you don't want to broadcast your status to a webpage, simply disable sharing + --- ``` + --- spoon.WatchForMeeting.sharing = { + --- enabled = false + --- } + --- ``` + --- + + _internal.sharingDefaults = { + enabled = true, + useServer = false, + port = 8080, + serverURL = nil, + key = nil, + maxConnectionAttempts = -1, --when less than 0, infinite retrys + waitBeforeRetry = 5, + } + _internal.sharing = setmetatable({}, {__index=_internal.sharingDefaults}) + + + --- WatchForMeeting.menubar + --- Variable + --- A Table containing the settings that control sharing. + --- + --- | Key | Description | Default | + --- | --- | ----------- | ------- | + --- | enabled | Whether or not to show the menu bar. | _true_ | + --- | color | Whether or not to use color icons. | _true_ | + --- | detailed | Whether or not to use the detailed icon set. | _true_ | + --- | showFullState | Whether the menubar icon should represent the full state<br/>(IE: Mic On/Off, Video On/Off, & Screen Sharing) | _true_ | + --- + --- + --- ## Icons + --- + --- <table> + --- <thead> + --- <tr> + --- <th> + --- <code>WatchForMeeting.menuBar = {...}</code> → + --- </th> + --- <th><code>color=true,</code><br/><code>detailed=true,</code></th> + --- <th><code>color=true,</code><br/><code>detailed=false,</code></th> + --- <th><code>color=false,</code><br/><code>detailed=true,</code></th> + --- <th><code>color=false,</code><br/><code>detailed=false,</code></th> + --- </tr> + --- <tr> + --- <th>State (See: <a href="#meetingState">WatchForMeeting.meetingState</a>) ↓ + --- </th> + --- <th colspan="4"><code>showFullState=true</code> or <code>showFullState=false</code></th> + --- </tr> + --- </thead> + --- <tbody> + --- <tr> + --- <td>Available</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Free.png" alt="Free slash Available" height="16" /></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Free.png" alt="Free slash Available" height="16" /></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Free.png" alt="Free slash Available" height="16" /></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Free.png" alt="Free slash Available" height="16" /></td> + --- </tr> + --- <tr> + --- <td>Busy</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting.png" alt="In meeting, no additional status" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting.png" alt="In meeting, no additional status" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting.png" alt="In meeting, no additional status" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting.png" alt="In meeting, no additional status" height="16"></td> + --- </tr> + --- <tr> + --- <td></td> + --- <th colspan="4"><code>showFullState=true</code> only</th> + --- </tr> + --- <tr> + --- <td>Busy + Mic On</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Mic.png" alt="In meeting, mic:on, video:off, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Mic.png" alt="In meeting, mic:on, video:off, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Mic.png" alt="In meeting, mic:on, video:off, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Mic.png" alt="In meeting, mic:on, video:off, screensharing:off" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Video On</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Vid.png" alt="In meeting, mic:off, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Vid.png" alt="In meeting, mic:off, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Vid.png" alt="In meeting, mic:off, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Vid.png" alt="In meeting, mic:off, video:on, screensharing:off" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Screen Sharing</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Screen.png" alt="In meeting, mic:off, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Screen.png" alt="In meeting, mic:off, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Screen.png" alt="In meeting, mic:off, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Screen.png" alt="In meeting, mic:off, video:off, screensharing:on" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Mic On + Video On</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Mic-Vid.png" alt="In meeting, mic:on, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Mic-Vid.png" alt="In meeting, mic:on, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Mic-Vid.png" alt="In meeting, mic:on, video:on, screensharing:off" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Mic-Vid.png" alt="In meeting, mic:on, video:on, screensharing:off" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Mic On + Screen Sharing</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Mic-Screen.png" alt="In meeting, mic:on, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Mic-Screen.png" alt="In meeting, mic:on, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Mic-Screen.png" alt="In meeting, mic:on, video:off, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Mic-Screen.png" alt="In meeting, mic:on, video:off, screensharing:on" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Video On + Screen Sharing</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Vid-Screen.png" alt="In meeting, mic:off, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Vid-Screen.png" alt="In meeting, mic:off, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Vid-Screen.png" alt="In meeting, mic:off, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Vid-Screen.png" alt="In meeting, mic:off, video:on, screensharing:on" height="16"></td> + --- </tr> + --- <tr> + --- <td>Busy + Mic On + Video On + Screen Sharing</td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.png" alt="In meeting, mic:on, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.png" alt="In meeting, mic:on, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.png" alt="In meeting, mic:on, video:on, screensharing:on" height="16"></td> + --- <td><img src="https://raw.githubusercontent.com/asp55/WatchForMeeting/main/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.png" alt="In meeting, mic:on, video:on, screensharing:on" height="16"></td> + --- </tr> + --- </tbody> + --- </table> + + + _internal.menubarDefaults = { + enabled = true, + color = true, + detailed = true, + showFullState = true + } + _internal.menubar__newIndex = function (table, key, value) + if(key=="enabled") then + if(value) then + _internal.meetingMenuBar:returnToMenuBar() + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + else + _internal.meetingMenuBar:removeFromMenuBar() + end + else + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + end + end + + _internal.menubar = setmetatable({}, {__index=_internal.menubarDefaults, __newindex=_internal.menubar__newIndex}) + + --- WatchForMeeting.mode + --- Variable + --- Number representing which mode WatchForMeeting should be running + --- + --- - *0* - Automatic (default) + --- -- Monitors Zoom and updates status accordingly + --- - *1* - Busy + --- -- Fakes a meeting. (Marks as in meeting, and signals that the mic is live, camera is on, and screen is sharing.) Useful when meeting type is not supported (Currently any platform that isn't zoom.) + _internal.mode = 0 + + --- WatchForMeeting.zoom + --- Variable + --- (Read-only) The hs.application for zoom if it is running, otherwise nil + _internal.zoom = nil + + --- WatchForMeeting.meetingState + --- Variable + --- (Read-only) Either false (when not in a meeting) or a table (when in a meeting) + --- + --- | Value | Description | + --- | ----------------------------------------------------------------------- | ----------- | + --- | `false` | Available | + --- | `{mic_open = [Boolean], video_on = [Boolean], sharing = [Boolean] }` | Busy | + _internal.meetingState = false + _internal.lastMeetingState = nil; + + + --- WatchForMeeting.faking + --- Variable + --- (Read-only) Boolean representing if the meeting is real or faked + + + -- MetaMethods + WatchForMeeting = setmetatable(WatchForMeeting, { + --GET + __index = function (table, key) + if(key=="zoom" or key=="meetingState" or key=="faking" or key=="menubar" or key=="mode" or key=="sharing") then + return _internal[key] + else + return rawget( table, key ) + end + end, + --SET + __newindex = function (table, key, value) + if(key=="zoom" or key=="meetingState" or key=="faking") then + --skip writing zoom or meeting state to watchformeeting as they are read-only fields + elseif(key=="menubar") then + _internal.menubar = setmetatable(value, {__index=_internal.menubarDefaults, __newindex=_internal.menubar__newIndex}) + if(_internal.menubar.enabled) then + _internal.meetingMenuBar:returnToMenuBar() + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + else + _internal.meetingMenuBar:removeFromMenuBar() + end + elseif(key=="mode") then + if(value == 1) then + table:fake() + else + table:auto() + end + elseif(key=="sharing") then + + _internal.sharing = setmetatable(value, {__index=_internal.sharingDefaults}) + else + return rawset(table, key, value) + end + end + }) + +------------------------------------------- +-- End of Declare Variables +------------------------------------------- + +------------------------------------------- +-- Menu Bar +------------------------------------------- + +_internal.meetingMenuBar = hs.menubar.new(false) + + +function _internal.updateMenuIcon(status, faking) + if(_internal.menubar.enabled) then + local iconPath = 'menubar-icons/' + + if(_internal.menubar.color) then + iconPath = iconPath..'Color/' + else + iconPath = iconPath..'Template/' + end + + if(_internal.menubar.detailed) then + iconPath = iconPath..'Detailed/' + else + iconPath = iconPath..'Minimal/' + end + + local iconFile = "" + if(status) then + iconFile = "Meeting" + if(_internal.menubar.showFullState and (status.mic_open or status.video_on or status.sharing)) then + if(status.mic_open) then iconFile = iconFile.."-Mic" end + if(status.video_on) then iconFile = iconFile.."-Vid" end + if(status.sharing) then iconFile = iconFile.."-Screen" end + end + if(faking) then iconFile = iconFile.."-Faking" end + iconFile = iconFile..".pdf" + else + iconFile = "Free.pdf" + end + + _internal.meetingMenuBar:setIcon(hs.spoons.resourcePath(iconPath..iconFile),not _internal.menubar.color) + end +end + +------------------------------------------- +-- End of Menu Bar +------------------------------------------- + + +------------------------------------------- +-- Web Server +------------------------------------------- +_internal.server = nil +_internal.websocketStatus = "closed" + +local function composeJsonUpdate(meetingState) + local message = {action="update", inMeeting=meetingState} + return hs.json.encode(message) +end + +local monitorfile = io.open(hs.spoons.resourcePath("monitor.html"), "r") +local htmlContent = monitorfile:read("*a") +monitorfile:close() + +local function selfhostHttpCallback() + local websocketPath = "ws://"..hs.network.interfaceDetails(hs.network.primaryInterfaces())["IPv4"]["Addresses"][1]..":"..WatchForMeeting.sharing.port.."/ws" + htmlContent = string.gsub(htmlContent,"%%websocketpath%%",websocketPath) + return htmlContent, 200, {} +end + +local function selfhostWebsocketCallback(msg) + return composeJsonUpdate(_internal.meetingState) +end +------------------------------------------- +-- End Web Server +------------------------------------------- + +------------------------------------------- +-- Zoom Monitor +------------------------------------------- + + +local function emit(event) + local fns=_internal.events[event] + if fns then + for fn in pairs(fns) do fn() end + end + end + +local function updateCallbacks() + if(_internal.server and _internal.websocketStatus == "open") then _internal.server:send(composeJsonUpdate(_internal.meetingState)) end + + -- Emit appropriate events + if type(_internal.meetingState)~=type(_internal.lastMeetingState) then + emit(WatchForMeeting.meetingChange) + if _internal.meetingState==false then + if type(_internal.lastMeetingState)=="table" then + emit(WatchForMeeting.micChange) + emit(WatchForMeeting.videoChange) + emit(WatchForMeeting.screensharingChange) + if _internal.lastMeetingState.mic_open then + emit(WatchForMeeting.micOff) + end + if _internal.lastMeetingState.video_on then + emit(WatchForMeeting.videoOff) + end + if _internal.lastMeetingState.sharing then + emit(WatchForMeeting.screensharingOff) + end + end + + emit(WatchForMeeting.meetingStopped) + else + emit(WatchForMeeting.meetingStarted) + end + end + + if type(_internal.meetingState)=="table" then + + if not _internal.lastMeetingState or _internal.lastMeetingState.mic_open~=_internal.meetingState.mic_open then + emit(WatchForMeeting.micChange) + if _internal.meetingState.mic_open then + emit(WatchForMeeting.micOn) + else + emit(WatchForMeeting.micOff) + end + end + + if not _internal.lastMeetingState or _internal.lastMeetingState.video_on~=_internal.meetingState.video_on then + emit(WatchForMeeting.videoChange) + if _internal.meetingState.video_on then + emit(WatchForMeeting.videoOn) + else + emit(WatchForMeeting.videoOff) + end + end + + if not _internal.lastMeetingState or _internal.lastMeetingState.sharing~=_internal.meetingState.sharing then + emit(WatchForMeeting.screensharingChange) + if _internal.meetingState.sharing then + emit(WatchForMeeting.screensharingOn) + else + emit(WatchForMeeting.screensharingOff) + end + end + end + + _internal.lastMeetingState = _internal.meetingState + +end + +local function currentlyInMeeting() + --If zoom is running and the second menu in zoom's menu bar is "Meeting" then we're in a meeting + local inMeetingState = (_internal.zoom ~= nil and _internal.zoom:getMenuItems()[2].AXTitle == "Meeting") + return inMeetingState +end + +--declare startStopWatchMeeting before watchMeeting, define it after. +local startStopWatchMeeting = function() end + +local watchMeeting = hs.timer.new(0.5, function() + + if(currentlyInMeeting() == false) then + _internal.updateMenuIcon(false) + -- No longer in a meeting, stop watching the meeting + startStopWatchMeeting() + + updateCallbacks() + return + else + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + --Watch for zoom menu items + local _mic_open = _internal.zoom:findMenuItem({"Meeting", "Unmute audio"})==nil + local _video_on = _internal.zoom:findMenuItem({"Meeting", "Start video"})==nil + local _sharing = _internal.zoom:findMenuItem({"Meeting", "Start share"})==nil + if((_internal.meetingState.mic_open ~= _mic_open) or (_internal.meetingState.video_on ~= _video_on) or (_internal.meetingState.sharing ~= _sharing)) then + _internal.meetingState = {mic_open = _mic_open, video_on = _video_on, sharing = _sharing} + WatchForMeeting.logger.d("In Meeting: ", (_internal.meetingState and true)," Open Mic: ",_internal.meetingState.mic_open," Video-ing:",_internal.meetingState.video_on," Sharing",_internal.meetingState.sharing) + updateCallbacks() + end + end +end) + +startStopWatchMeeting = function() + if(not _internal.faking) then + if(_internal.meetingState == false and currentlyInMeeting() == true) then + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + WatchForMeeting.logger.d("Start Meeting") + _internal.meetingState = {} + watchMeeting:start() + watchMeeting:fire() + elseif(_internal.meetingState and currentlyInMeeting() == false) then + _internal.updateMenuIcon(false) + WatchForMeeting.logger.d("End Meeting") + watchMeeting:stop() + _internal.meetingState = false + updateCallbacks() + end + else + --If we're faking the meeting we don't need watchMeeting to be regularly checking the status of the meeting elements + watchMeeting:stop() + end +end + + +local function checkMeetingStatus(window, name, event) + WatchForMeeting.logger.d("Check Meeting Status",window,name,event) + _internal.zoom = window:application() + startStopWatchMeeting() +end + +-- Monitor zoom for running meeting +hs.application.enableSpotlightForNameSearches(true) +_internal.zoomWindowFilter = hs.window.filter.new(false,"ZoomWindowFilterLog",0):setAppFilter('zoom.us') +_internal.zoomWindowFilter:subscribe(hs.window.filter.hasWindow,checkMeetingStatus,true) +_internal.zoomWindowFilter:subscribe(hs.window.filter.hasNoWindows,checkMeetingStatus) +_internal.zoomWindowFilter:subscribe(hs.window.filter.windowDestroyed,checkMeetingStatus) +_internal.zoomWindowFilter:subscribe(hs.window.filter.windowTitleChanged,checkMeetingStatus) +_internal.zoomWindowFilter:pause() + +------------------------------------------- +-- End of Zoom Monitor +------------------------------------------- + + +_internal.connectionAttempts = 0 +_internal.connectionError = false + + +--Declare function before start connection because they're circular +local function retryConnection() +end +local function stopConnection() + if(_internal.server) then + if(getmetatable(_internal.server).stop) then _internal.server:stop() end + if(getmetatable(_internal.server).close) then _internal.server:close() end + end +end + +local function serverWebsocketCallback(type, message) + if(type=="open") then + _internal.websocketStatus = "open" + _internal.connectionAttempts = 0 + + local draft = {action="identify", key=WatchForMeeting.sharing.key, type="room", status={inMeeting=_internal.meetingState}} + _internal.server:send(hs.json.encode(draft)) + elseif(type == "closed" and _internal.running) then + _internal.websocketStatus = "closed" + if(_internal.connectionError) then + WatchForMeeting.logger.d("Lost connection to websocket, will not reattempt due to error") + else + WatchForMeeting.logger.d("Lost connection to websocket, attempting to reconnect in "..WatchForMeeting.sharing.waitBeforeRetry.." seconds") + retryConnection() + end + elseif(type == "fail") then + _internal.websocketStatus = "fail" + if(WatchForMeeting.sharing.maxConnectionAttempts > 0) then + WatchForMeeting.logger.d("Could not connect to websocket server. attempting to reconnect in "..WatchForMeeting.sharing.waitBeforeRetry.." seconds. (Attempt ".._internal.connectionAttempts.."/"..WatchForMeeting.sharing.maxConnectionAttempts..")") + else + WatchForMeeting.logger.d("Could not connect to websocket server. attempting to reconnect in "..WatchForMeeting.sharing.waitBeforeRetry.." seconds. (Attempt ".._internal.connectionAttempts..")") + end + retryConnection() + elseif(type == "received") then + local parsed = hs.json.decode(message); + if(parsed.error) then + _internal.connectionError = true; + if(parsed.errorType == "badkey") then + stopConnection() + hs.showError("") + WatchForMeeting.logger.e("WatchForMeeting.sharing.key not valid. Make sure that key has been established on the server.") + end + else + WatchForMeeting.logger.d("Websocket Message received: ", hs.inspect.inspect(parsed)); + end + + else + WatchForMeeting.logger.d("Websocket Callback "..type, message) + end +end + + +local function startConnection() + if(WatchForMeeting.sharing) then + if(WatchForMeeting.sharing.useServer) then + WatchForMeeting.logger.d("Connecting to server at "..WatchForMeeting.sharing.serverURL) + _internal.connectionAttempts = _internal.connectionAttempts + 1 + _internal.websocketStatus = "connecting" + _internal.server = hs.websocket.new(WatchForMeeting.sharing.serverURL, serverWebsocketCallback); + else + WatchForMeeting.logger.d("Starting Self Hosted Server on port "..WatchForMeeting.sharing.port) + _internal.server = hs.httpserver.new() + _internal.server:websocket("/ws", selfhostWebsocketCallback) + _internal.websocketStatus = "open" + _internal.server:setPort(WatchForMeeting.sharing.port) + _internal.server:setCallback(selfhostHttpCallback) + _internal.server:start() + end + end +end + +--redefine retryConnection now that startConnection & stopConnection exist. +retryConnection = function() + if(WatchForMeeting.sharing.maxConnectionAttempts > 0 and _internal.connectionAttempts >= WatchForMeeting.sharing.maxConnectionAttempts) then + WatchForMeeting.logger.e("Maximum Connection Attempts failed") + stopConnection() + elseif(_internal.connectionError) then + stopConnection() + else + hs.timer.doAfter(WatchForMeeting.sharing.waitBeforeRetry, startConnection) + end +end + + +function validateShareSettings() + WatchForMeeting.logger.d("validateShareSettings") + if(WatchForMeeting.sharing.useServer and (WatchForMeeting.sharing.serverURL==nil or WatchForMeeting.sharing.key==nil)) then + hs.showError("") + if(WatchForMeeting.sharing.serverURL==nil) then WatchForMeeting.logger.e("WatchForMeeting.sharing.serverURL required when using a server") end + if(WatchForMeeting.sharing.key==nil) then WatchForMeeting.logger.e("WatchForMeeting.sharing.key required when using a server") end + return false + elseif(not WatchForMeeting.sharing.useServer and WatchForMeeting.sharing.port==nil) then + hs.showError("") + WatchForMeeting.logger.e("WatchForMeeting.sharing.port required when self hosting") + return false + else + return true + end +end + + +------------------------------------------- +-- Methods +------------------------------------------- + + +--- WatchForMeeting:start() -> WatchForMeeting +--- Method +--- Starts a WatchForMeeting object +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * The spoon.WatchForMeeting object +function WatchForMeeting:start() + if(not _internal.running) then + _internal.running = true + if(self.sharing.enabled and validateShareSettings()) then + startConnection() + end + + if(self.menubar.enabled) then + _internal.meetingMenuBar:returnToMenuBar() + end + + if(_internal.mode == 1 ) then + self:fake() + else + self:auto() + end + end + + return self +end + +--- WatchForMeeting:stop() +--- Method +--- Stops a WatchForMeeting object +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * The spoon.WatchForMeeting object +function WatchForMeeting:stop() + _internal.running = false + stopConnection() + + _internal.lastMeetingState = nil + + _internal.meetingMenuBar:removeFromMenuBar() + _internal.zoomWindowFilter:pause() + return self +end + +--- WatchForMeeting:start() +--- Method +--- Restarts a WatchForMeeting object +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * The spoon.WatchForMeeting object +function WatchForMeeting:restart() + self:stop() + return self:start() +end + + + +--- WatchForMeeting:auto() +--- Method +--- Monitors Zoom and updates status accordingly +--- +--- Parameters: +--- * None +--- +--- Returns: +--- * The spoon.WatchForMeeting object +function WatchForMeeting:auto() + _internal.mode = 0 + + if(_internal.running) then + _internal.faking = false + _internal.meetingState = false + startStopWatchMeeting() + + _internal.meetingMenuBar:setMenu({ + { title = "Meeting Status:", disabled = true }, + { title = "Automatic", checked = true }, + { title = "Busy", checked = false, fn=function() WatchForMeeting:fake() end } + }) + + + --Update everything + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + updateCallbacks() + + --turn on the zoom window monitor + _internal.zoomWindowFilter:resume() + end + + return self +end + + +--- WatchForMeeting:fake(mic_open, video_on, sharing) +--- Method +--- Disables monitoring and reports as being in a meeting. Useful when meeting type is not supported (currently any platform that isn't zoom.) +--- +--- Parameters: +--- * mic_open - A boolean indicating if the mic is open +--- * video_on - A boolean indicating if the video camera is on +--- * sharing - A boolean indicating if screen sharing is on +--- +--- Returns: +--- * The spoon.WatchForMeeting object +function WatchForMeeting:fake(_mic_open, _video_on, _sharing) + _internal.mode = 1 + + if(_internal.running) then + _internal.faking = true + _internal.meetingState = {mic_open = _mic_open, video_on = _video_on, sharing = _sharing} + startStopWatchMeeting() + + local meetingMenu = { + { title = "Meeting Status:", disabled = true }, + { title = "Automatic", checked = false, fn=function() WatchForMeeting:auto() end }, + { title = "Busy", checked = true }, + { title = "-"} + } + if(not (_mic_open and _video_on and _sharing)) then + table.insert(meetingMenu, { title = "Select All", fn=function() WatchForMeeting:fake(true, true, true) end }) + else + table.insert(meetingMenu, { title = "Select None", fn=function() WatchForMeeting:fake(false, false, false) end }) + end + + table.insert(meetingMenu, { title = "Mic On", indent=1, checked = _internal.meetingState.mic_open, fn=function() WatchForMeeting:fake(not _mic_open, _video_on, _sharing) end}) + table.insert(meetingMenu, { title = "Video On", indent=1, checked = _internal.meetingState.video_on, fn=function() WatchForMeeting:fake(_mic_open, not _video_on, _sharing) end }) + table.insert(meetingMenu, { title = "Sharing Screen", indent=1, checked = _internal.meetingState.sharing, fn=function() WatchForMeeting:fake(_mic_open, _video_on, not _sharing) end }) + + + if(_mic_open or _video_on or _sharing) then + table.insert(meetingMenu, { title = "Clear", fn=function() WatchForMeeting:fake(false, false, false) end }) + end + _internal.meetingMenuBar:setMenu(meetingMenu) + + _internal.zoomWindowFilter:pause() + + updateCallbacks() + _internal.updateMenuIcon(_internal.meetingState, _internal.faking) + end + + return self +end + + + +--- WatchForMeeting:subscribe(event, fn) +--- Method +--- Subscribe to one event with one or more functions +--- +--- Parameters: +--- * event - string of the event to subscribe to (see the `spoon.WatchForMeeting` constants) +--- * fn - function or list of functions, the callback(s) to add for the event(s); +--- +--- Returns: +--- * The `spoon.WatchForMeeting` object for method chaining + +function WatchForMeeting:subscribe(event, fns) + if not events[event] then error('invalid event: '..event,3) end + if type(fns)=='function' then fns = {fns} end + if type(fns)~='table' then error('fn must be a function or table of functions',3) end + for _,fn in pairs(fns) do + if type(fn)~='function' then error('fn must be a function or table of functions',3) end + if not _internal.events[event] then _internal.events[event]={} end + if not _internal.events[event][fn] then + _internal.events[event][fn]=true + WatchForMeeting.logger.df('added callback for event %s',event) + end + end + return self +end + +--- WatchForMeeting:unsubscribe(event, fn) -> hs.window.filter object +--- Method +--- Removes one or more event subscriptions +--- +--- Parameters: +--- * event - string of the event to unsubscribe; +--- * fn - function or list of functions, the callback(s) to remove; if omitted, all callbacks will be unsubscribed from `event`(s) +--- +--- Returns: +--- * The `spoon.WatchForMeeting` object for method chaining +--- +function WatchForMeeting:unsubscribe(event,fn) + if _internal.events[event] and _internal.events[event][fn] then + WatchForMeeting.logger.df('removed callback for event %s',event) + _internal.events[event][fn]=nil + if not next(_internal.events[event]) then + WatchForMeeting.logger.df('no more callbacks for event %s',event) + _internal.events[event]=nil + end + end +end + +--- WatchForMeeting:unsubscribeEvent(event) -> hs.window.filter object +--- Method +--- Removes all subscriptions from one event +--- +--- Parameters: +--- * event - string of the event to unsubscribe; ; +--- +--- Returns: +--- * The `spoon.WatchForMeeting` object for method chaining +--- +function WatchForMeeting:unsubscribeEvent(event) + if not events[event] then error('invalid event: '..event,3) end + if _internal.events[event] then WatchForMeeting.logger.df('removed all callbacks for event %s',event) end + _internal.events[event]=nil + end + + +------------------------------------------- +-- End of Methods +------------------------------------------- + +return WatchForMeeting diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.pdf new file mode 100644 index 00000000..ca50ecbb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.png new file mode 100644 index 00000000..e3a998be Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Free.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.pdf new file mode 100644 index 00000000..f80ecaa7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.png new file mode 100644 index 00000000..1d0acf45 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.pdf new file mode 100644 index 00000000..bc90d4d9 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.png new file mode 100644 index 00000000..b42a35fe Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.pdf new file mode 100644 index 00000000..7cdebf14 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.png new file mode 100644 index 00000000..d4d71130 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.pdf new file mode 100644 index 00000000..abfe439b Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.png new file mode 100644 index 00000000..b42b0438 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.pdf new file mode 100644 index 00000000..28c4e800 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.png new file mode 100644 index 00000000..db61c66c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..369fda4a Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.png new file mode 100644 index 00000000..398d7e19 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.pdf new file mode 100644 index 00000000..efcbd4ff Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.png new file mode 100644 index 00000000..cc21d26c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.pdf new file mode 100644 index 00000000..5aca4ded Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.png new file mode 100644 index 00000000..cd9c2077 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.pdf new file mode 100644 index 00000000..1eeeacaf Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.png new file mode 100644 index 00000000..759a5a0f Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Mic.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.pdf new file mode 100644 index 00000000..91e9edac Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.png new file mode 100644 index 00000000..7d967c3e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.pdf new file mode 100644 index 00000000..ea1fcf31 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.png new file mode 100644 index 00000000..a1f3186d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.pdf new file mode 100644 index 00000000..f374c7d0 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.png new file mode 100644 index 00000000..b9ec48e7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..c3fbc5bf Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.png new file mode 100644 index 00000000..80a03d51 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.pdf new file mode 100644 index 00000000..93377e0b Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.png new file mode 100644 index 00000000..9f174eb9 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.pdf new file mode 100644 index 00000000..c29d59a2 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.png new file mode 100644 index 00000000..9e438e78 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.pdf new file mode 100644 index 00000000..1cd0feb2 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.png new file mode 100644 index 00000000..2f138e2d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Detailed/Meeting.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.pdf new file mode 100644 index 00000000..33423ea2 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.png new file mode 100644 index 00000000..af02b107 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Free.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.pdf new file mode 100644 index 00000000..3811fcd5 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.png new file mode 100644 index 00000000..0deeb0eb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.pdf new file mode 100644 index 00000000..9c923c65 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.png new file mode 100644 index 00000000..448aff0d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.pdf new file mode 100644 index 00000000..7146b10e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.png new file mode 100644 index 00000000..3d17ff26 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.pdf new file mode 100644 index 00000000..333dad9e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.png new file mode 100644 index 00000000..fa661050 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.pdf new file mode 100644 index 00000000..7a535c93 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.png new file mode 100644 index 00000000..962feeab Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..be142e47 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.png new file mode 100644 index 00000000..7919c65c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.pdf new file mode 100644 index 00000000..4c30b01d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.png new file mode 100644 index 00000000..2cc8a84f Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.pdf new file mode 100644 index 00000000..7b8dc2a1 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.png new file mode 100644 index 00000000..341d06ed Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.pdf new file mode 100644 index 00000000..061926ed Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.png new file mode 100644 index 00000000..c1193b3a Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Mic.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.pdf new file mode 100644 index 00000000..e013187c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.png new file mode 100644 index 00000000..b6e6144e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.pdf new file mode 100644 index 00000000..63490a3b Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.png new file mode 100644 index 00000000..4cde1a70 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.pdf new file mode 100644 index 00000000..c7282e02 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.png new file mode 100644 index 00000000..89bd87e2 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..a967b151 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.png new file mode 100644 index 00000000..cbac2836 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.pdf new file mode 100644 index 00000000..b206d656 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.png new file mode 100644 index 00000000..2d074cbb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.pdf new file mode 100644 index 00000000..817961bc Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.png new file mode 100644 index 00000000..57761080 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.pdf new file mode 100644 index 00000000..443484ef Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.png b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.png new file mode 100644 index 00000000..9dcd6089 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Color/Minimal/Meeting.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.pdf new file mode 100644 index 00000000..6ba7beab Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.png new file mode 100644 index 00000000..a8f418c7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Free.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.pdf new file mode 100644 index 00000000..f5f2d4eb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.png new file mode 100644 index 00000000..868cd7c3 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.pdf new file mode 100644 index 00000000..c5eec598 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.png new file mode 100644 index 00000000..b567168a Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.pdf new file mode 100644 index 00000000..7ec0ba2d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.png new file mode 100644 index 00000000..04c96c61 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.pdf new file mode 100644 index 00000000..a75baabe Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.png new file mode 100644 index 00000000..9c16a44e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.pdf new file mode 100644 index 00000000..82209422 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.png new file mode 100644 index 00000000..149d9c32 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..bc5a8d7c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.png new file mode 100644 index 00000000..1b96e570 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.pdf new file mode 100644 index 00000000..a5412ec9 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.png new file mode 100644 index 00000000..466334ef Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.pdf new file mode 100644 index 00000000..af39236f Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.png new file mode 100644 index 00000000..bb3c02ef Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.pdf new file mode 100644 index 00000000..8e67fa54 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.png new file mode 100644 index 00000000..c4b28472 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Mic.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.pdf new file mode 100644 index 00000000..af8e7c24 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.png new file mode 100644 index 00000000..26f2d583 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.pdf new file mode 100644 index 00000000..edecadd4 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.png new file mode 100644 index 00000000..82ac43ff Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.pdf new file mode 100644 index 00000000..0513e277 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.png new file mode 100644 index 00000000..ff8054d5 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..5a6bb831 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.png new file mode 100644 index 00000000..49cc63e5 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.pdf new file mode 100644 index 00000000..1ec6450c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.png new file mode 100644 index 00000000..c46cf046 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.pdf new file mode 100644 index 00000000..9a1cd449 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.png new file mode 100644 index 00000000..81257b7c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.pdf new file mode 100644 index 00000000..8255e5e7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.png new file mode 100644 index 00000000..42a9adc8 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Detailed/Meeting.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.pdf new file mode 100644 index 00000000..ead3a84f Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.png new file mode 100644 index 00000000..0b721e8a Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Free.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.pdf new file mode 100644 index 00000000..2a8be9e6 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.png new file mode 100644 index 00000000..a6835652 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.pdf new file mode 100644 index 00000000..3e3e4355 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.png new file mode 100644 index 00000000..2ec93619 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.pdf new file mode 100644 index 00000000..03171ee1 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.png new file mode 100644 index 00000000..c3f9182d Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.pdf new file mode 100644 index 00000000..06de993e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.png new file mode 100644 index 00000000..0e4cf192 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.pdf new file mode 100644 index 00000000..cee32115 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.png new file mode 100644 index 00000000..86c46a9f Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..894db3d2 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.png new file mode 100644 index 00000000..0dff260c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.pdf new file mode 100644 index 00000000..272a6c51 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.png new file mode 100644 index 00000000..bcecfe06 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.pdf new file mode 100644 index 00000000..5ab86cc6 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.png new file mode 100644 index 00000000..33a0d180 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.pdf new file mode 100644 index 00000000..163126eb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.png new file mode 100644 index 00000000..72f99e26 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Mic.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.pdf new file mode 100644 index 00000000..06abc7d7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.png new file mode 100644 index 00000000..62eb6324 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.pdf new file mode 100644 index 00000000..08da50f5 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.png new file mode 100644 index 00000000..3f718cdf Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.pdf new file mode 100644 index 00000000..692d3cda Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.png new file mode 100644 index 00000000..d89c65cb Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.pdf new file mode 100644 index 00000000..604e620e Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.png new file mode 100644 index 00000000..337e035c Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen-Faking.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.pdf new file mode 100644 index 00000000..487982e6 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.png new file mode 100644 index 00000000..f6014d66 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid-Screen.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.pdf new file mode 100644 index 00000000..b532140b Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.png new file mode 100644 index 00000000..483fdbd8 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting-Vid.png differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.pdf b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.pdf new file mode 100644 index 00000000..f89685b7 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.pdf differ diff --git a/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.png b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.png new file mode 100644 index 00000000..bc976c30 Binary files /dev/null and b/Source/WatchForMeeting.spoon/menubar-icons/Template/Minimal/Meeting.png differ diff --git a/Source/WatchForMeeting.spoon/monitor.html b/Source/WatchForMeeting.spoon/monitor.html new file mode 100644 index 00000000..a7bb5bd0 --- /dev/null +++ b/Source/WatchForMeeting.spoon/monitor.html @@ -0,0 +1,271 @@ +<!DOCTYPE html> + <html> + <head> + <title>Zoom Panel</title> + <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" /> + <link href="https://fonts.googleapis.com/css2?family=Barlow:wght@600&display=swap" rel="stylesheet"> + <style type="text/css"> + :root { + --window-height: 100vh; + } + body { + margin: 0; + padding: 16px; + column-gap: 16px; + row-gap: 0; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: calc(100vh - 32px) 0; + justify-items: center; + align-items: center; + text-align: center; + width: calc(100vw - 32px); + height: calc(var(--window-height) - 32px); + transition: grid-template-rows .25s ease-in-out, row-gap .25s ease-in-out; + } + body.busy { + grid-template-rows: calc((var(--window-height) - 48px) * 0.625) calc((var(--window-height) - 48px) * 0.375); + row-gap: 16px; + transition: grid-template-rows .25s ease-in-out, row-gap .25s ease-in-out; + } + + svg { + width: 100%; + height: 100%; + } + + body #status{ + background: #fff; + width: 100%; + height: 100%; + grid-area: 1 / 1 / span 1 / span 3; + transition: background .25s ease-in-out; + } + body.free #status{ + background: #93E0FF; + } + body.busy #status{ + background: #C80037; + } + body.offline #status{ + background: #ccc; + } + + body #mic, + body #vid, + body #screenshare { + grid-row-start: 2; + grid-row-end: span 1; + grid-column-end: span 1; + + width: 100%; + height: 100%; + display: grid; + grid-template-rows: 1fr; + grid-template-columns: 1fr; + justify-items: center; + align-items: center; + transition: background .25s ease-in-out; + + } + body #mic svg, + body #vid svg, + body #screenshare svg { + width: 75%; + height: 75%; + max-width: 140px; + max-height: 140px; + transition: fill .25s ease-in-out; + } + body #mic { + grid-column-start: 1; + } + body #vid { + grid-column-start: 2; + } + body #screenshare { + grid-column-start: 3; + } + body .off { + background: #fff; + } + body .off svg { + fill: #C80037; + } + body .on { + background: #C80037; + } + body .on svg { + fill: #fff; + } + </style> + <script type="text/javascript"> + + function startWebsocket() { + var ws = new WebSocket('%websocketpath%') + + ws.onopen = () => { + console.log("WebSocket is open now."); + ws.send('Connected'); + } + + ws.onerror = (error) => { + console.log(`WebSocket error: ${error}`) + } + + ws.onclose = (event) => { + console.log("WebSocket is closed now. Reconnecting in 0.5s"); + offlinePanel(); + // connection closed, discard old websocket and create a new one in 0.5s + ws = null + setTimeout(startWebsocket, 500) + }; + + ws.onmessage = (e) => { + console.log(e.data) + var json = JSON.parse(e.data); + if(json.action === "update") { + updatePanel(json.inMeeting); + } + } + + return ws; + } + + + function htmlToElement(html) { + var template = document.createElement('template'); + html = html.trim(); // Never return a text node of whitespace as the result + template.innerHTML = html; + return template.content.firstChild; + } + + var statusSVG = htmlToElement(`<svg viewBox="0 0 1168 476" preserveAspectRatio="xMidYMid meet"> + <text x="584" y="238" fill="white" font-family="Barlow-SemiBold, Barlow" font-size="180" font-weight="600" text-anchor="middle"> + <tspan dy=".35em" id="statusLabel"></tspan> + </text> + </svg>`); + + var micStatus = htmlToElement(`<div id="mic"></div>`); + var micMutedSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="muted"> + <path d="M70,116.666667 C45.8482041,116.640946 26.2757208,97.0684626 26.25,72.9166667 L26.25,58.3333333 C26.25,55.1116723 23.6383277,52.5 20.4166667,52.5 C17.1950056,52.5 14.5833333,55.1116723 14.5833333,58.3333333 L14.5833333,72.9166667 C14.618151,100.763045 35.2830553,124.272431 62.895,127.878333 C63.6236456,127.969754 64.1694302,128.590647 64.1666667,129.325 L64.1666667,134.166667 C64.1666667,137.388328 66.778339,140 70,140 C73.221661,140 75.8333333,137.388328 75.8333333,134.166667 L75.8333333,129.325 C75.8305698,128.590647 76.3763544,127.969754 77.105,127.878333 C85.1171446,126.841648 92.8027249,124.053409 99.6158333,119.711667 C99.9900252,119.474686 100.236238,119.08049 100.285026,118.640264 C100.333814,118.200038 100.179889,117.761497 99.8666667,117.448333 L93.5025,111.084167 C93.0378562,110.622017 92.3219989,110.52657 91.7525,110.850833 C85.1387306,114.667306 77.6359258,116.673285 70,116.666667 L70,116.666667 Z M37.4908333,55.1075 C37.0744586,54.6900684 36.4476954,54.5644178 35.9025932,54.7890969 C35.3574911,55.0137761 35.001303,55.5445775 35,56.1341667 L35,72.9166667 C35.0010997,84.8026083 41.0343231,95.8756841 51.0210525,102.320903 C61.0077818,108.766122 73.5829931,109.702527 84.4141667,104.8075 C84.8406328,104.607982 85.144536,104.214842 85.2301862,103.751868 C85.3158364,103.288894 85.1726951,102.81305 84.8458333,102.474167 L37.4908333,55.1075 Z M138.290833,130.083333 L115.2375,107.03 C114.730167,106.52719 114.665746,105.729362 115.085833,105.151667 C121.823826,95.7554963 125.437766,84.4790244 125.416667,72.9166667 L125.416667,58.3333333 C125.416667,55.1116723 122.804994,52.5 119.583333,52.5 C116.361672,52.5 113.75,55.1116723 113.75,58.3333333 L113.75,72.9166667 C113.760873,81.1774303 111.419922,89.2706028 107.000833,96.25 C106.763152,96.6237129 106.367952,96.8685652 105.9275,96.915 C105.495996,96.9604213 105.06711,96.8081667 104.760833,96.5008333 L100.514167,92.26 C100.055822,91.7930311 99.9629509,91.0786367 100.286667,90.51 C103.39252,85.1682847 105.019405,79.0956527 105,72.9166667 L105,35 C104.992309,17.600931 92.2049862,2.84923948 74.9831661,0.37202804 C57.7613461,-2.1051834 41.334662,8.44432312 36.4233333,25.1358333 C36.2752647,25.6279438 35.8800368,26.0064018 35.3819727,26.133008 C34.8839086,26.2596142 34.355929,26.1158325 33.9908333,25.7541667 L9.9575,1.75 C7.66277634,-0.486273262 4.00389033,-0.486273262 1.70916667,1.75 C-0.568061307,4.0279158 -0.568061307,7.72041753 1.70916667,9.99833333 L130.0425,138.331667 L130.0425,138.331667 L130.0425,138.331667 L130.083333,138.3725 C132.406127,140.443348 135.945475,140.326277 138.126346,138.106461 C140.307217,135.886646 140.361631,132.34578 138.25,130.06 L138.290833,130.083333 Z"/> + </svg>`); + var micUnmutedSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="unmuted"> + <path d="M70,140 C73.5,140 75.8333333,137.666667 75.8333333,134.166667 L75.8333333,129.5 C75.8333333,128.916667 76.4166667,128.333333 77,128.333333 C104.416667,124.833333 125.416667,101.5 125.416667,73.5 L125.416667,58.3333333 C125.416667,54.8333333 123.083333,52.5 119.583333,52.5 C116.083333,52.5 113.75,54.8333333 113.75,58.3333333 L113.75,72.9166667 C113.75,96.8333333 93.9166667,116.666667 70,116.666667 C46.0833333,116.666667 26.25,96.8333333 26.25,72.9166667 L26.25,58.3333333 C26.25,54.8333333 23.9166667,52.5 20.4166667,52.5 C16.9166667,52.5 14.5833333,54.8333333 14.5833333,58.3333333 L14.5833333,72.9166667 C14.5833333,100.916667 35,124.25 63,127.75 C63.5833333,127.75 64.1666667,128.333333 64.1666667,128.916667 L64.1666667,134.166667 C64.1666667,137.666667 66.5,140 70,140 Z M70,107.916667 L70,107.916667 C89.25,107.916667 105,92.1666667 105,72.9166667 L105,35 C105,15.75 89.25,0 70,0 L70,0 C50.75,0 35,15.75 35,35 L35,72.9166667 C35,92.1666667 50.75,107.916667 70,107.916667 Z" /> + </svg>`); + + + var vidStatus = htmlToElement(`<div id="vid"></div>`); + var vidOffSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="vidoff"> + <path d="M18.4625,37.52 C18.0333847,37.0944592 17.3869091,36.9762862 16.835,37.2225 C13.6893741,38.6337568 11.6656953,41.7606374 11.6666663,45.2083333 L11.6666663,97.7083333 C11.6666663,102.540825 15.5841751,106.458336 20.4166667,106.458336 L83.8775,106.458336 C84.4681509,106.45939 85.0011122,106.104067 85.2272876,105.558435 C85.4534629,105.012803 85.3281825,104.384625 84.91,103.9675 L18.4625,37.52 Z M126.799167,43.4641667 L109.299167,53.445 C106.662886,54.9294253 105.022706,57.7112777 105,60.7366667 L105,82.18 C105.02512,85.20472 106.66464,87.9854529 109.299167,89.4716667 L126.799167,99.4525 C129.466246,101.029886 132.76868,101.079055 135.481536,99.581769 C138.194393,98.084483 139.91304,95.2640681 140,92.1666667 L140,50.75 C139.91304,47.6525985 138.194393,44.8321837 135.481536,43.3348976 C132.76868,41.8376116 129.466246,41.8867805 126.799167,43.4641667 L126.799167,43.4641667 Z M99.5925,91.3441667 C99.3186207,91.0705084 99.1653304,90.6988319 99.1666667,90.3116667 L99.1666667,45.2083333 C99.1666667,40.3758418 95.2491582,36.4583333 90.4166667,36.4583333 L45.3133333,36.4583333 C44.9261681,36.4596696 44.5544916,36.3063793 44.2808333,36.0325 L9.9575,1.70916667 C7.98091166,-0.265894464 4.88135785,-0.561905522 2.56666667,1.00333333 C2.25868982,1.21034001 1.97151255,1.44672403 1.70916667,1.70916667 C-0.568061307,3.98708247 -0.568061307,7.6795842 1.70916667,9.9575 L130.0425,138.290833 C132.331377,140.501505 135.969661,140.469889 138.219775,138.219775 C140.469889,135.969661 140.501505,132.331377 138.290833,130.0425 L99.5925,91.3441667 Z"/> + </svg>`); + var vidOnSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="vidon"> + <path d="M126.583333,43.75 L109.083333,53.6666667 C106.75,54.8333333 105,57.75 105,60.6666667 L105,82.25 C105,85.1666667 106.75,88.0833333 109.083333,89.25 L126.583333,99.1666667 C130.666667,101.5 135.916667,100.333333 138.833333,96.25 C139.416667,95.0833333 140,93.3333333 140,92.1666667 L140,50.75 C140,46.0833333 135.916667,42 131.25,42 C129.5,42 128.333333,42.5833333 126.583333,43.75 Z M90.4166667,36.1666667 L20.4166667,36.1666667 C15.75,36.1666667 11.6666667,40.25 11.6666667,45.5 L11.6666667,97.4166667 C11.6666667,102.666667 15.75,106.166667 20.4166667,106.166667 L89.8333333,106.166667 C95.0833333,106.166667 98.5833333,102.083333 98.5833333,97.4166667 L98.5833333,45.5 C99.1666667,40.25 95.0833333,36.1666667 90.4166667,36.1666667 Z" /> + </svg>`); + + + var screenshareStatus = htmlToElement(`<div id="screenshare"></div>`); + var screenshareOffSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="screenshareon"> + <path d="M99.39,120.37 C93.5077687,118.089653 87.3519962,116.590213 81.08,115.91 C80.4525944,115.834164 79.9804891,115.301972 79.98,114.67 L79.98,100.84 C80.0021104,99.8843643 79.6421907,98.9593706 78.98,98.27 L61.98,81.27 C61.1816244,80.4787648 60.1040309,80.0333595 58.98,80.03 L22.5,80.03 C21.8317281,80.0300481 21.1912543,79.7625419 20.7215413,79.2871924 C20.2518284,78.8118429 19.9919807,78.1682238 20,77.5 L20,40 C19.9968077,39.5548714 19.8211671,39.1283155 19.51,38.81 L12.87,32.18 C12.3843669,31.6921881 11.6502965,31.5499668 11.0176202,31.8211138 C10.3849439,32.0922608 9.98167624,32.7219127 9.9994007,33.41 L9.9994007,92.5 C9.9994007,96.6421356 13.3578644,100 17.5,100 L58.75,100 C59.4403559,100 60,100.559644 60,101.25 L60,114.67 C60.0040504,115.303688 59.5296506,115.838466 58.9,115.91 C52.6316729,116.586834 46.4791982,118.082928 40.6,120.36 C38.370177,121.255051 37.0922941,123.609615 37.5569273,125.967016 C38.0215604,128.324418 40.0973135,130.017996 42.5,130.000142 L97.5,130.000142 C99.8910652,130.00114 101.948293,128.309243 102.408783,125.962939 C102.869272,123.616634 101.604127,121.272683 99.39,120.37 Z M138.24,129.76 L108.49,100 L122.49,100 C126.632136,100 129.99,96.6421356 129.99,92.5 L129.99,17.5 C129.99,13.3578644 126.632136,10 122.49,10 L18.49,10 L10.24,1.76 C7.89745963,-0.579627024 4.10254037,-0.579627024 1.76,1.76 L1.76,1.76 C-0.579627024,4.10254037 -0.579627024,7.89745963 1.76,10.24 L129.76,138.24 C132.10254,140.579627 135.89746,140.579627 138.24,138.24 L138.24,138.24 C140.579627,135.89746 140.579627,132.10254 138.24,129.76 L138.24,129.76 Z M117.5,20 C118.880712,20 120,21.1192881 120,22.5 L120,77.5 C120,78.8807119 118.880712,80 117.5,80 L88.5,80 L28.5,20 L117.5,20 Z" /> + </svg>`); + var screenshareOnSVG = htmlToElement(`<svg viewBox="0 0 140 140" preserveAspectRatio="xMidYMid meet" id="screenshareon"> + <path d="M122.5,10 L17.5,10 C13.3578644,10 10,13.3578644 10,17.5 L10,92.5 C10,96.6421356 13.3578644,100 17.5,100 L58.75,100 C59.4403559,100 60,100.559644 60,101.25 L60,114.67 C60.0040504,115.303688 59.5296506,115.838466 58.9,115.91 C52.6316729,116.586834 46.4791982,118.082928 40.6,120.36 C38.370177,121.255051 37.0922941,123.609615 37.5569273,125.967016 C38.0215604,128.324418 40.0973135,130.017996 42.5,130.000142 L97.5,130.000142 C99.8910652,130.000142 101.948293,128.309243 102.408783,125.962939 C102.869272,123.616634 101.604127,121.272683 99.39,120.37 C93.5077687,118.089653 87.3519962,116.590213 81.08,115.91 C80.4525944,115.834164 79.9804891,115.301972 79.98,114.67 L79.98,101.25 C79.98,100.559644 80.5396441,100 81.23,100 L122.5,100 C126.642136,100 130,96.6421356 130,92.5 L130,17.5 C130,13.3578644 126.642136,10 122.5,10 Z M120,77.5 C120,78.8807119 118.880712,80 117.5,80 L22.5,80 C21.1192881,80 20,78.8807119 20,77.5 L20,22.5 C20,21.1192881 21.1192881,20 22.5,20 L117.5,20 C118.880712,20 120,21.1192881 120,22.5 L120,77.5 Z" /> + </svg>`); + + function updatePanel(inMeeting) { + console.log(inMeeting); + var panel = document.getElementsByTagName("BODY")[0]; + var status = document.getElementById("status"); + + if(status.classList.contains("init")) { + status.classList.remove("init"); + status.innerHTML = ""; + status.appendChild(statusSVG); + } + + panel.classList.remove("offline"); + + if(!inMeeting) { + panel.classList.remove("busy"); + panel.classList.add("free"); + statusSVG.getElementById("statusLabel").innerHTML = "FREE"; + + if(micStatus.parentNode === panel) panel.removeChild(micStatus); + if(vidStatus.parentNode === panel) panel.removeChild(vidStatus); + if(screenshareStatus.parentNode === panel) panel.removeChild(screenshareStatus); + } + else { + panel.classList.remove("free"); + panel.classList.add("busy"); + statusSVG.getElementById("statusLabel").innerHTML = "MEETING"; + + if(micStatus.parentNode !== panel) panel.appendChild(micStatus); + if(vidStatus.parentNode !== panel) panel.appendChild(vidStatus); + if(screenshareStatus.parentNode !== panel) panel.appendChild(screenshareStatus); + + micStatus.innerHTML = ""; + if(inMeeting.mic_open) { + micStatus.classList.remove("off"); + micStatus.classList.add("on"); + micStatus.appendChild(micUnmutedSVG); + } + else { + micStatus.classList.remove("on"); + micStatus.classList.add("off"); + micStatus.appendChild(micMutedSVG); + } + + vidStatus.innerHTML = ""; + if(inMeeting.video_on) { + vidStatus.classList.remove("off"); + vidStatus.classList.add("on"); + vidStatus.appendChild(vidOnSVG); + } + else { + vidStatus.classList.remove("on"); + vidStatus.classList.add("off"); + vidStatus.appendChild(vidOffSVG); + } + + screenshareStatus.innerHTML = ""; + if(inMeeting.sharing) { + screenshareStatus.classList.remove("off"); + screenshareStatus.classList.add("on"); + screenshareStatus.appendChild(screenshareOnSVG); + } + else { + screenshareStatus.classList.remove("on"); + screenshareStatus.classList.add("off"); + screenshareStatus.appendChild(screenshareOffSVG); + + } + } + } + + function offlinePanel() { + var panel = document.getElementsByTagName("BODY")[0]; + var status = document.getElementById("status"); + + panel.classList.remove("busy"); + panel.classList.remove("free"); + panel.classList.add("offline"); + + if(status.classList.contains("init")) { + status.classList.remove("init"); + status.innerHTML = ""; + status.appendChild(statusSVG); + } + + statusSVG.getElementById("statusLabel").innerHTML = "OFFLINE"; + + + + } + + var socket = startWebsocket(); + </script> + </head> + <body> + <div id="status" class="init">Initializing...</div> + </body> + </html> \ No newline at end of file