Compare commits

..

307 Commits

Author SHA1 Message Date
Andrew Welker
44e5753138 Merge pull request #810 from PepperDash/hotfix/mockvc-fixes
Hotfix/mockvc fixes
2021-09-02 08:38:58 -06:00
Andrew Welker
5d177104d8 Merge branch 'development' into hotfix/mockvc-fixes 2021-09-02 07:42:42 -06:00
Andrew Welker
d6878df267 Merge pull request #814 from PepperDash/hotfix/zoom-meetnow-updates
Hotfix/zoom meetnow updates
2021-08-31 20:02:44 -06:00
Andrew Welker
a379641595 Merge branch 'development' into hotfix/zoom-meetnow-updates 2021-08-31 19:00:26 -06:00
Neil Dorin
8539a6b79c fix(essentials): minor fixes to UI oddities 2021-08-31 18:18:24 -06:00
Neil Dorin
adec25104c feat(essentials): Updates to UI to deal with sharing feedback with no external sources. Adds End/Leave meeting button logic on meeting info subpage 2021-08-31 16:40:04 -06:00
Neil Dorin
a54cd9e1df feat(essentials): minor syntax updates 2021-08-31 10:13:56 -06:00
Neil Dorin
8af7b4b1db feat(essentials): Adds IsHost property to MeetingInfo to determine if the room is the host 2021-08-30 17:48:37 -06:00
Neil Dorin
3edb0145d0 fix(essentials): Adds JsonProperty attributes to MeetingInfo properties for serialization 2021-08-30 15:00:13 -06:00
Neil Dorin
0a1af09830 fix(essentials): Disables dial feedback phone formatting if codec is IHasStartMeeting 2021-08-30 12:07:09 -06:00
Neil Dorin
532f3ba237 feat(essentails): #811 Adds IHasMeetingInfo, implements on ZoomRoom and updates UI drivers #812 2021-08-27 17:57:21 -06:00
Andrew Welker
50aafb088e Merge branch 'development' into hotfix/mockvc-fixes 2021-08-27 10:11:47 -06:00
Andrew Welker
e1d9a46284 Merge pull request #809 from PepperDash/hotfix/mockvc-fixes
fix(essentials): Minor fixes for MockVc to make behavior more consist…
2021-08-27 10:11:36 -06:00
Neil Dorin
bfd383dfc7 fix(essentials): Minor fixes for MockVc to make behavior more consistent with real hardware 2021-08-27 09:48:43 -06:00
Andrew Welker
b853e8ed37 Merge pull request #808 from PepperDash/hotfix/4series-cultureinfo-for-time-formats-and-dm-4kz-fix
Hotfix/4series cultureinfo for time formats and dm 4kz fix
2021-08-26 18:15:18 -06:00
Andrew Welker
60c2d4df01 Merge branch 'development' into hotfix/4series-cultureinfo-for-time-formats-and-dm-4kz-fix 2021-08-26 17:59:32 -06:00
Neil Dorin
8ab87af859 Merge pull request #807 from PepperDash/hotfix/4series-cultureinfo-for-time-formats-and-dm-4kz-fix 2021-08-26 17:52:07 -06:00
Neil Dorin
47035d8386 fix(essentials): Attempt to catch case where meeting popup may not be dismissed if the room is on and the meeting is no longer joinable 2021-08-26 17:38:49 -06:00
Neil Dorin
c84ec4c899 fix(essentials): #806 switches method to print times for consistent 12h format
fix(essentials): #806 Updates more uses of ToShortTimeString()

fix(essentials): #806 fixes for VideoCodecUIDriver

fix(essentials): #806 final fixes for time format
2021-08-26 17:20:04 -06:00
Neil Dorin
4444328600 fix(essentials): #804 #805 Updates to prevent attempting to access null AudioSourceFeedback property on 4kz TX models 2021-08-26 16:48:56 -06:00
Andrew Welker
0d3eb42495 Merge pull request #803 from PepperDash/hotfix/add-IPasswordPrompt
Hotfix/add i password prompt
2021-08-20 17:06:21 -06:00
Neil Dorin
540a00861c fix(essentials): Adds missing file in .csproj 2021-08-19 16:02:24 -06:00
Neil Dorin
0014dd7a14 feat(essentials): #801 Adds IHasStartMeeting interface and implments on ZoomRoom 2021-08-19 15:19:19 -06:00
Neil Dorin
f922b871a1 refactor(essentials): Switches from zoom specific contact class to generic InvitableDirectoryContact class 2021-08-19 14:57:00 -06:00
Neil Dorin
b2331fa1e5 feat(essentials): Adds IsInvitableContact property with getter to DirectoryContact 2021-08-19 14:14:14 -06:00
Neil Dorin
8bf4b0d568 fix(essentails): Increase phonebook list limit to 10000 2021-08-19 12:49:11 -06:00
Neil Dorin
621205e65c fix(essentials): Hides password prompt on successful call connection 2021-08-19 12:04:11 -06:00
Neil Dorin
62fcf3856f fix(essentials): Minor update to clear out previous password text when displaying password prompt 2021-08-19 09:37:21 -06:00
Neil Dorin
dad986414c feat(essentials): Updated EssentialsVideoCodecUIDriver to display password prompt when joining password protected meeting 2021-08-18 17:55:04 -06:00
Andrew Welker
55766b21ba Merge pull request #799 from PepperDash/hotfix/room-method-updates
Hotfix/room method updates
2021-08-18 16:17:23 -06:00
Andrew Welker
21bc6a05de Merge branch 'development' into hotfix/room-method-updates 2021-08-18 16:05:46 -06:00
Neil Dorin
f298b5cc41 feat(essentials): Implements IPasswordPrompt on ZoomRoom 2021-08-18 14:45:16 -06:00
Neil Dorin
f8129fe7ae feat(essentials): #800 adds public access modifier on properties 2021-08-18 13:35:48 -06:00
Neil Dorin
dbdaedcca3 feat(essentials): #800 Adds IPasswordPrompt 2021-08-18 13:25:26 -06:00
Neil Dorin
893950d8c4 Merge pull request #798 from PepperDash/hotfix/room-method-updates 2021-08-18 08:30:53 -06:00
Andrew Welker
b780351bf0 Merge branch 'main' into hotfix/room-method-updates 2021-08-18 07:38:11 -06:00
Andrew Welker
6a1671aae1 ci: Remove PR trigger 2021-08-17 22:25:18 -06:00
Andrew Welker
9a1a23c88a fix: Instantiate Availability class when 2021-08-17 22:23:41 -06:00
Andrew Welker
3c352bbd20 Merge pull request #795 from PepperDash/hotfix/sg-ui-issue
Hotfix/sg UI issue
2021-08-17 22:04:56 -06:00
Andrew Welker
8ab3e45d96 Merge branch 'development' into hotfix/sg-ui-issue 2021-08-17 21:49:16 -06:00
Andrew Welker
11e5123fdf Merge pull request #790 from PepperDash/hotfix/zoomroom-duplicate-participant-in-list
Hotfix/zoomroom duplicate participant in list
2021-08-17 21:49:03 -06:00
Andrew Welker
2e4bb7466c Merge pull request #794 from PepperDash/hotfix/sg-ui-issue
fix(essentials): #792 refactors logic to show/hide SelectASourceVisible subpage
2021-08-17 21:48:50 -06:00
Neil Dorin
36a41ac477 fix(essentials): #792 refactors logic to show/hide SelectASourceVisible subpage 2021-08-17 18:00:41 -06:00
Andrew Welker
92f4d37cd6 refactor: Clean up Room Interfaces to help with ambiguities 2021-08-17 16:54:02 -06:00
Andrew Welker
b9479bab70 refactor: Rename Initialize to InitializeRoom 2021-08-17 16:54:01 -06:00
Andrew Welker
c6d10ba87e Merge branch 'development' into hotfix/zoomroom-duplicate-participant-in-list 2021-08-17 16:52:52 -06:00
Andrew Welker
fd94a94ee9 Merge pull request #789 from PepperDash/hotfix/zoomroom-duplicate-participant-in-list
Hotfix/zoomroom duplicate participant in list
2021-08-17 16:26:18 -06:00
Jason DeVito
3fa2954ca0 style: Updated formatting for ZoomRoom.cs and IHasParticipants.cs 2021-08-17 16:46:24 -05:00
Jason DeVito
c4f6afa412 Fixed debug references used for call status that were printing Status.Call.Sharing.State. Added GetCurrentCallParticipants method to referesh participant list. Updated UpdateCallStatus to resolve issue with duplicate participants when admitted from the waiting room. 2021-08-17 15:56:57 -05:00
Jason DeVito
6bdda5451b Updated UpdateCallStatus method to fix call status references previously looking at Status.Call.Sharing.State. Added else statement to 'if(ActiveCalls.Count == 0)' used to cleanup after calls that refreshs the participant list. Added GetCurrentCallParticipant method used to poll the participant list. 2021-08-17 15:14:02 -05:00
Andrew Welker
f3ab364a4d Merge pull request #787 from PepperDash/hotfix/IMobileControl3
Hotfix/i mobile control3
2021-08-16 15:32:30 -06:00
Andrew Welker
538f81c18e Merge branch 'development' into hotfix/IMobileControl3 2021-08-16 15:16:44 -06:00
Andrew Welker
3ee8c07ecd Merge pull request #786 from PepperDash/hotfix/IMobileControl3
feat(essentials): Adds IMobileControl3 interface to maintain backwards compatibility
2021-08-16 15:06:00 -06:00
Neil Dorin
01b713e6e1 feat(essentials): Adds IMobileControl3 interface to maintain backwards compatibility 2021-08-16 14:27:28 -06:00
Neil Dorin
002cc07b52 Merge pull request #784 from PepperDash/hotfix/UI-codec-directory-fixes
Hotfix/UI codec directory fixes
2021-08-16 12:43:28 -06:00
Neil Dorin
16f993852c Merge branch 'development' into hotfix/UI-codec-directory-fixes 2021-08-16 11:52:35 -06:00
Neil Dorin
ffd0fbc57b Merge pull request #783 from PepperDash/hotfix/UI-codec-directory-fixes
refactor: Add RefreshDirectory overload
2021-08-16 11:47:52 -06:00
Neil Dorin
9ac1d77c2a Merge pull request #777 from PepperDash/feature/visca-presets-fix
Feature/visca presets fix
2021-08-16 11:47:35 -06:00
Neil Dorin
2dd0c53a08 Merge branch 'development' into feature/visca-presets-fix 2021-08-16 11:35:13 -06:00
Andrew Welker
fab1219146 Merge pull request #780 from PepperDash/hotfix/zoomroom-fixes
Hotfix/zoomroom fixes
2021-08-16 11:22:48 -06:00
Andrew Welker
23e8280904 refactor: Add RefreshDirectory overload
This allows the directory to be refreshed by the event that updates the directory correctly
2021-08-16 09:42:11 -06:00
Andrew Welker
60d0f50cd2 Merge branch 'development' into hotfix/zoomroom-fixes 2021-08-16 08:22:38 -06:00
Andrew Welker
6708be0d15 Merge pull request #779 from PepperDash/hotfix/zoomroom-fixes
Hotfix/zoomroom fixes
2021-08-16 08:22:19 -06:00
Jason DeVito
d193de79da Added GetSelfViewMode method to ZoomRoom.cs, upated Removed if statement in HideConfSelfVideo property that was blocking feedback updates and setting of the property from boot. Tested with Zoom Rooms system on site and verified working. Closes #781. 2021-08-16 08:26:27 -05:00
Neil Dorin
87ab43c745 fix(essentials): Updates to resolve oddities with ZoomRoom directory browsing and dialing 2021-08-13 13:45:46 -06:00
Neil Dorin
2a37e44d7d fix(essentials): fixes inverted activity call button state 2021-08-12 15:33:17 -06:00
Neil Dorin
efa801137c fix(Essentials): moves code inside null check for CurrentRoom 2021-08-12 14:53:41 -06:00
Neil Dorin
4b4f1f3c3d fix(essentials): Updates to staging subpage join logic 2021-08-12 14:48:19 -06:00
Neil Dorin
6e7bf061cf fix(essentials): Add null check for CurrentRoom 2021-08-12 13:51:48 -06:00
Louis Iacovelli
c26d7d73f8 Removed commented line. 2021-08-12 15:14:23 -04:00
Neil Dorin
7f6160eb44 fix(essentials): Updates to better set feedback for current mode 2021-08-12 12:24:23 -06:00
Andrew Welker
854a0691d3 Merge branch 'development' into feature/visca-presets-fix 2021-08-12 12:12:31 -06:00
Neil Dorin
61c638452b Merge pull request #775 from PepperDash/hotfix/zoom-camera-fixes
Hotfix/zoom camera fixes
2021-08-12 12:06:16 -06:00
Louis Iacovelli
072411e4f6 Added method to set preset for multiple situations 2021-08-12 11:49:59 -04:00
Andrew Welker
7dc9afa119 Merge branch 'development' into hotfix/zoom-camera-fixes 2021-08-11 11:47:48 -06:00
Andrew Welker
b5004d5b1d Merge pull request #774 from PepperDash/hotfix/zoom-camera-fixes
Hotfix/zoom camera fixes
2021-08-11 11:46:42 -06:00
Jason T Alborough
6f0bfedac1 Merge branch 'main' into feature/visca-presets-fix 2021-08-11 13:31:06 -04:00
Jason Alborough
71881addab fix(Devices.Common.Cameras.CameraVisca): add and fire the event OnPresetsListHasChanged() after LinkToApi so that the camera presets populate to the bridge correctly 2021-08-11 13:00:51 -04:00
Neil Dorin
cea1d2fcdd Updates to mute toggle for ZoomRoom 2021-08-09 16:20:55 -06:00
Neil Dorin
22aea3089d #773 Adds some debug and sets the Capabilities value on ZoomRoomCamera
Also updates RunRouteAction in EssentialsHuddleVtc1Room to still call down in to main RunRouteAction method if called with a sourceListKey
2021-08-09 14:41:31 -06:00
Andrew Welker
3aab807631 Merge pull request #770 from PepperDash/bugfix/FixLargeFrameInstantiation
Bugfix/fix large frame instantiation
2021-08-03 12:13:39 -06:00
Andrew Welker
9ec090397f Merge branch 'development' into bugfix/FixLargeFrameInstantiation 2021-08-02 18:10:10 -06:00
Neil Dorin
86916c4357 Merge pull request #772 from PepperDash/hotfix/zoomroom-updates
Hotfix/zoomroom updates
2021-08-02 17:56:11 -06:00
Andrew Welker
40151c5f8f Merge branch 'development' into hotfix/zoomroom-updates 2021-08-02 17:42:53 -06:00
Andrew Welker
4210df693a Merge pull request #766 from PepperDash/release/1.9.2
Release/1.9.2
2021-08-02 17:42:39 -06:00
Andrew Welker
56bb872d2b Merge branch 'development' into hotfix/zoomroom-updates 2021-08-02 17:36:51 -06:00
Andrew Welker
43256acfcd Merge pull request #771 from PepperDash/hotfix/zoomroom-updates
Hotfix/zoomroom updates
2021-08-02 17:36:28 -06:00
Neil Dorin
1696ef3ac1 Adds LocalLayoutToggle() implementation and minor updates to UI Driver 2021-08-02 16:58:32 -06:00
Trevor Payne
864e0675ea Resolves #769
Fix issues with large dm frame instantiation
2021-08-02 10:34:41 -05:00
Neil Dorin
19cbe480a6 reverts IMobileControl back to using EssentialsRoomBase instead of IEssentialsRoom for now 2021-07-29 16:39:34 -06:00
Neil Dorin
49c1c61a88 updates ZoomRoom to use GenericQueue 2021-07-29 14:51:16 -06:00
Andrew Welker
c306e2c1a1 Merge branch 'development' into release/1.9.2 2021-07-29 14:37:12 -06:00
Neil Dorin
0cd3c1bdc5 fix(ZoomRoom): #767 #768 Adds Output2 on ZoomRoom and updates to UI for VTC
Updates EssentialsVideoCodecUiDriver and codec classes to better deal with dynamic use of IHasCallHistory and
2021-07-29 13:01:02 -06:00
Andrew Welker
d64cbc639a Merge pull request #762 from PepperDash/release/1.9.2
Release/1.9.2
2021-07-28 18:47:34 -06:00
Andrew Welker
82af1366df Merge pull request #765 from PepperDash/hotfix/add-tsw7xx-types-to-factory
Hotfix/add tsw7xx types to factory
2021-07-28 17:39:54 -06:00
Andrew Welker
56492a00cd Merge branch 'development' into hotfix/add-tsw7xx-types-to-factory 2021-07-28 17:07:13 -06:00
Andrew Welker
8404e7d5a4 Merge branch 'main' into release/1.9.2 2021-07-28 17:07:01 -06:00
Andrew Welker
e4135a958c Merge pull request #764 from PepperDash/hotfix/add-tsw7xx-types-to-factory
Hotfix/add tsw7xx types to factory
2021-07-28 17:06:45 -06:00
Neil Dorin
f4ac4e6319 Minor updates to resolve oddities with ZoomRoom and TSW UI drivers (call status) 2021-07-28 16:38:20 -06:00
Andrew Welker
b026174cd2 Merge branch 'main' into release/1.9.2 2021-07-28 15:47:46 -06:00
Neil Dorin
66ff6b2e07 Brings in another small zoom update from @jkdevito 2021-07-28 15:33:04 -06:00
Neil Dorin
e7bbfbd40a Update to add populate call Id and Name properties to allow proper removal of inactive calls 2021-07-28 15:27:47 -06:00
Neil Dorin
eec86fde48 Updates incorrect path for ZoomRoom feedback exclusions and adds toll_free_callinList 2021-07-28 15:23:57 -06:00
Neil Dorin
7b57ce439e #761 Adds support for x70 series touchpanels 2021-07-28 14:47:17 -06:00
Neil Dorin
28bac18667 Merge pull request #756 from PepperDash/feature/fixes-for-multiple-room
Feature/fixes for multiple room
2021-07-27 12:15:29 -06:00
Andrew Welker
ea254ef983 feat: Update some internal Essentials devices to use Initialize method 2021-07-23 19:56:29 -06:00
Andrew Welker
76e4d4a82d feat: Add method call to constructor for EssentialsDevice 2021-07-23 19:44:41 -06:00
Andrew Welker
4bd777f6b9 feat: Update Essentials Device to call Initialize method 2021-07-23 16:53:36 -06:00
Andrew Welker
f607394ee7 chore: Update PD Core to 1.0.48 2021-07-23 16:53:11 -06:00
Andrew Welker
085a64c87b fix: Correct functioning while in test mode 2021-07-23 13:41:21 -06:00
Andrew Welker
5d120391a5 Merge pull request #752 from PepperDash/feature/ndorin-patch-1
Adds a prompt to capture affected version
2021-07-21 13:16:59 -06:00
Andrew Welker
290e887903 refactor: Modify debug messages
Exceptions now print the device key and the error message. To see stack traces, use `appdebug:XX 1`.

There are also now debug messages indicating when the different activation cycles are complete.
2021-07-20 17:32:00 -06:00
Andrew Welker
de7a74eaff feat: Update Fusion to create a GUID file per room
This allows for multiple rooms to be designated and created without any issues. Also moved post Activation action to it's own method rather than a lambda.

In the interest of backwards compatibility, the Fusion class will look for a GUID file with the old file name and migrate it to the new file name.
2021-07-20 17:30:45 -06:00
Andrew Welker
88e5c49663 refactor: Add Fusion IP-ID info to debug messages 2021-07-20 17:29:02 -06:00
Andrew Welker
1415999d86 refactor: Update a debug message with IP-ID info 2021-07-20 17:28:40 -06:00
Neil Dorin
db5aa319ec Adds a prompt to capture affected version 2021-07-20 16:49:03 -06:00
Andrew Welker
5f6b650dba fix: Update fusion IP-ID for multiple rooms 2021-07-20 11:15:37 -06:00
Andrew Welker
94c0e92f6b fix: Initialize lists for partitions and scenarios
also removed unnecessary else
2021-07-20 08:28:25 -06:00
Neil Dorin
a5046df671 Merge branch 'development' into feature/room-combining 2021-07-19 15:45:56 -06:00
Neil Dorin
5a4f7b6a28 Adds new keyword to intentionally hide properties 2021-07-19 15:44:28 -06:00
Neil Dorin
10f5516a5a Merge pull request #749 from PepperDash/feature/occ-aggregator
Update Occupancy Aggregator to be real device
2021-07-19 15:43:58 -06:00
Neil Dorin
dfaaa3f6bc #742 Adds factory for EssentialsRoomCombiner 2021-07-19 15:41:10 -06:00
Andrew Welker
45e6dff26d fix: update access level for config constructor 2021-07-19 15:10:03 -06:00
Andrew Welker
10129b8178 feat: Add post activation action for aggregator 2021-07-19 15:09:37 -06:00
Andrew Welker
9128e108f7 feat: Add clear method to BoolOutputLogical
and do a bit of refactoring
2021-07-19 15:09:03 -06:00
Andrew Welker
760ec8be92 feat: Add occupancy aggregator factory and config 2021-07-19 14:08:57 -06:00
Andrew Welker
bbcdd3e179 Merge pull request #747 from PepperDash/feature/C2N-IO-add
Add C2NIoController to csproj
2021-07-19 14:06:33 -06:00
Andrew Welker
7a649f4ea8 Merge branch 'development' into feature/C2N-IO-add 2021-07-19 13:48:43 -06:00
Andrew Welker
6946946c12 chore: Add c2nIoController to csproj 2021-07-19 13:47:17 -06:00
Neil Dorin
dca73e1508 Merge pull request #745 from PepperDash/feature/C2N-IO-add
Add C2N-IO
2021-07-19 11:55:15 -06:00
Andrew Welker
990090e1de feat: Add support for C2N-IO 2021-07-19 10:29:30 -06:00
Neil Dorin
377cccf912 Updates type for Partitions on IEssentialsRoomController 2021-07-16 16:10:06 -06:00
Neil Dorin
9795637d75 #742 EssentialsRoomCombiner substantially complete. Adds debounce timer when changing scenarios 2021-07-16 16:09:38 -06:00
Neil Dorin
6f6ca50c37 Removes set from interface 2021-07-16 15:36:59 -06:00
Neil Dorin
7b7ec53355 #742 Updates to room combination interfaces and EssentialsRoomCombiner and EssentialsPartitionController 2021-07-16 15:35:52 -06:00
Neil Dorin
e3920132bf #743 Adds SetValueFunc() to all Feedback types 2021-07-16 14:11:27 -06:00
Neil Dorin
c2e5bd290a #742 Adding EssentialsRoomCombiner device (in progress) 2021-07-15 16:40:25 -06:00
Neil Dorin
7fd52814a0 implements IKeyName as required on config classes 2021-07-15 10:11:27 -06:00
Neil Dorin
06a3dda2e4 Starts on interfaces for room combination 2021-07-14 22:12:41 -06:00
Neil Dorin
d97ca6d5a4 #741 Adds EssentialsRoomCombinerPropertiesConfig 2021-07-14 14:42:13 -06:00
Neil Dorin
4c50d6980f #740 Adds IPartitionStateProvider interface and adds to GlsParitionSensorController 2021-07-14 14:38:18 -06:00
Andrew Welker
3b843104d8 Merge pull request #738 from PepperDash/feature/room-combining
Feature/room combining
2021-07-13 22:14:42 -06:00
Neil Dorin
a37814ab3c #736 adds IEssentialsHuddleVtc1Room and refactors to use interface rather than EssentialsHudleVtc1Room 2021-07-12 21:49:54 -06:00
Neil Dorin
2181410927 #736 Adds IEssentialsRoom and IEssentialsHuddleSpaceRoom interfaces
Refactors all references to EssentialsRoomBase and EssentialsHuddleSpaceRoom to use the new interfaces instead
2021-07-12 17:22:36 -06:00
Andrew Welker
d4191ceb75 Merge pull request #735 from PepperDash/hotfix/zip-release-upload
Hotfix/zip release upload
2021-07-08 12:04:57 -06:00
Andrew Welker
5f6d15c6c0 Merge branch 'development' into hotfix/zip-release-upload 2021-07-08 11:41:40 -06:00
Andrew Welker
4cc40227fd Merge pull request #734 from PepperDash/hotfix/zip-release-upload
ci: Remove condition on upload to release
2021-07-08 11:41:32 -06:00
Andrew Welker
d0dbbe095f ci: Remove condition on upload to release 2021-07-08 11:16:41 -06:00
Andrew Welker
c7180db2b7 Merge pull request #730 from PepperDash/feature/update-ihasscheduleawareness
Feature/update ihasscheduleawareness
2021-07-03 00:39:29 -06:00
Neil Dorin
d95ee27979 #728 corrects casing of folder name in Global.FilePathPrefix at startup 2021-07-02 15:01:51 -06:00
Neil Dorin
e964172200 #729 Updates to get CheckSchedule method working as designed 2021-07-02 15:01:17 -06:00
Neil Dorin
840934502b Working on getting meeting change events to trigger properly 2021-06-29 17:35:22 -06:00
Neil Dorin
a76f4c15dc Updates to IHasScheduleAwareness 2021-06-29 09:47:56 -06:00
Neil Dorin
b19e2e38ad Merge pull request #727 from PepperDash/hotfix/add-eisc-server-client-options-to-eiscapiadvanced-factory
Hotfix/add eisc server client options to eiscapiadvanced factory
2021-06-24 16:53:14 -06:00
Neil Dorin
9a7fe553f9 Merge branch 'development' into hotfix/add-eisc-server-client-options-to-eiscapiadvanced-factory 2021-06-24 16:35:29 -06:00
Neil Dorin
e6ecaf3a1e Merge pull request #726 from PepperDash/hotfix/add-eisc-server-client-options-to-eiscapiadvanced-factory
adds new type options to use EISCClient and EISCServer in eiscapiadvanced
2021-06-24 16:35:18 -06:00
Andrew Welker
895b76a0cd Merge pull request #723 from PepperDash/hotfix/zoom-room-views
Hotfix/zoom room views
2021-06-24 13:28:48 -06:00
Neil Dorin
5c9996e728 adds new type options to use EISCClient and EISCServer in eiscapiadvanced 2021-06-17 13:49:09 -06:00
Andrew Welker
0cc2328276 Merge branch 'development' into hotfix/zoom-room-views 2021-06-17 10:03:42 -06:00
Andrew Welker
8fadfa98f2 Merge pull request #724 from PepperDash/hotfix/zoom-room-views
Hotfix/zoom room views
2021-06-17 10:02:17 -06:00
Andrew Welker
1ccf54003f Merge branch 'main' into hotfix/zoom-room-views 2021-06-17 09:32:04 -06:00
Neil Dorin
54769ce270 Merge pull request #722 from PepperDash/hotfix/plugin-loading
Hotfix/plugin loading
2021-06-16 17:20:57 -06:00
Andrew Welker
6b85323949 Merge branch 'development' into hotfix/plugin-loading 2021-06-16 15:17:52 -06:00
Andrew Welker
319d8f99c5 Merge pull request #721 from PepperDash/hotfix/plugin-loading
adds check for abstract class before attempting to create instance
2021-06-16 15:17:32 -06:00
Neil Dorin
cc742f4291 adds check for abstract class before attempting to create instance 2021-06-16 13:11:45 -06:00
Jason DeVito
6beff106ec Updated packages.config to track latest PepperDash Core release. Updated ZoomRoom LinkZoomRoomToApi to update the bridge when it comes online. 2021-06-10 17:45:23 -05:00
Neil Dorin
cb35aa13f5 lets it fall through conditions to fire the ParticipantsChanged event if an already pinned participant is found 2021-06-09 17:55:55 -06:00
Neil Dorin
b71523bd2d Adds condition to not check for already pinned participant if incoming message has user id < 0 2021-06-09 17:38:50 -06:00
Neil Dorin
0c56da112c Changes to respond to new pinned participant message by clearing out already pinned participant fb first. 2021-06-09 17:07:25 -06:00
Neil Dorin
08f4d8e9a2 Attempts to unpin participant from same screenIndex if one is already pinned. 2021-06-09 16:57:42 -06:00
Neil Dorin
25e7e9634a updates PD Core version to include xsig token fixes 2021-06-09 16:56:56 -06:00
Neil Dorin
7ac3f81ea5 Initialize SreenIndexIsPinnedToFb to -1. Adds debugging for xsig tokens 2021-06-09 16:14:18 -06:00
Neil Dorin
1c06e8381b Fixed inverted video mute FB and adds debug statements to help see participant pin status 2021-06-09 13:30:36 -06:00
Jason DeVito
f5305197b3 Updates to LinkZoomRoomApi method 2021-06-09 13:37:01 -05:00
Jason DeVito
1805ebaf0f Converted ZoomRoom Layout Size properties and methods to interface, IHasSelfviewSize, following the patterns implemented for IHasSelfviewPosition and SelfviewPipPosition. 2021-06-08 17:56:00 -05:00
Jason DeVito
ca8207f2bd Removed Layout Position properties and methods. Using SelfviewPipPosition properties and methods. 2021-06-08 15:20:42 -05:00
Neil Dorin
4e81859695 Merge pull request #719 from PepperDash/bugfix/setdevicestreamdebug-fix
Bugfix/setdevicestreamdebug fix
2021-06-08 13:45:08 -06:00
Jason DeVito
655bb954fa Updated IHasCodecLayouts and ZoomRoom to test feedback of Layout Size and Layout Position. 2021-06-08 14:38:33 -05:00
Andrew Welker
1ebacf3f0f Fix formatting issue 2021-06-08 12:12:59 -06:00
Andrew Welker
7de0251188 add ConvertType method to convert type
This method should allow for using a string value in place of an enum as a parameter. Integers will still fail.
2021-06-08 11:44:09 -06:00
Jason DeVito
492e593263 Updated IHasCodecLayouts to implement Layout Position selection and feedback, updated bridge map to map Layout Position signals. Updated ZoomRoom to implement Layout Position selection and feedback. 2021-06-07 22:22:00 -05:00
Jason DeVito
afe2046c81 Added methods to select Call Layout Size. 2021-06-07 14:45:40 -05:00
Neil Dorin
98d3a4a2fa Merge pull request #713 from PepperDash/release/1.9.0
1.9.0
2021-05-25 14:48:52 -06:00
Andrew Welker
1702c69b73 Merge branch 'main' into release/1.9.0 2021-05-25 14:37:27 -06:00
Andrew Welker
722d28b1b3 Merge pull request #710 from PepperDash/hotfix/techroom-preset-recall-event
Hotfix/techroom preset recall event
2021-05-24 13:32:46 -06:00
Andrew Welker
ef7da0d7af Merge branch 'development' into hotfix/techroom-preset-recall-event 2021-05-24 13:19:45 -06:00
Neil Dorin
77d8e63a31 Merge pull request #709 from PepperDash/hotfix/techroom-preset-recall-event
Hotfix/techroom preset recall event
2021-05-24 12:59:54 -06:00
Neil Dorin
d2d99d4bfa Merge branch 'main' into hotfix/techroom-preset-recall-event 2021-05-24 12:45:27 -06:00
Neil Dorin
4bd71b04bf Merge pull request #708 from PepperDash/feature/add-zoomroom-participant-actions
Feature/add zoomroom participant actions
2021-05-20 20:34:45 -06:00
Andrew Welker
c5bcd89695 Merge branch 'development' into feature/add-zoomroom-participant-actions 2021-05-20 18:34:58 -06:00
Jason DeVito
c7cc98bff7 Removed old TODO's from VideoCodecBase.cs. Marked TODO's for Issue #697. 2021-05-20 19:32:11 -05:00
Jason DeVito
db60f8f1be ResponseObject.cs updates: Added and tested SortParticipantListtByHandStatus method. Found an issue with HandStatus response, property names include ': ', updated JsonProperty definitions to account for issues with expected returns vs. actual returns. 2021-05-20 15:43:02 -05:00
Andrew Welker
e82efdde2d Merge pull request #707 from PepperDash/hotfix/devjson-overload-fix
fix:(EssentialsCore) Add ability for `devjson` command to handle overloads
2021-05-20 11:04:23 -06:00
Andrew Welker
d00c8bed5f fix:(EssentialsCore) Add ability for devjson command to handle overloads
The `devjson` command needs to ability to handle overloads. With this change, if a method is an overloaded method, the command will get all methods on an object that match the entered method name, then get the first entry in the list of methods that matches the length of the provided parameters list.

This won't work in all cases, as there may be situations where the parameters of the methods have the same length, but different types. In that situation, it's likely that the conversion from `Object` to the desired type will fail, in which case, the command will notify the user that something went wrong.
2021-05-20 10:52:12 -06:00
Andrew Welker
a91af6bd75 Merge pull request #703 from PepperDash/feature/add-rfi-issue-template
Adds RFI Template
2021-05-20 09:22:23 -06:00
Neil Dorin
2d36b80800 Adds CH5 option 2021-05-14 15:46:12 -06:00
Neil Dorin
e152b9a504 Merge branch 'development' into feature/add-rfi-issue-template 2021-05-14 15:05:25 -06:00
Neil Dorin
eac7c91327 Create rfi_request.md 2021-05-13 18:07:03 -06:00
Jason DeVito
ac0d5e59a0 ZoomRoom.cs, commented out Debug statement @ line 874 to remove 'JSON Curly Brace Count:' messages in console when using debug level 2. 2021-05-12 11:27:33 -05:00
Andrew Welker
78be8ec5f2 Merge pull request #701 from PepperDash/feature/reconfigurable-device-write-control
Feature/reconfigurable device write control
2021-05-11 20:22:04 -06:00
Jason Alborough
5fc4ff6027 #700 FIxes issue where ConfigWrite.UpdateDeviceConfig and UpdateRoomConfig do not write config to file 2021-05-11 20:28:15 -04:00
Neil Dorin
63853739f3 Updates object structure to deal with a bug in ZoomRoom 5.6.3 that responds with an incorrect object structure for the layout style property 2021-05-11 17:23:26 -06:00
Jason DeVito
c14193f9ac Updates to VideoCodecControllerJoinMap to fix joins for Participant triggers. Updated ZoomRoomJoinMaps to implement zConfiguration.eLayoutStyle to pass the name across the bridge. 2021-05-11 18:13:18 -05:00
Jason T Alborough
da179c01f5 Fixes UpdateDeviceConfig() 2021-05-11 17:52:26 -04:00
Jason Alborough
0ded3e30f9 Merge branch 'development' into feature/reconfigurable-device-write-control 2021-05-11 15:24:40 -04:00
Jason T Alborough
8d215930d9 Adds WriteControlProperty to ReconfigurableDevice
CameraBase now uses ReconfigurableDevice
2021-05-11 15:20:35 -04:00
Neil Dorin
b4edb021ee #698 merged in join map updates from JKD. Fixed enum bit comparison for available layout feedbacks 2021-05-11 12:23:27 -06:00
Neil Dorin
52caa98f33 Merge branch 'feature/add-zoomroom-layout-controls' into feature/add-zoomroom-participant-actions 2021-05-11 11:10:37 -06:00
Jason DeVito
4e041d1773 Removed unused DecodecParticipantsXSig method. 2021-05-11 11:36:17 -05:00
Jason DeVito
118bd5a54a Updates VideoCodecControllerJoinMap.cs to organize joins. Update to VideoCodecBase.cs UpdateParticipantsXSig. 2021-05-11 11:19:33 -05:00
Jason DeVito
116abbf962 Updates ZoomRoomJoinMap.cs with join numbers. Updated VideoCodecControllerJoinMap.cs to organize the joins in a logical pattern. Update VideoCodecBase to add the maxAnalogs to the logic calculating xsig indexes and offsets. 2021-05-11 08:34:16 -05:00
Neil Dorin
a06333e1c3 Adds temp join numbers for participant actions 2021-05-10 15:38:33 -06:00
Neil Dorin
d937dc14fc #698 Adds actions to toggle audio/video mute and pinning for participants 2021-05-10 15:09:33 -06:00
Neil Dorin
604f4ca22d #698 Adds join objects for participants audio/video mute and pin toggle 2021-05-10 12:58:07 -06:00
Neil Dorin
2d7ad8ba2a #698 Updates to add participant hand raised and pin/unpin 2021-05-07 18:07:25 -06:00
Neil Dorin
e4a3933743 Mostly coded. Needs join numbers for ZoomRoomJoinMap values 2021-05-06 16:54:41 -06:00
Neil Dorin
d6445861f5 Adds IHasZoomRoomLayouts interface. Update zStatus.Layout class to extend NotifiableObject 2021-05-05 21:55:28 -06:00
Jason T Alborough
03b076c8eb Merge pull request #696 from PepperDash/feature/dm-audio-routing-fix
Fixes DM Audio Routing
2021-04-30 15:28:08 -04:00
Andrew Welker
7c58221acc Merge branch 'development' into feature/dm-audio-routing-fix 2021-04-30 13:12:18 -06:00
Andrew Welker
14f7c27b33 Merge pull request #695 from PepperDash/bugfix/discovery-routing-endless-loop
Bugfix/discovery routing endless loop
2021-04-30 13:12:10 -06:00
Jason Alborough
9ea65883b7 Fixes DM Audio Routing 2021-04-30 14:58:39 -04:00
Alex Johnson
9d0020d999 Merge branch 'development' into bugfix/discovery-routing-endless-loop 2021-04-29 13:52:36 -04:00
Alex Johnson
fb44a3b93c Resolves looping in IRouting by adding the device to the tracking list before iterating down further. Adds debug statement to print when this condition occurs - "Skipping input <device> on <router-device>, this was already checked" 2021-04-29 13:44:47 -04:00
Andrew Welker
6b7c5c01f8 Merge pull request #693 from PepperDash/hotfix/console-response-crlf-fixes
Hotfix/console response crlf fixes
2021-04-28 21:58:44 -06:00
Andrew Welker
1a3eb9a546 Merge branch 'development' into hotfix/console-response-crlf-fixes 2021-04-28 09:54:39 -06:00
Andrew Welker
44c171c8f4 Merge pull request #692 from PepperDash/hotfix/console-response-crlf-fixes
Changes cr to crlf for some console command responses
2021-04-28 09:54:24 -06:00
Alex Johnson
ae9833ffaa Changes cr to crlf for some console command responses 2021-04-23 08:58:43 -04:00
Neil Dorin
685c344785 #690 fixes incorrect conditional check before firing feedback 2021-04-20 14:38:24 -06:00
Andrew Welker
e6f5142fc3 Merge pull request #688 from PepperDash/feature/secretsManager
Feature/secrets manager
2021-04-16 12:13:38 -06:00
Trevor Payne
3c9ca1e527 Resoves #688
Added some QoL improvements to SecretsManager meant to protect the integrity of the providers dictionary from accidental manipulation

