Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions examples/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
import asyncio
import base64

APP_KEY = ''
APP_SECRET = ''
APP_KEY = ""
APP_SECRET = ""
CAMERA_ID = ''


def webrtc_offer(device_id, format, offer):
def get_webrtc_answer(device_id, offer):
sdp_offer = base64.b64decode(offer)
print('device_id: {} format: {} offer: {}'.format(
device_id, format, sdp_offer))
print('device_id: {} offer: {}'.format(device_id, offer))

mediamtx_url = "http://<mediamtx-hostname>:8889/<device>/whep" # PORT 8889 for WebRTC
# PORT 8889 for WebRTC. eg: for PiCam, use http://<mediamtx-hostname>:8889/cam/whep
mediamtx_url = "http://<mediamtx-hostname>:8889/<device>/whep"
headers = {"Content-Type": "application/sdp"}
response = requests.post(mediamtx_url, headers=headers, data=sdp_offer)

Expand All @@ -25,12 +24,23 @@ def webrtc_offer(device_id, format, offer):


def power_state(device_id, state):
print('device_id: {} state: {}'.format(device_id, state))
print('device_id: {} power state: {}'.format(device_id, state))
return True, state

def get_camera_stream_url(device_id, protocol):
# Google Home: RTSP protocol not supported. Requires a Chromecast TV or Google Nest Hub
# Alexa: RTSP url must be interleaved TCP on port 443 (for both RTP and RTSP) over TLS 1.2 port 443

print('device_id: {} protocol: {}'.format(device_id, protocol))

if protocol == "rstp":
return True, 'rtsp://rtspurl:443' # RSTP.
else:
return True, 'https://cph-p2p-msl.akamaized.net/hls/live/2000341/test/master.m3u8' # HLS

callbacks = {
SinricProConstants.WEBRTC_OFFER: webrtc_offer,
SinricProConstants.GET_WEBRTC_ANSWER: get_webrtc_answer,
SinricProConstants.GET_CAMERA_STREAM_URL: get_camera_stream_url,
SinricProConstants.SET_POWER_STATE: power_state
}