Debug statement improvements

Improvements to verbosity of console command returns for the SecretsManager
2021-04-16 12:29:41 -05:00
Trevor Payne
452d0a5a39 Changed DeviceFactory.GetSecret(SecretsPropertiesConfig data) to return an empty string instead of null on a failed retrieval 2021-04-16 09:59:58 -05:00
Trevor Payne
8643ed2caf #685 - Requested Fixes per AWelker 2021-04-15 19:14:24 -05:00
Trevor Payne
2787c7fc52 Close #685
Adds support for Secrets
2021-04-15 18:47:13 -05:00
Trevor Payne
babc3e4f1a fixed minor registration error 2021-04-15 14:36:43 -05:00
Trevor Payne
0a4ff82af0 Added SecretsManager
Added ISecrets

Added ISecretsProvider
2021-04-15 13:47:46 -05:00
Andrew Welker
b455e1af21 Merge pull request #684 from PepperDash/release/v1.8.3
add flags on both ends to prevent input switching loop
2021-04-09 16:50:54 -06:00
Andrew Welker
e12a5e95bf Merge pull request #683 from PepperDash/release/v1.8.3
add flags on both ends to prevent input switching loop
2021-04-09 16:50:44 -06:00
Andrew Welker
3e7144f7ef Merge branch 'main' into release/v1.8.3 2021-04-09 16:40:56 -06:00
Andrew Welker
19e070ed76 add flags on both ends to prevent input switching loop 2021-04-09 16:36:11 -06:00
Neil Dorin
b818c14713 Merge pull request #681 from PepperDash/release/v1.8.3
Release/v1.8.3
2021-04-09 15:31:02 -06:00
Andrew Welker
63e7866890 Merge pull request #680 from PepperDash/feature/imobilecontrolroombridge-upgrades
Feature/imobilecontrolroombridge upgrades
2021-04-08 16:26:47 -06:00
Neil Dorin
922d1d8133 Merge branch 'development' into feature/imobilecontrolroombridge-upgrades 2021-04-08 16:17:02 -06:00
Andrew Welker
d26ea01ed9 Merge pull request #673 from PepperDash/hotfix/genericqueue-dispose-issue
Hotfix/genericqueue dispose issue
2021-04-08 16:15:31 -06:00
Andrew Welker
4ebd1b53b5 Merge branch 'development' into hotfix/genericqueue-dispose-issue 2021-04-08 15:37:15 -06:00
Andrew Welker
0226b58b1e Merge pull request #672 from PepperDash/hotfix/genericqueue-dispose-issue
#671 switches to private Dispose() on program stop
2021-04-08 15:36:53 -06:00
Neil Dorin
be41922c54 Merge pull request #679 from PepperDash/feature/plugin-loading-issues
Clean up messaging
2021-04-08 14:43:43 -06:00
Andrew Welker
56f93c5491 Merge branch 'main' into hotfix/genericqueue-dispose-issue 2021-04-08 14:25:11 -06:00
Andrew Welker
8cfd58bb7e Fix error printing for plugin loading 2021-04-08 14:21:32 -06:00
Andrew Welker
6ddfdc4b38 Updating plugin loading mechanism to provide better feedback 2021-04-08 13:54:06 -06:00
Neil Dorin
7640b95701 Clears camera mute mode when turning camera auto mode off 2021-04-06 20:08:59 -06:00
Neil Dorin
6c2abc7abd Adds missing space in video mute command 2021-04-06 19:54:01 -06:00
Neil Dorin
1849d118b4 gets rid of console print when queue items are processed. 2021-04-06 18:15:37 -06:00
Neil Dorin
22e7f004a5 adds missing code parameter 2021-04-06 18:06:15 -06:00
Neil Dorin
0dcaacb038 Merge branch 'feature/multi-display-adds' into feature/imobilecontrolroombridge-upgrades 2021-04-06 16:37:17 -06:00
Neil Dorin
cf8e673677 Adds ClientJoined event 2021-04-06 12:25:33 -06:00
Neil Dorin
200080247a closes #671 and changes to aborting thread in Dispose instead of attempting to join 2021-04-06 10:38:39 -06:00
Neil Dorin
ba247ee8d6 removes commented code for clarity 2021-04-06 10:36:03 -06:00
Neil Dorin
b3617d04c8 Merge pull request #677 from PepperDash/hotfix/stream-debug-console-issues
Hotfix/stream debug console issues
2021-04-06 10:30:08 -06:00
Neil Dorin
85cf51876a Merge branch 'development' into hotfix/stream-debug-console-issues 2021-04-06 10:01:44 -06:00
Neil Dorin
b6f47168c0 Merge pull request #676 from PepperDash/hotfix/stream-debug-console-issues
Fix FormatException happening from fall-through
2021-04-06 10:01:07 -06:00
Neil Dorin
1e755df9bb #675 Adds UserPromptedForCode event and method to show code on CiscoSparkCodec 2021-04-05 17:01:34 -06:00
Neil Dorin
037f8ed043 #671 Updates to GenericQueue to resolve issues when stopping program 2021-04-02 17:07:06 -06:00
Neil Dorin
e327d2d359 passes true when disposing of generic queue in deconstructor 2021-04-02 16:09:54 -06:00
Andrew Welker
e832566168 change to CrestronConsole.ConsoleCommandResponse for help text 2021-04-02 09:44:02 -06:00
Andrew Welker
252d037380 change to method signature instead of lambda 2021-04-02 09:09:13 -06:00
Andrew Welker
dd0f7a586e fix Console statement and add help 2021-04-02 09:03:39 -06:00
Neil Dorin
2feec62052 Removes explicit call to dispose when program stops. Clears queue in dispose 2021-03-31 16:35:33 -06:00
Neil Dorin
33a1b1697a Removes call to dispose on program stop event. Adds debug statement in Dispose() 2021-03-31 15:29:55 -06:00
Neil Dorin
955d68b3f3 #671 switches to private Dispose() on program stop 2021-03-30 15:56:05 -06:00
Neil Dorin
0f28d46f34 #671 Calls the private dispose method on program stop 2021-03-30 15:53:10 -06:00
Andrew Welker
275759143b Merge branch 'development' into feature/multi-display-adds 2021-03-29 13:31:10 -06:00
Andrew Welker
3a4737b6f6 Merge pull request #670 from PepperDash/hotfix/dm-routing-issues
Hotfix/dm routing issues
2021-03-29 13:29:46 -06:00
Andrew Welker
2b2308d5c2 Merge branch 'development' into hotfix/dm-routing-issues 2021-03-29 13:18:00 -06:00
Andrew Welker
185f03065e Merge pull request #669 from PepperDash/hotfix/dm-routing-issues
Fix LinkToApi methods for chassis to use ExecuteNumericSwitch
2021-03-29 13:17:51 -06:00
Andrew Welker
3803263598 Merge branch 'development' into feature/multi-display-adds 2021-03-29 12:54:54 -06:00
Andrew Welker
358fa1a9c0 Merge pull request #667 from PepperDash/hotfix/generic-queue-disposed-check
Hotfix/generic queue disposed check
2021-03-29 12:53:43 -06:00
Andrew Welker
7fbbc9f2b8 Fix LinkToApi methods for chassis to use ExecuteNumericSwitch 2021-03-29 12:50:43 -06:00
Andrew Welker
f4f400f9b6 Merge branch 'development' into hotfix/generic-queue-disposed-check 2021-03-29 09:55:40 -06:00
Andrew Welker
d3383db890 Merge pull request #666 from PepperDash/hotfix/generic-queue-disposed-check
added a Disposed check and Debug Message to prevent enqueing messages…
2021-03-29 07:55:29 -06:00
Nick Genovese
139e5370ea added a Disposed check and Debug Message to prevent enqueing messages after the Generic Queue has been disposed; typically happens at program stop 2021-03-29 09:42:31 -04:00
Andrew Welker
4f9fa05e3f Merge branch 'development' into feature/multi-display-adds 2021-03-25 16:06:48 -06:00
Andrew Welker
7ab2574912 Merge pull request #662 from PepperDash/hotfix/scheduler-fixes
Hotfix/scheduler fixes
2021-03-25 16:05:25 -06:00
Andrew Welker
35167d77f9 Merge branch 'development' into hotfix/scheduler-fixes 2021-03-25 15:48:59 -06:00
Andrew Welker
bdd17dfa27 Merge pull request #661 from PepperDash/hotfix/scheduler-fixes
Hotfix/scheduler fixes
2021-03-25 15:48:45 -06:00
Andrew Welker
6443e00428 update back to latest builder image 2021-03-22 09:48:23 -06:00
Andrew Welker
008279e867 Initialize some properties that were causing a nullRef 2021-03-22 09:39:08 -06:00
Andrew Welker
c4a6d20791 fixing a nullref issue with cisco spark 2021-03-22 09:23:07 -06:00
Andrew Welker
61b4002e5a Fix to test build image again 2021-03-19 09:22:21 -06:00
Andrew Welker
da63d0917e Updates to test new builder image
this build WILL fail
2021-03-19 09:03:05 -06:00
Andrew Welker
0228fd1c0f fix a ; 2021-03-18 16:27:08 -06:00
Andrew Welker
085ba134c4 Set event to not be acknowledgable
Added logic to acknowledge event
Added debug statement to show that event was being fired
2021-03-18 12:44:20 -06:00
Andrew Welker
a9fce3237c Added check for key to Clear command
If the key was wrong or wasn't in the group, a `KeyNotFoundException` was thrown.

Also added acknowledgment of a successful deletion
2021-03-18 12:43:29 -06:00
Andrew Welker
840fb21e15 Added console command to list events for a group 2021-03-18 12:42:29 -06:00
Neil Dorin
6ab4d4d090 #658 Initializes config properties in constructor(s) 2021-03-17 10:52:17 -06:00
Andrew Welker
fcdee3b9fd Merge branch 'development' into feature/multi-display-adds 2021-03-17 09:01:00 -06:00
Neil Dorin
4e15d7fe5a Adds necessary config properteis 2021-03-16 15:36:14 -06:00
Andrew Welker
2a76e2b3f9 Merge pull request #656 from PepperDash/hotfix/namespace-issues
Hotfix/namespace issues
2021-03-12 17:37:14 -07:00
Andrew Welker
8ed236abae Merge pull request #654 from PepperDash/hotfix/namespace-issues
Fix old/wrong namespace versions to have copies of types that are in the correct one
2021-03-12 16:48:01 -07:00
Andrew Welker
74231a428d removed unnecessary stack trace printing 2021-03-05 17:31:21 -07:00
Andrew Welker
8ba0920cc0 add jsonProperty decorator for PreferredName 2021-03-05 15:41:22 -07:00
Andrew Welker
e23fe06fef update name change in csproj 2021-03-05 15:32:21 -07:00
Andrew Welker
79fd1f7424 add destinationListKey property and remove DDVC 2021-03-03 14:43:42 -07:00
Andrew Welker
d2ebc340bd add Sink Type property 2021-03-01 15:15:50 -07:00
Andrew Welker
7c7ae65d40 Update PepperDash_Essentials_Core.csproj 2021-02-25 17:01:24 -07:00
Andrew Welker
0dcbb652df add properties to SourceListItem
add XML Comments
2021-02-25 17:00:57 -07:00
Andrew Welker
0649cea367 Add DestinationLists to config 2021-02-25 16:53:51 -07:00
Andrew Welker
9f840fae41 add DestinationListItem 2021-02-25 16:53:35 -07:00
106 changed files with 13562 additions and 7820 deletions

View File

@@ -7,6 +7,9 @@ assignees: ''
---
**Was this bug identified in a specific build version?**
Please note the build version where this bug was identified
**Describe the bug**
A clear and concise description of what the bug is.

27
.github/ISSUE_TEMPLATE/rfi_request.md vendored Normal file
View File

@@ -0,0 +1,27 @@
---
name: Request for Information
about: Request specific information about capabilities of the framework
title: "[RFI]-"
labels: RFI
assignees: ''
---
**What is your request?**
Please provide as much detail as possible.
**What is the intended use case**
- [ ] Essentials Standalone Application
- [ ] Essentials + SIMPL Windows Hybrid
**User Interface Requirements**
- [ ] Not Applicable (logic only)
- [ ] Crestron Smart Graphics Touchpanel
- [ ] Cisco Touch10
- [ ] Mobile Control
- [ ] Crestron CH5 Touchpanel interface
**Additional context**
Add any other context or screenshots about the request here.

View File

@@ -8,12 +8,9 @@ on:
- bugfix/*
- release/*
- development
pull_request:
branches:
- development
env:
# solution path doesn't need slashes unless there it is multiple folders deep
# solution path doesn't need slashes unless it is multiple folders deep
# solution name does not include extension. .sln is assumed
SOLUTION_PATH: PepperDashEssentials
SOLUTION_FILE: PepperDashEssentials
@@ -90,7 +87,6 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Upload the build package to the release
- name: Upload Release Package
if: contains(env.VERSION,'-rc-') || contains(env.VERSION,'-hotfix-')
id: upload_release
uses: actions/upload-release-asset@v1
with:

View File

@@ -36,6 +36,7 @@ namespace PepperDash.Essentials
Thread.MaxNumberOfUserThreads = 400;
Global.ControlSystem = this;
DeviceManager.Initialize(this);
SecretsManager.Initialize();
SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true;
}
@@ -71,7 +72,7 @@ namespace PepperDash.Essentials
CrestronConsole.AddNewConsoleCommand(s =>
{
foreach (var tl in TieLineCollection.Default)
CrestronConsole.ConsoleCommandResponse(" {0}\r", tl);
CrestronConsole.ConsoleCommandResponse(" {0}\r\n", tl);
},
"listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
@@ -85,8 +86,8 @@ namespace PepperDash.Essentials
CrestronConsole.AddNewConsoleCommand(s =>
{
CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" +
"System URL: {0}\r" +
CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r\n" +
"System URL: {0}\r\n" +
"Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl);
}, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator);
@@ -130,29 +131,46 @@ namespace PepperDash.Essentials
if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) // Handles 3-series running Windows CE OS
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on {1} Appliance", Global.AssemblyVersion, Global.ProcessorSeries.ToString());
string userFolder;
string nvramFolder;
bool is4series = false;
if (eCrestronSeries.Series4 == (Global.ProcessorSeries & eCrestronSeries.Series4)) // Handle 4-series
{
is4series = true;
// Set path to user/
userFolder = "user";
nvramFolder = "nvram";
}
else
{
userFolder = "User";
nvramFolder = "Nvram";
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on {1} Appliance", Global.AssemblyVersion, is4series ? "4-series" : "3-series");
// Check if User/ProgramX exists
if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + "User"
if (Directory.Exists(Global.ApplicationDirectoryPathPrefix + dirSeparator + userFolder
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
{
Debug.Console(0, @"User/program{0} directory found", InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + "User"
Debug.Console(0, @"{0}/program{1} directory found", userFolder, InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + userFolder
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
}
// Check if Nvram/Programx exists
else if (Directory.Exists(directoryPrefix + dirSeparator + "Nvram"
else if (Directory.Exists(directoryPrefix + dirSeparator + nvramFolder
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber)))
{
Debug.Console(0, @"Nvram/program{0} directory found", InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + "Nvram"
Debug.Console(0, @"{0}/program{1} directory found", nvramFolder, InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + nvramFolder
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
}
// If neither exists, set path to User/ProgramX
else
{
Debug.Console(0, @"No previous directory found. Using User/program{0}", InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + "User"
Debug.Console(0, @"No previous directory found. Using {0}/program{1}", userFolder, InitialParametersClass.ApplicationNumber);
filePathPrefix = directoryPrefix + dirSeparator + userFolder
+ dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator;
}
}
@@ -432,14 +450,13 @@ namespace PepperDash.Essentials
return;
}
uint fusionIpId = 0xf1;
foreach (var roomConfig in ConfigReader.ConfigObject.Rooms)
{
var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as EssentialsRoomBase;
var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as IEssentialsRoom;
if (room != null)
{
// default IPID
uint fusionIpId = 0xf1;
// default to no join map key
string fusionJoinMapKey = string.Empty;
@@ -456,11 +473,11 @@ namespace PepperDash.Essentials
}
}
if (room is EssentialsHuddleSpaceRoom)
if (room is IEssentialsHuddleSpaceRoom)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey));
@@ -468,12 +485,12 @@ namespace PepperDash.Essentials
CreateMobileControlBridge(room);
}
else if (room is EssentialsHuddleVtc1Room)
else if (room is IEssentialsHuddleVtc1Room)
{
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion");
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((IEssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge...");
@@ -484,7 +501,7 @@ namespace PepperDash.Essentials
DeviceManager.AddDevice(room);
Debug.Console(0, Debug.ErrorLogLevel.Notice,
"Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion");
"Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion with IP-ID {0:X2}", fusionIpId);
DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey));
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge");
@@ -497,22 +514,35 @@ namespace PepperDash.Essentials
DeviceManager.AddDevice(room);
}
fusionIpId += 1;
}
else
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Notice: Cannot create room from config, key '{0}' - Is this intentional? This may be a valid configuration.", roomConfig.Key);
}
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded.");
}
private static void CreateMobileControlBridge(EssentialsRoomBase room)
private static void CreateMobileControlBridge(object room)
{
var mobileControl = GetMobileControlDevice();
if (mobileControl == null) return;
mobileControl.CreateMobileControlRoomBridge(room, mobileControl);
var mobileControl3 = mobileControl as IMobileControl3;
if (mobileControl3 != null)
{
mobileControl3.CreateMobileControlRoomBridge(room as IEssentialsRoom, mobileControl);
}
else
{
mobileControl.CreateMobileControlRoomBridge(room as EssentialsRoomBase, mobileControl);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Mobile Control Bridge Added...");
}

View File

@@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Fusion
{
BooleanSigData CodecIsInCall;
public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId, string joinMapKey)
public EssentialsHuddleVtc1FusionController(IEssentialsHuddleVtc1Room room, uint ipId, string joinMapKey)
: base(room, ipId, joinMapKey)
{
@@ -37,7 +37,7 @@ namespace PepperDash.Essentials.Fusion
{
try
{
var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
var codec = (Room as IEssentialsHuddleVtc1Room).VideoCodec;
if (codec == null)
{
@@ -141,7 +141,7 @@ namespace PepperDash.Essentials.Fusion
void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e)
{
var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec;
var codec = (Room as IEssentialsHuddleVtc1Room).VideoCodec;
CodecIsInCall.InputSig.BoolValue = codec.IsInCall;
}
@@ -150,7 +150,7 @@ namespace PepperDash.Essentials.Fusion
protected override void CreateSymbolAndBasicSigs(uint ipId)
{
Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
Debug.Console(0, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();
@@ -174,11 +174,11 @@ namespace PepperDash.Essentials.Fusion
// Moved to
CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, eSigIoMask.InputSigOnly);
// Don't think we need to get current status of this as nothing should be alive yet.
(Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange;
(Room as IEssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange;
FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource);
FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey));
FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as IEssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource);
FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as IEssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey));
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
@@ -187,7 +187,7 @@ namespace PepperDash.Essentials.Fusion
protected override void SetUpSources()
{
// Sources
var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).SourceListKey);
var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as IEssentialsHuddleVtc1Room).SourceListKey);
if (dict != null)
{
// NEW PROCESS:
@@ -238,7 +238,7 @@ namespace PepperDash.Essentials.Fusion
else
{
Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'",
(Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key);
(Room as IEssentialsHuddleVtc1Room).SourceListKey, Room.Key);
}
}
@@ -259,7 +259,7 @@ namespace PepperDash.Essentials.Fusion
display.UsageTracker.DeviceUsageEnded += new EventHandler<DeviceUsageEventArgs>(UsageTracker_DeviceUsageEnded);
}
var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase;
var defaultDisplay = (Room as IEssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase;
if (defaultDisplay == null)
{
Debug.Console(1, this, "Cannot link null display to Fusion because default display is null");
@@ -332,7 +332,7 @@ namespace PepperDash.Essentials.Fusion
string displayName = string.Format("Display {0} - ", displayIndex);
if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay)
if (display == (Room as IEssentialsHuddleVtc1Room).DefaultDisplay)
{
// Power on
var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig);
@@ -351,7 +351,7 @@ namespace PepperDash.Essentials.Fusion
// Current Source
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig);
defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ;
defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b => { if (!b) (Room as IEssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey); }); ;
}
}
}

View File

@@ -137,7 +137,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Room\Config\EssentialsDualDisplayRoomPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsNDisplayRoomPropertiesConfig.cs" />
<Compile Include="Room\Config\DDVC01RoomPropertiesConfig.cs" />
<Compile Include="Room\Config\SimplRoomPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsPresentationPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsHuddleRoomPropertiesConfig.cs" />
<Compile Include="Room\Config\EssentialsHuddleVtc1PropertiesConfig.cs" />
@@ -149,6 +149,8 @@
<Compile Include="Room\Types\EssentialsNDisplayRoomBase.cs" />
<Compile Include="Room\Config\EssentialsRoomConfig.cs" />
<Compile Include="Room\Types\EssentialsTechRoom.cs" />
<Compile Include="Room\Types\Interfaces\IEssentialsHuddleSpaceRoom.cs" />
<Compile Include="Room\Types\Interfaces\IEssentialsHuddleVtc1Room.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsEnvironmentDriver.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsLightingDriver.cs" />
<Compile Include="UIDrivers\Environment Drivers\EssentialsShadeDriver.cs" />

View File

@@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Room.Config
/// Gets and operating, standalone emergegncy object that can be plugged into a room.
/// Returns null if there is no emergency defined
/// </summary>
public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, EssentialsRoomBase room)
public static EssentialsRoomEmergencyBase GetEmergency(EssentialsRoomPropertiesConfig props, IEssentialsRoom room)
{
// This emergency
var emergency = props.Emergency;
@@ -96,7 +96,7 @@ namespace PepperDash.Essentials.Room.Config
if (behaviour == "trackroomstate")
{
// Tie LED enable to room power state
var essRoom = room as EssentialsRoomBase;
var essRoom = room as IEssentialsRoom;
essRoom.OnFeedback.OutputChange += (o, a) =>
{
if (essRoom.OnFeedback.BoolValue)
@@ -196,9 +196,20 @@ namespace PepperDash.Essentials.Room.Config
public string DefaultAudioKey { get; set; }
[JsonProperty("sourceListKey")]
public string SourceListKey { get; set; }
[JsonProperty("destinationListKey")]
public string DestinationListKey { get; set; }
[JsonProperty("defaultSourceItem")]
public string DefaultSourceItem { get; set; }
/// <summary>
/// Indicates if the room supports advanced sharing
/// </summary>
[JsonProperty("supportsAdvancedSharing")]
public bool SupportsAdvancedSharing { get; set; }
/// <summary>
/// Indicates if non-tech users can change the share mode
/// </summary>
[JsonProperty("userCanChangeShareMode")]
public bool UserCanChangeShareMode { get; set; }
}
public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig

View File

@@ -8,19 +8,19 @@ using Newtonsoft.Json;
namespace PepperDash.Essentials.Room.Config
{
public class DDVC01RoomPropertiesConfig : EssentialsHuddleVtc1PropertiesConfig
public class SimplRoomPropertiesConfig : EssentialsHuddleVtc1PropertiesConfig
{
[JsonProperty("roomPhoneNumber")]
public string RoomPhoneNumber { get; set; }
[JsonProperty("roomURI")]
public string RoomURI { get; set; }
[JsonProperty("speedDials")]
public List<DDVC01SpeedDial> SpeedDials { get; set; }
public List<SimplSpeedDial> SpeedDials { get; set; }
[JsonProperty("volumeSliderNames")]
public List<string> VolumeSliderNames { get; set; }
}
public class DDVC01SpeedDial
public class SimplSpeedDial
{
[JsonProperty("name")]
public string Name { get; set; }

View File

@@ -17,11 +17,11 @@ namespace PepperDash.Essentials.Room
public class EssentialsRoomEmergencyContactClosure : EssentialsRoomEmergencyBase
{
EssentialsRoomBase Room;
IEssentialsRoom Room;
string Behavior;
bool TriggerOnClose;
public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, EssentialsRoomBase room) :
public EssentialsRoomEmergencyContactClosure(string key, EssentialsRoomEmergencyConfig config, IEssentialsRoom room) :
base(key)
{
Room = room;

View File

@@ -207,7 +207,7 @@ namespace PepperDash.Essentials
DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls;
Initialize();
InitializeRoom();
}
catch (Exception e)
{
@@ -215,7 +215,7 @@ namespace PepperDash.Essentials
}
}
void Initialize()
void InitializeRoom()
{
if (DefaultAudioDevice is IBasicVolumeControls)
DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls;
@@ -645,9 +645,9 @@ namespace PepperDash.Essentials
public static void AllRoomsOff()
{
var allRooms = DeviceManager.AllDevices.Where(d =>
d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
d is IEssentialsHuddleSpaceRoom && !(d as IEssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
foreach (var room in allRooms)
(room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff", (room as EssentialsHuddleSpaceRoom).SourceListKey);
(room as IEssentialsHuddleSpaceRoom).RunRouteAction("roomOff", (room as IEssentialsHuddleSpaceRoom).SourceListKey);
}
#region IPrivacy Members

View File

@@ -13,7 +13,7 @@ using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials
{
public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasCurrentVolumeControls, IHasDefaultDisplay
public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IEssentialsHuddleSpaceRoom
{
public event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
public event SourceInfoChangeHandler CurrentSourceChange;
@@ -156,7 +156,7 @@ namespace PepperDash.Essentials
DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IRoutingSinkWithSwitching;
Initialize();
InitializeRoom();
}
catch (Exception e)
{
@@ -164,7 +164,7 @@ namespace PepperDash.Essentials
}
}
void Initialize()
void InitializeRoom()
{
if (DefaultAudioDevice is IBasicVolumeControls)
DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls;

View File

@@ -17,9 +17,9 @@ using PepperDash.Essentials.Core.DeviceTypeInterfaces;
namespace PepperDash.Essentials
{
public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IHasCurrentSourceInfoChange,
IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback
{
public class EssentialsHuddleVtc1Room : EssentialsRoomBase, IEssentialsHuddleVtc1Room
{
private bool _codecExternalSourceChange;
public event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
public event SourceInfoChangeHandler CurrentSourceChange;
@@ -178,10 +178,12 @@ namespace PepperDash.Essentials
handler(_CurrentSourceInfo, ChangeType.DidChange);
var vc = VideoCodec as IHasExternalSourceSwitching;
if (vc != null)
if (vc != null && !_codecExternalSourceChange)
{
vc.SetSelectedSource(CurrentSourceInfoKey);
}
_codecExternalSourceChange = false;
}
}
SourceListItem _CurrentSourceInfo;
@@ -224,7 +226,7 @@ namespace PepperDash.Essentials
DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IBasicVolumeControls;
Initialize();
InitializeRoom();
}
catch (Exception e)
{
@@ -232,7 +234,7 @@ namespace PepperDash.Essentials
}
}
void Initialize()
void InitializeRoom()
{
try
{
@@ -421,6 +423,12 @@ namespace PepperDash.Essentials
return true;
}
public void RunRouteActionCodec(string routeKey, string sourceListKey)
{
_codecExternalSourceChange = true;
RunRouteAction(routeKey, sourceListKey);
}
/// <summary>
///
/// </summary>
@@ -446,7 +454,8 @@ namespace PepperDash.Essentials
else
{
Debug.Console(1, this, "sourceListKey present but not yet implemented");
throw new NotImplementedException();
RunRouteAction(routeKey, new Action(() => { }));
}
}
@@ -463,7 +472,11 @@ namespace PepperDash.Essentials
RunRouteAction(routeKey, successCallback);
}
else
throw new NotImplementedException();
{
Debug.Console(1, this, "sourceListKey present but not yet implemented");
RunRouteAction(routeKey, successCallback);
}
}
/// <summary>
@@ -724,10 +737,10 @@ namespace PepperDash.Essentials
/// </summary>
public static void AllRoomsOff()
{
var allRooms = DeviceManager.AllDevices.Where(d =>
d is EssentialsHuddleSpaceRoom && !(d as EssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
var allRooms = DeviceManager.AllDevices.Where(d =>
d is IEssentialsRoom && !(d as IEssentialsHuddleSpaceRoom).ExcludeFromGlobalFunctions);
foreach (var room in allRooms)
(room as EssentialsHuddleSpaceRoom).RunRouteAction("roomOff");
(room as IEssentialsHuddleSpaceRoom).RunRouteAction("roomOff");
}
@@ -750,7 +763,7 @@ namespace PepperDash.Essentials
x => x.DestinationKey == VideoCodec.Key && x.DestinationPort == videoCodecWithExternalSwitching.ExternalSourceInputPort).DestinationPort;
videoCodecWithExternalSwitching.ClearExternalSources();
videoCodecWithExternalSwitching.RunRouteAction = RunRouteAction;
videoCodecWithExternalSwitching.RunRouteAction = RunRouteActionCodec;
var srcList = ConfigReader.ConfigObject.SourceLists.SingleOrDefault(x => x.Key == SourceListKey).Value.OrderBy(kv => kv.Value.Order); ;
foreach (var kvp in srcList)

View File

@@ -107,14 +107,18 @@ namespace PepperDash.Essentials
private void TunerPresetsOnPresetRecalled(ISetTopBoxNumericKeypad device, string channel)
{
//Debug.Console(2, this, "TunerPresetsOnPresetRecalled");
if (!_currentPresets.ContainsKey(device.Key))
{
return;
}
//Debug.Console(2, this, "Tuner Key: {0} Channel: {1}", device.Key, channel);
_currentPresets[device.Key] = channel;
if (!CurrentPresetsFeedbacks.ContainsKey(device.Key))
if (CurrentPresetsFeedbacks.ContainsKey(device.Key))
{
CurrentPresetsFeedbacks[device.Key].FireUpdate();
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.AudioCodec;
using PepperDash.Core;
namespace PepperDash.Essentials
{
public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IHasDefaultDisplay
{
bool ExcludeFromGlobalFunctions { get; }
void RunRouteAction(string routeKey);
EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; }
IBasicVolumeControls CurrentVolumeControls { get; }
event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
}
public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange,
IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay
{
EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; }
void RunRouteAction(string routeKey);
IHasScheduleAwareness ScheduleSource { get; }
string DefaultCodecRouteString { get; }
}
}

View File

@@ -0,0 +1,24 @@
using System;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Config;
namespace PepperDash.Essentials
{
public interface IEssentialsHuddleSpaceRoom : IEssentialsRoom, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasDefaultDisplay
{
bool ExcludeFromGlobalFunctions { get; }
void RunRouteAction(string routeKey);
EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; }
IBasicVolumeControls CurrentVolumeControls { get; }
event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
}
}

View File

@@ -0,0 +1,25 @@
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.AudioCodec;
namespace PepperDash.Essentials
{
public interface IEssentialsHuddleVtc1Room : IEssentialsRoom, IHasCurrentSourceInfoChange,
IPrivacy, IHasCurrentVolumeControls, IRunRouteAction, IRunDefaultCallRoute, IHasVideoCodec, IHasAudioCodec, IHasDefaultDisplay, IHasInCallFeedback
{
EssentialsHuddleVtc1PropertiesConfig PropertiesConfig { get; }
void RunRouteAction(string routeKey);
IHasScheduleAwareness ScheduleSource { get; }
new BoolFeedback InCallFeedback { get; }
new BoolFeedback PrivacyModeIsOnFeedback { get; }
string DefaultCodecRouteString { get; }
}
}

View File

@@ -79,6 +79,16 @@ namespace PepperDash.Essentials
Panel = new Tsw1052(id, Global.ControlSystem);
else if (type == "tsw1060")
Panel = new Tsw1060(id, Global.ControlSystem);
else if (type == "tsw570")
Panel = new Tsw570(id, Global.ControlSystem);
else if (type == "tsw770")
Panel = new Tsw770(id, Global.ControlSystem);
else if (type == "ts770")
Panel = new Ts770(id, Global.ControlSystem);
else if (type == "tsw1070")
Panel = new Tsw1070(id, Global.ControlSystem);
else if (type == "ts1070")
Panel = new Ts1070(id, Global.ControlSystem);
else
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create TSW controller with type '{0}'", type);
@@ -203,7 +213,7 @@ namespace PepperDash.Essentials
{
public EssentialsTouchpanelControllerFactory()
{
TypeNames = new List<string>() { "tsw550", "tsw750", "tsw1050", "tsw560", "tsw760", "tsw1060", "xpanel" };
TypeNames = new List<string>() { "tsw550", "tsw750", "tsw1050", "tsw560", "tsw760", "tsw1060", "tsw570", "tsw770", "ts770", "tsw1070", "ts1070", "xpanel" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
@@ -222,7 +232,7 @@ namespace PepperDash.Essentials
// spin up different room drivers depending on room type
var room = DeviceManager.GetDeviceForKey(props.DefaultRoomKey);
if (room is EssentialsHuddleSpaceRoom)
if (room is IEssentialsHuddleSpaceRoom)
{
// Screen Saver Driver
mainDriver.ScreenSaverController = new ScreenSaverController(mainDriver, props);
@@ -236,7 +246,7 @@ namespace PepperDash.Essentials
var avDriver = new EssentialsHuddlePanelAvFunctionsDriver(mainDriver, props);
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
avDriver.CurrentRoom = room as EssentialsHuddleSpaceRoom;
avDriver.CurrentRoom = room as IEssentialsHuddleSpaceRoom;
// Environment Driver
if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0)
@@ -270,7 +280,7 @@ namespace PepperDash.Essentials
tsw.Down.UserObject = new Action<bool>(avDriver.VolumeDownPress);
}
}
else if (room is EssentialsHuddleVtc1Room)
else if (room is IEssentialsHuddleVtc1Room)
{
Debug.Console(0, panelController, "Adding huddle space VTC AV driver");
@@ -284,11 +294,11 @@ namespace PepperDash.Essentials
var avDriver = new EssentialsHuddleVtc1PanelAvFunctionsDriver(mainDriver, props);
var codecDriver = new PepperDash.Essentials.UIDrivers.VC.EssentialsVideoCodecUiDriver(panelController.Panel, avDriver,
(room as EssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver);
(room as IEssentialsHuddleVtc1Room).VideoCodec, mainDriver.HeaderDriver);
avDriver.SetVideoCodecDriver(codecDriver);
avDriver.DefaultRoomKey = props.DefaultRoomKey;
mainDriver.AvDriver = avDriver;
avDriver.CurrentRoom = room as EssentialsHuddleVtc1Room;
avDriver.CurrentRoom = room as IEssentialsHuddleVtc1Room;
// Environment Driver
if (avDriver.CurrentRoom.PropertiesConfig.Environment != null && avDriver.CurrentRoom.PropertiesConfig.Environment.DeviceKeys.Count > 0)

View File

@@ -49,6 +49,16 @@ namespace PepperDash.Essentials
/// 1006
/// </summary>
public const uint CallEndAllConfirmVisible = 1006;
/// <summary>
/// 1007
/// </summary>
public const uint MeetingPasswordVisible = 1007;
/// <summary>
/// 1008
/// </summary>
public const uint MeetingLeavePress = 1008;
@@ -103,7 +113,7 @@ namespace PepperDash.Essentials
/// <summary>
/// 1202
/// </summary>
public const uint VCStagingInactivePopoverVisible = 1202;
public const uint VCStagingInactivePopoverWithRecentsVisible = 1202;
/// <summary>
///
/// </summary>
@@ -121,6 +131,11 @@ namespace PepperDash.Essentials
/// </summary>
public const uint VCRecentsVisible = 1206;
/// <summary>
/// 1202
/// </summary>
public const uint VCStagingInactivePopoverWithoutRecentsVisible = 1207;
/// <summary>
/// 1208
/// </summary>
@@ -148,6 +163,11 @@ namespace PepperDash.Essentials
public const uint VCFavoriteVisibleStart = 1221;
// RANGE IN USE
public const uint VCFavoriteVisibleEnd = 1225;
/// <summary>
/// 1230
/// </summary>
public const uint VCStagingMeetNowPress = 1230;
/// <summary>
/// 1231
/// </summary>
@@ -243,6 +263,10 @@ namespace PepperDash.Essentials
/// </summary>
public const uint VCCameraSelectBarWithoutModeVisible = 1261;
/// <summary>
/// 1262
/// </summary>
public const uint VCCameraAutoModeIsOnFb = 1262;
/// <summary>
/// 1271
@@ -744,9 +768,9 @@ namespace PepperDash.Essentials
/// </summary>
public const uint SourceBackgroundOverlayClosePress = 15044;
/// <summary>
/// 15045 - Visibility for the bar containing call navigation button list
/// 15045
/// </summary>
public const uint CallStagingBarVisible = 15045;
public const uint ZoomRoomContentSharingVisible = 15045;
/// <summary>
/// 15046
/// </summary>
@@ -764,6 +788,10 @@ namespace PepperDash.Essentials
/// </summary>
public const uint NextMeetingModalVisible = 15049;
/// <summary>
/// 15050
/// </summary>
public const uint NextMeetingNotificationRibbonVisible = 15050;
/// <summary>
/// 15051
/// </summary>
public const uint Display1SelectPressAndFb = 15051;
@@ -831,6 +859,11 @@ namespace PepperDash.Essentials
/// 15067
/// </summary>
public const uint NotificationRibbonVisible = 15067;
/// <summary>
/// 15068
/// </summary>
public const uint HeaderMeetingInfoVisible = 15068;
/// <summary>
/// 15083 - Press for Call help desk on AC/VC
/// </summary>
@@ -935,5 +968,24 @@ namespace PepperDash.Essentials
/// 15214
/// </summary>
public const uint PinDialogDot4 = 15214;
// Password Prompt Dialog **************************
/// <summary>
/// 15301
/// </summary>
public const uint PasswordPromptDialogVisible = 15301;
/// <summary>
/// 15302
/// </summary>
public const uint PasswordPromptTextPress = 15302;
/// <summary>
/// 15306
/// </summary>
public const uint PasswordPromptCancelPress = 15306;
/// <summary>
/// 15307
/// </summary>
public const uint PasswordPromptErrorVisible = 15307;
}
}

View File

@@ -27,6 +27,28 @@ namespace PepperDash.Essentials
/// 1004
/// </summary>
public const uint CallSharedSourceNameText = 1004;
/// <summary>
/// 1005
/// </summary>
public const uint MeetingIdText = 1005;
/// <summary>
/// 1006
/// </summary>
public const uint MeetingHostText = 1006;
/// <summary>
/// 1007
/// </summary>
public const uint MeetingPasswordText = 1007;
/// <summary>
/// 1008
/// </summary>
public const uint MeetingLeaveText = 1008;
/// <summary>
/// 1009
/// </summary>
public const uint MeetingNameText = 1009;
/// <summary>
@@ -118,6 +140,14 @@ namespace PepperDash.Essentials
//----- through 3120
/// <summary>
/// 3201
/// </summary>
public const uint PasswordPromptMessageText = 3201;
/// <summary>
/// 3202
/// </summary>
public const uint PasswordPromptPasswordText = 3202;
/// <summary>
/// 3812

View File

@@ -146,18 +146,18 @@
// }
// void CurrentRoom_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
// void CurrentRoom_CurrentSourceInfoChange(IEssentialsRoom room, SourceListItem info, ChangeType type)
// {
// }
// void CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
// void CurrentRoom_CurrentDisplay1SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type)
// {
// TriList.StringInput[UIStringJoin.Display1SourceLabel].StringValue = PendingSource.PreferredName;
// }
// void CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
// void CurrentRoom_CurrentDisplay2SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type)
// {
// TriList.StringInput[UIStringJoin.Display2SourceLabel].StringValue = PendingSource.PreferredName;
// }

View File

@@ -52,7 +52,7 @@ namespace PepperDash.Essentials
CaretInterlock = new JoinedSigInterlock(TriList);
}
void SetUpGear(IAVDriver avDriver, EssentialsRoomBase currentRoom)
void SetUpGear(IAVDriver avDriver, IEssentialsRoom currentRoom)
{
// Gear
TriList.SetString(UIStringJoin.HeaderButtonIcon5, "Gear");
@@ -105,7 +105,7 @@ namespace PepperDash.Essentials
{
string message = null;
var room = DeviceManager.GetDeviceForKey(Config.DefaultRoomKey)
as EssentialsHuddleSpaceRoom;
as IEssentialsHuddleSpaceRoom;
if (room != null)
message = room.PropertiesConfig.HelpMessage;
else
@@ -164,7 +164,7 @@ namespace PepperDash.Essentials
CallCaretVisible = tempJoin + 10;
TriList.SetSigFalseAction(tempJoin, () =>
{
avDriver.ShowActiveCallsList();
avDriver.ShowActiveCallsListOrMeetingInfo();
if(avDriver.CurrentRoom.InCallFeedback.BoolValue)
CaretInterlock.ShowInterlocked(CallCaretVisible);
});
@@ -207,6 +207,7 @@ namespace PepperDash.Essentials
//TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 1);
// Set the call status text
Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count);
if (codec.ActiveCalls.Count > 0)
{
if (codec.ActiveCalls.Count == 1)
@@ -221,7 +222,7 @@ namespace PepperDash.Essentials
/// <summary>
/// Sets up Header Buttons for the EssentialsHuddleVtc1Room type
/// </summary>
public void SetupHeaderButtons(EssentialsHuddleVtc1PanelAvFunctionsDriver avDriver, EssentialsHuddleVtc1Room currentRoom)
public void SetupHeaderButtons(EssentialsHuddleVtc1PanelAvFunctionsDriver avDriver, IEssentialsHuddleVtc1Room currentRoom)
{
HeaderButtonsAreSetUp = false;
@@ -255,7 +256,7 @@ namespace PepperDash.Essentials
TriList.SetSigFalseAction(UIBoolJoin.HeaderCallStatusLabelPress,
() =>
{
avDriver.ShowActiveCallsList();
avDriver.ShowActiveCallsListOrMeetingInfo();
if (avDriver.CurrentRoom.InCallFeedback.BoolValue)
CaretInterlock.ShowInterlocked(CallCaretVisible);
});
@@ -283,7 +284,7 @@ namespace PepperDash.Essentials
/// <summary>
/// Sets up Header Buttons for the EssentialsHuddleSpaceRoom type
/// </summary>
public void SetupHeaderButtons(EssentialsHuddlePanelAvFunctionsDriver avDriver, EssentialsHuddleSpaceRoom currentRoom)
public void SetupHeaderButtons(EssentialsHuddlePanelAvFunctionsDriver avDriver, IEssentialsHuddleSpaceRoom currentRoom)
{
HeaderButtonsAreSetUp = false;
@@ -353,6 +354,8 @@ namespace PepperDash.Essentials
headerPopupShown = true;
else if (e.NewJoin == UIBoolJoin.HeaderActiveCallsListVisible)
headerPopupShown = true;
else if (e.NewJoin == UIBoolJoin.HeaderMeetingInfoVisible)
headerPopupShown = true;
else if (e.NewJoin == UIBoolJoin.HelpPageVisible)
headerPopupShown = true;
else if (e.NewJoin == UIBoolJoin.MeetingsOrContacMethodsListVisible)

View File

@@ -983,7 +983,7 @@
// /// <summary>
// /// Handles source change
// /// </summary>
// void _CurrentRoom_SourceInfoChange(EssentialsRoomBase room,
// void _CurrentRoom_SourceInfoChange(IEssentialsRoom room,
// SourceListItem info, ChangeType change)
// {
// if (change == ChangeType.WillChange)
@@ -995,7 +995,7 @@
// /// <summary>
// ///
// /// </summary>
// void _CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
// void _CurrentRoom_CurrentDisplay1SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type)
// {
// if (type == ChangeType.DidChange)
// {
@@ -1021,7 +1021,7 @@
// /// <summary>
// ///
// /// </summary>
// void _CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type)
// void _CurrentRoom_CurrentDisplay2SourceChange(IEssentialsRoom room, SourceListItem info, ChangeType type)
// {
// if (type == ChangeType.DidChange)
// {

View File

@@ -78,7 +78,7 @@ namespace PepperDash.Essentials
/// <summary>
///
/// </summary>
public EssentialsHuddleSpaceRoom CurrentRoom
public IEssentialsHuddleSpaceRoom CurrentRoom
{
get { return _CurrentRoom; }
set
@@ -86,7 +86,7 @@ namespace PepperDash.Essentials
SetCurrentRoom(value);
}
}
EssentialsHuddleSpaceRoom _CurrentRoom;
IEssentialsHuddleSpaceRoom _CurrentRoom;
/// <summary>
///
@@ -498,7 +498,7 @@ namespace PepperDash.Essentials
TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true;
// Run default source when room is off and share is pressed
if (!CurrentRoom.OnFeedback.BoolValue)
CurrentRoom.RunDefaultPresentRoute();
(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute();
}
@@ -583,7 +583,7 @@ namespace PepperDash.Essentials
void UiSelectSource(string key)
{
// Run the route and when it calls back, show the source
CurrentRoom.RunRouteAction(key, new Action(() => { }));
CurrentRoom.RunRouteAction(key);
}
/// <summary>
@@ -745,7 +745,7 @@ namespace PepperDash.Essentials
/// <summary>
/// Helper for property setter. Sets the panel to the given room, latching up all functionality
/// </summary>
public void RefreshCurrentRoom(EssentialsHuddleSpaceRoom room)
public void RefreshCurrentRoom(IEssentialsHuddleSpaceRoom room)
{
if (_CurrentRoom != null)
{
@@ -836,7 +836,7 @@ namespace PepperDash.Essentials
}
}
void SetCurrentRoom(EssentialsHuddleSpaceRoom room)
void SetCurrentRoom(IEssentialsHuddleSpaceRoom room)
{
if (_CurrentRoom == room) return;
// Disconnect current (probably never called)
@@ -871,7 +871,7 @@ namespace PepperDash.Essentials
UpdateMCJoins(_CurrentRoom);
}
void UpdateMCJoins(EssentialsHuddleSpaceRoom room)
void UpdateMCJoins(IEssentialsHuddleSpaceRoom room)
{
TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl);
TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl);
@@ -918,6 +918,7 @@ namespace PepperDash.Essentials
TriList.BooleanInput[StartPageVisibleJoin].BoolValue = true;
TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false;
TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false;
TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false;
}
}

View File

@@ -8,7 +8,7 @@ using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.SmartObjects;
using PepperDash.Essentials.Core.Touchpanels.Keyboards;
@@ -316,7 +316,7 @@ namespace PepperDash.Essentials.UIDrivers
void CommunicationMonitor_StatusChange(object sender, MonitorStatusChangeEventArgs e)
{
var c = sender as ICommunicationMonitor;
if (StatusListDeviceIndexes.ContainsKey(c))
if (c != null && StatusListDeviceIndexes.ContainsKey(c))
{
var i = StatusListDeviceIndexes[c];
StatusList.UShortInputSig(i, 1).UShortValue = (ushort)e.Status;

View File

@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.UI;
@@ -13,6 +14,7 @@ using PepperDash.Essentials.Core.PageManagers;
using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
namespace PepperDash.Essentials
{
@@ -50,7 +52,7 @@ namespace PepperDash.Essentials
/// <summary>
///
/// </summary>
public EssentialsHuddleVtc1Room CurrentRoom
public IEssentialsHuddleVtc1Room CurrentRoom
{
get { return _CurrentRoom; }
set
@@ -58,7 +60,7 @@ namespace PepperDash.Essentials
SetCurrentRoom(value);
}
}
EssentialsHuddleVtc1Room _CurrentRoom;
IEssentialsHuddleVtc1Room _CurrentRoom;
/// <summary>
/// For hitting feedbacks
@@ -98,6 +100,9 @@ namespace PepperDash.Essentials
/// </summary>
public SubpageReferenceList MeetingOrContactMethodModalSrl { get; set; }
public uint CallListOrMeetingInfoPopoverVisibilityJoin { get; private set; }
/// <summary>
/// The list of buttons on the header. Managed with visibility only
/// </summary>
@@ -173,10 +178,38 @@ namespace PepperDash.Essentials
/// </summary>
public PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; private set; }
private UiDisplayMode _currentMode;
private bool _isZoomRoomWithNoExternalSources
{
get
{
return CurrentRoom.VideoCodec is Essentials.Devices.Common.VideoCodec.ZoomRoom.ZoomRoom && _sourceListCount <= 1;
}
}
private uint _sourceListCount;
/// <summary>
/// The mode showing. Presentation or call.
/// </summary>
UiDisplayMode CurrentMode = UiDisplayMode.Start;
UiDisplayMode CurrentMode
{
get
{
return _currentMode;
}
set
{
if (value != _currentMode)
{
_currentMode = value;
SetActivityFooterFeedbacks();
}
}
}
CTimer NextMeetingTimer;
@@ -207,6 +240,7 @@ namespace PepperDash.Essentials
MeetingOrContactMethodModalSrl = new SubpageReferenceList(TriList, UISmartObjectJoin.MeetingListSRL, 3, 3, 5);
CurrentMode = UiDisplayMode.Start;
// buttons are added in SetCurrentRoom
//HeaderButtonsList = new SmartObjectHeaderButtonList(TriList.SmartObjects[UISmartObjectJoin.HeaderButtonList]);
@@ -331,15 +365,17 @@ namespace PepperDash.Essentials
/// <summary>
/// Allows PopupInterlock to be toggled if the calls list is already visible, or if the codec is in a call
/// </summary>
public void ShowActiveCallsList()
public void ShowActiveCallsListOrMeetingInfo()
{
TriList.SetBool(UIBoolJoin.CallEndAllConfirmVisible, true);
if(PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible)
PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible);
if(PopupInterlock.CurrentJoin == CallListOrMeetingInfoPopoverVisibilityJoin)
PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin);
else
{
if((CurrentRoom.ScheduleSource as VideoCodecBase).IsInCall)
PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible);
if(CurrentRoom.VideoCodec.IsInCall)
PopupInterlock.ShowInterlockedWithToggle(CallListOrMeetingInfoPopoverVisibilityJoin);
}
}
@@ -451,12 +487,16 @@ namespace PepperDash.Essentials
Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*",
CurrentRoom.OnFeedback.BoolValue,
LastMeetingDismissedId,
lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToShortTimeString() : "");
lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : "");
var meeting = meetings.LastOrDefault(m => m.Joinable);
if (CurrentRoom.OnFeedback.BoolValue
&& lastMeetingDismissed == meeting)
{
// meeting no longer joinable, hide popup
if(meeting == null)
HideNextMeetingPopup();
return;
}
@@ -468,9 +508,11 @@ namespace PepperDash.Essentials
}
else
{
TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting");
TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToShortTimeString());
TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToShortTimeString());
TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture));
TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture));
TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title);
TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer);
TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join");
@@ -493,7 +535,7 @@ namespace PepperDash.Essentials
// indexOf = 3, 4 meetings :
if (indexOfNext < meetings.Count)
TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText,
meetings[indexOfNext].StartTime.ToShortTimeString());
meetings[indexOfNext].StartTime.ToString("t", Global.Culture));
else
TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today");
@@ -607,11 +649,39 @@ namespace PepperDash.Essentials
/// </summary>
void SetActivityFooterFeedbacks()
{
CallButtonSig.BoolValue = CurrentMode == UiDisplayMode.Call
&& CurrentRoom.ShutdownType == eShutdownType.None;
ShareButtonSig.BoolValue = CurrentMode == UiDisplayMode.Presentation
&& CurrentRoom.ShutdownType == eShutdownType.None;
EndMeetingButtonSig.BoolValue = CurrentRoom.ShutdownType != eShutdownType.None;
if (CurrentRoom != null)
{
var startMode = CurrentMode == UiDisplayMode.Start;
var presentationMode = CurrentMode == UiDisplayMode.Presentation;
var callMode = CurrentMode == UiDisplayMode.Call;
TriList.SetBool(StartPageVisibleJoin, startMode ? true : false);
if (presentationMode &&_isZoomRoomWithNoExternalSources)
{
// For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage
TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false);
TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true);
}
else
{
// Otherwise, show the staging bar
TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false);
TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, presentationMode ? true : false);
}
if (!presentationMode)
{
TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, false);
TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
}
CallButtonSig.BoolValue = callMode
&& CurrentRoom.ShutdownType == eShutdownType.None;
ShareButtonSig.BoolValue = presentationMode
&& CurrentRoom.ShutdownType == eShutdownType.None;
EndMeetingButtonSig.BoolValue = CurrentRoom.ShutdownType != eShutdownType.None;
}
}
/// <summary>
@@ -623,14 +693,13 @@ namespace PepperDash.Essentials
return;
HideLogo();
HideNextMeetingPopup();
TriList.SetBool(StartPageVisibleJoin, false);
TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false);
TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
//TriList.SetBool(StartPageVisibleJoin, false);
//TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false);
//TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
if (CurrentSourcePageManager != null)
CurrentSourcePageManager.Hide();
PowerOnFromCall();
CurrentMode = UiDisplayMode.Call;
SetActivityFooterFeedbacks();
VCDriver.Show();
}
@@ -643,29 +712,39 @@ namespace PepperDash.Essentials
if (VCDriver.IsVisible)
VCDriver.Hide();
HideNextMeetingPopup();
TriList.SetBool(StartPageVisibleJoin, false);
TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false);
TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true);
// Run default source when room is off and share is pressed
if (!CurrentRoom.OnFeedback.BoolValue)
{
if (!CurrentRoom.OnFeedback.BoolValue)
{
// If there's no default, show UI elements
if (!CurrentRoom.RunDefaultPresentRoute())
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
}
}
else // room is on show what's active or select a source if nothing is yet active
if (_isZoomRoomWithNoExternalSources)
{
if(CurrentRoom.CurrentSourceInfo == null || CurrentRoom.CurrentSourceInfoKey == CurrentRoom.DefaultCodecRouteString)
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
else if (CurrentSourcePageManager != null)
CurrentSourcePageManager.Show();
(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute();
// For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage
TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true);
if (CurrentSourcePageManager != null)
CurrentSourcePageManager.Hide();
}
else
{
// Run default source when room is off and share is pressed
if (!CurrentRoom.OnFeedback.BoolValue)
{
// If there's no default, show UI elements
if (!(CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute())
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
}
else // room is on show what's active or select a source if nothing is yet active
{
if (CurrentRoom.CurrentSourceInfo == null || (CurrentRoom.VideoCodec != null && CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key))
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
else if (CurrentSourcePageManager != null)
{
TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
CurrentSourcePageManager.Show();
}
}
SetupSourceList();
}
CurrentMode = UiDisplayMode.Presentation;
SetupSourceList();
SetActivityFooterFeedbacks();
}
/// <summary>
@@ -704,9 +783,11 @@ namespace PepperDash.Essentials
/// </summary>
void ShowCurrentSource()
{
if (CurrentRoom.CurrentSourceInfo == null)
if (CurrentRoom.CurrentSourceInfo == null || _isZoomRoomWithNoExternalSources)
return;
CurrentMode = UiDisplayMode.Presentation;
if (CurrentRoom.CurrentSourceInfo.SourceDevice == null)
{
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
@@ -743,7 +824,7 @@ namespace PepperDash.Essentials
void UiSelectSource(string key)
{
// Run the route and when it calls back, show the source
CurrentRoom.RunRouteAction(key, new Action(() => { }));
CurrentRoom.RunRouteAction(key);
}
/// <summary>
@@ -894,7 +975,7 @@ namespace PepperDash.Essentials
/// <summary>
/// Helper for property setter. Sets the panel to the given room, latching up all functionality
/// </summary>
void RefreshCurrentRoom(EssentialsHuddleVtc1Room room)
void RefreshCurrentRoom(IEssentialsHuddleVtc1Room room)
{
if (_CurrentRoom != null)
@@ -912,6 +993,18 @@ namespace PepperDash.Essentials
_CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange;
_CurrentRoom.IsCoolingDownFeedback.OutputChange -= CurrentRoom_IsCoolingDownFeedback_OutputChange;
_CurrentRoom.InCallFeedback.OutputChange -= CurrentRoom_InCallFeedback_OutputChange;
var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness;
if (scheduleAwareCodec != null)
{
scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged -= CodecSchedule_MeetingsListHasChanged;
}
var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo;
if (meetingInfoCodec != null)
{
meetingInfoCodec.MeetingInfoChanged -= meetingInfoCodec_MeetingInfoChanged;
}
}
_CurrentRoom = room;
@@ -944,9 +1037,23 @@ namespace PepperDash.Essentials
_CurrentRoom.CurrentSourceChange += CurrentRoom_SourceInfoChange;
RefreshSourceInfo();
if (_CurrentRoom.VideoCodec is IHasScheduleAwareness)
var scheduleAwareCodec = _CurrentRoom.VideoCodec as IHasScheduleAwareness;
if (scheduleAwareCodec != null)
{
(_CurrentRoom.VideoCodec as IHasScheduleAwareness).CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged;
scheduleAwareCodec.CodecSchedule.MeetingsListHasChanged += CodecSchedule_MeetingsListHasChanged;
}
var meetingInfoCodec = _CurrentRoom.VideoCodec as IHasMeetingInfo;
if (meetingInfoCodec != null)
{
meetingInfoCodec.MeetingInfoChanged += new EventHandler<MeetingInfoEventArgs>(meetingInfoCodec_MeetingInfoChanged);
CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderMeetingInfoVisible;
}
else
{
CallListOrMeetingInfoPopoverVisibilityJoin = UIBoolJoin.HeaderActiveCallsListVisible;
}
CallSharingInfoVisibleFeedback = new BoolFeedback(() => _CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue);
@@ -958,7 +1065,8 @@ namespace PepperDash.Essentials
if (_CurrentRoom != null)
_CurrentRoom.CurrentSourceChange += new SourceInfoChangeHandler(CurrentRoom_CurrentSingleSourceChange);
TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey));
// Moved to EssentialsVideoCodecUiDriver
//TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, () => _CurrentRoom.RunRouteAction("codecOsd", _CurrentRoom.SourceListKey));
(Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom);
}
@@ -969,7 +1077,22 @@ namespace PepperDash.Essentials
}
}
void SetCurrentRoom(EssentialsHuddleVtc1Room room)
void meetingInfoCodec_MeetingInfoChanged(object sender, MeetingInfoEventArgs e)
{
TriList.SetString(UIStringJoin.MeetingIdText, e.Info.Id);
TriList.SetString(UIStringJoin.MeetingHostText, e.Info.Host);
TriList.SetString(UIStringJoin.MeetingNameText, e.Info.Name);
TriList.SetString(UIStringJoin.MeetingPasswordText, e.Info.Password);
// Show the password fields if one is present
TriList.SetBool(UIBoolJoin.MeetingPasswordVisible, string.IsNullOrEmpty(e.Info.Password) ? false : true);
TriList.SetString(UIStringJoin.CallSharedSourceNameText, e.Info.ShareStatus);
TriList.SetString(UIStringJoin.MeetingLeaveText, e.Info.IsHost ? "End Meeting" : "Leave Meeting");
}
void SetCurrentRoom(IEssentialsHuddleVtc1Room room)
{
if (_CurrentRoom == room) return;
// Disconnect current (probably never called)
@@ -1004,7 +1127,7 @@ namespace PepperDash.Essentials
UpdateMCJoins(_CurrentRoom);
}
void UpdateMCJoins(EssentialsHuddleVtc1Room room)
void UpdateMCJoins(IEssentialsHuddleVtc1Room room)
{
TriList.SetString(UIStringJoin.RoomMcUrl, room.MobileControlRoomBridge.McServerUrl);
TriList.SetString(UIStringJoin.RoomMcQrCodeUrl, room.MobileControlRoomBridge.QrCodeUrl);
@@ -1035,7 +1158,7 @@ namespace PepperDash.Essentials
if (CurrentRoom.CurrentSourceInfo != null && CurrentRoom.CurrentSourceInfo.DisableCodecSharing)
{
Debug.Console(1, CurrentRoom, "Transitioning to in-call, cancelling non-sharable source");
CurrentRoom.RunRouteAction("codecOsd", CurrentRoom.SourceListKey);
CurrentRoom.RunRouteAction("codecOsd");
}
}
@@ -1082,7 +1205,8 @@ namespace PepperDash.Essentials
Debug.Console(1, "**** KEY {0}", kvp.Key);
}
SourceStagingSrl.Count = (ushort)(i - 1);
_sourceListCount = (i - 1);
SourceStagingSrl.Count = (ushort)_sourceListCount;
}
}
@@ -1105,6 +1229,30 @@ namespace PepperDash.Essentials
/// <param name="type"></param>
void CurrentRoom_CurrentSingleSourceChange(SourceListItem info, ChangeType type)
{
Debug.Console(1, "AvFunctionsDriver: CurrentSingleSourceChange");
// Show the Select a source subpage
if (TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue)
{
Debug.Console(1, "AvFunctionsDriver: CurrentSingleSourceChange SourceStagingBarVisisble: true");
if (_CurrentRoom.CurrentSourceInfo == null || (_CurrentRoom.VideoCodec != null && _CurrentRoom.CurrentSourceInfo.SourceDevice.Key == _CurrentRoom.VideoCodec.OsdSource.Key))
{
Debug.Console(1, "AvFunctionsDriver: CurrentSingleSourceChange Showing SelectASourceVisible");
TriList.SetBool(UIBoolJoin.SelectASourceVisible, true);
}
else
{
TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
Debug.Console(1, "AvFunctionsDriver: CurrentSingleSourceChange Hiding SelectASourceVisible");
}
}
else
{
Debug.Console(1, "AvFunctionsDriver: CurrentSingleSourceChange Hiding SelectASourceVisible");
TriList.SetBool(UIBoolJoin.SelectASourceVisible, false);
}
if (_CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && _CurrentRoom.CurrentSourceInfo != null)
TriList.StringInput[UIStringJoin.CallSharedSourceNameText].StringValue = _CurrentRoom.CurrentSourceInfo.PreferredName;
}
@@ -1156,8 +1304,8 @@ namespace PepperDash.Essentials
foreach (var m in CurrentRoom.ScheduleSource.CodecSchedule.Meetings)
{
i++;
MeetingOrContactMethodModalSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToShortTimeString();
MeetingOrContactMethodModalSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToShortTimeString();
MeetingOrContactMethodModalSrl.StringInputSig(i, 1).StringValue = m.StartTime.ToString("t", Global.Culture);
MeetingOrContactMethodModalSrl.StringInputSig(i, 2).StringValue = m.EndTime.ToString("t", Global.Culture);
MeetingOrContactMethodModalSrl.StringInputSig(i, 3).StringValue = m.Title;
MeetingOrContactMethodModalSrl.StringInputSig(i, 4).StringValue = string.Format("<br>{0}",m.Organizer);
MeetingOrContactMethodModalSrl.StringInputSig(i, 5).StringValue = "Join";
@@ -1202,12 +1350,12 @@ namespace PepperDash.Essentials
var value = _CurrentRoom.OnFeedback.BoolValue;
TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value;
TriList.BooleanInput[StartPageVisibleJoin].BoolValue = !value;
//TriList.BooleanInput[StartPageVisibleJoin].BoolValue = !value;
if (value) //ON
{
SetupActivityFooterWhenRoomOn();
TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false;
//TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false;
TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = true;
}
@@ -1218,9 +1366,8 @@ namespace PepperDash.Essentials
VCDriver.Hide();
SetupActivityFooterWhenRoomOff();
ShowLogo();
SetActivityFooterFeedbacks();
TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = false;
TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false;
//TriList.BooleanInput[UIBoolJoin.VolumeDualMute1Visible].BoolValue = false;
//TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false;
// Clear this so that the pesky meeting warning can resurface every minute when off
LastMeetingDismissedId = null;
}
@@ -1443,7 +1590,7 @@ namespace PepperDash.Essentials
/// </summary>
public interface IAVWithVCDriver : IAVDriver
{
EssentialsHuddleVtc1Room CurrentRoom { get; }
IEssentialsHuddleVtc1Room CurrentRoom { get; }
PepperDash.Essentials.Core.Touchpanels.Keyboards.HabaneroKeyboardController Keyboard { get; }
/// <summary>
@@ -1455,6 +1602,8 @@ namespace PepperDash.Essentials
/// </summary>
void PrepareForCodecIncomingCall();
uint CallListOrMeetingInfoPopoverVisibilityJoin { get; }
SubpageReferenceList MeetingOrContactMethodModalSrl { get; }
}
}

View File

@@ -38,7 +38,7 @@ namespace PepperDash.Essentials
/// <summary>
/// Sets feedback for the given room
/// </summary>
public void SetFeedbackForRoom(EssentialsHuddleSpaceRoom room)
public void SetFeedbackForRoom(IEssentialsHuddleSpaceRoom room)
{
var itemToSet = Items.FirstOrDefault(i => i.Room == room);
if (itemToSet != null)
@@ -48,11 +48,11 @@ namespace PepperDash.Essentials
public class SmartObjectRoomsListItem
{
public EssentialsHuddleSpaceRoom Room { get; private set; }
public IEssentialsHuddleSpaceRoom Room { get; private set; }
SmartObjectRoomsList Parent;
public uint Index { get; private set; }
public SmartObjectRoomsListItem(EssentialsHuddleSpaceRoom room, uint index, SmartObjectRoomsList parent,
public SmartObjectRoomsListItem(IEssentialsHuddleSpaceRoom room, uint index, SmartObjectRoomsList parent,
Action<bool> buttonAction)
{
Room = room;

View File

@@ -12,5 +12,5 @@ namespace PepperDash.Essentials
///// <summary>
///// The handler type for a Room's SourceInfoChange
///// </summary>
//public delegate void SourceInfoChangeHandler(EssentialsRoomBase room, SourceListItem info, ChangeType type);
//public delegate void SourceInfoChangeHandler(IEssentialsRoom room, SourceListItem info, ChangeType type);
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
@@ -14,6 +15,7 @@ using PepperDash.Essentials.Core.SmartObjects;
using PepperDash.Essentials.Core.Touchpanels.Keyboards;
using PepperDash.Essentials.Devices.Common.Codec;
using PepperDash.Essentials.Devices.Common.VideoCodec;
using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces;
using PepperDash.Essentials.Devices.Common.Cameras;
namespace PepperDash.Essentials.UIDrivers.VC
@@ -83,6 +85,9 @@ namespace PepperDash.Essentials.UIDrivers.VC
StringBuilder SearchStringBuilder = new StringBuilder();
BoolFeedback SearchStringBackspaceVisibleFeedback;
StringFeedback PasswordStringFeedback;
StringBuilder PasswordStringBuilder = new StringBuilder();
ModalDialog IncomingCallModal;
eKeypadMode KeypadMode;
@@ -123,12 +128,6 @@ namespace PepperDash.Essentials.UIDrivers.VC
codec.CallStatusChange += new EventHandler<CodecCallStatusItemChangeEventArgs>(Codec_CallStatusChange);
// If the codec is ready, then get the values we want, otherwise wait
if (Codec.IsReady)
Codec_IsReady();
else
codec.IsReadyChange += (o, a) => Codec_IsReady();
//InCall = new BoolFeedback(() => false);
LocalPrivacyIsMuted = new BoolFeedback(() => false);
@@ -142,7 +141,10 @@ namespace PepperDash.Essentials.UIDrivers.VC
VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible);
StagingBarsInterlock = new JoinedSigInterlock(triList);
StagingBarsInterlock.SetButDontShow(UIBoolJoin.VCStagingInactivePopoverVisible);
if(Codec is IHasCallHistory)
StagingBarsInterlock.SetButDontShow(UIBoolJoin.VCStagingInactivePopoverWithRecentsVisible);
else
StagingBarsInterlock.SetButDontShow(UIBoolJoin.VCStagingInactivePopoverWithoutRecentsVisible);
StagingButtonsFeedbackInterlock = new JoinedSigInterlock(triList);
StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress);
@@ -150,7 +152,8 @@ namespace PepperDash.Essentials.UIDrivers.VC
// Return formatted when dialing, straight digits when in call
DialStringFeedback = new StringFeedback(() =>
{
if (KeypadMode == eKeypadMode.Dial)
// Format the number feedback if in dial mode and the codec is not IHasStartMeeting (ZoomRoom)
if (KeypadMode == eKeypadMode.Dial && !(Codec is IHasStartMeeting))
return GetFormattedDialString(DialStringBuilder.ToString());
else
return DialStringBuilder.ToString();
@@ -177,8 +180,22 @@ namespace PepperDash.Essentials.UIDrivers.VC
});
SearchStringFeedback.LinkInputSig(triList.StringInput[UIStringJoin.CodecDirectorySearchEntryText]);
SetupDirectoryList();
PasswordStringFeedback = new StringFeedback(() =>
{
if (PasswordStringBuilder.Length > 0)
{
Parent.Keyboard.EnableGoButton();
return PasswordStringBuilder.ToString();
}
else
{
Parent.Keyboard.DisableGoButton();
return "";
}
});
PasswordStringFeedback.LinkInputSig(triList.StringInput[UIStringJoin.PasswordPromptPasswordText]);
SetupDirectoryList();
SearchStringBackspaceVisibleFeedback = new BoolFeedback(() => SearchStringBuilder.Length > 0);
SearchStringBackspaceVisibleFeedback.LinkInputSig(triList.BooleanInput[UIBoolJoin.VCDirectoryBackspaceVisible]);
@@ -196,6 +213,18 @@ namespace PepperDash.Essentials.UIDrivers.VC
triList.SetSigHeldAction(UIBoolJoin.VCDirectoryBackspacePress, 500,
StartSearchBackspaceRepeat, StopSearchBackspaceRepeat, SearchKeypadBackspacePress);
if (Codec is IPasswordPrompt)
{
SetupPasswordPrompt();
}
// If the codec is ready, then get the values we want, otherwise wait
if (Codec.IsReady)
Codec_IsReady();
else
codec.IsReadyChange += (o, a) => Codec_IsReady();
}
catch (Exception e)
{
@@ -296,6 +325,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
{
case eCodecCallStatus.Connected:
// fire at SRL item
HidePasswordPrompt();
KeypadMode = eKeypadMode.DTMF;
DialStringBuilder.Remove(0, DialStringBuilder.Length);
DialStringFeedback.FireUpdate();
@@ -351,10 +381,15 @@ namespace PepperDash.Essentials.UIDrivers.VC
TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = (ushort)(Codec.IsInCall ? 1 : 0);
uint stageJoin;
if (Codec.IsInCall)
stageJoin = UIBoolJoin.VCStagingActivePopoverVisible;
else
stageJoin = UIBoolJoin.VCStagingInactivePopoverVisible;
if (Codec.IsInCall)
stageJoin = UIBoolJoin.VCStagingActivePopoverVisible;
else
{
if (Codec is IHasCallHistory)
stageJoin = UIBoolJoin.VCStagingInactivePopoverWithRecentsVisible;
else
stageJoin = UIBoolJoin.VCStagingInactivePopoverWithoutRecentsVisible;
}
if (IsVisible)
StagingBarsInterlock.ShowInterlocked(stageJoin);
else
@@ -389,8 +424,8 @@ namespace PepperDash.Essentials.UIDrivers.VC
ActiveCallsSRL.Count = (ushort)activeList.Count;
// If Active Calls list is visible and codec is not in a call, hide the list
if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == UIBoolJoin.HeaderActiveCallsListVisible)
Parent.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.HeaderActiveCallsListVisible);
if (!Codec.IsInCall && Parent.PopupInterlock.CurrentJoin == Parent.CallListOrMeetingInfoPopoverVisibilityJoin)
Parent.PopupInterlock.ShowInterlockedWithToggle(Parent.CallListOrMeetingInfoPopoverVisibilityJoin);
}
/// <summary>
@@ -481,20 +516,46 @@ namespace PepperDash.Essentials.UIDrivers.VC
TriList.SetSigFalseAction(UIBoolJoin.VCStagingRecentsPress, ShowRecents);
TriList.SetSigFalseAction(UIBoolJoin.VCStagingCameraPress, ShowCameraControls);
TriList.SetSigFalseAction(UIBoolJoin.VCStagingConnectPress, ConnectPress);
TriList.SetSigFalseAction(UIBoolJoin.VCStagingMeetNowPress, MeetNowPress);
TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, CallStopSharingPress);
TriList.SetSigFalseAction(UIBoolJoin.CallEndPress, () =>
{
if (Codec.ActiveCalls.Count > 1)
{
Parent.PopupInterlock.ShowInterlocked(UIBoolJoin.HeaderActiveCallsListVisible);
Parent.PopupInterlock.ShowInterlocked(Parent.CallListOrMeetingInfoPopoverVisibilityJoin);
}
else
Codec.EndAllCalls();
});
TriList.SetSigFalseAction(UIBoolJoin.CallEndAllConfirmPress, () =>
{
Parent.PopupInterlock.HideAndClear();
Codec.EndAllCalls();
});
var meetingInfoCodec = Codec as IHasMeetingInfo;
if (meetingInfoCodec != null)
{
TriList.SetSigFalseAction(UIBoolJoin.MeetingLeavePress, () =>
{
Parent.PopupInterlock.HideAndClear();
if (meetingInfoCodec.MeetingInfo.IsHost)
{
Codec.EndAllCalls();
}
else
{
var startMeetingCodec = Codec as IHasStartMeeting;
if (startMeetingCodec != null)
{
startMeetingCodec.LeaveMeeting();
}
}
});
}
}
void SetupCameraControls()
@@ -513,13 +574,18 @@ namespace PepperDash.Essentials.UIDrivers.VC
var codecOffCameras = Codec as IHasCameraOff;
var supportsCameraOffMode = Codec.SupportsCameraOff;
var codecAutoCameras = Codec as IHasCameraAutoMode;
if (codecAutoCameras != null)
var supportsAutoCameraMode = Codec.SupportsCameraAutoMode;
if (codecAutoCameras != null && supportsAutoCameraMode)
{
CameraModeList.SetItemButtonAction(1,(b) => codecAutoCameras.CameraAutoModeOn());
TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanInput["Item 1 Visible"].BoolValue = true;
codecAutoCameras.CameraAutoModeIsOnFeedback.LinkInputSig(CameraModeList.SmartObject.BooleanInput["Item 1 Selected"]);
codecAutoCameras.CameraAutoModeIsOnFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.VCCameraAutoModeIsOnFb]);
//TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanOutput["Item 1 Pressed"].SetSigFalseAction(
//() => codecAutoCameras.CameraAutoModeOn());
@@ -554,7 +620,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
//TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanOutput["Item 2 Pressed"].SetSigFalseAction(
// () => ShowCameraManualMode());
if (codecOffCameras != null)
if (codecOffCameras != null && supportsCameraOffMode)
{
TriList.SmartObjects[UISmartObjectJoin.VCCameraMode].BooleanInput["Item 3 Visible"].BoolValue = true;
codecOffCameras.CameraIsOffFeedback.LinkInputSig(CameraModeList.SmartObject.BooleanInput["Item 3 Selected"]);
@@ -769,12 +835,14 @@ namespace PepperDash.Essentials.UIDrivers.VC
if (camerasCodec != null && camerasCodec.SelectedCamera != null)
{
Debug.Console(2, "Attempting to map camera actions to selected camera: '{0}'", camerasCodec.SelectedCamera.Key);
var dpad = CameraPtzPad;
var camera = camerasCodec.SelectedCamera as IHasCameraPtzControl;
if (camera != null)
{
Debug.Console(2, "Selected camera is IHasCameraPtzControl");
if (camerasCodec.SelectedCamera.CanTilt)
{
dpad.SigUp.SetBoolSigAction((b) =>
@@ -839,25 +907,46 @@ namespace PepperDash.Essentials.UIDrivers.VC
}
}
else
{
Debug.Console(2, "Selected Camera is not IHasCameraPtzControl. No controls to map");
}
}
else
{
Debug.Console(2, "Codec does not have cameras of selected camera is null");
}
}
// Determines if codec is in manual camera control mode and shows feedback
void ShowCameraManualMode()
{
Debug.Console(2, "ShowCameraManualMode");
var inManualMode = true;
var codecOffCameras = Codec as IHasCameraOff;
var codecAutoCameras = Codec as IHasCameraAutoMode;
var supportsAutoCameras = codecAutoCameras != null && Codec.SupportsCameraAutoMode;
if (codecOffCameras != null && codecOffCameras.CameraIsOffFeedback.BoolValue)
{
inManualMode = false;
var codecCameraMute = Codec as IHasCameraMute;
if (codecCameraMute != null)
{
codecCameraMute.CameraMuteOff();
inManualMode = true;
}
}
// Clear auto mode
if (codecAutoCameras != null )
if (supportsAutoCameras)
{
if (codecAutoCameras.CameraAutoModeIsOnFeedback.BoolValue)
{
@@ -948,7 +1037,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
// if it's today, show a simpler string
string timeText = null;
if (c.StartTime.Date == DateTime.Now.Date)
timeText = c.StartTime.ToShortTimeString();
timeText = c.StartTime.ToString("t", Global.Culture);
else if (c.StartTime == DateTime.MinValue)
timeText = "";
else
@@ -1005,22 +1094,21 @@ namespace PepperDash.Essentials.UIDrivers.VC
void SetupDirectoryList()
{
var codec = Codec as IHasDirectory;
if (codec != null)
{
DirectoryList = new SmartObjectDynamicList(TriList.SmartObjects[UISmartObjectJoin.VCDirectoryList],
true, 1300);
codec.DirectoryResultReturned += new EventHandler<DirectoryEventArgs>(dir_DirectoryResultReturned);
if (codec == null)
{
return;
}
if (codec.PhonebookSyncState.InitialSyncComplete)
SetCurrentDirectoryToRoot();
else
{
codec.PhonebookSyncState.InitialSyncCompleted += new EventHandler<EventArgs>(PhonebookSyncState_InitialSyncCompleted);
}
DirectoryList = new SmartObjectDynamicList(TriList.SmartObjects[UISmartObjectJoin.VCDirectoryList],
true, 1300);
codec.DirectoryResultReturned += dir_DirectoryResultReturned;
RefreshDirectory();
}
if (codec.PhonebookSyncState.InitialSyncComplete)
SetCurrentDirectoryToRoot();
else
{
codec.PhonebookSyncState.InitialSyncCompleted += PhonebookSyncState_InitialSyncCompleted;
}
}
/// <summary>
@@ -1028,11 +1116,15 @@ namespace PepperDash.Essentials.UIDrivers.VC
/// </summary>
void SetCurrentDirectoryToRoot()
{
(Codec as IHasDirectory).SetCurrentDirectoryToRoot();
var hasDirectory = Codec as IHasDirectory;
if (hasDirectory == null)
{
return;
}
hasDirectory.SetCurrentDirectoryToRoot();
SearchKeypadClear();
RefreshDirectory();
}
/// <summary>
@@ -1044,10 +1136,17 @@ namespace PepperDash.Essentials.UIDrivers.VC
{
var codec = Codec as IHasDirectory;
SetCurrentDirectoryToRoot();
if (codec == null)
{
return;
}
RefreshDirectory();
if (!codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue)
{
return;
}
SetCurrentDirectoryToRoot();
}
/// <summary>
@@ -1057,8 +1156,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
/// <param name="e"></param>
void dir_DirectoryResultReturned(object sender, DirectoryEventArgs e)
{
RefreshDirectory();
RefreshDirectory(e.Directory);
}
/// <summary>
@@ -1087,16 +1185,27 @@ namespace PepperDash.Essentials.UIDrivers.VC
}
/// <summary>
///
/// </summary>
/// <param name="dir"></param>
void RefreshDirectory()
/// <summary>
///
/// </summary>
void RefreshDirectory()
{
if ((Codec as IHasDirectory).CurrentDirectoryResult.CurrentDirectoryResults.Count > 0)
var codec = Codec as IHasDirectory;
if (codec == null)
{
return;
}
RefreshDirectory(codec.CurrentDirectoryResult);
}
void RefreshDirectory(CodecDirectory directory)
{
if (directory.CurrentDirectoryResults.Count > 0)
{
ushort i = 0;
foreach (var r in (Codec as IHasDirectory).CurrentDirectoryResult.CurrentDirectoryResults)
foreach (var r in directory.CurrentDirectoryResults)
{
if (i == DirectoryList.MaxCount)
{
@@ -1116,19 +1225,33 @@ namespace PepperDash.Essentials.UIDrivers.VC
// If more than one contact method, show contact method modal dialog
DirectoryList.SetItemButtonAction(i, b =>
{
if (!b)
if (b)
{
// Refresh the contact methods list
RefreshContactMethodsModalList(dc);
Parent.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MeetingsOrContacMethodsListVisible);
return;
}
// Refresh the contact methods list
RefreshContactMethodsModalList(dc);
Parent.PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.MeetingsOrContacMethodsListVisible);
});
}
else if (dc.ContactMethods.Count == 1)
{
var invitableContact = dc as IInvitableContact;
if (invitableContact != null)
{
DirectoryList.SetItemButtonAction(i, b => { if (!b) Codec.Dial(invitableContact); });
}
else
{
// If only one contact method, just dial that method
DirectoryList.SetItemButtonAction(i, b => { if (!b) Codec.Dial(dc.ContactMethods[0].Number); });
}
}
else
{
// If only one contact method, just dial that method
DirectoryList.SetItemButtonAction(i, b => { if (!b) Codec.Dial(dc.ContactMethods[0].Number); });
Debug.Console(1, "Unable to dial contact. No availble ContactMethod(s) specified");
}
}
else // is DirectoryFolder
@@ -1155,8 +1278,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
DirectoryList.SetItemMainText(1, "No Results Found");
}
}
}
void RefreshContactMethodsModalList(DirectoryContact contact)
{
@@ -1201,7 +1323,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
var lc = Codec as IHasCodecLayouts;
if (lc != null)
{
TriList.SetSigFalseAction(UIBoolJoin.VCLayoutTogglePress, lc.LocalLayoutToggleSingleProminent);
lc.LocalLayoutFeedback.LinkInputSig(TriList.StringInput[UIStringJoin.VCLayoutModeText]);
lc.LocalLayoutFeedback.OutputChange += (o,a) =>
{
@@ -1214,14 +1336,24 @@ namespace PepperDash.Essentials.UIDrivers.VC
var cisco = Codec as PepperDash.Essentials.Devices.Common.VideoCodec.Cisco.CiscoSparkCodec;
if (cisco != null)
{
TriList.SetSigFalseAction(UIBoolJoin.VCLayoutTogglePress, lc.LocalLayoutToggleSingleProminent);
// Cisco has min/max buttons that need special sauce
cisco.SharingContentIsOnFeedback.OutputChange += CiscoSharingAndPresentation_OutputChanges;
//cisco.PresentationViewMaximizedFeedback.OutputChange += CiscoSharingAndPresentation_OutputChanges;
TriList.SetSigFalseAction(UIBoolJoin.VCMinMaxPress, cisco.MinMaxLayoutToggle);
}
var zoomRoom = Codec as PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom.ZoomRoom;
if (zoomRoom != null)
{
TriList.BooleanInput[UIBoolJoin.VCLayoutToggleEnable].BoolValue = true;
TriList.SetSigFalseAction(UIBoolJoin.VCLayoutTogglePress, lc.LocalLayoutToggle);
}
}
}
/// <summary>
@@ -1249,7 +1381,21 @@ namespace PepperDash.Essentials.UIDrivers.VC
/// </summary>
void RevealKeyboard()
{
if (VCControlsInterlock.CurrentJoin == UIBoolJoin.VCKeypadWithFavoritesVisible && KeypadMode == eKeypadMode.Dial)
if (_passwordPromptDialogVisible)
{
Debug.Console(2, "Attaching Keyboard to PasswordPromptDialog");
DetachDialKeyboard();
DetachSearchKeyboard();
var kb = Parent.Keyboard;
kb.KeyPress -= Keyboard_PasswordKeyPress;
kb.KeyPress += Keyboard_PasswordKeyPress;
kb.HideAction = this.DetachPasswordKeyboard;
kb.GoButtonText = "Submit";
kb.GoButtonVisible = true;
PasswordStringCheckEnables();
kb.Show();
}
else if (VCControlsInterlock.CurrentJoin == UIBoolJoin.VCKeypadWithFavoritesVisible && KeypadMode == eKeypadMode.Dial)
{
var kb = Parent.Keyboard;
kb.KeyPress -= Keyboard_DialKeyPress;
@@ -1271,6 +1417,7 @@ namespace PepperDash.Essentials.UIDrivers.VC
SearchStringKeypadCheckEnables();
kb.Show();
}
}
/// <summary>
@@ -1326,6 +1473,32 @@ namespace PepperDash.Essentials.UIDrivers.VC
}
}
/// <summary>
/// Event handler for keyboard dialing
/// </summary>
void Keyboard_PasswordKeyPress(object sender, PepperDash.Essentials.Core.Touchpanels.Keyboards.KeyboardControllerPressEventArgs e)
{
if (_passwordPromptDialogVisible)
{
if (e.Text != null)
PasswordStringBuilder.Append(e.Text);
else
{
if (e.SpecialKey == KeyboardSpecialKey.Backspace)
PasswordKeypadBackspacePress();
else if (e.SpecialKey == KeyboardSpecialKey.Clear)
PasswordKeypadClear();
else if (e.SpecialKey == KeyboardSpecialKey.GoButton)
{
(Codec as IPasswordPrompt).SubmitPassword(PasswordStringBuilder.ToString());
HidePasswordPrompt();
}
}
PasswordStringFeedback.FireUpdate();
PasswordStringCheckEnables();
}
}
/// <summary>
/// Call
/// </summary>
@@ -1339,6 +1512,11 @@ namespace PepperDash.Essentials.UIDrivers.VC
Parent.Keyboard.KeyPress -= Keyboard_SearchKeyPress;
}
void DetachPasswordKeyboard()
{
Parent.Keyboard.KeyPress -= Keyboard_PasswordKeyPress;
}
/// <summary>
/// Shows the camera controls subpage
/// </summary>
@@ -1416,6 +1594,22 @@ namespace PepperDash.Essentials.UIDrivers.VC
StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingRecentsPress);
}
/// <summary>
/// Meet Now button
/// </summary>
void MeetNowPress()
{
var startMeetingCodec = Codec as IHasStartMeeting;
if (startMeetingCodec != null)
{
startMeetingCodec.StartMeeting(startMeetingCodec.DefaultMeetingDurationMin);
}
else
{
Debug.Console(2, "Codce does not implment IHasStartMeeting. Cannot meet now");
}
}
/// <summary>
/// Connect call button
/// </summary>
@@ -1426,6 +1620,16 @@ namespace PepperDash.Essentials.UIDrivers.VC
Codec.Dial(DialStringBuilder.ToString());
}
/// <summary>
/// Stop Sharing button
/// </summary>
void CallStopSharingPress()
{
Codec.StopSharing();
Parent.CurrentRoom.RunRouteAction("codecOsd", Parent.CurrentRoom.SourceListKey);
}
/// <summary>
///
/// </summary>
@@ -1592,6 +1796,40 @@ namespace PepperDash.Essentials.UIDrivers.VC
Parent.Keyboard.DisableGoButton();
}
/// <summary>
/// Clears the Password keypad
/// </summary>
void PasswordKeypadClear()
{
PasswordStringBuilder.Remove(0, SearchStringBuilder.Length);
PasswordStringFeedback.FireUpdate();
PasswordStringCheckEnables();
}
/// <summary>
///
/// </summary>
void PasswordKeypadBackspacePress()
{
PasswordStringBuilder.Remove(PasswordStringBuilder.Length - 1, 1);
PasswordStringFeedback.FireUpdate();
PasswordStringCheckEnables();
}
/// <summary>
/// Checks the enabled states of various elements around the keypad
/// </summary>
void PasswordStringCheckEnables()
{
var textIsEntered = PasswordStringBuilder.Length > 0;
if (textIsEntered)
Parent.Keyboard.EnableGoButton();
else
Parent.Keyboard.DisableGoButton();
}
/// <summary>
/// Returns the text value for the keypad dial entry field
@@ -1637,5 +1875,61 @@ namespace PepperDash.Essentials.UIDrivers.VC
Dial = 0,
DTMF
}
void SetupPasswordPrompt()
{
var passwordPromptCodec = Codec as IPasswordPrompt;
passwordPromptCodec.PasswordRequired += new EventHandler<PasswordPromptEventArgs>(passwordPromptCodec_PasswordRequired);
TriList.SetSigFalseAction(UIBoolJoin.PasswordPromptCancelPress, HidePasswordPrompt);
TriList.SetSigFalseAction(UIBoolJoin.PasswordPromptTextPress, RevealKeyboard);
}
void passwordPromptCodec_PasswordRequired(object sender, PasswordPromptEventArgs e)
{
if (e.LoginAttemptCancelled)
{
HidePasswordPrompt();
return;
}
if (!string.IsNullOrEmpty(e.Message))
{
TriList.SetString(UIStringJoin.PasswordPromptMessageText, e.Message);
}
if (e.LoginAttemptFailed)
{
// TODO: Show a message modal to indicate the login attempt failed
return;
}
TriList.SetBool(UIBoolJoin.PasswordPromptErrorVisible, e.LastAttemptWasIncorrect);
ShowPasswordPrompt();
}
private bool _passwordPromptDialogVisible;
void ShowPasswordPrompt()
{
// Clear out any previous data
PasswordKeypadClear();
_passwordPromptDialogVisible = true;
TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible);
RevealKeyboard();
}
void HidePasswordPrompt()
{
if (_passwordPromptDialogVisible)
{
_passwordPromptDialogVisible = false;
Parent.Keyboard.Hide();
TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible);
}
}
}
}

View File

@@ -385,7 +385,7 @@ namespace PepperDash.Essentials.Core.Bridges
{
public EiscApiAdvancedFactory()
{
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "vceiscapiadv", "vceiscapiadvanced" };
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
@@ -403,6 +403,16 @@ namespace PepperDash.Essentials.Core.Bridges
controlProperties.TcpSshProperties.Address, Global.ControlSystem);
return new EiscApiAdvanced(dc, eisc);
}
case "eiscapiadvancedserver":
{
var eisc = new EISCServer(controlProperties.IpIdInt, Global.ControlSystem);
return new EiscApiAdvanced(dc, eisc);
}
case "eiscapiadvancedclient":
{
var eisc = new EISCClient(controlProperties.IpIdInt, controlProperties.TcpSshProperties.Address, Global.ControlSystem);
return new EiscApiAdvanced(dc, eisc);
}
case "vceiscapiadv":
case "vceiscapiadvanced":
{

View File

@@ -25,6 +25,7 @@ namespace PepperDash.Essentials.Core
public GenericComm(DeviceConfig config)
: base(config)
{
PropertiesConfig = CommFactory.GetControlPropertiesConfig(config);
var commPort = CommFactory.CreateCommForDevice(config);

View File

@@ -1,11 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using Newtonsoft.Json.Linq;
@@ -25,12 +20,25 @@ namespace PepperDash.Essentials.Core.Config
[JsonProperty("sourceLists")]
public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }
[JsonProperty("tieLines")]
[JsonProperty("destinationLists")]
public Dictionary<string, Dictionary<string,DestinationListItem>> DestinationLists { get; set; }
[JsonProperty("tieLines")]
public List<TieLineConfig> TieLines { get; set; }
[JsonProperty("joinMaps")]
public Dictionary<string, JObject> JoinMaps { get; set; }
public BasicConfig()
{
Info = new InfoConfig();
Devices = new List<DeviceConfig>();
SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
TieLines = new List<TieLineConfig>();
JoinMaps = new Dictionary<string, JObject>();
}
/// <summary>
/// Checks SourceLists for a given list and returns it if found. Otherwise, returns null
/// </summary>
@@ -42,6 +50,21 @@ namespace PepperDash.Essentials.Core.Config
return SourceLists[key];
}
/// <summary>
/// Retrieves a DestinationListItem based on the key
/// </summary>
/// <param name="key">key of the item to retrieve</param>
/// <returns>DestinationListItem if the key exists, null otherwise</returns>
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
{
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
{
return null;
}
return DestinationLists[key];
}
/// <summary>
/// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null
/// </summary>

View File

@@ -30,7 +30,19 @@ namespace PepperDash.Essentials.Core.Config
[JsonProperty("properties")]
[JsonConverter(typeof(DevicePropertiesConverter))]
public JToken Properties { get; set; }
public JToken Properties { get; set; }
public DeviceConfig(DeviceConfig dc)
{
Key = dc.Key;
Uid = dc.Uid;
Name = dc.Name;
Group = dc.Group;
Type = dc.Type;
Properties = JToken.FromObject(dc.Properties);
}
public DeviceConfig() {}
}
/// <summary>

View File

@@ -1,162 +1,162 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Responsible for updating config at runtime, and writing the updates out to a local file
/// </summary>
public class ConfigWriter
{
public const string LocalConfigFolder = "LocalConfig";
public const long WriteTimeout = 30000;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Responsible for updating config at runtime, and writing the updates out to a local file
/// </summary>
public class ConfigWriter
{
public const string LocalConfigFolder = "LocalConfig";
public const long WriteTimeout = 30000;
public static CTimer WriteTimer;
static CCriticalSection fileLock = new CCriticalSection();
/// <summary>
/// Updates the config properties of a device
/// </summary>
/// <param name="deviceKey"></param>
/// <param name="properties"></param>
/// <returns></returns>
public static bool UpdateDeviceProperties(string deviceKey, JToken properties)
{
bool success = false;
// Get the current device config
var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));
if (deviceConfig != null)
{
// Replace the current properties JToken with the new one passed into this method
deviceConfig.Properties = properties;
Debug.Console(1, "Updated properties of device: '{0}'", deviceKey);
success = true;
}
ResetTimer();
return success;
}
public static bool UpdateDeviceConfig(DeviceConfig config)
{
bool success = false;
var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(config.Key));
if (deviceConfig != null)
{
deviceConfig = config;
Debug.Console(1, "Updated config of device: '{0}'", config.Key);
success = true;
}
ResetTimer();
return success;
}
public static bool UpdateRoomConfig(DeviceConfig config)
{
bool success = false;
var deviceConfig = ConfigReader.ConfigObject.Rooms.FirstOrDefault(d => d.Key.Equals(config.Key));
if (deviceConfig != null)
{
deviceConfig = config;
Debug.Console(1, "Updated config of device: '{0}'", config.Key);
success = true;
}
ResetTimer();
return success;
}
/// <summary>
/// Resets (or starts) the write timer
/// </summary>
static void ResetTimer()
{
if (WriteTimer == null)
WriteTimer = new CTimer(WriteConfigFile, WriteTimeout);
WriteTimer.Reset(WriteTimeout);
Debug.Console(1, "Config File write timer has been reset.");
}
/// <summary>
/// Writes the current config to a file in the LocalConfig subfolder
/// </summary>
/// <returns></returns>
private static void WriteConfigFile(object o)
{
var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + "configurationFile.json";
var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject);
WriteFile(filePath, configData);
}
/// <summary>
/// Writes
/// </summary>
/// <param name="filepath"></param>
/// <param name="o"></param>
public static void WriteFile(string filePath, string configData)
{
if (WriteTimer != null)
WriteTimer.Stop();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Writing Configuration to file");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to write config file: '{0}'", filePath);
try
{
if (fileLock.TryEnter())
{
using (StreamWriter sw = new StreamWriter(filePath))
{
sw.Write(configData);
sw.Flush();
}
}
else
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to enter FileLock");
}
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Config write failed: \r{0}", e);
}
finally
{
if (fileLock != null && !fileLock.Disposed)
fileLock.Leave();
}
}
}
static CCriticalSection fileLock = new CCriticalSection();
/// <summary>
/// Updates the config properties of a device
/// </summary>
/// <param name="deviceKey"></param>
/// <param name="properties"></param>
/// <returns></returns>
public static bool UpdateDeviceProperties(string deviceKey, JToken properties)
{
bool success = false;
// Get the current device config
var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(deviceKey));
if (deviceConfig != null)
{
// Replace the current properties JToken with the new one passed into this method
deviceConfig.Properties = properties;
Debug.Console(1, "Updated properties of device: '{0}'", deviceKey);
success = true;
}
ResetTimer();
return success;
}
public static bool UpdateDeviceConfig(DeviceConfig config)
{
bool success = false;
var deviceConfigIndex = ConfigReader.ConfigObject.Devices.FindIndex(d => d.Key.Equals(config.Key));
if (deviceConfigIndex >= 0)
{
ConfigReader.ConfigObject.Devices[deviceConfigIndex] = config;
Debug.Console(1, "Updated config of device: '{0}'", config.Key);
success = true;
}
ResetTimer();
return success;
}
public static bool UpdateRoomConfig(DeviceConfig config)
{
bool success = false;
var roomConfigIndex = ConfigReader.ConfigObject.Rooms.FindIndex(d => d.Key.Equals(config.Key));
if (roomConfigIndex >= 0)
{
ConfigReader.ConfigObject.Rooms[roomConfigIndex] = config;
Debug.Console(1, "Updated room of device: '{0}'", config.Key);
success = true;
}
ResetTimer();
return success;
}
/// <summary>
/// Resets (or starts) the write timer
/// </summary>
static void ResetTimer()
{
if (WriteTimer == null)
WriteTimer = new CTimer(WriteConfigFile, WriteTimeout);
WriteTimer.Reset(WriteTimeout);
Debug.Console(1, "Config File write timer has been reset.");
}
/// <summary>
/// Writes the current config to a file in the LocalConfig subfolder
/// </summary>
/// <returns></returns>
private static void WriteConfigFile(object o)
{
var filePath = Global.FilePathPrefix + LocalConfigFolder + Global.DirectorySeparator + "configurationFile.json";
var configData = JsonConvert.SerializeObject(ConfigReader.ConfigObject);
WriteFile(filePath, configData);
}
/// <summary>
/// Writes
/// </summary>
/// <param name="filepath"></param>
/// <param name="o"></param>
public static void WriteFile(string filePath, string configData)
{
if (WriteTimer != null)
WriteTimer.Stop();
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Writing Configuration to file");
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to write config file: '{0}'", filePath);
try
{
if (fileLock.TryEnter())
{
using (StreamWriter sw = new StreamWriter(filePath))
{
sw.Write(configData);
sw.Flush();
}
}
else
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to enter FileLock");
}
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Error: Config write failed: \r{0}", e);
}
finally
{
if (fileLock != null && !fileLock.Disposed)
fileLock.Leave();
}
}
}
}

View File

@@ -51,6 +51,13 @@ namespace PepperDash.Essentials.Core.Config
[JsonProperty("rooms")]
public List<DeviceConfig> Rooms { get; set; }
public EssentialsConfig()
: base()
{
Rooms = new List<DeviceConfig>();
}
}
/// <summary>

View File

@@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core.CrestronIO
{
public class C2NIoController:CrestronGenericBaseDevice, IComPorts, IIROutputPorts, IRelayPorts
{
private C2nIo _device;
public C2NIoController(string key, Func<DeviceConfig, C2nIo> preActivationFunc, DeviceConfig config):base(key, config.Name)
{
AddPreActivationAction(() =>
{
_device = preActivationFunc(config);
RegisterCrestronGenericBase(_device);
});
}
#region Implementation of IComPorts
public CrestronCollection<ComPort> ComPorts
{
get { return _device.ComPorts; }
}
public int NumberOfComPorts
{
get { return _device.NumberOfComPorts; }
}
#endregion
#region Implementation of IIROutputPorts
public CrestronCollection<IROutputPort> IROutputPorts
{
get { return _device.IROutputPorts; }
}
public int NumberOfIROutputPorts
{
get { return _device.NumberOfIROutputPorts; }
}
#endregion
#region Implementation of IRelayPorts
public CrestronCollection<Relay> RelayPorts
{
get { return _device.RelayPorts; }
}
public int NumberOfRelayPorts
{
get { return _device.NumberOfRelayPorts; }
}
#endregion
}
public class C2NIoControllerFactory : EssentialsDeviceFactory<C2nRthsController>
{
public C2NIoControllerFactory()
{
TypeNames = new List<string>() { "c2nio" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.Console(1, "Factory Attempting to create new C2N-IO Device");
return new C2NIoController(dc.Key, GetC2NIoDevice, dc);
}
static C2nIo GetC2NIoDevice(DeviceConfig dc)
{
var control = CommFactory.GetControlPropertiesConfig(dc);
var cresnetId = control.CresnetIdInt;
var branchId = control.ControlPortNumber;
var parentKey = string.IsNullOrEmpty(control.ControlPortDevKey) ? "processor" : control.ControlPortDevKey;
if (parentKey.Equals("processor", StringComparison.CurrentCultureIgnoreCase))
{
Debug.Console(0, "Device {0} is a valid cresnet master - creating new C2nIo", parentKey);
return new C2nIo(cresnetId, Global.ControlSystem);
}
var cresnetBridge = DeviceManager.GetDeviceForKey(parentKey) as IHasCresnetBranches;
if (cresnetBridge != null)
{
Debug.Console(0, "Device {0} is a valid cresnet master - creating new C2nIo", parentKey);
return new C2nIo(cresnetId, cresnetBridge.CresnetBranches[branchId]);
}
Debug.Console(0, "Device {0} is not a valid cresnet master", parentKey);
return null;
}
}
}

View File

@@ -13,6 +13,14 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
void LinkSystemMonitorToAppServer();
}
/// <summary>
/// Describes a MobileSystemController that accepts IEssentialsRoom
/// </summary>
public interface IMobileControl3 : IMobileControl
{
void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent);
}
/// <summary>
/// Describes a MobileControl Room Bridge
/// </summary>
@@ -20,6 +28,10 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
event EventHandler<EventArgs> UserCodeChanged;
event EventHandler<EventArgs> UserPromptedForCode;
event EventHandler<EventArgs> ClientJoined;
string UserCode { get; }
string QrCodeUrl { get; }

View File

@@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Describes the functionality required to prompt a user to enter a password
/// </summary>
public interface IPasswordPrompt
{
/// <summary>
/// Notifies when a password is required or is entered incorrectly
/// </summary>
event EventHandler<PasswordPromptEventArgs> PasswordRequired;
/// <summary>
/// Submits the password
/// </summary>
/// <param name="password"></param>
void SubmitPassword(string password);
}
public class PasswordPromptEventArgs : EventArgs
{
/// <summary>
/// Indicates if the last submitted password was incorrect
/// </summary>
public bool LastAttemptWasIncorrect { get; private set; }
/// <summary>
/// Indicates that the login attempt has failed
/// </summary>
public bool LoginAttemptFailed { get; private set; }
/// <summary>
/// Indicates that the process was cancelled and the prompt should be dismissed
/// </summary>
public bool LoginAttemptCancelled { get; private set; }
/// <summary>
/// A message to be displayed to the user
/// </summary>
public string Message { get; private set; }
public PasswordPromptEventArgs(bool lastAttemptIncorrect, bool loginFailed, bool loginCancelled, string message)
{
LastAttemptWasIncorrect = lastAttemptIncorrect;
LoginAttemptFailed = loginFailed;
LoginAttemptCancelled = loginCancelled;
Message = message;
}
}
}

View File

@@ -0,0 +1,54 @@
using Newtonsoft.Json;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core
{
public class DestinationListItem
{
[JsonProperty("sinkKey")]
public string SinkKey { get; set; }
private EssentialsDevice _sinkDevice;
[JsonIgnore]
public EssentialsDevice SinkDevice
{
get { return _sinkDevice ?? (_sinkDevice = DeviceManager.GetDeviceForKey(SinkKey) as EssentialsDevice); }
}
[JsonProperty("preferredName")]
public string PreferredName
{
get
{
if (!string.IsNullOrEmpty(Name))
{
return Name;
}
return SinkDevice == null ? "---" : SinkDevice.Name;
}
}
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("includeInDestinationList")]
public bool IncludeInDestinationList { get; set; }
[JsonProperty("order")]
public int Order { get; set; }
[JsonProperty("surfaceLocation")]
public int SurfaceLocation { get; set; }
[JsonProperty("verticalLocation")]
public int VerticalLocation { get; set; }
[JsonProperty("horizontalLocation")]
public int HorizontalLocation { get; set; }
[JsonProperty("sinkType")]
public eRoutingSignalType SinkType { get; set; }
}
}

View File

@@ -20,8 +20,23 @@ namespace PepperDash.Essentials.Core
/// <param name="json"></param>
public static void DoDeviceActionWithJson(string json)
{
var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);
DoDeviceAction(action);
if (String.IsNullOrEmpty(json))
{
CrestronConsole.ConsoleCommandResponse(
"Please provide a JSON object matching the format {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}.\r\nIf the method has no parameters, the \"params\" object may be omitted.");
return;
}
try
{
var action = JsonConvert.DeserializeObject<DeviceActionWrapper>(json);
DoDeviceAction(action);
}
catch (Exception ex)
{
CrestronConsole.ConsoleCommandResponse("Incorrect format for JSON. Please check that the format matches {\"deviceKey\":\"myDevice\", \"methodName\":\"someMethod\", \"params\": [\"param1\", true]}");
}
}
@@ -33,31 +48,65 @@ namespace PepperDash.Essentials.Core
{
var key = action.DeviceKey;
var obj = FindObjectOnPath(key);
if (obj == null)
return;
if (obj == null)
{
CrestronConsole.ConsoleCommandResponse("Unable to find object at path {0}", key);
return;
}
CType t = obj.GetType();
var method = t.GetMethod(action.MethodName);
if (method == null)
{
Debug.Console(0, "Method '{0}' not found", action.MethodName);
return;
}
var mParams = method.GetParameters();
// Add empty params if not provided
if (action.Params == null) action.Params = new object[0];
if (mParams.Length > action.Params.Length)
{
Debug.Console(0, "Method '{0}' requires {1} params", action.MethodName, mParams.Length);
return;
}
object[] convertedParams = mParams
.Select((p, i) => Convert.ChangeType(action.Params[i], p.ParameterType,
System.Globalization.CultureInfo.InvariantCulture))
.ToArray();
object ret = method.Invoke(obj, convertedParams);
if (action.Params == null)
{
//no params, so setting action.Params to empty array
action.Params = new object[0];
}
CType t = obj.GetType();
try
{
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
var method = methods.Count == 1 ? methods[0] : methods.FirstOrDefault(m => m.GetParameters().Length == action.Params.Length);
if (method == null)
{
CrestronConsole.ConsoleCommandResponse(
"Unable to find method with name {0} and that matches parameters {1}", action.MethodName,
action.Params);
return;
}
var mParams = method.GetParameters();
var convertedParams = mParams
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
.ToArray();
method.Invoke(obj, convertedParams);
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
action.DeviceKey);
}
catch (Exception ex)
{
CrestronConsole.ConsoleCommandResponse("Unable to call method with name {0}. {1}", action.MethodName,
ex.Message);}
}
private static object ConvertType(object value, Type conversionType)
{
if (!conversionType.IsEnum)
{
return Convert.ChangeType(value, conversionType, System.Globalization.CultureInfo.InvariantCulture);
}
var stringValue = Convert.ToString(value);
if (String.IsNullOrEmpty(stringValue))
{
throw new InvalidCastException(
String.Format("{0} cannot be converted to a string prior to conversion to enum"));
}
return Enum.Parse(conversionType, stringValue, true);
}
/// <summary>
/// Gets the properties on a device
/// </summary>
@@ -242,6 +291,8 @@ namespace PepperDash.Essentials.Core
//var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
//return JsonConvert.SerializeObject(props, Formatting.Indented);
}
}
public class DeviceActionWrapper

View File

@@ -46,7 +46,7 @@ namespace PepperDash.Essentials.Core
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => SetDeviceStreamDebugging(s), "setdevicestreamdebug", "set comm debug [deviceKey] [off/rx/tx/both] ([minutes])", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SetDeviceStreamDebugging, "setdevicestreamdebug", "set comm debug [deviceKey] [off/rx/tx/both] ([minutes])", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => DisableAllDeviceStreamDebugging(), "disableallstreamdebug", "disables stream debugging on all devices", ConsoleAccessLevelEnum.AccessOperator);
}
@@ -60,6 +60,7 @@ namespace PepperDash.Essentials.Core
DeviceCriticalSection.Enter();
AddDeviceEnabled = false;
// PreActivate all devices
Debug.Console(0,"****PreActivation starting...****");
foreach (var d in Devices.Values)
{
try
@@ -69,9 +70,12 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device PreActivation failure:\r{0}", e);
Debug.Console(0, d, "ERROR: Device {1} PreActivation failure: {0}", e.Message, d.Key);
Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
Debug.Console(0, "****PreActivation complete****");
Debug.Console(0, "****Activation starting...****");
// Activate all devices
foreach (var d in Devices.Values)
@@ -83,10 +87,14 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device Activation failure:\r{0}", e);
Debug.Console(0, d, "ERROR: Device {1} Activation failure: {0}", e.Message, d.Key);
Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
Debug.Console(0, "****Activation complete****");
Debug.Console(0, "****PostActivation starting...****");
// PostActivate all devices
foreach (var d in Devices.Values)
{
@@ -97,10 +105,13 @@ namespace PepperDash.Essentials.Core
}
catch (Exception e)
{
Debug.Console(0, d, "ERROR: Device PostActivation failure:\r{0}", e);
Debug.Console(0, d, "ERROR: Device {1} PostActivation failure: {0}", e.Message, d.Key);
Debug.Console(1, d, "Stack Trace: {0}", e.StackTrace);
}
}
Debug.Console(0, "****PostActivation complete****");
OnAllDevicesActivated();
}
finally
@@ -387,6 +398,15 @@ namespace PepperDash.Essentials.Core
/// <param name="s"></param>
public static void SetDeviceStreamDebugging(string s)
{
if (String.IsNullOrEmpty(s) || s.Contains("?"))
{
CrestronConsole.ConsoleCommandResponse(
@"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]
{deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use
timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes");
return;
}
var args = s.Split(' ');
var deviceKey = args[0];
@@ -426,7 +446,7 @@ namespace PepperDash.Essentials.Core
var min = Convert.ToUInt32(timeout);
device.StreamDebugging.SetDebuggingWithSpecificTimeout(debugSetting, min);
Debug.Console(0, "Device: '{0}' debug level set to {1) for {2} minutes", deviceKey, debugSetting, min);
Debug.Console(0, "Device: '{0}' debug level set to {1} for {2} minutes", deviceKey, debugSetting, min);
}
catch (Exception e)
@@ -437,7 +457,7 @@ namespace PepperDash.Essentials.Core
else
{
device.StreamDebugging.SetDebuggingWithDefaultTimeout(debugSetting);
Debug.Console(0, "Device: '{0}' debug level set to {1) for default time (30 minutes)", deviceKey, debugSetting);
Debug.Console(0, "Device: '{0}' debug level set to {1} for default time (30 minutes)", deviceKey, debugSetting);
}
}

View File

@@ -1,113 +1,134 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class
/// </summary>
[Description("The base Essentials Device Class")]
public abstract class EssentialsDevice : Device
{
protected EssentialsDevice(string key)
: base(key)
{
}
protected EssentialsDevice(string key, string name)
: base(key, name)
{
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class DescriptionAttribute : Attribute
{
private string _Description;
public DescriptionAttribute(string description)
{
Debug.Console(2, "Setting Description: {0}", description);
_Description = description;
}
public string Description
{
get { return _Description; }
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ConfigSnippetAttribute : Attribute
{
private string _ConfigSnippet;
public ConfigSnippetAttribute(string configSnippet)
{
Debug.Console(2, "Setting Config Snippet {0}", configSnippet);
_ConfigSnippet = configSnippet;
}
public string ConfigSnippet
{
get { return _ConfigSnippet; }
}
}
/// <summary>
/// Devices the basic needs for a Device Factory
/// </summary>
public abstract class EssentialsDeviceFactory<T> : IDeviceFactory where T:EssentialsDevice
{
#region IDeviceFactory Members
/// <summary>
/// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device
/// </summary>
public List<string> TypeNames { get; protected set; }
/// <summary>
/// Loads an item to the DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list
/// </summary>
public void LoadTypeFactories()
{
foreach (var typeName in TypeNames)
{
Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Defines the basic needs for an EssentialsDevice to enable it to be build by an IDeviceFactory class
/// </summary>
[Description("The base Essentials Device Class")]
public abstract class EssentialsDevice : Device
{
protected EssentialsDevice(string key)
: base(key)
{
SubscribeToActivateComplete();
}
protected EssentialsDevice(string key, string name)
: base(key, name)
{
SubscribeToActivateComplete();
}
private void SubscribeToActivateComplete()
{
DeviceManager.AllDevicesActivated += DeviceManagerOnAllDevicesActivated;
}
private void DeviceManagerOnAllDevicesActivated(object sender, EventArgs eventArgs)
{
CrestronInvoke.BeginInvoke((o) =>
{
try
{
Initialize();
}
catch (Exception ex)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Exception initializing device: {0}", ex.Message);
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stack Trace: {0}", ex.StackTrace);
}
});
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class DescriptionAttribute : Attribute
{
private string _Description;
public DescriptionAttribute(string description)
{
Debug.Console(2, "Setting Description: {0}", description);
_Description = description;
}
public string Description
{
get { return _Description; }
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ConfigSnippetAttribute : Attribute
{
private string _ConfigSnippet;
public ConfigSnippetAttribute(string configSnippet)
{
Debug.Console(2, "Setting Config Snippet {0}", configSnippet);
_ConfigSnippet = configSnippet;
}
public string ConfigSnippet
{
get { return _ConfigSnippet; }
}
}
/// <summary>
/// Devices the basic needs for a Device Factory
/// </summary>
public abstract class EssentialsDeviceFactory<T> : IDeviceFactory where T:EssentialsDevice
{
#region IDeviceFactory Members
/// <summary>
/// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device
/// </summary>
public List<string> TypeNames { get; protected set; }
/// <summary>
/// Loads an item to the DeviceFactory.FactoryMethods dictionary for each entry in the TypeNames list
/// </summary>
public void LoadTypeFactories()
{
foreach (var typeName in TypeNames)
{
Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName);
var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[];
string description = descriptionAttribute[0].Description;
var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
DeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice);
}
}
/// <summary>
/// The method that will build the device
/// </summary>
/// <param name="dc">The device config</param>
/// <returns>An instance of the device</returns>
public abstract EssentialsDevice BuildDevice(DeviceConfig dc);
#endregion
}
/// <summary>
/// Devices the basic needs for a Device Factory
/// </summary>
public abstract class EssentialsPluginDeviceFactory<T> : EssentialsDeviceFactory<T>, IPluginDeviceFactory where T : EssentialsDevice
{
/// <summary>
/// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33")
/// </summary>
public string MinimumEssentialsFrameworkVersion { get; protected set; }
}
var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[];
DeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice);
}
}
/// <summary>
/// The method that will build the device
/// </summary>
/// <param name="dc">The device config</param>
/// <returns>An instance of the device</returns>
public abstract EssentialsDevice BuildDevice(DeviceConfig dc);
#endregion
}
/// <summary>
/// Devices the basic needs for a Device Factory
/// </summary>
public abstract class EssentialsPluginDeviceFactory<T> : EssentialsDeviceFactory<T>, IPluginDeviceFactory where T : EssentialsDevice
{
/// <summary>
/// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33")
/// </summary>
public string MinimumEssentialsFrameworkVersion { get; protected set; }
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core.Devices
{
public interface IReconfigurableDevice
{
event EventHandler<EventArgs> ConfigChanged;
DeviceConfig Config { get; }
void SetConfig(DeviceConfig config);
}
}

View File

@@ -7,13 +7,15 @@ using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.Devices
{
/// <summary>
///
/// </summary>
public abstract class ReconfigurableDevice : EssentialsDevice
public abstract class ReconfigurableDevice : EssentialsDevice, IReconfigurableDevice
{
public event EventHandler<EventArgs> ConfigChanged;
@@ -52,6 +54,8 @@ namespace PepperDash.Essentials.Core.Devices
Name = config.Name;
}
/// <summary>
/// Used by the extending class to allow for any custom actions to be taken (tell the ConfigWriter to write config, etc)
/// </summary>

View File

@@ -130,10 +130,24 @@ namespace PepperDash.Essentials.Core
[JsonProperty("sourceListKey")]
public string SourceListKey { get; set; }
/// <summary>
/// Indicates if the device associated with this source is controllable
/// </summary>
[JsonProperty("isControllable")]
public bool IsControllable { get; set; }
/// <summary>
/// Indicates that the device associated with this source has audio available
/// </summary>
[JsonProperty("isAudioSource")]
public bool IsAudioSource { get; set; }
public SourceListItem()
{
Icon = "Blank";
}
}
public class SourceRouteListItem

View File

@@ -59,6 +59,9 @@ namespace PepperDash.Essentials.Core
VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; });
MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted);
WarmupTime = 10000;
CooldownTime = 5000;
}
public override void PowerOn()

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core
{
public static class JsonExtensions
{
public static List<JToken> FindTokens(this JToken containerToken, string name)
{
List<JToken> matches = new List<JToken>();
FindTokens(containerToken, name, matches);
return matches;
}
private static void FindTokens(JToken containerToken, string name, List<JToken> matches)
{
if (containerToken.Type == JTokenType.Object)
{
foreach (JProperty child in containerToken.Children<JProperty>())
{
if (child.Name == name)
{
matches.Add(child.Value);
}
FindTokens(child.Value, name, matches);
}
}
else if (containerToken.Type == JTokenType.Array)
{
foreach (JToken child in containerToken.Children())
{
FindTokens(child, name, matches);
}
}
}
}
}

View File

@@ -1,149 +1,208 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Core.Touchpanels;
namespace PepperDash.Essentials.Core
{
public class DeviceFactoryWrapper
{
public CType CType { get; set; }
public string Description { get; set; }
public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }
public DeviceFactoryWrapper()
{
CType = null;
Description = "Not Available";
}
}
public class DeviceFactory
{
public DeviceFactory()
{
var assy = Assembly.GetExecutingAssembly();
PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy);
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
if (types != null)
{
foreach (var type in types)
{
try
{
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
factory.LoadTypeFactories();
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to load type: '{1}' DeviceFactory: {0}", e, type.Name);
}
}
}
}
/// <summary>
/// A dictionary of factory methods, keyed by config types, added by plugins.
/// These methods are looked up and called by GetDevice in this class.
/// </summary>
static Dictionary<string, DeviceFactoryWrapper> FactoryMethods =
new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Adds a plugin factory method
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public static void AddFactoryForType(string typeName, Func<DeviceConfig, IKeyed> method)
{
Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
}
public static void AddFactoryForType(string typeName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
{
Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
if(FactoryMethods.ContainsKey(typeName))
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to add type: '{0}'. Already exists in DeviceFactory", typeName);
return;
}
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
}
/// <summary>
/// The factory method for Core "things". Also iterates the Factory methods that have
/// been loaded from plugins
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public static IKeyed GetDevice(DeviceConfig dc)
{
var key = dc.Key;
var name = dc.Name;
var type = dc.Type;
var properties = dc.Properties;
var typeName = dc.Type.ToLower();
// Check for types that have been added by plugin dlls.
if (FactoryMethods.ContainsKey(typeName))
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type);
return FactoryMethods[typeName].FactoryMethod(dc);
}
return null;
}
/// <summary>
/// Prints the type names and associated metadata from the FactoryMethods collection.
/// </summary>
/// <param name="command"></param>
public static void GetDeviceFactoryTypes(string filter)
{
Dictionary<string, DeviceFactoryWrapper> types = new Dictionary<string, DeviceFactoryWrapper>();
if (!string.IsNullOrEmpty(filter))
{
types = FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
}
else
{
types = FactoryMethods;
}
Debug.Console(0, "Device Types:");
foreach (var type in types.OrderBy(t => t.Key))
{
var description = type.Value.Description;
var cType = "Not Specified by Plugin";
if(type.Value.CType != null)
{
cType = type.Value.CType.FullName;
}
Debug.Console(0,
@"Type: '{0}'
CType: '{1}'
Description: {2}", type.Key, cType, description);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using Crestron.SimplSharp.Reflection;
using PepperDash.Core;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.CrestronIO;
using PepperDash.Essentials.Core.Touchpanels;
namespace PepperDash.Essentials.Core
{
public class DeviceFactoryWrapper
{
public CType CType { get; set; }
public string Description { get; set; }
public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }
public DeviceFactoryWrapper()
{
CType = null;
Description = "Not Available";
}
}
public class DeviceFactory
{
public DeviceFactory()
{
var assy = Assembly.GetExecutingAssembly();
PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy);
var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);
if (types != null)
{
foreach (var type in types)
{
try
{
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
factory.LoadTypeFactories();
}
catch (Exception e)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to load type: '{1}' DeviceFactory: {0}", e, type.Name);
}
}
}
}
/// <summary>
/// A dictionary of factory methods, keyed by config types, added by plugins.
/// These methods are looked up and called by GetDevice in this class.
/// </summary>
static Dictionary<string, DeviceFactoryWrapper> FactoryMethods =
new Dictionary<string, DeviceFactoryWrapper>(StringComparer.OrdinalIgnoreCase);
/// <summary>
/// Adds a plugin factory method
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public static void AddFactoryForType(string typeName, Func<DeviceConfig, IKeyed> method)
{
Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
}
public static void AddFactoryForType(string typeName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
{
Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName);
if(FactoryMethods.ContainsKey(typeName))
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Unable to add type: '{0}'. Already exists in DeviceFactory", typeName);
return;
}
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
}
private static void CheckForSecrets(IEnumerable<JProperty> obj)
{
foreach (var prop in obj.Where(prop => prop.Value as JObject != null))
{
if (prop.Name.ToLower() == "secret")
{
var secret = GetSecret(prop.Children().First().ToObject<SecretsPropertiesConfig>());
//var secret = GetSecret(JsonConvert.DeserializeObject<SecretsPropertiesConfig>(prop.Children().First().ToString()));
prop.Parent.Replace(secret);
}
var recurseProp = prop.Value as JObject;
if (recurseProp == null) return;
CheckForSecrets(recurseProp.Properties());
}
}
private static string GetSecret(SecretsPropertiesConfig data)
{
var secretProvider = SecretsManager.GetSecretProviderByKey(data.Provider);
if (secretProvider == null) return null;
var secret = secretProvider.GetSecret(data.Key);
if (secret != null) return (string) secret.Value;
Debug.Console(1,
"Unable to retrieve secret {0}{1} - Make sure you've added it to the secrets provider",
data.Provider, data.Key);
return String.Empty;
}
/// <summary>
/// The factory method for Core "things". Also iterates the Factory methods that have
/// been loaded from plugins
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public static IKeyed GetDevice(DeviceConfig dc)
{
try
{
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading '{0}' from Essentials Core", dc.Type);
var localDc = new DeviceConfig(dc);
var key = localDc.Key;
var name = localDc.Name;
var type = localDc.Type;
var properties = localDc.Properties;
//var propRecurse = properties;
var typeName = localDc.Type.ToLower();
var jObject = properties as JObject;
if (jObject != null)
{
var jProp = jObject.Properties();
CheckForSecrets(jProp);
}
Debug.Console(2, "typeName = {0}", typeName);
// Check for types that have been added by plugin dlls.
return !FactoryMethods.ContainsKey(typeName) ? null : FactoryMethods[typeName].FactoryMethod(localDc);
}
catch (Exception ex)
{
Debug.Console(0, Debug.ErrorLogLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
Debug.Console(2, "{0}", ex.StackTrace);
if (ex.InnerException == null)
{
return null;
}
Debug.Console(0, Debug.ErrorLogLevel.Error, "Inner exception while creating device {0}: {1}", dc.Key,
ex.InnerException.Message);
Debug.Console(2, "{0}", ex.InnerException.StackTrace);
return null;
}
}
/// <summary>
/// Prints the type names and associated metadata from the FactoryMethods collection.
/// </summary>
/// <param name="command"></param>
public static void GetDeviceFactoryTypes(string filter)
{
Dictionary<string, DeviceFactoryWrapper> types = new Dictionary<string, DeviceFactoryWrapper>();
if (!string.IsNullOrEmpty(filter))
{
types = FactoryMethods.Where(k => k.Key.Contains(filter)).ToDictionary(k => k.Key, k => k.Value);
}
else
{
types = FactoryMethods;
}
Debug.Console(0, "Device Types:");
foreach (var type in types.OrderBy(t => t.Key))
{
var description = type.Value.Description;
var cType = "Not Specified by Plugin";
if(type.Value.CType != null)
{
cType = type.Value.CType.FullName;
}
Debug.Console(0,
@"Type: '{0}'
CType: '{1}'
Description: {2}", type.Key, cType, description);
}
}
}
}

View File

@@ -62,6 +62,11 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
public void SetValueFunc(Func<bool> newFunc)
{
ValueFunc = newFunc;
}
public override void FireUpdate()
{
bool newValue = InTestMode ? TestValue : ValueFunc.Invoke();

View File

@@ -23,7 +23,7 @@ namespace PepperDash.Essentials.Core
protected bool ComputedValue;
public BoolFeedbackLogic()
protected BoolFeedbackLogic()
{
Output = new BoolFeedback(() => ComputedValue);
}
@@ -40,21 +40,18 @@ namespace PepperDash.Essentials.Core
public void AddOutputsIn(List<BoolFeedback> outputs)
{
foreach (var o in outputs)
{
// skip existing
if (OutputsIn.Contains(o)) continue;
OutputsIn.Add(o);
o.OutputChange += AnyInput_OutputChange;
}
Evaluate();
foreach (var o in outputs.Where(o => !OutputsIn.Contains(o)))
{
OutputsIn.Add(o);
o.OutputChange += AnyInput_OutputChange;
}
Evaluate();
}
public void RemoveOutputIn(BoolFeedback output)
public void RemoveOutputIn(BoolFeedback output)
{
// Don't double up outputs
if (OutputsIn.Contains(output)) return;
if (!OutputsIn.Contains(output)) return;
OutputsIn.Remove(output);
output.OutputChange -= AnyInput_OutputChange;
@@ -71,6 +68,12 @@ namespace PepperDash.Essentials.Core
Evaluate();
}
public void ClearOutputs()
{
OutputsIn.Clear();
Evaluate();
}
void AnyInput_OutputChange(object sender, EventArgs e)
{
Evaluate();
@@ -85,11 +88,12 @@ namespace PepperDash.Essentials.Core
{
var prevValue = ComputedValue;
var newValue = OutputsIn.All(o => o.BoolValue);
if (newValue != prevValue)
{
ComputedValue = newValue;
Output.FireUpdate();
}
if (newValue == prevValue)
{
return;
}
ComputedValue = newValue;
Output.FireUpdate();
}
}
@@ -99,33 +103,35 @@ namespace PepperDash.Essentials.Core
{
var prevValue = ComputedValue;
var newValue = OutputsIn.Any(o => o.BoolValue);
if (newValue != prevValue)
{
ComputedValue = newValue;
Output.FireUpdate();
}
if (newValue == prevValue)
{
return;
}
ComputedValue = newValue;
Output.FireUpdate();
}
}
public class BoolFeedbackLinq : BoolFeedbackLogic
{
Func<IEnumerable<BoolFeedback>, bool> Predicate;
readonly Func<IEnumerable<BoolFeedback>, bool> _predicate;
public BoolFeedbackLinq(Func<IEnumerable<BoolFeedback>, bool> predicate)
: base()
{
Predicate = predicate;
_predicate = predicate;
}
protected override void Evaluate()
{
var prevValue = ComputedValue;
var newValue = Predicate(OutputsIn);
if (newValue != prevValue)
{
ComputedValue = newValue;
Output.FireUpdate();
}
var newValue = _predicate(OutputsIn);
if (newValue == prevValue)
{
return;
}
ComputedValue = newValue;
Output.FireUpdate();
}
}
}

View File

@@ -51,6 +51,12 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
public void SetValueFunc(Func<int> newFunc)
{
ValueFunc = newFunc;
}
public override void FireUpdate()
{
var newValue = InTestMode ? TestValue : ValueFunc.Invoke();

View File

@@ -52,7 +52,10 @@ namespace PepperDash.Essentials.Core
ValueFunc = valueFunc;
}
public void SetValueFunc(Func<string> newFunc)
{
ValueFunc = newFunc;
}
public override void FireUpdate()
{

View File

@@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Core.Fusion
protected FusionRoom FusionRoom;
protected Dictionary<int, FusionAsset> FusionStaticAssets;
public long PushNotificationTimeout = 5000;
protected EssentialsRoomBase Room;
protected IEssentialsRoom Room;
public long SchedulePollInterval = 300000;
private Event _currentMeeting;
@@ -86,7 +86,7 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion
public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId, string joinMapKey)
public EssentialsHuddleSpaceFusionSystemControllerBase(IEssentialsRoom room, uint ipId, string joinMapKey)
: base(room.Key + "-fusion")
{
try
@@ -119,9 +119,21 @@ namespace PepperDash.Essentials.Core.Fusion
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId);
_guidFileExists = File.Exists(guidFilePath);
var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
if (File.Exists(oldGuidFilePath))
{
Debug.Console(0, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
File.Copy(oldGuidFilePath, guidFilePath);
File.Delete(oldGuidFilePath);
}
_guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
@@ -149,19 +161,7 @@ namespace PepperDash.Essentials.Core.Fusion
}
AddPostActivationAction(() =>
{
CreateSymbolAndBasicSigs(_ipId);
SetUpSources();
SetUpCommunitcationMonitors();
SetUpDisplay();
SetUpError();
ExecuteCustomSteps();
FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath);
});
AddPostActivationAction(() => PostActivate(guidFilePath));
}
catch (Exception e)
{
@@ -169,6 +169,20 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
private void PostActivate(string guidFilePath)
{
CreateSymbolAndBasicSigs(_ipId);
SetUpSources();
SetUpCommunitcationMonitors();
SetUpDisplay();
SetUpError();
ExecuteCustomSteps();
FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath);
}
protected string RoomGuid
{
get { return _guiDs.RoomGuid; }
@@ -314,7 +328,7 @@ namespace PepperDash.Essentials.Core.Fusion
protected virtual void CreateSymbolAndBasicSigs(uint ipId)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid);
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid);
FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use();

View File

@@ -82,7 +82,7 @@ namespace PepperDash.Essentials.Core.Fusion
deviceConfig.Properties = JToken.FromObject(devProps);
}
else if (device is EssentialsRoomBase)
else if (device is IEssentialsRoom)
{
// Set the room name
if (!string.IsNullOrEmpty(roomInfo.Name))

View File

@@ -1,5 +1,6 @@
using System;
using System.Text.RegularExpressions;
using System.Globalization;
using Crestron.SimplSharp;
using System.Collections.Generic;
using Crestron.SimplSharp.CrestronIO;
@@ -28,6 +29,10 @@ namespace PepperDash.Essentials.Core
public static eCrestronSeries ProcessorSeries { get { return CrestronEnvironment.ProgramCompatibility; } }
// TODO: consider making this configurable later
public static IFormatProvider Culture = CultureInfo.CreateSpecificCulture("en-US");
/// <summary>
/// The file path prefix to the folder containing configuration files
/// </summary>

View File

@@ -24,6 +24,10 @@ namespace PepperDash.Essentials.Core
CrestronConsole.AddNewConsoleCommand(ClearEventsFromGroup, "ClearAllEvents", "Clears all scheduled events for this group", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListAllEventGroups, "ListAllEventGroups", "Lists all the event groups by key", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(ListAllEventsForGroup, "ListEventsForGroup",
"Lists all events for the given group", ConsoleAccessLevelEnum.AccessOperator);
}
/// <summary>
@@ -32,12 +36,26 @@ namespace PepperDash.Essentials.Core
/// <param name="groupName"></param>
static void ClearEventsFromGroup(string groupName)
{
if (!EventGroups.ContainsKey(groupName))
{
Debug.Console(0,
"[Scheduler]: Unable to delete events from group '{0}'. Group not found in EventGroups dictionary.",
groupName);
return;
}
var group = EventGroups[groupName];
if (group != null)
{
group.ClearAllEvents();
Debug.Console(0, "[Scheduler]: All events deleted from group '{0}'", groupName);
}
else
Debug.Console(0, "[Scheduler]: Unable to delete events from group '{0}'. Group not found in EventGroups dictionary.", groupName);
Debug.Console(0,
"[Scheduler]: Unable to delete events from group '{0}'. Group not found in EventGroups dictionary.",
groupName);
}
static void ListAllEventGroups(string command)
@@ -49,6 +67,33 @@ namespace PepperDash.Essentials.Core
}
}
static void ListAllEventsForGroup(string args)
{
Debug.Console(0, "Getting events for group {0}...", args);
ScheduledEventGroup group;
if (!EventGroups.TryGetValue(args, out group))
{
Debug.Console(0, "Unabled to get event group for key {0}", args);
return;
}
foreach (var evt in group.ScheduledEvents)
{
Debug.Console(0,
@"
****Event key {0}****
Event date/time: {1}
Persistent: {2}
Acknowlegable: {3}
Recurrence: {4}
Recurrence Days: {5}
********************", evt.Key, evt.Value.DateAndTime, evt.Value.Persistent, evt.Value.Acknowledgeable,
evt.Value.Recurrence.Recurrence, evt.Value.Recurrence.RecurrenceDays);
}
}
/// <summary>
/// Adds the event group to the global list
/// </summary>

View File

@@ -88,11 +88,6 @@ namespace PepperDash.Essentials.Core.Privacy
else
Debug.Console(0, this, "Unable to add Red LED device");
DeviceManager.AllDevicesActivated += (o, a) =>
{
CheckPrivacyMode();
};
AddPostActivationAction(() => {
PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange -= PrivacyModeIsOnFeedback_OutputChange;
PrivacyDevice.PrivacyModeIsOnFeedback.OutputChange += PrivacyModeIsOnFeedback_OutputChange;
@@ -103,6 +98,15 @@ namespace PepperDash.Essentials.Core.Privacy
return base.CustomActivate();
}
#region Overrides of Device
public override void Initialize()
{
CheckPrivacyMode();
}
#endregion
public void SetPrivacyDevice(IPrivacy privacyDevice)
{
PrivacyDevice = privacyDevice;

View File

@@ -2,17 +2,18 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Aggregates the RoomIsOccupied feedbacks of one or more IOccupancyStatusProvider objects
/// </summary>
public class IOccupancyStatusProviderAggregator : Device, IOccupancyStatusProvider
public class IOccupancyStatusProviderAggregator : EssentialsDevice, IOccupancyStatusProvider
{
/// <summary>
/// Aggregated feedback of all linked IOccupancyStatusProvider devices
@@ -21,16 +22,51 @@ namespace PepperDash.Essentials.Core
{
get
{
return AggregatedOccupancyStatus.Output;
return _aggregatedOccupancyStatus.Output;
}
}
private BoolFeedbackOr AggregatedOccupancyStatus;
private readonly BoolFeedbackOr _aggregatedOccupancyStatus;
public IOccupancyStatusProviderAggregator(string key, string name)
: base(key, name)
{
AggregatedOccupancyStatus = new BoolFeedbackOr();
_aggregatedOccupancyStatus = new BoolFeedbackOr();
}
public IOccupancyStatusProviderAggregator(string key, string name, OccupancyAggregatorConfig config)
: this(key, name)
{
AddPostActivationAction(() =>
{
if (config.DeviceKeys.Count == 0)
{
return;
}
foreach (var deviceKey in config.DeviceKeys)
{
var device = DeviceManager.GetDeviceForKey(deviceKey);
if (device == null)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice,
"Unable to retrieve Occupancy provider with key {0}", deviceKey);
continue;
}
var provider = device as IOccupancyStatusProvider;
if (provider == null)
{
Debug.Console(0, this, Debug.ErrorLogLevel.Notice,
"Device with key {0} does NOT implement IOccupancyStatusProvider. Please check configuration.");
continue;
}
AddOccupancyStatusProvider(provider);
}
});
}
/// <summary>
@@ -39,7 +75,35 @@ namespace PepperDash.Essentials.Core
/// <param name="statusProvider"></param>
public void AddOccupancyStatusProvider(IOccupancyStatusProvider statusProvider)
{
AggregatedOccupancyStatus.AddOutputIn(statusProvider.RoomIsOccupiedFeedback);
_aggregatedOccupancyStatus.AddOutputIn(statusProvider.RoomIsOccupiedFeedback);
}
public void RemoveOccupancyStatusProvider(IOccupancyStatusProvider statusProvider)
{
_aggregatedOccupancyStatus.RemoveOutputIn(statusProvider.RoomIsOccupiedFeedback);
}
public void ClearOccupancyStatusProviders()
{
_aggregatedOccupancyStatus.ClearOutputs();
}
}
public class OccupancyAggregatorFactory : EssentialsDeviceFactory<IOccupancyStatusProviderAggregator>
{
public OccupancyAggregatorFactory()
{
TypeNames = new List<string> { "occupancyAggregator", "occAggregate" };
}
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.Console(1, "Factory Attempting to create new GlsOccupancySensorBaseController Device");
var config = dc.Properties.ToObject<OccupancyAggregatorConfig>();
return new IOccupancyStatusProviderAggregator(dc.Key, dc.Name, config);
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
public class OccupancyAggregatorConfig
{
[JsonProperty("deviceKeys")] public List<string> DeviceKeys { get; set; }
public OccupancyAggregatorConfig()
{
DeviceKeys = new List<string>();
}
}
}

View File

@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents an abstract controller device for a partition dividing rooms that are combinable
///
/// In Auto mode, it can use a partition sensor to automatically determine whether the partition is present.
///
/// In Manual mode it accepts user input to tell it whether the partition is present.
/// </summary>
public class EssentialsPartitionController : IPartitionController
{
private IPartitionStateProvider _partitionSensor;
private bool isInAutoMode;
private bool partitionPresent;
public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)
{
Key = key;
Name = name;
AdjacentRoomKeys = adjacentRoomKeys;
if (sensor != null)
{
_partitionSensor = sensor;
if (!defaultToManualMode)
{
SetAutoMode();
}
else
{
SetManualMode();
}
}
else
{
SetManualMode();
}
PartitionPresentFeedback.FireUpdate();
}
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
if (isInAutoMode)
{
PartitionPresentFeedback.FireUpdate();
}
}
#region IPartitionController Members
public List<string> AdjacentRoomKeys { get; private set; }
public void SetAutoMode()
{
isInAutoMode = true;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
}
else
{
PartitionPresentFeedback = new BoolFeedback(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
}
if (_partitionSensor != null)
{
_partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
}
}
public void SetManualMode()
{
isInAutoMode = false;
if (PartitionPresentFeedback != null)
{
PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
}
else
{
PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
}
if (_partitionSensor != null)
{
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
}
}
public void SetPartitionStatePresent()
{
if (!isInAutoMode)
{
partitionPresent = true;
PartitionPresentFeedback.FireUpdate();
}
}
public void SetPartitionStateNotPresent()
{
if (!isInAutoMode)
{
partitionPresent = false;
PartitionPresentFeedback.FireUpdate();
}
}
public void ToggglePartitionState()
{
if (!isInAutoMode)
{
partitionPresent = !partitionPresent;
PartitionPresentFeedback.FireUpdate();
}
}
#endregion
#region IPartitionStateProvider Members
public BoolFeedback PartitionPresentFeedback { get; private set; }
#endregion
#region IKeyName Members
public string Name { get; private set; }
#endregion
#region IKeyed Members
public string Key { get; private set; }
#endregion
}
}

View File

@@ -13,13 +13,13 @@ using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials.Core
{
[Description("Wrapper class for GLS Cresnet Partition Sensor")]
public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice
public class GlsPartitionSensorController : CrestronGenericBridgeableBaseDevice, IPartitionStateProvider
{
private GlsPartCn _partitionSensor;
public StringFeedback NameFeedback { get; private set; }
public BoolFeedback EnableFeedback { get; private set; }
public BoolFeedback PartitionSensedFeedback { get; private set; }
public BoolFeedback PartitionPresentFeedback { get; private set; }
public BoolFeedback PartitionNotSensedFeedback { get; private set; }
public IntFeedback SensitivityFeedback { get; private set; }
@@ -39,10 +39,10 @@ namespace PepperDash.Essentials.Core
RegisterCrestronGenericBase(_partitionSensor);
NameFeedback = new StringFeedback(() => Name);
EnableFeedback = new BoolFeedback(() => _partitionSensor.EnableFeedback.BoolValue);
PartitionSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionSensedFeedback.BoolValue);
PartitionNotSensedFeedback = new BoolFeedback(() => _partitionSensor.PartitionNotSensedFeedback.BoolValue);
SensitivityFeedback = new IntFeedback(() => _partitionSensor.SensitivityFeedback.UShortValue);
EnableFeedback = new BoolFeedback(() => InTestMode ? TestEnableFeedback : _partitionSensor.EnableFeedback.BoolValue);
PartitionPresentFeedback = new BoolFeedback(() => InTestMode ? TestPartitionSensedFeedback : _partitionSensor.PartitionSensedFeedback.BoolValue);
PartitionNotSensedFeedback = new BoolFeedback(() => InTestMode ? !TestPartitionSensedFeedback : _partitionSensor.PartitionNotSensedFeedback.BoolValue);
SensitivityFeedback = new IntFeedback(() => InTestMode ? TestSensitivityFeedback : _partitionSensor.SensitivityFeedback.UShortValue);
if (_partitionSensor != null) _partitionSensor.BaseEvent += PartitionSensor_BaseEvent;
});
@@ -61,7 +61,7 @@ namespace PepperDash.Essentials.Core
}
case (GlsPartCn.PartitionSensedFeedbackEventId):
{
PartitionSensedFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
break;
}
case (GlsPartCn.PartitionNotSensedFeedbackEventId):
@@ -93,6 +93,9 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestEnableFeedback = state;
EnableFeedback.FireUpdate();
Debug.Console(1, this, "TestEnableFeedback: {0}", TestEnableFeedback.ToString());
return;
}
@@ -105,6 +108,10 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestPartitionSensedFeedback = state;
PartitionPresentFeedback.FireUpdate();
PartitionNotSensedFeedback.FireUpdate();
Debug.Console(1, this, "TestPartitionSensedFeedback: {0}", TestPartitionSensedFeedback.ToString());
return;
}
@@ -117,6 +124,8 @@ namespace PepperDash.Essentials.Core
if (InTestMode)
{
TestSensitivityFeedback = value;
SensitivityFeedback.FireUpdate();
Debug.Console(1, this, "TestSensitivityFeedback: {0}", TestSensitivityFeedback);
return;
}
@@ -186,7 +195,7 @@ namespace PepperDash.Essentials.Core
// link output to simpl
IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]);
EnableFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Enable.JoinNumber]);
PartitionSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]);
PartitionPresentFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionSensed.JoinNumber]);
PartitionNotSensedFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PartitionNotSensed.JoinNumber]);
SensitivityFeedback.LinkInputSig(trilist.UShortInput[joinMap.Sensitivity.JoinNumber]);
@@ -216,7 +225,7 @@ namespace PepperDash.Essentials.Core
IsOnline.FireUpdate();
NameFeedback.FireUpdate();
EnableFeedback.FireUpdate();
PartitionSensedFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate();
PartitionNotSensedFeedback.FireUpdate();
SensitivityFeedback.FireUpdate();
}
@@ -257,7 +266,7 @@ namespace PepperDash.Essentials.Core
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.Console(1, "Factory Attempting to create new C2N-RTHS Device");
Debug.Console(1, "Factory Attempting to create new GlsPartitionSensorController Device");
return new GlsPartitionSensorController(dc.Key, GetGlsPartCnDevice, dc);
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Describes the functionality of a device that senses and provides partition state
/// </summary>
public interface IPartitionStateProvider : IKeyName
{
BoolFeedback PartitionPresentFeedback { get; }
}
/// <summary>
/// Describes the functionality of a device that can provide partition state either manually via user input or optionally via a sensor state
/// </summary>
public interface IPartitionController : IPartitionStateProvider
{
List<string> AdjacentRoomKeys { get; }
void SetPartitionStatePresent();
void SetPartitionStateNotPresent();
void ToggglePartitionState();
void SetManualMode();
void SetAutoMode();
}
}

View File

@@ -162,6 +162,7 @@
<Compile Include="Config\Essentials\ConfigWriter.cs" />
<Compile Include="Config\Essentials\EssentialsConfig.cs" />
<Compile Include="Config\SourceDevicePropertiesConfigBase.cs" />
<Compile Include="Crestron IO\C2nIo\C2nIoController.cs" />
<Compile Include="Crestron IO\C2nRts\C2nRthsController.cs" />
<Compile Include="Crestron IO\Cards\C3CardControllerBase.cs" />
<Compile Include="Crestron IO\Cards\C3Com3Controller.cs" />
@@ -189,6 +190,7 @@
<Compile Include="Device Info\IDeviceInfoProvider.cs" />
<Compile Include="Devices\CodecInterfaces.cs" />
<Compile Include="Devices\CrestronProcessor.cs" />
<Compile Include="Devices\DestinationListItem.cs" />
<Compile Include="Devices\DeviceApiBase.cs" />
<Compile Include="Devices\DeviceFeedbackExtensions.cs" />
<Compile Include="Devices\EssentialsBridgeableDevice.cs" />
@@ -196,10 +198,12 @@
<Compile Include="Devices\GenericIRController.cs" />
<Compile Include="Devices\IDspPreset.cs" />
<Compile Include="Devices\IProjectorInterfaces.cs" />
<Compile Include="Devices\IReconfigurableDevice.cs" />
<Compile Include="Devices\PC\InRoomPc.cs" />
<Compile Include="Devices\PC\Laptop.cs" />
<Compile Include="Devices\ReconfigurableDevice.cs" />
<Compile Include="Devices\VolumeDeviceChangeEventArgs.cs" />
<Compile Include="DeviceTypeInterfaces\IPasswordPrompt.cs" />
<Compile Include="DeviceTypeInterfaces\ITvPresetsProvider.cs" />
<Compile Include="DeviceTypeInterfaces\LanguageLabel.cs" />
<Compile Include="DeviceTypeInterfaces\ILanguageProvider.cs" />
@@ -208,6 +212,7 @@
<Compile Include="DeviceTypeInterfaces\IHasFarEndContentStatus.cs" />
<Compile Include="DeviceTypeInterfaces\IHasPhoneDialing.cs" />
<Compile Include="DeviceTypeInterfaces\IMobileControl.cs" />
<Compile Include="Extensions\JsonExtensions.cs" />
<Compile Include="Factory\DeviceFactory.cs" />
<Compile Include="Factory\IDeviceFactory.cs" />
<Compile Include="Factory\ReadyEventArgs.cs" />
@@ -230,6 +235,9 @@
<Compile Include="Interfaces\ILogStringsWithLevel.cs" />
<Compile Include="Occupancy\GlsOccupancySensorPropertiesConfig.cs" />
<Compile Include="Occupancy\GlsOirOccupancySensorController.cs" />
<Compile Include="PartitionSensor\EssentialsPartitionController.cs" />
<Compile Include="PartitionSensor\IPartitionStateProvider.cs" />
<Compile Include="Occupancy\OccupancyAggregatorConfig.cs" />
<Compile Include="Queues\ComsMessage.cs" />
<Compile Include="Queues\ProcessStringMessage.cs" />
<Compile Include="Queues\GenericQueue.cs" />
@@ -284,8 +292,13 @@
<Compile Include="Remotes\CrestronRemotePropertiesConfig.cs" />
<Compile Include="Remotes\Hrxx0WirelessRemoteController.cs" />
<Compile Include="Room\Behaviours\RoomOnToDefaultSourceWhenOccupied.cs" />
<Compile Include="Room\Combining\EssentialsRoomCombiner.cs" />
<Compile Include="Room\Combining\EssentialsRoomCombinerPropertiesConfig.cs" />
<Compile Include="Room\Combining\IEssentialsRoomCombiner.cs" />
<Compile Include="Room\Combining\RoomCombinationScenario.cs" />
<Compile Include="Room\EssentialsRoomBase.cs" />
<Compile Include="Room\Config\EssentialsRoomScheduledEventsConfig.cs" />
<Compile Include="Room\IEssentialsRoom.cs" />
<Compile Include="Room\Interfaces.cs" />
<Compile Include="Room\iOccupancyStatusProvider.cs" />
<Compile Include="Routing\DummyRoutingInputsDevice.cs" />
@@ -319,6 +332,10 @@
<Compile Include="Feedbacks\BoolFeedbackPulseExtender.cs" />
<Compile Include="Routing\RoutingPortNames.cs" />
<Compile Include="Routing\TieLineConfig.cs" />
<Compile Include="Secrets\CrestronSecretsProvider.cs" />
<Compile Include="Secrets\Interfaces.cs" />
<Compile Include="Secrets\SecretsManager.cs" />
<Compile Include="Secrets\SecretsPropertiesConfig.cs" />
<Compile Include="Shades\Shade Interfaces.cs" />
<Compile Include="Shades\ShadeBase.cs" />
<Compile Include="Shades\ShadeController.cs" />

View File

@@ -358,14 +358,27 @@ namespace PepperDash.Essentials
try
{
var assy = loadedAssembly.Assembly;
var types = assy.GetTypes();
CType[] types = {};
try
{
types = assy.GetTypes();
}
catch (TypeLoadException e)
{
Debug.Console(0, Debug.ErrorLogLevel.Warning, "Unable to get types for assembly {0}: {1}",
loadedAssembly.Name, e.Message);
Debug.Console(2, e.StackTrace);
continue;
}
foreach (var type in types)
{
try
{
if (typeof(IPluginDeviceFactory).IsAssignableFrom(type))
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
{
var plugin = (IPluginDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
var plugin =
(IPluginDeviceFactory) Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
LoadCustomPlugin(plugin, loadedAssembly);
}
else
@@ -378,10 +391,15 @@ namespace PepperDash.Essentials
}
}
}
catch (NotSupportedException e)
{
//this happens for dlls that aren't PD dlls, like ports of Mono classes into S#. Swallowing.
}
catch (Exception e)
{
Debug.Console(2, "Load Plugin not found. {0}.{2} is not a plugin factory. Exception: {1}",
loadedAssembly.Name, e, type.Name);
loadedAssembly.Name, e.Message, type.Name);
continue;
}
@@ -389,7 +407,9 @@ namespace PepperDash.Essentials
}
catch (Exception e)
{
Debug.Console(2, "Error Loading Assembly: {0} Exception: {1} ", loadedAssembly.Name, e);
Debug.Console(0, Debug.ErrorLogLevel.Warning, "Error Loading assembly {0}: {1}",
loadedAssembly.Name, e.Message);
Debug.Console(2, "{0}", e.StackTrace);
continue;
}
}

View File

@@ -14,7 +14,7 @@ namespace PepperDash.Essentials.Core.Queues
protected readonly CrestronQueue<IQueueMessage> _queue;
protected readonly Thread _worker;
protected readonly CEvent _waitHandle = new CEvent();
private bool _delayEnabled;
private int _delayTime;
@@ -155,10 +155,10 @@ namespace PepperDash.Essentials.Core.Queues
if (programEvent != eProgramStatusEventType.Stopping)
return;
Dispose();
Dispose(true);
};
}
/// <summary>
/// Thread callback
/// </summary>
@@ -180,7 +180,7 @@ namespace PepperDash.Essentials.Core.Queues
{
try
{
Debug.Console(2, this, "Processing queue item: '{0}'", item.ToString());
//Debug.Console(2, this, "Processing queue item: '{0}'", item.ToString());
item.Dispatch();
if (_delayEnabled)
@@ -199,6 +199,12 @@ namespace PepperDash.Essentials.Core.Queues
public void Enqueue(IQueueMessage item)
{
if (Disposed)
{
Debug.Console(1, this, "I've been disposed so you can't enqueue any messages. Are you trying to dispatch a message while the program is stopping?");
return;
}
_queue.Enqueue(item);
_waitHandle.Set();
}
@@ -225,8 +231,13 @@ namespace PepperDash.Essentials.Core.Queues
if (disposing)
{
Enqueue(null);
_worker.Join();
Debug.Console(2, this, "Disposing...");
if (_queue != null && !_queue.Disposed)
{
_queue.Clear();
Enqueue(null);
}
_worker.Abort();
_waitHandle.Close();
}
@@ -235,7 +246,7 @@ namespace PepperDash.Essentials.Core.Queues
~GenericQueue()
{
Dispose(false);
Dispose(true);
}
/// <summary>
@@ -401,7 +412,7 @@ namespace PepperDash_Essentials_Core.Queues
if (programEvent != eProgramStatusEventType.Stopping)
return;
Dispose();
Dispose(true);
};
}
@@ -471,8 +482,13 @@ namespace PepperDash_Essentials_Core.Queues
if (disposing)
{
Enqueue(null);
_worker.Join();
Debug.Console(2, this, "Disposing...");
if (_queue != null && !_queue.Disposed)
{
_queue.Clear();
Enqueue(null);
}
_worker.Abort();
_waitHandle.Close();
}
@@ -481,7 +497,7 @@ namespace PepperDash_Essentials_Core.Queues
~GenericQueue()
{
Dispose(false);
Dispose(true);
}
/// <summary>

View File

@@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core
ScheduledEventGroup FeatureEventGroup;
public EssentialsRoomBase Room { get; private set; }
public IEssentialsRoom Room { get; private set; }
private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
@@ -84,7 +84,7 @@ namespace PepperDash.Essentials.Core
/// </summary>
void SetUpDevice()
{
Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as EssentialsRoomBase;
Room = DeviceManager.GetDeviceForKey(PropertiesConfig.RoomKey) as IEssentialsRoom;
if (Room != null)
{
@@ -169,10 +169,15 @@ namespace PepperDash.Essentials.Core
void FeatureEventGroup_UserGroupCallBack(ScheduledEvent SchEvent, ScheduledEventCommon.eCallbackReason type)
{
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "{0}:{1} @ {2}", SchEvent.Name, type, DateTime.Now);
if (type == ScheduledEventCommon.eCallbackReason.NormalExpiration)
{
SchEvent.Acknowledge();
if (SchEvent.Name == FeatureEnableEventName)
{
if (PropertiesConfig.EnableRoomOnWhenOccupied)
FeatureEnabled = true;
@@ -248,9 +253,8 @@ namespace PepperDash.Essentials.Core
schEvent = new ScheduledEvent(name, FeatureEventGroup);
// Set up its initial properties
if(!schEvent.Acknowledgeable)
schEvent.Acknowledgeable = true;
schEvent.Acknowledgeable = false;
if(!schEvent.Persistent)
schEvent.Persistent = true;
@@ -287,7 +291,7 @@ namespace PepperDash.Essentials.Core
Debug.Console(1, this, "Event '{0}' Absolute time set to {1}", schEvent.Name, schEvent.DateAndTime.ToString());
CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);
//CalculateAndSetAcknowledgeExpirationTimeout(schEvent, FeatureEnabledTime, FeatureDisabledTime);
schEvent.Recurrence.Weekly(eventRecurrennce);

View File

@@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class EssentialsRoomCombiner : EssentialsDevice, IEssentialsRoomCombiner
{
private EssentialsRoomCombinerPropertiesConfig _propertiesConfig;
private IRoomCombinationScenario _currentScenario;
private List<IEssentialsRoom> _rooms;
private bool isInAutoMode;
private CTimer _scenarioChangeDebounceTimer;
private int _scenarioChangeDebounceTimeSeconds = 10; // default to 10s
public EssentialsRoomCombiner(string key, EssentialsRoomCombinerPropertiesConfig props)
: base(key)
{
_propertiesConfig = props;
Partitions = new List<IPartitionController>();
RoomCombinationScenarios = new List<IRoomCombinationScenario>();
if (_propertiesConfig.ScenarioChangeDebounceTimeSeconds > 0)
{
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
}
IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
// default to auto mode
isInAutoMode = true;
if (_propertiesConfig.defaultToManualMode)
{
isInAutoMode = false;
}
IsInAutoModeFeedback.FireUpdate();
CreateScenarios();
AddPostActivationAction(() =>
{
SetupPartitionStateProviders();
SetRooms();
});
}
void CreateScenarios()
{
RoomCombinationScenarios = new List<IRoomCombinationScenario>();
foreach (var scenarioConfig in _propertiesConfig.Scenarios)
{
var scenario = new RoomCombinationScenario(scenarioConfig);
RoomCombinationScenarios.Add(scenario);
}
}
void SetRooms()
{
_rooms = new List<IEssentialsRoom>();
foreach (var roomKey in _propertiesConfig.RoomKeys)
{
var room = DeviceManager.GetDeviceForKey(roomKey) as IEssentialsRoom;
if (room != null)
{
_rooms.Add(room);
}
}
}
void SetupPartitionStateProviders()
{
foreach (var pConfig in _propertiesConfig.Partitions)
{
var sensor = DeviceManager.GetDeviceForKey(pConfig.DeviceKey) as IPartitionStateProvider;
var partition = new EssentialsPartitionController(pConfig.Key, pConfig.Name, sensor, _propertiesConfig.defaultToManualMode, pConfig.AdjacentRoomKeys);
partition.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
Partitions.Add(partition);
}
}
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
{
StartDebounceTimer();
}
void StartDebounceTimer()
{
var time = _scenarioChangeDebounceTimeSeconds * 1000;
if (_scenarioChangeDebounceTimer == null)
{
_scenarioChangeDebounceTimer = new CTimer((o) => DetermineRoomCombinationScenario(), time);
}
else
{
_scenarioChangeDebounceTimer.Reset(time);
}
}
/// <summary>
/// Determines the current room combination scenario based on the state of the partition sensors
/// </summary>
void DetermineRoomCombinationScenario()
{
if (_scenarioChangeDebounceTimer != null)
{
_scenarioChangeDebounceTimer.Dispose();
_scenarioChangeDebounceTimer = null;
}
var currentScenario = RoomCombinationScenarios.FirstOrDefault((s) =>
{
// iterate the partition states
foreach (var partitionState in s.PartitionStates)
{
// get the partition by key
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
if (partition != null && partitionState.PartitionPresent != partition.PartitionPresentFeedback.BoolValue)
{
// the partition can't be found or the state doesn't match
return false;
}
}
// if it hasn't returned false by now we have the matching scenario
return true;
});
if (currentScenario != null)
{
CurrentScenario = currentScenario;
}
}
#region IEssentialsRoomCombiner Members
public event EventHandler<EventArgs> RoomCombinationScenarioChanged;
public IRoomCombinationScenario CurrentScenario
{
get
{
return _currentScenario;
}
set
{
if (value != _currentScenario)
{
_currentScenario = value;
Debug.Console(1, this, "Current Scenario: {0}", _currentScenario.Name);
var handler = RoomCombinationScenarioChanged;
if (handler != null)
{
handler(this, new EventArgs());
}
}
}
}
public BoolFeedback IsInAutoModeFeedback { get; private set; }
public void SetAutoMode()
{
isInAutoMode = true;
IsInAutoModeFeedback.FireUpdate();
}
public void SetManualMode()
{
isInAutoMode = false;
IsInAutoModeFeedback.FireUpdate();
}
public void ToggleMode()
{
isInAutoMode = !isInAutoMode;
IsInAutoModeFeedback.FireUpdate();
}
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
public List<IPartitionController> Partitions { get; private set; }
public void TogglePartitionState(string partitionKey)
{
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
if (partition != null)
{
partition.ToggglePartitionState();
}
}
public void SetRoomCombinationScenario(string scenarioKey)
{
if (isInAutoMode)
{
Debug.Console(0, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
return;
}
// Get the scenario
var scenario = RoomCombinationScenarios.FirstOrDefault((s) => s.Key.Equals(scenarioKey));
// Set the parition states from the scenario manually
if (scenario != null)
{
foreach (var partitionState in scenario.PartitionStates)
{
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionState.PartitionKey));
if (partition != null)
{
if (partitionState.PartitionPresent)
{
partition.SetPartitionStatePresent();
}
else
{
partition.SetPartitionStateNotPresent();
}
}
}
}
}
#endregion
}
public class EssentialsRoomCombinerFactory : EssentialsDeviceFactory<EssentialsRoomCombiner>
{
public EssentialsRoomCombinerFactory()
{
TypeNames = new List<string> { "essentialsroomcombiner" };
}
public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
{
Debug.Console(1, "Factory Attempting to create new EssentialsRoomCombiner Device");
var props = dc.Properties.ToObject<EssentialsRoomCombinerPropertiesConfig>();
return new EssentialsRoomCombiner(dc.Key, props);
}
}
}

View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Config properties for an EssentialsRoomCombiner device
/// </summary>
public class EssentialsRoomCombinerPropertiesConfig
{
/// <summary>
/// The list of partitions that device the rooms
/// </summary>
[JsonProperty("partitions")]
public List<PartitionConfig> Partitions {get; set;}
/// <summary>
/// The list of combinations scenarios for the rooms
/// </summary>
[JsonProperty("scenarios")]
public List<RoomCombinationScenarioConfig> Scenarios { get; set; }
/// <summary>
/// The list of rooms keys that can be combined
/// </summary>
[JsonProperty("roomMap")]
public List<string> RoomKeys {get; set;}
/// <summary>
/// Set to true to default to manual mode
/// </summary>
[JsonProperty("defaultToManualMode")]
public bool defaultToManualMode { get; set; }
/// <summary>
/// The key of the scenario to default to at system startup if in manual mode
/// </summary>
[JsonProperty("defaultScenarioKey")]
public string defaultScenarioKey { get; set; }
[JsonProperty("scenarioChangeDebounceTimeSeconds")]
public int ScenarioChangeDebounceTimeSeconds { get; set; }
}
/// <summary>
/// Config properties for a partition that separates rooms
/// </summary>
public class PartitionConfig : IKeyName
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Key of the device that implements IPartitionStateProvider to provide the state of the partition
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
/// <summary>
/// Keys of the rooms that this partion would be located between
/// </summary>
[JsonProperty("adjacentRoomKeys")]
public List<string> AdjacentRoomKeys { get; set; }
}
/// <summary>
/// Config propeties for a room combination scenario
/// </summary>
public class RoomCombinationScenarioConfig : IKeyName
{
[JsonProperty("key")]
public string Key { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("partitionStates")]
public List<PartitionState> PartitionStates { get; set; }
[JsonProperty("uiMap")]
public Dictionary<string, string> UiMap { get; set; }
[JsonProperty("activationActions")]
public List<DeviceActionWrapper> ActivationActions { get; set; }
[JsonProperty("deactivationActions")]
public List<DeviceActionWrapper> DeactivationActions { get; set; }
}
/// <summary>
/// Config properties to represent the state of a partition sensor in a RoomCombinationScenario
/// </summary>
public class PartitionState
{
[JsonProperty("partitionKey")]
public string PartitionKey { get; set; }
[JsonProperty("partitionSensedState")]
public bool PartitionPresent { get; set; }
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Describes the functionality for an EssentailsRoomCombiner device
/// </summary>
public interface IEssentialsRoomCombiner : IKeyed
{
/// <summary>
/// Indicates that the room combination scenario has changed
/// </summary>
event EventHandler<EventArgs> RoomCombinationScenarioChanged;
/// <summary>
/// The current room combination scenario
/// </summary>
IRoomCombinationScenario CurrentScenario { get; }
/// <summary>
/// When true, indicates the current mode is auto mode
/// </summary>
BoolFeedback IsInAutoModeFeedback {get;}
/// <summary>
/// Sets auto mode
/// </summary>
void SetAutoMode();
/// <summary>
/// Sets manual mode
/// </summary>
void SetManualMode();
/// <summary>
/// Toggles the current mode between auto and manual
/// </summary>
void ToggleMode();
/// <summary>
/// The available room combinatino scenarios
/// </summary>
List<IRoomCombinationScenario> RoomCombinationScenarios { get; }
/// <summary>
/// The partition
/// </summary>
List<IPartitionController> Partitions { get; }
/// <summary>
/// Toggles the state of a manual partition sensor
/// </summary>
/// <param name="partitionKey"></param>
void TogglePartitionState(string partitionKey);
/// <summary>
/// Sets the room combination scenario (if in manual mode)
/// </summary>
/// <param name="scenarioKey"></param>
void SetRoomCombinationScenario(string scenarioKey);
}
public interface IRoomCombinationScenario : IKeyName
{
/// <summary>
/// When true, indicates that this room combination scenario is active
/// </summary>
BoolFeedback IsActiveFeedback { get; }
/// <summary>
/// Activates this room combination scenario
/// </summary>
void Activate();
/// <summary>
/// The state of the partitions that would activate this scenario
/// </summary>
List<PartitionState> PartitionStates { get; }
/// <summary>
/// The mapping of UIs by key to rooms by key
/// </summary>
Dictionary<string, string> UiMap { get; set; }
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a room combination scenario
/// </summary>
public class RoomCombinationScenario: IRoomCombinationScenario
{
private RoomCombinationScenarioConfig _config;
public string Key { get; set; }
public string Name { get; set; }
public List<PartitionState> PartitionStates { get; private set; }
public Dictionary<string, string> UiMap { get; set; }
private bool _isActive;
public BoolFeedback IsActiveFeedback { get; private set; }
List<DeviceActionWrapper> activationActions;
List<DeviceActionWrapper> deactivationActions;
public RoomCombinationScenario(RoomCombinationScenarioConfig config)
{
Key = config.Key;
Name = config.Name;
PartitionStates = config.PartitionStates;
UiMap = config.UiMap;
activationActions = config.ActivationActions;
deactivationActions = config.DeactivationActions;
_config = config;
IsActiveFeedback = new BoolFeedback(() => _isActive);
}
public void Activate()
{
if (activationActions != null)
{
foreach (var action in activationActions)
{
DeviceJsonApi.DoDeviceAction(action);
}
}
_isActive = true;
IsActiveFeedback.FireUpdate();
}
public void Deactivate()
{
if (deactivationActions != null)
{
foreach (var action in deactivationActions)
{
DeviceJsonApi.DoDeviceAction(action);
}
}
_isActive = false;
IsActiveFeedback.FireUpdate();
}
}
}

View File

@@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core
/// <summary>
///
/// </summary>
public abstract class EssentialsRoomBase : ReconfigurableDevice
public abstract class EssentialsRoomBase : ReconfigurableDevice, IEssentialsRoom
{
/// <summary>
///

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using PepperDash.Essentials.Room.Config;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Describes the basic functionality of an EssentialsRoom
/// </summary>
public interface IEssentialsRoom : IKeyName, IReconfigurableDevice, IRunDefaultPresentRoute
{
BoolFeedback OnFeedback { get; }
event EventHandler<EventArgs> RoomOccupancyIsSet;
BoolFeedback IsWarmingUpFeedback { get; }
BoolFeedback IsCoolingDownFeedback { get; }
IOccupancyStatusProvider RoomOccupancy { get; }
bool OccupancyStatusProviderIsRemote { get; }
bool IsMobileControlEnabled { get; }
IMobileControlRoomBridge MobileControlRoomBridge { get; }
string SourceListKey { get; }
SecondsCountdownTimer ShutdownPromptTimer { get; }
int ShutdownPromptSeconds { get; }
int ShutdownVacancySeconds { get; }
eShutdownType ShutdownType { get; }
EssentialsRoomEmergencyBase Emergency { get; }
Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }
string LogoUrlLightBkgnd { get; }
string LogoUrlDarkBkgnd { get; }
eVacancyMode VacancyMode { get; }
bool ZeroVolumeWhenSwtichingVolumeDevices { get; }
void StartShutdown(eShutdownType type);
void StartRoomVacancyTimer(eVacancyMode mode);
void Shutdown();
void SetRoomOccupancy(IOccupancyStatusProvider statusProvider, int timeoutMinutes);
void PowerOnToDefaultOrLastSource();
void SetDefaultLevels();
void RoomVacatedForTimeoutPeriod(object o);
}
}

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Core
{
/// <summary>
@@ -64,5 +65,7 @@ namespace PepperDash.Essentials.Core
{
bool RunDefaultCallRoute();
}
}

View File

@@ -123,25 +123,34 @@ namespace PepperDash.Essentials.Core
// No direct tie? Run back out on the inputs' attached devices...
// Only the ones that are routing devices
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
if (alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
foreach (var inputTieToTry in attachedMidpoints)
{
Debug.Console(2, destination, "Trying to find route on {0}", inputTieToTry.SourcePort.ParentDevice.Key);
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
Debug.Console(2, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
// Check if this previous device has already been walked
if (!(alreadyCheckedDevices != null && alreadyCheckedDevices.Contains(upstreamRoutingDevice)))
{
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
if (upstreamRoutingSuccess)
{
Debug.Console(2, destination, "Upstream device route found");
goodInputPort = inputTieToTry.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
{
Debug.Console(2, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
continue;
}
// haven't seen this device yet. Do it. Pass the output port to the next
// level to enable switching on success
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
alreadyCheckedDevices, signalType, cycle, routeTable);
if (upstreamRoutingSuccess)
{
Debug.Console(2, destination, "Upstream device route found");
goodInputPort = inputTieToTry.DestinationPort;
break; // Stop looping the inputs in this cycle
}
}
}
@@ -163,10 +172,6 @@ namespace PepperDash.Essentials.Core
//Debug.Console(2, destination, "Exiting cycle {0}", cycle);
return true;
}
if(alreadyCheckedDevices == null)
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
Debug.Console(2, destination, "No route found to {0}", source.Key);
return false;

View File

@@ -0,0 +1,97 @@
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronDataStore;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public class CrestronSecretsProvider : ISecretProvider
{
public string Key { get; set; }
//Added for reference
private static readonly bool SecureSupported;
public CrestronSecretsProvider(string key)
{
Key = key;
}
static CrestronSecretsProvider()
{
//Added for future encrypted reference
SecureSupported = CrestronSecureStorage.Supported;
CrestronDataStoreStatic.InitCrestronDataStore();
if (SecureSupported)
{
//doThingsFuture
}
}
/// <summary>
/// Set secret for item in the CrestronSecretsProvider
/// </summary>
/// <param name="key">Secret Key</param>
/// <param name="value">Secret Value</param>
public bool SetSecret(string key, object value)
{
var secret = value as string;
if (String.IsNullOrEmpty(secret))
{
Debug.Console(2, this, "Unable to set secret for {0}:{1} - value is empty.", Key, key);
return false;
}
var setErrorCode = CrestronDataStoreStatic.SetLocalStringValue(key, secret);
switch (setErrorCode)
{
case CrestronDataStore.CDS_ERROR.CDS_SUCCESS:
Debug.Console(1, this,"Secret Successfully Set for {0}:{1}", Key, key);
return true;
default:
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Unable to set secret for {0}:{1} - {2}", Key, key, setErrorCode.ToString());
return false;
}
}
/// <summary>
/// Retrieve secret for item in the CrestronSecretsProvider
/// </summary>
/// <param name="key">Secret Key</param>
/// <returns>ISecret Object containing key, provider, and value</returns>
public ISecret GetSecret(string key)
{
string mySecret;
var getErrorCode = CrestronDataStoreStatic.GetLocalStringValue(key, out mySecret);
switch (getErrorCode)
{
case CrestronDataStore.CDS_ERROR.CDS_SUCCESS:
Debug.Console(2, this, "Secret Successfully retrieved for {0}:{1}", Key, key);
return new CrestronSecret(key, mySecret, this);
default:
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Unable to retrieve secret for {0}:{1} - {2}",
Key, key, getErrorCode.ToString());
return null;
}
}
}
/// <summary>
/// Special container class for CrestronSecret provider
/// </summary>
public class CrestronSecret : ISecret
{
public ISecretProvider Provider { get; private set; }
public string Key { get; private set; }
public object Value { get; private set; }
public CrestronSecret(string key, string value, ISecretProvider provider)
{
Key = key;
Value = value;
Provider = provider;
}
}
}

View File

@@ -0,0 +1,24 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// All ISecrecretProvider classes must implement this interface.
/// </summary>
public interface ISecretProvider : IKeyed
{
bool SetSecret(string key, object value);
ISecret GetSecret(string key);
}
/// <summary>
/// interface for delivering secrets in Essentials.
/// </summary>
public interface ISecret
{
ISecretProvider Provider { get; }
string Key { get; }
object Value { get; }
}
}

View File

@@ -0,0 +1,281 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
public static class SecretsManager
{
public static Dictionary<string, ISecretProvider> Secrets { get; private set; }
/// <summary>
/// Initialize the SecretsManager
/// </summary>
public static void Initialize()
{
AddSecretProvider("default", new CrestronSecretsProvider("default"));
CrestronConsole.AddNewConsoleCommand(SetSecretProcess, "setsecret",
"Adds secrets to secret provider",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(UpdateSecretProcess, "updatesecret",
"Updates secrets in secret provider",
ConsoleAccessLevelEnum.AccessAdministrator);
CrestronConsole.AddNewConsoleCommand(DeleteSecretProcess, "deletesecret",
"Deletes secrets in secret provider",
ConsoleAccessLevelEnum.AccessAdministrator);
}
static SecretsManager()
{
Secrets = new Dictionary<string, ISecretProvider>();
}
/// <summary>
/// Get Secret Provider from dictionary by key
/// </summary>
/// <param name="key">Dictionary Key for provider</param>
/// <returns>ISecretProvider</returns>
public static ISecretProvider GetSecretProviderByKey(string key)
{
ISecretProvider secret;
Secrets.TryGetValue(key, out secret);
if (secret == null)
{
Debug.Console(1, "SecretsManager unable to retrieve SecretProvider with the key '{0}'", key);
}
return secret;
}
/// <summary>
/// Add secret provider to secrets dictionary
/// </summary>
/// <param name="key">Key of new entry</param>
/// <param name="provider">New Provider Entry</param>
public static void AddSecretProvider(string key, ISecretProvider provider)
{
if (!Secrets.ContainsKey(key))
{
Secrets.Add(key, provider);
Debug.Console(1, "Secrets provider '{0}' added to SecretsManager", key);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to add Provider '{0}' to Secrets. Provider with that key already exists", key );
}
/// <summary>
/// Add secret provider to secrets dictionary, with optional overwrite parameter
/// </summary>
/// <param name="key">Key of new entry</param>
/// <param name="provider">New provider entry</param>
/// <param name="overwrite">true to overwrite any existing providers in the dictionary</param>
public static void AddSecretProvider(string key, ISecretProvider provider, bool overwrite)
{
if (!Secrets.ContainsKey(key))
{
Secrets.Add(key, provider);
Debug.Console(1, "Secrets provider '{0}' added to SecretsManager", key);
}
if (overwrite)
{
Secrets.Add(key, provider);
Debug.Console(1, Debug.ErrorLogLevel.Notice, "Provider with the key '{0}' already exists in secrets. Overwriting with new secrets provider.", key);
}
Debug.Console(0, Debug.ErrorLogLevel.Notice, "Unable to add Provider '{0}' to Secrets. Provider with that key already exists", key);
}
private static void SetSecretProcess(string cmd)
{
string response;
var args = cmd.Split(' ');
if (args.Length == 0)
{
//some Instructional Text
response = "Adds secrets to secret provider. Format 'setsecret <provider> <secretKey> <secret>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length == 1 && args[0] == "?")
{
response = "Adds secrets to secret provider. Format 'setsecret <provider> <secretKey> <secret>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length < 3)
{
response = "Improper number of arguments";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var provider = GetSecretProviderByKey(args[0]);
if (provider == null)
{
//someFail
response = "Provider key invalid";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var key = args[1];
var secret = args[2];
if (provider.GetSecret(key) == null)
{
response = provider.SetSecret(key, secret)
? String.Format(
"Secret successfully set for {0}:{1}",
provider.Key, key)
: String.Format(
"Unable to set secret for {0}:{1}",
provider.Key, key);
CrestronConsole.ConsoleCommandResponse(response);
return;
}
response =
String.Format(
"Unable to set secret for {0}:{1} - Please use the 'UpdateSecret' command to modify it");
CrestronConsole.ConsoleCommandResponse(response);
}
private static void UpdateSecretProcess(string cmd)
{
string response;
var args = cmd.Split(' ');
if (args.Length == 0)
{
//some Instructional Text
response = "Updates secrets in secret provider. Format 'updatesecret <provider> <secretKey> <secret>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length == 1 && args[0] == "?")
{
response = "Updates secrets in secret provider. Format 'updatesecret <provider> <secretKey> <secret>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length < 3)
{
//someFail
response = "Improper number of arguments";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var provider = GetSecretProviderByKey(args[0]);
if (provider == null)
{
//someFail
response = "Provider key invalid";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var key = args[1];
var secret = args[2];
if (provider.GetSecret(key) != null)
{
response = provider.SetSecret(key, secret)
? String.Format(
"Secret successfully set for {0}:{1}",
provider.Key, key)
: String.Format(
"Unable to set secret for {0}:{1}",
provider.Key, key);
CrestronConsole.ConsoleCommandResponse(response);
return;
}
response =
String.Format(
"Unable to update secret for {0}:{1} - Please use the 'SetSecret' command to create a new secret");
CrestronConsole.ConsoleCommandResponse(response);
}
private static void DeleteSecretProcess(string cmd)
{
string response;
var args = cmd.Split(' ');
if (args.Length == 0)
{
//some Instructional Text
response = "Deletes secrets in secret provider. Format 'deletesecret <provider> <secretKey>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length == 1 && args[0] == "?")
{
response = "Deletes secrets in secret provider. Format 'deletesecret <provider> <secretKey>";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
if (args.Length < 2)
{
//someFail
response = "Improper number of arguments";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var provider = GetSecretProviderByKey(args[0]);
if (provider == null)
{
//someFail
response = "Provider key invalid";
CrestronConsole.ConsoleCommandResponse(response);
return;
}
var key = args[1];
provider.SetSecret(key, "");
response = provider.SetSecret(key, "")
? String.Format(
"Secret successfully deleted for {0}:{1}",
provider.Key, key)
: String.Format(
"Unable to delete secret for {0}:{1}",
provider.Key, key);
CrestronConsole.ConsoleCommandResponse(response);
return;
}
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Provide a way to easily deserialize into a secret object from config
/// </summary>
public class SecretsPropertiesConfig
{
[JsonProperty("provider")]
public string Provider { get; set; }
[JsonProperty("key")]
public string Key { get; set; }
}
}

View File

@@ -16,7 +16,8 @@ using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.DM.Config;
namespace PepperDash.Essentials.DM {
namespace PepperDash.Essentials.DM
{
/// <summary>
/// Builds a controller for basic DM-RMCs with Com and IR ports and no control functions
///
@@ -75,8 +76,10 @@ namespace PepperDash.Essentials.DM {
/// Factory method to create a new chassis controller from config data. Limited to 8x8 right now
/// </summary>
public static DmBladeChassisController GetDmChassisController(string key, string name,
string type, DMChassisPropertiesConfig properties) {
try {
string type, DMChassisPropertiesConfig properties)
{
try
{
type = type.ToLower();
uint ipid = properties.Control.IpIdInt;
@@ -85,7 +88,8 @@ namespace PepperDash.Essentials.DM {
else if (type == "dmmd128x128") { chassis = new DmMd128x128(ipid, Global.ControlSystem); }
if (chassis == null) {
if (chassis == null)
{
return null;
}
@@ -93,11 +97,13 @@ namespace PepperDash.Essentials.DM {
// add the cards and port names
foreach (var kvp in properties.InputSlots)
controller.AddInputBlade(kvp.Value, kvp.Key);
foreach (var kvp in properties.OutputSlots) {
foreach (var kvp in properties.OutputSlots)
{
controller.AddOutputBlade(kvp.Value, kvp.Key);
}
foreach (var kvp in properties.VolumeControls) {
foreach (var kvp in properties.VolumeControls)
{
// get the card
// check it for an audio-compatible type
// make a something-something that will make it work
@@ -123,7 +129,8 @@ namespace PepperDash.Essentials.DM {
controller.PropertiesConfig = properties;
return controller;
}
catch (System.Exception e) {
catch (System.Exception e)
{
Debug.Console(0, "Error creating DM chassis:\r{0}", e);
}
return null;
@@ -137,7 +144,8 @@ namespace PepperDash.Essentials.DM {
/// <param name="name"></param>
/// <param name="chassis"></param>
public DmBladeChassisController(string key, string name, BladeSwitch chassis)
: base(key, name, chassis) {
: base(key, name, chassis)
{
Chassis = chassis;
InputPorts = new RoutingPortCollection<RoutingInputPort>();
OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
@@ -161,68 +169,87 @@ namespace PepperDash.Essentials.DM {
InputCardHdcpCapabilityFeedbacks = new Dictionary<uint, IntFeedback>();
InputCardHdcpCapabilityTypes = new Dictionary<uint, eHdcpCapabilityType>();
for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) {
for (uint x = 1; x <= Chassis.NumberOfOutputs; x++)
{
var tempX = x;
if (Chassis.Outputs[tempX] != null) {
VideoOutputFeedbacks[tempX] = new IntFeedback(() => {
if (Chassis.Outputs[tempX] != null)
{
VideoOutputFeedbacks[tempX] = new IntFeedback(() =>
{
if (Chassis.Outputs[tempX].VideoOutFeedback != null) { return (ushort)Chassis.Outputs[tempX].VideoOutFeedback.Number; }
else { return 0; };
});
OutputNameFeedbacks[tempX] = new StringFeedback(() => {
if (Chassis.Outputs[tempX].NameFeedback != null) {
OutputNameFeedbacks[tempX] = new StringFeedback(() =>
{
if (Chassis.Outputs[tempX].NameFeedback != null)
{
return Chassis.Outputs[tempX].NameFeedback.StringValue;
}
else {
else
{
return "";
}
});
OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() => {
if (Chassis.Outputs[tempX].VideoOutFeedback != null) {
OutputVideoRouteNameFeedbacks[tempX] = new StringFeedback(() =>
{
if (Chassis.Outputs[tempX].VideoOutFeedback != null)
{
return Chassis.Outputs[tempX].VideoOutFeedback.NameFeedback.StringValue;
}
else {
else
{
return "";
}
});
OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => {
OutputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() =>
{
//if (Chassis.Outputs[tempX].Endpoint != null)
// return Chassis.Outputs[tempX].Endpoint.IsOnline;
//else
return Chassis.Outputs[tempX].EndpointOnlineFeedback;
return Chassis.Outputs[tempX].EndpointOnlineFeedback;
});
}
if (Chassis.Inputs[tempX] != null) {
UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() => {
if (Chassis.Inputs[tempX] != null)
{
UsbInputRoutedToFeebacks[tempX] = new IntFeedback(() =>
{
if (Chassis.Inputs[tempX].USBRoutedToFeedback != null) { return (ushort)Chassis.Inputs[tempX].USBRoutedToFeedback.Number; }
else { return 0; };
});
VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() => {
VideoInputSyncFeedbacks[tempX] = new BoolFeedback(() =>
{
if (Chassis.Inputs[tempX].VideoDetectedFeedback != null)
return Chassis.Inputs[tempX].VideoDetectedFeedback.BoolValue;
else
return false;
});
InputNameFeedbacks[tempX] = new StringFeedback(() => {
if (Chassis.Inputs[tempX].NameFeedback != null) {
InputNameFeedbacks[tempX] = new StringFeedback(() =>
{
if (Chassis.Inputs[tempX].NameFeedback != null)
{
return Chassis.Inputs[tempX].NameFeedback.StringValue;
}
else {
else
{
return "";
}
});
InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() => {
InputEndpointOnlineFeedbacks[tempX] = new BoolFeedback(() =>
{
return Chassis.Inputs[tempX].EndpointOnlineFeedback;
});
InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() => {
InputCardHdcpCapabilityFeedbacks[tempX] = new IntFeedback(() =>
{
var inputCard = Chassis.Inputs[tempX];
if (inputCard.Card is DmHdmi4kInputBladeCard) {
if (inputCard.Card is DmHdmi4kInputBladeCard)
{
InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support;
if ((inputCard.Card as DmHdmi4kInputBladeCard).Hdmi4kInput.HdcpSupportOnFeedback.BoolValue)
@@ -231,7 +258,8 @@ namespace PepperDash.Essentials.DM {
return 0;
}
if (inputCard.Card is DmC4kInputBladeCard) {
if (inputCard.Card is DmC4kInputBladeCard)
{
InputCardHdcpCapabilityTypes[tempX] = eHdcpCapabilityType.Hdcp2_2Support;
if ((inputCard.Card as DmC4kInputBladeCard).DmInput.HdcpCapabilityFeedback.Equals(eHdcpCapabilityType.HdcpSupportOff))
@@ -252,45 +280,56 @@ namespace PepperDash.Essentials.DM {
/// </summary>
/// <param name="type"></param>
/// <param name="number"></param>
public void AddInputBlade(string type, uint number) {
public void AddInputBlade(string type, uint number)
{
Debug.Console(2, this, "Adding input blade '{0}', slot {1}", type, number);
type = type.ToLower();
if (type == "dmb4kihd") {
if (type == "dmb4kihd")
{
var inputBlade = new Dmb4kIHd(number, this.Chassis);
foreach (var item in inputBlade.Inputs) {
foreach (var item in inputBlade.Inputs)
{
var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput;
var cecPort = card as ICec;
AddHdmiInBladePorts(item.Number, cecPort);
}
}
else if (type == "dmb4kihddnt") {
else if (type == "dmb4kihddnt")
{
var inputBlade = new Dmb4kIHd(number, this.Chassis);
foreach (var item in inputBlade.Inputs) {
foreach (var item in inputBlade.Inputs)
{
var card = (item.Card as DmHdmi4kInputBladeCard).Hdmi4kInput;
var cecPort = card as ICec;
AddHdmiInBladePorts(item.Number, cecPort);
}
}
else if (type == "dmb4kic") {
else if (type == "dmb4kic")
{
var inputBlade = new Dmb4kIC(number, this.Chassis);
foreach (var item in inputBlade.Inputs) {
foreach (var item in inputBlade.Inputs)
{
AddDmInBladePorts(item.Number);
}
}
else if (type == "dmbis") {
else if (type == "dmbis")
{
var inputBlade = new DmbIS(number, this.Chassis);
foreach (var item in inputBlade.Inputs) {
foreach (var item in inputBlade.Inputs)
{
AddDmInMmFiberPorts(item.Number);
}
}
else if (type == "dmbis2") {
else if (type == "dmbis2")
{
var inputBlade = new DmbIS2(number, this.Chassis);
foreach (var item in inputBlade.Inputs) {
foreach (var item in inputBlade.Inputs)
{
AddDmInSmFiberPorts(item.Number);
}
}
@@ -304,22 +343,26 @@ namespace PepperDash.Essentials.DM {
{
var newEvent = NumericSwitchChange;
if (newEvent != null) newEvent(this, e);
}
}
void AddHdmiInBladePorts(uint number, ICec cecPort) {
void AddHdmiInBladePorts(uint number, ICec cecPort)
{
AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, cecPort);
}
void AddDmInBladePorts(uint number) {
void AddDmInBladePorts(uint number)
{
AddInputPortWithDebug(number, "dmCIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat);
}
void AddDmInMmFiberPorts(uint number) {
void AddDmInMmFiberPorts(uint number)
{
AddInputPortWithDebug(number, "dmMmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber);
}
void AddDmInSmFiberPorts(uint number) {
void AddDmInSmFiberPorts(uint number)
{
AddInputPortWithDebug(number, "dmSmIn", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber);
}
@@ -328,64 +371,81 @@ namespace PepperDash.Essentials.DM {
/// </summary>
/// <param name="type"></param>
/// <param name="number"></param>
public void AddOutputBlade(string type, uint number) {
public void AddOutputBlade(string type, uint number)
{
type = type.ToLower();
Debug.Console(2, this, "Adding output blade '{0}', slot {1}", type, number);
if (type == "dmb4kohd") {
if (type == "dmb4kohd")
{
var outputBlade = new Dmb4KOHD(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddHdmiOutBladePorts(item.Number);
}
}
else if (type == "dmb4kohddnt") {
else if (type == "dmb4kohddnt")
{
var outputBlade = new Dmb4KOHD(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddHdmiOutBladePorts(item.Number);
}
}
else if (type == "dmb4koc") {
else if (type == "dmb4koc")
{
var outputBlade = new Dmb4KOC(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddDmOutBladePorts(item.Number);
}
}
else if (type == "dmb4koc") {
else if (type == "dmb4koc")
{
var outputBlade = new Dmb4KOC(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddDmOutBladePorts(item.Number);
}
}
else if (type == "dmbos") {
else if (type == "dmbos")
{
var outputBlade = new DmbOS(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddDmOutMmFiberBladePorts(item.Number);
}
}
else if (type == "dmbos2") {
else if (type == "dmbos2")
{
var outputBlade = new DmbOS2(number, Chassis);
foreach (var item in outputBlade.Outputs) {
foreach (var item in outputBlade.Outputs)
{
AddDmOutSmFiberBladePorts(item.Number);
}
}
}
void AddHdmiOutBladePorts(uint number) {
AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("hdmiOut{0}", number) , eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]);
void AddHdmiOutBladePorts(uint number)
{
AddOutputPortWithDebug(number, "hdmiOut", eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, Chassis.Outputs[number]);
}
void AddDmOutBladePorts(uint number) {
AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]);
void AddDmOutBladePorts(uint number)
{
AddOutputPortWithDebug(number, "dmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, Chassis.Outputs[number]);
}
void AddDmOutMmFiberBladePorts(uint number) {
AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]);
void AddDmOutMmFiberBladePorts(uint number)
{
AddOutputPortWithDebug(number, "dmMmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, Chassis.Outputs[number]);
}
void AddDmOutSmFiberBladePorts(uint number) {
AddOutputPortWithDebug(String.Format("outputBlade{0}", (number / 8 > 0 ? 1 : number / 8)), String.Format("dmOut{0}", number), eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]);
void AddDmOutSmFiberBladePorts(uint number)
{
AddOutputPortWithDebug(number, "dmSmOut", eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, Chassis.Outputs[number]);
}
@@ -417,23 +477,44 @@ namespace PepperDash.Essentials.DM {
}
/// <summary>
/// Adds OutputPort
/// </summary>
void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) {
/*void AddOutputPortWithDebug(string cardName, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector) {
var portKey = string.Format("{0}--{1}", cardName, portName);
Debug.Console(2, this, "Adding output port '{0}'", portKey);
OutputPorts.Add(new RoutingOutputPort(portKey, sigType, portType, selector, this)
{
FeedbackMatchObject = Chassis.Outputs[(uint)selector]
});
}*/
/// <summary>
/// Adds OutputPort
/// </summary>
void AddOutputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, object selector)
{
try
{
var portKey = string.Format("outputCard{0}--{1}", cardNum, portName);
Debug.Console(2, this, "Adding output port '{0}'", portKey);
var outputPort = new RoutingOutputPort(portKey, sigType, portType, selector, this)
{
FeedbackMatchObject = Chassis.Outputs[cardNum]
};
OutputPorts.Add(outputPort);
}
catch (Exception ex)
{
Debug.Console(0, this, "Exception : {0}", ex);
}
}
/// <summary>
///
/// </summary>
void AddVolumeControl(uint number, Audio.Output audio) {
void AddVolumeControl(uint number, Audio.Output audio)
{
VolumeControls.Add(number, new DmCardAudioOutputController(audio));
}
@@ -443,35 +524,43 @@ namespace PepperDash.Essentials.DM {
//}
void Chassis_DMInputChange(Switch device, DMInputEventArgs args) {
void Chassis_DMInputChange(Switch device, DMInputEventArgs args)
{
switch (args.EventId) {
case DMInputEventIds.EndpointOnlineEventId: {
switch (args.EventId)
{
case DMInputEventIds.EndpointOnlineEventId:
{
Debug.Console(2, this, "DM Input EndpointOnlineEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
break;
}
case DMInputEventIds.OnlineFeedbackEventId: {
case DMInputEventIds.OnlineFeedbackEventId:
{
Debug.Console(2, this, "DM Input OnlineFeedbackEventId for input: {0}. State: {1}", args.Number, device.Inputs[args.Number].EndpointOnlineFeedback);
InputEndpointOnlineFeedbacks[args.Number].FireUpdate();
break;
}
case DMInputEventIds.VideoDetectedEventId: {
case DMInputEventIds.VideoDetectedEventId:
{
Debug.Console(2, this, "DM Input {0} VideoDetectedEventId", args.Number);
VideoInputSyncFeedbacks[args.Number].FireUpdate();
break;
}
case DMInputEventIds.InputNameEventId: {
case DMInputEventIds.InputNameEventId:
{
Debug.Console(2, this, "DM Input {0} NameFeedbackEventId", args.Number);
InputNameFeedbacks[args.Number].FireUpdate();
break;
}
case DMInputEventIds.HdcpCapabilityFeedbackEventId: {
case DMInputEventIds.HdcpCapabilityFeedbackEventId:
{
Debug.Console(2, this, "DM Input {0} HdcpCapabilityFeedbackEventId", args.Number);
InputCardHdcpCapabilityFeedbacks[args.Number].FireUpdate();
break;
}
default: {
default:
{
Debug.Console(2, this, "DMInputChange fired for Input {0} with Unhandled EventId: {1}", args.Number, args.EventId);
break;
}
@@ -487,74 +576,74 @@ namespace PepperDash.Essentials.DM {
switch (args.EventId)
{
case DMOutputEventIds.VolumeEventId:
{
if (VolumeControls.ContainsKey(output))
{
VolumeControls[args.Number].VolumeEventFromChassis();
if (VolumeControls.ContainsKey(output))
{
VolumeControls[args.Number].VolumeEventFromChassis();
}
break;
}
break;
}
case DMOutputEventIds.EndpointOnlineEventId:
{
Debug.Console(2, this,
"Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}",
args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
if (Chassis.Outputs[output].Endpoint != null)
{
Debug.Console(2, this,
"Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}",
args.Number, Chassis.Outputs[output].Endpoint.IsOnline);
"Output {0} DMOutputEventIds.EndpointOnlineEventId fired. EndpointOnlineFeedback State: {1}",
args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
if (Chassis.Outputs[output].Endpoint != null)
Debug.Console(2, this,
"Output {0} DMOutputEventIds.EndpointOnlineEventId fired. Endpoint.IsOnline State: {1}",
args.Number, Chassis.Outputs[output].Endpoint.IsOnline);
OutputEndpointOnlineFeedbacks[output].FireUpdate();
break;
}
OutputEndpointOnlineFeedbacks[output].FireUpdate();
break;
}
case DMOutputEventIds.OnlineFeedbackEventId:
{
Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}",
args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
OutputEndpointOnlineFeedbacks[output].FireUpdate();
break;
}
{
Debug.Console(2, this, "Output {0} DMInputEventIds.OnlineFeedbackEventId fired. State: {1}",
args.Number, Chassis.Outputs[output].EndpointOnlineFeedback);
OutputEndpointOnlineFeedbacks[output].FireUpdate();
break;
}
case DMOutputEventIds.VideoOutEventId:
{
var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number;
Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name,
inputNumber, output);
if (VideoOutputFeedbacks.ContainsKey(output))
{
var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback);
var localOutputPort =
OutputPorts.FirstOrDefault(p => (DMOutput) p.FeedbackMatchObject == Chassis.Outputs[output]);
var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis.Outputs[output].VideoOutFeedback.Number;
Debug.Console(2, this, "DMSwitchAudioVideo:{0} Routed Input:{1} Output:{2}'", this.Name,
inputNumber, output);
if (VideoOutputFeedbacks.ContainsKey(output))
{
var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == Chassis.Outputs[output].VideoOutFeedback);
var localOutputPort =
OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == Chassis.Outputs[output]);
VideoOutputFeedbacks[output].FireUpdate();
OnSwitchChange(new RoutingNumericEventArgs(output,
inputNumber,
localOutputPort,
localInputPort,
eRoutingSignalType.AudioVideo));
VideoOutputFeedbacks[output].FireUpdate();
OnSwitchChange(new RoutingNumericEventArgs(output,
inputNumber,
localOutputPort,
localInputPort,
eRoutingSignalType.AudioVideo));
}
if (OutputVideoRouteNameFeedbacks.ContainsKey(output))
{
OutputVideoRouteNameFeedbacks[output].FireUpdate();
}
break;
}
if (OutputVideoRouteNameFeedbacks.ContainsKey(output))
{
OutputVideoRouteNameFeedbacks[output].FireUpdate();
}
break;
}
case DMOutputEventIds.OutputNameEventId:
{
Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output);
OutputNameFeedbacks[output].FireUpdate();
break;
}
{
Debug.Console(2, this, "DM Output {0} NameFeedbackEventId", output);
OutputNameFeedbacks[output].FireUpdate();
break;
}
default:
{
Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}",
args.Number, args.EventId);
break;
}
{
Debug.Console(2, this, "DMOutputChange fired for Output {0} with Unhandled EventId: {1}",
args.Number, args.EventId);
break;
}
}
}
@@ -564,7 +653,8 @@ namespace PepperDash.Essentials.DM {
///
/// </summary>
/// <param name="pnt"></param>
void StartOffTimer(PortNumberType pnt) {
void StartOffTimer(PortNumberType pnt)
{
if (RouteOffTimers.ContainsKey(pnt))
return;
RouteOffTimers[pnt] = new CTimer(o => ExecuteSwitch(null, pnt.Selector, pnt.Type), RouteOffTime);
@@ -572,8 +662,10 @@ namespace PepperDash.Essentials.DM {
// Send out sigs when coming online
void IsOnline_OutputChange(object sender, EventArgs e) {
if (IsOnline.BoolValue) {
void IsOnline_OutputChange(object sender, EventArgs e)
{
if (IsOnline.BoolValue)
{
Chassis.EnableUSBBreakaway.BoolValue = true;
if (InputNames != null)
@@ -587,12 +679,13 @@ namespace PepperDash.Essentials.DM {
#region IRouting Members
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType) {
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType sigType)
{
Debug.Console(2, this, "Making an awesome DM route from {0} to {1} {2}", inputSelector, outputSelector, sigType);
var input = inputSelector as DMInput; // Cast can sometimes fail
var output = outputSelector as DMOutput;
if (output == null)
{
@@ -605,11 +698,14 @@ namespace PepperDash.Essentials.DM {
// Check to see if there's an off timer waiting on this and if so, cancel
var key = new PortNumberType(output, sigType);
if (input == null) {
if (input == null)
{
StartOffTimer(key);
}
else {
if (RouteOffTimers.ContainsKey(key)) {
else
{
if (RouteOffTimers.ContainsKey(key))
{
Debug.Console(2, this, "{0} cancelling route off due to new source", output);
RouteOffTimers[key].Stop();
RouteOffTimers.Remove(key);
@@ -671,7 +767,7 @@ namespace PepperDash.Essentials.DM {
var ioSlotJoin = ioSlot - 1;
// Control
trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteSwitch(o, ioSlot, eRoutingSignalType.Video));
trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin, o => ExecuteNumericSwitch(o, (ushort)ioSlot, eRoutingSignalType.Video));
if (TxDictionary.ContainsKey(ioSlot))
{

View File

@@ -228,9 +228,9 @@ namespace PepperDash.Essentials.DM
// Control
trilist.SetUShortSigAction(joinMap.OutputVideo.JoinNumber + ioSlotJoin,
o => ExecuteSwitch(o, ioSlot, eRoutingSignalType.Video));
o => ExecuteNumericSwitch(o, (ushort) ioSlot, eRoutingSignalType.Video));
trilist.SetUShortSigAction(joinMap.OutputAudio.JoinNumber + ioSlotJoin,
o => ExecuteSwitch(o, ioSlot, eRoutingSignalType.Audio));
o => ExecuteNumericSwitch(o, (ushort) ioSlot, eRoutingSignalType.Audio));
trilist.SetStringSigAction(joinMap.OutputNames.JoinNumber + ioSlotJoin, s =>
{

View File

@@ -1,40 +1,40 @@
using Crestron.SimplSharpPro;
using System;
using System.Linq;
//using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
namespace PepperDash.Essentials.DM
{
using eVst = eX02VideoSourceType;
using Crestron.SimplSharpPro;
using System;
using System.Linq;
//using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.DM;
using Crestron.SimplSharpPro.DM.Endpoints;
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
namespace PepperDash.Essentials.DM
{
using eVst = eX02VideoSourceType;
using eAst = eX02AudioSourceType;
public class DmTx4kz202CController : DmTxControllerBase, ITxRoutingWithFeedback,
IIROutputPorts, IComPorts
{
public DmTx4kz202C Tx { get; private set; }
public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; }
public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; }
public RoutingOutputPort DmOut { get; private set; }
public RoutingOutputPort HdmiLoopOut { get; private set; }
public override StringFeedback ActiveVideoInputFeedback { get; protected set; }
public IntFeedback VideoSourceNumericFeedback { get; protected set; }
public IntFeedback AudioSourceNumericFeedback { get; protected set; }
public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; }
public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; }
public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; }
public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; }
//public override IntFeedback HdcpSupportAllFeedback { get; protected set; }
public class DmTx4kz202CController : DmTxControllerBase, ITxRoutingWithFeedback,
IIROutputPorts, IComPorts
{
public DmTx4kz202C Tx { get; private set; }
public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; }
public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; }
public RoutingOutputPort DmOut { get; private set; }
public RoutingOutputPort HdmiLoopOut { get; private set; }
public override StringFeedback ActiveVideoInputFeedback { get; protected set; }
public IntFeedback VideoSourceNumericFeedback { get; protected set; }
public IntFeedback AudioSourceNumericFeedback { get; protected set; }
public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; }
public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; }
public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; }
public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; }
//public override IntFeedback HdcpSupportAllFeedback { get; protected set; }
//public override ushort HdcpSupportCapability { get; protected set; }
//IroutingNumericEvent
public event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
@@ -47,48 +47,48 @@ namespace PepperDash.Essentials.DM
{
var newEvent = NumericSwitchChange;
if (newEvent != null) newEvent(this, e);
}
/// <summary>
/// Helps get the "real" inputs, including when in Auto
/// </summary>
public eX02VideoSourceType ActualActiveVideoInput
{
get
{
if (Tx.VideoSourceFeedback != eVst.Auto)
return Tx.VideoSourceFeedback;
if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue)
return eVst.Hdmi1;
return Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue ? eVst.Hdmi2 : eVst.AllDisabled;
}
}
public RoutingPortCollection<RoutingInputPort> InputPorts
{
get
{
return new RoutingPortCollection<RoutingInputPort>
{
HdmiIn1,
HdmiIn2,
AnyVideoInput
};
}
}
public RoutingPortCollection<RoutingOutputPort> OutputPorts
{
get
{
return new RoutingPortCollection<RoutingOutputPort> { DmOut, HdmiLoopOut };
}
}
public DmTx4kz202CController(string key, string name, DmTx4kz202C tx)
: base(key, name, tx)
{
}
/// <summary>
/// Helps get the "real" inputs, including when in Auto
/// </summary>
public eX02VideoSourceType ActualActiveVideoInput
{
get
{
if (Tx.VideoSourceFeedback != eVst.Auto)
return Tx.VideoSourceFeedback;
if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue)
return eVst.Hdmi1;
return Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue ? eVst.Hdmi2 : eVst.AllDisabled;
}
}
public RoutingPortCollection<RoutingInputPort> InputPorts
{
get
{
return new RoutingPortCollection<RoutingInputPort>
{
HdmiIn1,
HdmiIn2,
AnyVideoInput
};
}
}
public RoutingPortCollection<RoutingOutputPort> OutputPorts
{
get
{
return new RoutingPortCollection<RoutingOutputPort> { DmOut, HdmiLoopOut };
}
}
public DmTx4kz202CController(string key, string name, DmTx4kz202C tx)
: base(key, name, tx)
{
Tx = tx;
HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1,
@@ -104,208 +104,208 @@ namespace PepperDash.Essentials.DM
FeedbackMatchObject = eVst.Hdmi2
};
ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput",
() => ActualActiveVideoInput.ToString());
Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent;
Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent;
Tx.BaseEvent += Tx_BaseEvent;
Tx.OnlineStatusChange += Tx_OnlineStatusChange;
VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback);
//Return VideoSourceFeedback here as DM-TX-4KZ-202-C does not support audio breakaway
AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback);
HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback);
HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback);
HdcpStateFeedback =
new IntFeedback(
() =>
tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback
? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback
: (int)tx.HdmiInputs[2].HdcpCapabilityFeedback);
HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support;
Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue);
Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue);
var combinedFuncs = new VideoStatusFuncsWrapper
{
HdcpActiveFeedbackFunc = () =>
(ActualActiveVideoInput == eVst.Hdmi1
&& tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue)
|| (ActualActiveVideoInput == eVst.Hdmi2
&& tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue),
HdcpStateFeedbackFunc = () =>
{
if (ActualActiveVideoInput == eVst.Hdmi1)
return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString();
if (ActualActiveVideoInput == eVst.Hdmi2)
return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString();
return "";
},
VideoResolutionFeedbackFunc = () =>
{
if (ActualActiveVideoInput == eVst.Hdmi1)
return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString();
if (ActualActiveVideoInput == eVst.Hdmi2)
return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString();
return "";
},
VideoSyncFeedbackFunc = () =>
(ActualActiveVideoInput == eVst.Hdmi1
&& tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue)
|| (ActualActiveVideoInput == eVst.Hdmi2
&& tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue)
};
AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn,
eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs);
DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.DmCat, null, this);
HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback,
AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback,
AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback,
AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback,
Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback);
// Set Ports for CEC
HdmiIn1.Port = Tx.HdmiInputs[1];
HdmiIn2.Port = Tx.HdmiInputs[2];
HdmiLoopOut.Port = Tx.HdmiOutput;
DmOut.Port = Tx.DmOutput;
}
public override bool CustomActivate()
{
// Link up all of these damned events to the various RoutingPorts via a helper handler
Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId);
Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId);
Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId);
Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId);
// Base does register and sets up comm monitoring.
return base.CustomActivate();
}
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
var joinMap = GetDmTxJoinMap(joinStart, joinMapKey);
if (Hdmi1VideoSyncFeedback != null)
{
Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]);
}
if (Hdmi2VideoSyncFeedback != null)
{
Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]);
}
LinkDmTxToApi(this, trilist, joinMap, bridge);
}
public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type)
{
Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input);
switch (type)
{
case eRoutingSignalType.Video:
switch (input)
{
case 0:
{
ExecuteSwitch(eVst.Auto, null, type);
break;
}
case 1:
{
ExecuteSwitch(HdmiIn1.Selector, null, type);
break;
}
case 2:
{
ExecuteSwitch(HdmiIn2.Selector, null, type);
break;
}
case 3:
{
ExecuteSwitch(eVst.AllDisabled, null, type);
break;
}
}
break;
case eRoutingSignalType.Audio:
switch (input)
{
case 0:
{
ExecuteSwitch(eAst.Auto, null, type);
break;
}
case 1:
{
ExecuteSwitch(eAst.Hdmi1, null, type);
break;
}
case 2:
{
ExecuteSwitch(eAst.Hdmi2, null, type);
break;
}
case 3:
{
ExecuteSwitch(eAst.AllDisabled, null, type);
break;
}
}
break;
}
}
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
{
if ((signalType & eRoutingSignalType.Video) == eRoutingSignalType.Video)
Tx.VideoSource = (eVst)inputSelector;
if(((signalType & eRoutingSignalType.Audio) == eRoutingSignalType.Audio))
Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key);
}
void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args)
{
Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString());
switch (args.EventId)
{
case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId:
case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId:
case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId:
if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate();
if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate();
break;
case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId:
if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate();
if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate();
break;
}
ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput",
() => ActualActiveVideoInput.ToString());
Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent;
Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent;
Tx.BaseEvent += Tx_BaseEvent;
Tx.OnlineStatusChange += Tx_OnlineStatusChange;
VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback);
//Return VideoSourceFeedback here as DM-TX-4KZ-202-C does not support audio breakaway
AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback);
HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback);
HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback);
HdcpStateFeedback =
new IntFeedback(
() =>
tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback
? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback
: (int)tx.HdmiInputs[2].HdcpCapabilityFeedback);
HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support;
Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue);
Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue);
var combinedFuncs = new VideoStatusFuncsWrapper
{
HdcpActiveFeedbackFunc = () =>
(ActualActiveVideoInput == eVst.Hdmi1
&& tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue)
|| (ActualActiveVideoInput == eVst.Hdmi2
&& tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue),
HdcpStateFeedbackFunc = () =>
{
if (ActualActiveVideoInput == eVst.Hdmi1)
return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString();
if (ActualActiveVideoInput == eVst.Hdmi2)
return tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString();
return "";
},
VideoResolutionFeedbackFunc = () =>
{
if (ActualActiveVideoInput == eVst.Hdmi1)
return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString();
if (ActualActiveVideoInput == eVst.Hdmi2)
return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString();
return "";
},
VideoSyncFeedbackFunc = () =>
(ActualActiveVideoInput == eVst.Hdmi1
&& tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue)
|| (ActualActiveVideoInput == eVst.Hdmi2
&& tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue)
};
AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn,
eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs);
DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.DmCat, null, this);
HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, null, this);
AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback,
AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback,
AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback,
AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback,
Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback);
// Set Ports for CEC
HdmiIn1.Port = Tx.HdmiInputs[1];
HdmiIn2.Port = Tx.HdmiInputs[2];
HdmiLoopOut.Port = Tx.HdmiOutput;
DmOut.Port = Tx.DmOutput;
}
public override bool CustomActivate()
{
// Link up all of these damned events to the various RoutingPorts via a helper handler
Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId);
Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId);
Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId);
Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId);
// Base does register and sets up comm monitoring.
return base.CustomActivate();
}
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
var joinMap = GetDmTxJoinMap(joinStart, joinMapKey);
if (Hdmi1VideoSyncFeedback != null)
{
Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]);
}
if (Hdmi2VideoSyncFeedback != null)
{
Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]);
}
LinkDmTxToApi(this, trilist, joinMap, bridge);
}
public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type)
{
Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input);
switch (type)
{
case eRoutingSignalType.Video:
switch (input)
{
case 0:
{
ExecuteSwitch(eVst.Auto, null, type);
break;
}
case 1:
{
ExecuteSwitch(HdmiIn1.Selector, null, type);
break;
}
case 2:
{
ExecuteSwitch(HdmiIn2.Selector, null, type);
break;
}
case 3:
{
ExecuteSwitch(eVst.AllDisabled, null, type);
break;
}
}
break;
case eRoutingSignalType.Audio:
switch (input)
{
case 0:
{
ExecuteSwitch(eAst.Auto, null, type);
break;
}
case 1:
{
ExecuteSwitch(eAst.Hdmi1, null, type);
break;
}
case 2:
{
ExecuteSwitch(eAst.Hdmi2, null, type);
break;
}
case 3:
{
ExecuteSwitch(eAst.AllDisabled, null, type);
break;
}
}
break;
}
}
public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType)
{
if ((signalType & eRoutingSignalType.Video) == eRoutingSignalType.Video)
Tx.VideoSource = (eVst)inputSelector;
if(((signalType & eRoutingSignalType.Audio) == eRoutingSignalType.Audio))
Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key);
}
void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args)
{
Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString());
switch (args.EventId)
{
case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId:
case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId:
case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId:
if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate();
if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate();
break;
case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId:
if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate();
if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate();
break;
}
}
void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
@@ -313,7 +313,7 @@ namespace PepperDash.Essentials.DM
var localVideoInputPort =
InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
var localAudioInputPort =
InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback);
InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
ActiveVideoInputFeedback.FireUpdate();
VideoSourceNumericFeedback.FireUpdate();
@@ -338,68 +338,68 @@ namespace PepperDash.Essentials.DM
OnSwitchChange(new RoutingNumericEventArgs(1, VideoSourceNumericFeedback.UShortValue, OutputPorts.First(), localVideoInputPort, eRoutingSignalType.Video));
break;
case EndpointTransmitterBase.AudioSourceFeedbackEventId:
var localInputAudioPort = InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback);
Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback);
var localInputAudioPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
Debug.Console(2, this, " Audio Source: {0}", Tx.VideoSourceFeedback);
AudioSourceNumericFeedback.FireUpdate();
OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio));
break;
}
}
/// <summary>
/// Relays the input stream change to the appropriate RoutingInputPort.
/// </summary>
void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId)
{
if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId)
{
return;
}
inputPort.VideoStatus.VideoSyncFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate();
}
/// <summary>
/// Relays the VideoAttributes change to a RoutingInputPort
/// </summary>
void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId)
{
//// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds
//Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}",
// args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType());
switch (eventId)
{
case VideoAttributeEventIds.HdcpActiveFeedbackEventId:
inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate();
AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate();
break;
case VideoAttributeEventIds.HdcpStateFeedbackEventId:
inputPort.VideoStatus.HdcpStateFeedback.FireUpdate();
AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate();
break;
case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId:
case VideoAttributeEventIds.VerticalResolutionFeedbackEventId:
inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate();
break;
case VideoAttributeEventIds.FramesPerSecondFeedbackEventId:
inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate();
break;
}
}
#region IIROutputPorts Members
public CrestronCollection<IROutputPort> IROutputPorts { get { return Tx.IROutputPorts; } }
public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } }
#endregion
#region IComPorts Members
public CrestronCollection<ComPort> ComPorts { get { return Tx.ComPorts; } }
public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } }
#endregion
}
}
/// <summary>
/// Relays the input stream change to the appropriate RoutingInputPort.
/// </summary>
void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId)
{
if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId)
{
return;
}
inputPort.VideoStatus.VideoSyncFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate();
}
/// <summary>
/// Relays the VideoAttributes change to a RoutingInputPort
/// </summary>
void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId)
{
//// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds
//Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}",
// args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType());
switch (eventId)
{
case VideoAttributeEventIds.HdcpActiveFeedbackEventId:
inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate();
AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate();
break;
case VideoAttributeEventIds.HdcpStateFeedbackEventId:
inputPort.VideoStatus.HdcpStateFeedback.FireUpdate();
AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate();
break;
case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId:
case VideoAttributeEventIds.VerticalResolutionFeedbackEventId:
inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate();
break;
case VideoAttributeEventIds.FramesPerSecondFeedbackEventId:
inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate();
AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate();
break;
}
}
#region IIROutputPorts Members
public CrestronCollection<IROutputPort> IROutputPorts { get { return Tx.IROutputPorts; } }
public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } }
#endregion
#region IComPorts Members
public CrestronCollection<ComPort> ComPorts { get { return Tx.ComPorts; } }
public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } }
#endregion
}
}