Expand Down
29 changes: 22 additions & 7 deletions sinric/_callback_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from ._tv_contorller import TvController
from ._speaker_controller import SpeakerController
from ._mode_controller import ModeController
from ._webrtc_controller import WebRTCController
from ._camera_stream_controller import CameraStreamController
from ._lock_controller import LockStateController
from ._signature import Signature
from ._leaky_bucket import LeakyBucket
Expand All @@ -30,7 +30,7 @@
# noinspection PyBroadException
class CallBackHandler(PowerLevelController, PowerController, BrightnessController, ColorController, ColorTemperatureController,
ThermostateMode, RangeValueController, TemperatureController, TvController, SpeakerController,
LockStateController, ModeController, WebRTCController, Signature):
LockStateController, ModeController, CameraStreamController, Signature):
def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key=""):
self.myHmac = None
self.secret_key = secret_key
Expand All @@ -50,7 +50,7 @@ def __init__(self, callbacks, trace_bool, logger, enable_track=False, secret_key
SpeakerController.__init__(self)
ModeController.__init__(self)
ColorTemperatureController.__init__(self, 0, [2200, 2700, 4000, 5500, 7000])
WebRTCController.__init__(self)
CameraStreamController.__init__(self)

self.callbacks = callbacks
self.logger = logger
Expand Down Expand Up @@ -175,8 +175,11 @@ def json_response(action, resp, data_dict, instance_id='') -> dict:
elif action == SinricProConstants.SET_LOCK_STATE:
await self._handle_set_lock_state(connection, udp_client, jsn, handle_response, json_response, action)

elif action == SinricProConstants.WEBRTC_OFFER:
await self._handle_webrtc_offer(connection, udp_client, jsn, handle_response, json_response, action)
elif action == SinricProConstants.GET_WEBRTC_ANSWER:
await self._handle_get_webrtc_answer(connection, udp_client, jsn, handle_response, json_response, action)

elif action == SinricProConstants.GET_CAMERA_STREAM_URL:
await self._handle_get_camera_stream_url(connection, udp_client, jsn, handle_response, json_response, action)

# Handle events

Expand Down Expand Up @@ -281,9 +284,21 @@ def json_response(action, resp, data_dict, instance_id='') -> dict:
self.logger.info('Sending push notification event')
await connection.send(dumps(jsn))

async def _handle_webrtc_offer(self, connection, udp_client, jsn, handle_response, json_response, action):
async def _handle_get_camera_stream_url(self, connection, udp_client, jsn, handle_response, json_response, action):
try:
resp, url = await self.get_camera_stream_url(jsn, self.callbacks.get(action))
response = json_response(action, resp, { "url": url })

if resp:
await handle_response(response, connection, udp_client)
except AssertionError:
self.logger.error("Signature verification failed for " + jsn.get('payload').get('action'))
except Exception as e:
self.logger.error(str(e))

async def _handle_get_webrtc_answer(self, connection, udp_client, jsn, handle_response, json_response, action):
try:
resp, value = await self.webrtc_offer(jsn, self.callbacks.get(action))
resp, value = await self.get_webrtc_answer(jsn, self.callbacks.get(action))
response = json_response(action, resp, { "answer": value })

if resp:
Expand Down
22 changes: 22 additions & 0 deletions sinric/_camera_stream_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
* Copyright (c) 2019-2023 Sinric. All rights reserved.
* Licensed under Creative Commons Attribution-Share Alike (CC BY-SA)
*
* This file is part of the Sinric Pro (https://github.com/sinricpro/)
"""

from ._sinricpro_constants import SinricProConstants

class CameraStreamController:

async def get_webrtc_answer(self, jsn, get_webrtc_answer_callback):
self.offer = jsn.get("payload").get("value").get("offer")

return get_webrtc_answer_callback(jsn.get("payload").get(SinricProConstants.DEVICEID),
self.offer)

async def get_camera_stream_url(self, jsn, get_camera_stream_url_callback):
self.protocol = jsn.get("payload").get("value").get("protocol")

return get_camera_stream_url_callback(jsn.get("payload").get(SinricProConstants.DEVICEID),
self.protocol)
5 changes: 3 additions & 2 deletions sinric/_sinricpro_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class SinricProConstants(object):
RESET_BANDS = 'resetBands'
SET_MODE = 'setMode'
SET_LOCK_STATE = 'setLockState'
WEBRTC_OFFER = 'webrtcOffer'
GET_WEBRTC_ANSWER = 'getWebRTCAnswer'
GET_CAMERA_STREAM_URL = 'getCameraStreamUrl'
PUSH_NOTIFICATION = 'pushNotification'
MOTION = 'motion'
SET_CONTACT_STATE = 'setContactState'
Expand Down Expand Up @@ -57,7 +58,7 @@ class SinricProConstants(object):
NAME = 'name'
BANDS = 'bands'
THERMOSTATMODE = 'thermostatMode'
MODE: 'mode',
MODE = 'mode'


# values
Expand Down
4 changes: 2 additions & 2 deletions sinric/_sinricpro_websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ async def connect(self): # Producer
'true' if self.restore_states else 'false')},
ping_interval=30000, ping_timeout=10000)
if self.connection.open:
self.logger.success(f"{'Connected :)'}")
self.logger.success("Connected :)")
timestamp = await self.connection.recv()
if (int(time()) - json.loads(timestamp).get('timestamp') > 60000):
self.logger.warning(f'Timestamp is not in sync. Please check your system time.')
self.logger.warning('Timestamp is not in sync. Please check your system time.')
return self.connection

async def receive_message(self, connection):
Expand Down
17 changes: 0 additions & 17 deletions sinric/_webrtc_controller.py

This file was deleted.