View File

@@ -339,7 +339,7 @@ namespace PepperDash.Essentials.DM
var localVideoInputPort =
InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
var localAudioInputPort =
InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback);
InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
ActiveVideoInputFeedback.FireUpdate();
VideoSourceNumericFeedback.FireUpdate();
@@ -364,8 +364,8 @@ namespace PepperDash.Essentials.DM
OnSwitchChange(new RoutingNumericEventArgs(1, VideoSourceNumericFeedback.UShortValue, OutputPorts.First(), localVideoInputPort, eRoutingSignalType.Video));
break;
case EndpointTransmitterBase.AudioSourceFeedbackEventId:
var localInputAudioPort = InputPorts.FirstOrDefault(p => (eAst)p.Selector == Tx.AudioSourceFeedback);
Debug.Console(2, this, " Audio Source: {0}", Tx.AudioSourceFeedback);
var localInputAudioPort = InputPorts.FirstOrDefault(p => (eVst)p.Selector == Tx.VideoSourceFeedback);
Debug.Console(2, this, " Audio Source: {0}", Tx.VideoSourceFeedback);
AudioSourceNumericFeedback.FireUpdate();
OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio));
break;

View File

@@ -11,10 +11,9 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// <summary>
/// For rooms that have audio codec
/// </summary>
public interface IHasAudioCodec
public interface IHasAudioCodec:IHasInCallFeedback
{
AudioCodecBase AudioCodec { get; }
BoolFeedback InCallFeedback { get; }
///// <summary>
///// Make this more specific

View File

@@ -8,6 +8,8 @@ using Crestron.SimplSharp.Reflection;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Presets;
using PepperDash.Essentials.Devices.Common.Codec;
@@ -25,7 +27,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
Focus = 8
}
public abstract class CameraBase : EssentialsDevice, IRoutingOutputs
public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs
{
public eCameraControlMode ControlMode { get; protected set; }
@@ -70,12 +72,18 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
// A bitmasked value to indicate the movement capabilites of this camera
protected eCameraCapabilities Capabilities { get; set; }
protected CameraBase(string key, string name) :
base(key, name)
{
OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
protected CameraBase(DeviceConfig config) : base(config)
{
OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
ControlMode = eCameraControlMode.Manual;
ControlMode = eCameraControlMode.Manual;
}
protected CameraBase(string key, string name) :
this (new DeviceConfig{Name = name, Key = key})
{
}
protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
@@ -208,21 +216,11 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
var presetsCamera = cameraDevice as IHasCameraPresets;
presetsCamera.PresetsListHasChanged += new EventHandler<EventArgs>((o, a) =>
{
for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++)
{
int tempNum = i - 1;
string label = "";
var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i));
if (preset != null)
label = preset.Description;
trilist.SetString((ushort) (joinMap.PresetLabelStart.JoinNumber + tempNum), label);
}
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
});
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
for (int i = 0; i < joinMap.NumberOfPresets.JoinNumber; i++)
{
int tempNum = i;
@@ -238,10 +236,35 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
presetsCamera.PresetStore(tempNum, label);
});
}
trilist.OnlineStatusChange += (sender, args) =>
{
if (!args.DeviceOnLine)
{ return; }
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
};
}
}
private void SendCameraPresetNamesToApi(IHasCameraPresets presetsCamera, CameraControllerJoinMap joinMap, BasicTriList trilist)
{
for (int i = 1; i <= joinMap.NumberOfPresets.JoinNumber; i++)
{
int tempNum = i - 1;
string label = "";
var preset = presetsCamera.Presets.FirstOrDefault(p => p.ID.Equals(i));
if (preset != null)
label = preset.Description;
trilist.SetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum), label);
}
}
}
public class CameraPreset : PresetBase
{
public CameraPreset(int id, string description, bool isDefined, bool isDefinable)

View File

@@ -525,6 +525,15 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
public event EventHandler<EventArgs> PresetsListHasChanged;
protected void OnPresetsListHasChanged()
{
var handler = PresetsListHasChanged;
if (handler == null)
return;
handler.Invoke(this, EventArgs.Empty);
}
public List<CameraPreset> Presets { get; private set; }
public void PresetSelect(int preset)
@@ -537,6 +546,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
SavePreset(preset);
}
#endregion
#region IHasCameraFocusControl Members

View File

@@ -69,6 +69,22 @@ namespace PepperDash.Essentials.Devices.Common.Codec
[JsonProperty("directoryResults")]
public List<DirectoryItem> CurrentDirectoryResults { get; private set; }
public List<DirectoryItem> Contacts
{
get
{
return CurrentDirectoryResults.OfType<DirectoryContact>().Cast<DirectoryItem>().ToList();
}
}
public List<DirectoryItem> Folders
{
get
{
return CurrentDirectoryResults.OfType<DirectoryFolder>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Used to store the ID of the current folder for CurrentDirectoryResults
/// </summary>
@@ -104,6 +120,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec
SortDirectory();
}
/// <summary>
/// Filters the CurrentDirectoryResults by the predicate
/// </summary>
/// <param name="predicate"></param>
public void FilterContacts(Func<DirectoryItem, bool> predicate)
{
CurrentDirectoryResults = CurrentDirectoryResults.Where(predicate).ToList();
}
/// <summary>
/// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically
/// </summary>
@@ -135,7 +160,19 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary>
public interface IInvitableContact
{
bool IsInvitableContact { get; }
}
public class InvitableDirectoryContact : DirectoryContact, IInvitableContact
{
[JsonProperty("isInvitableContact")]
public bool IsInvitableContact
{
get
{
return this is IInvitableContact;
}
}
}
/// <summary>
@@ -184,8 +221,6 @@ namespace PepperDash.Essentials.Devices.Common.Codec
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("contactMethods")]
public List<ContactMethod> ContactMethods { get; set; }

View File

@@ -4,15 +4,20 @@ using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
[Flags]
public enum eMeetingEventChangeType
{
Unkown = 0,
MeetingStartWarning,
MeetingStart,
MeetingEndWarning,
MeetingEnd
Unknown = 0,
MeetingStartWarning = 1,
MeetingStart = 2,
MeetingEndWarning = 4,
MeetingEnd = 8
}
public interface IHasScheduleAwareness
@@ -32,6 +37,10 @@ namespace PepperDash.Essentials.Devices.Common.Codec
private int _meetingWarningMinutes = 5;
private Meeting _previousChangedMeeting;
private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown;
public int MeetingWarningMinutes
{
get { return _meetingWarningMinutes; }
@@ -75,38 +84,72 @@ namespace PepperDash.Essentials.Devices.Common.Codec
_scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime);
}
/// <summary>
/// Helper method to fire MeetingEventChange. Should only fire once for each changeType on each meeting
/// </summary>
/// <param name="changeType"></param>
/// <param name="meeting"></param>
private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting)
{
var handler = MeetingEventChange;
if (handler != null)
Debug.Console(2, "*****************OnMeetingChange. id: {0} changeType: {1}**********************", meeting.Id, changeType);
if (changeType != (changeType & meeting.NotifiedChangeTypes))
{
handler(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });
// Add this change type to the NotifiedChangeTypes
meeting.NotifiedChangeTypes |= changeType;
var handler = MeetingEventChange;
if (handler != null)
{
handler(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });
}
}
else
{
Debug.Console(2, "Meeting: {0} already notified of changeType: {1}", meeting.Id, changeType);
}
}
/// <summary>
/// Checks the schedule to see if any MeetingEventChange updates should be fired
/// </summary>
/// <param name="o"></param>
private void CheckSchedule(object o)
{
// Iterate the meeting list and check if any meeting need to do anythingk
// Iterate the meeting list and check if any meeting need to do anything
const double meetingTimeEpsilon = 0.0001;
const double meetingTimeEpsilon = 0.05;
foreach (var m in Meetings)
{
var changeType = eMeetingEventChangeType.Unkown;
var changeType = eMeetingEventChangeType.Unknown;
if (m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to start
if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0) // Meeting is about to start
{
Debug.Console(2, "********************* MeetingStartWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds);
changeType = eMeetingEventChangeType.MeetingStartWarning;
else if (Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start
}
else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start
{
Debug.Console(2, "********************* MeetingStart");
changeType = eMeetingEventChangeType.MeetingStart;
else if (m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes) // Meeting is about to end
}
else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0) // Meeting is about to end
{
Debug.Console(2, "********************* MeetingEndWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds);
changeType = eMeetingEventChangeType.MeetingEndWarning;
else if (Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended
}
else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended
{
Debug.Console(2, "********************* MeetingEnd");
changeType = eMeetingEventChangeType.MeetingEnd;
}
if (changeType != eMeetingEventChangeType.Unkown)
OnMeetingChange(changeType, m);
if (changeType != eMeetingEventChangeType.Unknown)
{
OnMeetingChange(changeType, m);
}
}
}
}
@@ -115,17 +158,24 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary>
public class Meeting
{
[JsonProperty("minutesBeforeMeeting")]
public int MinutesBeforeMeeting;
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("organizer")]
public string Organizer { get; set; }
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("agenda")]
public string Agenda { get; set; }
[JsonProperty("meetingWarningMinutes")]
public TimeSpan MeetingWarningMinutes
{
get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); }
}
[JsonProperty("timeToMeetingStart")]
public TimeSpan TimeToMeetingStart
{
get
@@ -133,6 +183,7 @@ namespace PepperDash.Essentials.Devices.Common.Codec
return StartTime - DateTime.Now;
}
}
[JsonProperty("timeToMeetingEnd")]
public TimeSpan TimeToMeetingEnd
{
get
@@ -140,8 +191,11 @@ namespace PepperDash.Essentials.Devices.Common.Codec
return EndTime - DateTime.Now;
}
}
[JsonProperty("startTime")]
public DateTime StartTime { get; set; }
[JsonProperty("endTime")]
public DateTime EndTime { get; set; }
[JsonProperty("duration")]
public TimeSpan Duration
{
get
@@ -149,21 +203,34 @@ namespace PepperDash.Essentials.Devices.Common.Codec
return EndTime - StartTime;
}
}
[JsonProperty("privacy")]
public eMeetingPrivacy Privacy { get; set; }
[JsonProperty("joinable")]
public bool Joinable
{
get
{
return StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now
&& DateTime.Now <= EndTime; //.AddMinutes(-5);
var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now
&& DateTime.Now <= EndTime.AddMinutes(-5);
//Debug.Console(2, "Meeting Id: {0} joinable: {1}", Id, joinable);
return joinable;
}
}
//public string ConferenceNumberToDial { get; set; }
[JsonProperty("conferencePassword")]
public string ConferencePassword { get; set; }
[JsonProperty("isOneButtonToPushMeeting")]
public bool IsOneButtonToPushMeeting { get; set; }
[JsonProperty("calls")]
public List<Call> Calls { get; private set; }
/// <summary>
/// Tracks the change types that have already been notified for
/// </summary>
[JsonIgnore]
public eMeetingEventChangeType NotifiedChangeTypes { get; set; }
public Meeting()
{
Calls = new List<Call>();

View File

@@ -1,17 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Displays
{
public interface IInputHdmi1 { void InputHdmi1(); }
public interface IInputHdmi2 { void InputHdmi2(); }
public interface IInputHdmi3 { void InputHdmi3(); }
public interface IInputHdmi4 { void InputHdmi4(); }
public interface IInputDisplayPort1 { void InputDisplayPort1(); }
public interface IInputDisplayPort2 { void InputDisplayPort2(); }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Displays
{
public interface IInputHdmi1 { void InputHdmi1(); }
public interface IInputHdmi2 { void InputHdmi2(); }
public interface IInputHdmi3 { void InputHdmi3(); }
public interface IInputHdmi4 { void InputHdmi4(); }
public interface IInputDisplayPort1 { void InputDisplayPort1(); }
public interface IInputDisplayPort2 { void InputDisplayPort2(); }
public interface IInputVga1 { void InputVga1(); }
}

View File

@@ -120,8 +120,11 @@
<Compile Include="VideoCodec\CiscoCodec\RoomPresets.cs" />
<Compile Include="Cameras\CameraControl.cs" />
<Compile Include="Display\PanasonicThDisplay.cs" />
<Compile Include="VideoCodec\Interfaces\IHasMeetingInfo.cs" />
<Compile Include="VideoCodec\Interfaces\IHasParticipants.cs" />
<Compile Include="VideoCodec\Interfaces\IHasSelfviewPosition.cs" />
<Compile Include="VideoCodec\Interfaces\IHasSelfviewSize.cs" />
<Compile Include="VideoCodec\Interfaces\IHasStartMeeting.cs" />
<Compile Include="VideoCodec\Interfaces\iVideoCodecInfo.cs" />
<Compile Include="Codec\iHasCallFavorites.cs" />
<Compile Include="Codec\iHasCallHistory.cs" />
@@ -176,6 +179,7 @@
<Compile Include="VideoCodec\ZoomRoom\ResponseObjects.cs" />
<Compile Include="VideoCodec\ZoomRoom\ZoomRoom.cs" />
<Compile Include="VideoCodec\ZoomRoom\ZoomRoomCamera.cs" />
<Compile Include="VideoCodec\ZoomRoom\ZoomRoomJoinMap.cs" />
<Compile Include="VideoCodec\ZoomRoom\ZoomRoomPropertiesConfig.cs" />
<None Include="Properties\ControlSystem.cfg" />
</ItemGroup>

View File

@@ -30,6 +30,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView,
ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute
{
private bool _externalSourceChangeRequested;
public event EventHandler<DirectoryEventArgs> DirectoryResultReturned;
private CTimer _brandingTimer;
@@ -312,7 +314,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
FarEndIsSharingContentFeedback = new BoolFeedback(FarEndIsSharingContentFeedbackFunc);
CameraIsOffFeedback = new BoolFeedback(() => CodecStatus.Status.Video.Input.MainVideoMute.BoolValue);
CameraIsMutedFeedback = CameraIsOffFeedback;
SupportsCameraOff = true;
PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized");
@@ -368,21 +370,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
CodecSchedule = new CodecScheduleAwareness();
//Set Feedback Actions
CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate;
CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate;
CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate;
CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate;
CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate;
CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate;
CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate;
CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate;
CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate;
SetFeedbackActions();
CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video,
CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this);
HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
eRoutingPortConnectionType.Hdmi, new Action(SelectPresentationSource1), this);
@@ -418,7 +408,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
_brandingUrl = props.UiBranding.BrandingUrl;
}
/// <summary>
private void SetFeedbackActions()
{
CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate;
CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate;
CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate;
CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate;
CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate;
CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate;
CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate;
CodecStatus.Status.Cameras.SpeakerTrack.Availability.ValueChangedAction = () => { SupportsCameraAutoMode = CodecStatus.Status.Cameras.SpeakerTrack.Availability.BoolValue; };
CodecStatus.Status.Video.Selfview.Mode.ValueChangedAction = SelfviewIsOnFeedback.FireUpdate;
CodecStatus.Status.Video.Selfview.PIPPosition.ValueChangedAction = ComputeSelfviewPipStatus;
CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate;
CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate;
try
{
CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate;
}
catch (Exception ex)
{
Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
if (ex.InnerException != null)
{
Debug.Console(0, this, "Error setting MainVideuMute Action: {0}", ex);
}
}
}
/// <summary>
/// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input
/// to enable routing
/// </summary>
@@ -473,11 +493,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
Debug.Console(2, this, "Setting QR code URL: {0}", mcBridge.QrCodeUrl);
mcBridge.UserCodeChanged += (o, a) => SendMcBrandingUrl(mcBridge);
mcBridge.UserPromptedForCode += (o, a) => DisplayUserCode(mcBridge.UserCode);
SendMcBrandingUrl(mcBridge);
}
}
/// <summary>
/// Displays the code for the specified duration
/// </summary>
/// <param name="code">Mobile Control user code</param>
private void DisplayUserCode(string code)
{
SendText(string.Format("xcommand userinterface message alert display title:\"Mobile Control User Code:\" text:\"{0}\" duration: 30", code));
}
private void SendMcBrandingUrl(IMobileControlRoomBridge mcBridge)
{
if (mcBridge == null)
@@ -521,6 +551,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
CrestronConsole.AddNewConsoleCommand(GetPhonebook, "GetCodecPhonebook", "Triggers a refresh of the codec phonebook", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(GetBookings, "GetCodecBookings", "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator);
return base.CustomActivate();
}
#region Overrides of Device
public override void Initialize()
{
var socket = Communication as ISocketStatus;
if (socket != null)
{
@@ -529,9 +566,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
Communication.Connect();
CommunicationMonitor.Start();
CommunicationMonitor.Start();
string prefix = "xFeedback register ";
const string prefix = "xFeedback register ";
CliFeedbackRegistrationExpression =
prefix + "/Configuration" + Delimiter +
@@ -544,15 +581,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
prefix + "/Status/Standby" + Delimiter +
prefix + "/Status/Video/Selfview" + Delimiter +
prefix + "/Status/Video/Layout" + Delimiter +
prefix + "/Status/Video/Input/MainVideoMute" + Delimiter +
prefix + "/Bookings" + Delimiter +
prefix + "/Event/CallDisconnect" + Delimiter +
prefix + "/Event/CallDisconnect" + Delimiter +
prefix + "/Event/Bookings" + Delimiter +
prefix + "/Event/CameraPresetListUpdated" + Delimiter +
prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter;
return base.CustomActivate();
prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter;
}
#endregion
/// <summary>
/// Fires when initial codec sync is completed. Used to then send commands to get call history, phonebook, bookings, etc.
/// </summary>
@@ -705,6 +743,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
}
/// <summary>
/// Appends the delimiter and send the command to the codec
/// </summary>
/// <param name="command"></param>
public void SendText(string command)
{
if (CommDebuggingIsOn)
@@ -933,10 +975,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
JsonConvert.PopulateObject(response, eventReceived);
Debug.Console(2, this, "*** Got an External Source Selection {0} {1}", eventReceived, eventReceived.Event.UserInterface, eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value);
if (RunRouteAction != null)
if (RunRouteAction != null && !_externalSourceChangeRequested)
{
RunRouteAction(eventReceived.Event.UserInterface.Presentation.ExternalSource.Selected.SourceIdentifier.Value, null);
}
_externalSourceChangeRequested = false;
}
}
else if (response.IndexOf("\"CommandResponse\":{") > -1 || response.IndexOf("\"CommandResponse\": {") > -1)
@@ -1633,12 +1677,21 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void CameraAutoModeOn()
{
if (CameraIsOffFeedback.BoolValue)
{
CameraMuteOff();
}
SendText("xCommand Cameras SpeakerTrack Activate");
CameraMuteOff();
}
public void CameraAutoModeOff()
{
if (CameraIsOffFeedback.BoolValue)
{
CameraMuteOff();
}
SendText("xCommand Cameras SpeakerTrack Deactivate");
}
@@ -1957,6 +2010,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public void SetSelectedSource(string key)
{
SendText(string.Format("xCommand UserInterface Presentation ExternalSource Select SourceIdentifier: {0}", key));
_externalSourceChangeRequested = true;
}
/// <summary>
@@ -1994,7 +2048,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary>
public void CameraMuteOn()
{
SendText("xCommand Video InputMainVideo Mute");
SendText("xCommand Video Input MainVideo Mute");
}
/// <summary>
@@ -2002,7 +2056,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
/// </summary>
public void CameraMuteOff()
{
SendText("xCommand Video InputMainVideo Unmute");
SendText("xCommand Video Input MainVideo Unmute");
}
/// <summary>

View File

@@ -8,6 +8,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Devices.Common.VideoCodec.CiscoCodec;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
@@ -276,9 +277,25 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public SoftwareID SoftwareID { get; set; }
}
public class Availability
public class Availability : ValueProperty
{
public string Value { get; set; }
string _Value;
public bool BoolValue { get; private set; }
public string Value
{
get
{
return _Value;
}
set
{
// If the incoming value is "Available" it sets the BoolValue true, otherwise sets it false
_Value = value;
BoolValue = value == "Available";
OnValueChanged();
}
}
}
public class Status2 : ValueProperty
@@ -310,6 +327,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public SpeakerTrack()
{
Status = new Status2();
Availability = new Availability();
}
}
@@ -1680,6 +1698,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
public MainVideoSource MainVideoSource { get; set; }
public MainVideoMute MainVideoMute { get; set; }
public List<Source> Source { get; set; }
public Input2()
{
MainVideoMute = new MainVideoMute();
}
}
public class Local : ValueProperty
@@ -1875,6 +1898,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco
{
Selfview = new Selfview();
Layout = new Layout();
Input = new Input2();
}
}

View File

@@ -19,4 +19,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
void LocalLayoutToggleSingleProminent();
void MinMaxLayoutToggle();
}
/// <summary>
/// Defines the requirements for Zoom Room layout control
/// </summary>
public interface IHasZoomRoomLayouts : IHasCodecLayouts
{
event EventHandler<LayoutInfoChangedEventArgs> AvailableLayoutsChanged;
BoolFeedback LayoutViewIsOnFirstPageFeedback { get; } // TODO: #697 [*] Consider modifying to report button visibility in func
BoolFeedback LayoutViewIsOnLastPageFeedback { get; } // TODO: #697 [*] Consider modifying to report button visibility in func
BoolFeedback CanSwapContentWithThumbnailFeedback { get; }
BoolFeedback ContentSwappedWithThumbnailFeedback { get; }
ZoomRoom.zConfiguration.eLayoutStyle LastSelectedLayout { get; }
ZoomRoom.zConfiguration.eLayoutStyle AvailableLayouts { get; }
void GetAvailableLayouts(); // Mot sure this is necessary if we're already subscribed to zStatus Call Layout
void SetLayout(ZoomRoom.zConfiguration.eLayoutStyle layoutStyle);
void SwapContentWithThumbnail();
void LayoutTurnNextPage();
void LayoutTurnPreviousPage();
}
public class LayoutInfoChangedEventArgs : EventArgs
{
public ZoomRoom.zConfiguration.eLayoutStyle AvailableLayouts { get; set; }
}
}

View File

@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces
{
/// <summary>
/// Describes a device that provides meeting information (like a ZoomRoom)
/// </summary>
public interface IHasMeetingInfo
{
event EventHandler<MeetingInfoEventArgs> MeetingInfoChanged;
MeetingInfo MeetingInfo { get; }
}
/// <summary>
/// Represents the information about a meeting in progress
/// Currently used for Zoom meetings
/// </summary>
public class MeetingInfo
{
[JsonProperty("id")]
public string Id { get; private set; }
[JsonProperty("name")]
public string Name { get; private set; }
[JsonProperty("host")]
public string Host { get; private set; }
[JsonProperty("password")]
public string Password { get; private set; }
[JsonProperty("shareStatus")]
public string ShareStatus { get; private set; }
[JsonProperty("isHost")]
public Boolean IsHost { get; private set; }
public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost)
{
Id = id;
Name = name;
Host = host;
Password = password;
ShareStatus = shareStatus;
IsHost = isHost;
}
}
public class MeetingInfoEventArgs : EventArgs
{
public MeetingInfo Info { get; private set; }
public MeetingInfoEventArgs(MeetingInfo info)
{
Info = info;
}
}
}

View File

@@ -1,59 +1,112 @@
using System;
using System.Linq;
using System.Collections.Generic;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces
{
public interface IHasParticipants
{
CodecParticipants Participants { get; }
}
/// <summary>
/// Describes a device that has call participants
/// </summary>
public interface IHasParticipants
{
CodecParticipants Participants { get; }
}
public interface IHasParticipantVideoMute:IHasParticipants
{
void MuteVideoForParticipant(int userId);
void UnmuteVideoForParticipant(int userId);
void ToggleVideoForParticipant(int userId);
}
/// <summary>
/// Describes the ability to mute and unmute a participant's video in a meeting
/// </summary>
public interface IHasParticipantVideoMute : IHasParticipants
{
void MuteVideoForParticipant(int userId);
void UnmuteVideoForParticipant(int userId);
void ToggleVideoForParticipant(int userId);
}
public interface IHasParticipantAudioMute:IHasParticipantVideoMute
{
void MuteAudioForParticipant(int userId);
void UnmuteAudioForParticipant(int userId);
void ToggleAudioForParticipant(int userId);
}
/// <summary>
/// Describes the ability to mute and unmute a participant's audio in a meeting
/// </summary>
public interface IHasParticipantAudioMute : IHasParticipantVideoMute
{
void MuteAudioForParticipant(int userId);
void UnmuteAudioForParticipant(int userId);
void ToggleAudioForParticipant(int userId);
}
public class CodecParticipants
{
private List<Participant> _currentParticipants;
public List<Participant> CurrentParticipants {
get { return _currentParticipants; }
set
/// <summary>
/// Describes the ability to pin and unpin a participant in a meeting
/// </summary>
public interface IHasParticipantPinUnpin : IHasParticipants
{
IntFeedback NumberOfScreensFeedback { get; }
int ScreenIndexToPinUserTo { get; }
void PinParticipant(int userId, int screenIndex);
void UnPinParticipant(int userId);
void ToggleParticipantPinState(int userId, int screenIndex);
}
public class CodecParticipants
{
private List<Participant> _currentParticipants;
public List<Participant> CurrentParticipants
{
get { return _currentParticipants; }
set
{
_currentParticipants = value;
OnParticipantsChanged();
}
}
public Participant Host
{
get
{
_currentParticipants = value;
var handler = ParticipantsListHasChanged;
if(handler == null) return;
handler(this, new EventArgs());
return _currentParticipants.FirstOrDefault(p => p.IsHost);
}
}
public event EventHandler<EventArgs> ParticipantsListHasChanged;
public event EventHandler<EventArgs> ParticipantsListHasChanged;
public CodecParticipants()
{
_currentParticipants = new List<Participant>();
}
}
public CodecParticipants()
{
_currentParticipants = new List<Participant>();
}
public class Participant
{
public bool IsHost { get; set; }
public string Name { get; set; }
public bool CanMuteVideo { get; set; }
public bool CanUnmuteVideo { get; set; }
public bool VideoMuteFb { get; set; }
public bool AudioMuteFb { get; set; }
}
public void OnParticipantsChanged()
{
var handler = ParticipantsListHasChanged;
if (handler == null) return;
handler(this, new EventArgs());
}
}
/// <summary>
/// Represents a call participant
/// </summary>
public class Participant
{
public int UserId { get; set; }
public bool IsHost { get; set; }
public bool IsMyself { get; set; }
public string Name { get; set; }
public bool CanMuteVideo { get; set; }
public bool CanUnmuteVideo { get; set; }
public bool VideoMuteFb { get; set; }
public bool AudioMuteFb { get; set; }
public bool HandIsRaisedFb { get; set; }
public bool IsPinnedFb { get; set; }
public int ScreenIndexIsPinnedToFb { get; set; }
public Participant()
{
// Initialize to -1 (no screen)
ScreenIndexIsPinnedToFb = -1;
}
}
}

View File

@@ -0,0 +1,13 @@
using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IHasSelfviewSize
{
StringFeedback SelfviewPipSizeFeedback { get; }
void SelfviewPipSizeSet(CodecCommandWithLabel size);
void SelfviewPipSizeToggle();
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces
{
/// <summary>
/// Describes the ability to start an ad-hoc meeting
/// </summary>
public interface IHasStartMeeting
{
/// <summary>
/// The default meeting duration in minutes
/// </summary>
uint DefaultMeetingDurationMin { get; }
/// <summary>
/// Start an ad-hoc meeting for the specified duration
/// </summary>
/// <param name="duration"></param>
void StartMeeting(uint duration);
/// <summary>
/// Leaves a meeting without ending it
/// </summary>
void LeaveMeeting();
}
}

View File

@@ -11,10 +11,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// <summary>
/// For rooms that have video codec
/// </summary>
public interface IHasVideoCodec
public interface IHasVideoCodec:IHasInCallFeedback,IPrivacy
{
VideoCodecBase VideoCodec { get; }
BoolFeedback InCallFeedback { get; }
///// <summary>
///// Make this more specific
@@ -26,11 +25,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
/// </summary>
IntFeedback CallTypeFeedback { get; }
/// <summary>
///
/// </summary>
BoolFeedback PrivacyModeIsOnFeedback { get; }
/// <summary>
/// When something in the room is sharing with the far end or through other means
/// </summary>

View File

@@ -78,6 +78,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
SetupCameras();
CreateOsdSource();
SetIsReady();
}
@@ -117,6 +119,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
}
bool _StandbyIsOn;
/// <summary>
/// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input
/// to enable routing
/// </summary>
private void CreateOsdSource()
{
OsdSource = new DummyRoutingInputsDevice(Key + "[osd]");
DeviceManager.AddDevice(OsdSource);
var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn);
TieLineCollection.Default.Add(tl);
//foreach(var input in Status.Video.
}
/// <summary>
/// Dials, yo!
@@ -139,7 +154,20 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
public override void Dial(Meeting meeting)
{
throw new NotImplementedException();
Debug.Console(1, this, "Dial Meeting: {0}", meeting.Id);
var call = new CodecActiveCallItem() { Name = meeting.Title, Number = meeting.Id, Id = meeting.Id, Status = eCodecCallStatus.Dialing, Direction = eCodecCallDirection.Outgoing, Type = eCodecCallType.Video };
ActiveCalls.Add(call);
OnCallStatusChange(call);
//ActiveCallCountFeedback.FireUpdate();
// Simulate 2-second ring, then connecting, then connected
new CTimer(o =>
{
call.Type = eCodecCallType.Video;
SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connecting, call);
new CTimer(oo => SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus.Connected, call), 1000);
}, 2000);
}
/// <summary>
@@ -396,12 +424,15 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
if (_CodecSchedule == null || _CodecSchedule.Meetings.Count == 0
|| _CodecSchedule.Meetings[_CodecSchedule.Meetings.Count - 1].StartTime < DateTime.Now)
{
_CodecSchedule = new CodecScheduleAwareness();
_CodecSchedule = new CodecScheduleAwareness(1000);
for (int i = 0; i < 5; i++)
{
var m = new Meeting();
m.StartTime = DateTime.Now.AddMinutes(3).AddHours(i);
m.EndTime = DateTime.Now.AddHours(i).AddMinutes(30);
m.MinutesBeforeMeeting = 5;
m.Id = i.ToString();
m.Organizer = "Employee " + 1;
m.StartTime = DateTime.Now.AddMinutes(6).AddHours(i);
m.EndTime = DateTime.Now.AddHours(i).AddMinutes(16);
m.Title = "Meeting " + i;
m.Calls.Add(new Call() { Number = i + "meeting@fake.com"});
_CodecSchedule.Meetings.Add(m);
@@ -551,6 +582,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
void SetupCameras()
{
SupportsCameraAutoMode = true;
SupportsCameraOff = false;
Cameras = new List<CameraBase>();
var internalCamera = new MockVCCamera(Key + "-camera1", "Near End", this);

Some files were not shown because too many files have changed in this diff Show More