Skip to content

Commit

Permalink
Version 1.5.2. Details in /releases/release_1.5.2.md
Browse files Browse the repository at this point in the history
  • Loading branch information
tropicoo committed Oct 17, 2022
1 parent 69c3ae1 commit 11dbdba
Show file tree
Hide file tree
Showing 12 changed files with 70 additions and 28 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Hikvision Telegram Camera Bot
Telegram Bot which sends snapshots from your Hikvision cameras.

Version: 1.5. [Release details](releases/release_1.5.md).
Version: 1.5.2. [Release details](releases/release_1.5.2.md).

## Features
1. Send full/resized pictures on request.
Expand Down Expand Up @@ -86,6 +86,7 @@ Configuration files are stored in JSON format and can be found in the `configs`
"group": "Default group",
"api": {
"host": "http://192.168.1.1",
"port": 80,
"auth": {
"user": "dummy-user",
"password": "dummy-password"
Expand Down Expand Up @@ -195,8 +196,17 @@ If you want to use the default UTC time format, set Greenwich Mean Time timezone

2. Build an image and run a container in a detached mode
```bash
# Build container and start
sudo docker-compose build && sudo docker-compose up -d && sudo docker-compose logs -f --tail=1000
# Stop running containers while you're in the bot directory
sudo docker-compose stop
# Check whether any containers are running
sudo docker ps
```



# Commands
| Command | Description |
Expand Down
2 changes: 2 additions & 0 deletions configs/config-template.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"group": "Default group",
"api": {
"host": "http://192.168.1.1",
"port": 80,
"auth": {
"user": "dummy-user",
"password": "dummy-password"
Expand Down Expand Up @@ -129,6 +130,7 @@
"group": "Default group",
"api": {
"host": "http://192.168.1.2",
"port": 80,
"auth": {
"user": "dummy-user",
"password": "dummy-password"
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: "3"
version: "3.8"

services:
hikvision-camera-bot:
Expand Down
5 changes: 3 additions & 2 deletions hikcamerabot/clients/hikvision/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self, conf: Dict) -> None:
self._log = logging.getLogger(self.__class__.__name__)
self._conf = conf
self.host: str = self._conf.host
self.port: int = self._conf.port
self.session = httpx.AsyncClient(
auth=DigestAuthCached(
username=self._conf.auth.user,
Expand All @@ -36,8 +37,8 @@ async def request(
method: str = 'GET',
timeout: float = CONN_TIMEOUT,
) -> httpx.Response:
url = urljoin(self.host, endpoint)
self._log.debug('Request: %s - %s - %s', method, endpoint, data)
url = urljoin(f'{self.host}:{self.port}', endpoint)
self._log.debug('Request: %s - %s - %s', method, url, data)
try:
response = await self.session.request(
method,
Expand Down
18 changes: 9 additions & 9 deletions hikcamerabot/clients/hikvision/endpoints/config_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ class CameraConfigSwitch:
)
XML_HEADERS = {'Content-Type': 'application/xml'}

def __init__(self, api: 'HikvisionAPIClient') -> None:
def __init__(self, api_client: 'HikvisionAPIClient') -> None:
self._log = logging.getLogger(self.__class__.__name__)
self._api = api
self._api_client = api_client

async def switch_enabled_state(
self, trigger: Detection, enable: bool
self, trigger: Detection, state: bool
) -> Optional[str]:
endpoint = Endpoint[trigger.value.upper()].value
full_name: str = DETECTION_SWITCH_MAP[trigger]['name'].value
Expand All @@ -45,19 +45,19 @@ async def switch_enabled_state(
self._log.error(err_msg)
raise HikvisionAPIError(err_msg)

if is_enabled and enable:
if is_enabled and state:
return f'{full_name} already enabled'
if not is_enabled and not enable:
if not is_enabled and not state:
return f'{full_name} already disabled'

xml_payload = self._prepare_xml_payload(xml, enable)
xml_payload = self._prepare_xml_payload(xml, state)
try:
response = await self._api.request(
response = await self._api_client.request(
endpoint, headers=self.XML_HEADERS, data=xml_payload, method='PUT'
)
response = response.text
except APIRequestError:
action = 'enable' if enable else 'disable'
action = 'enable' if state else 'disable'
err_msg = f'Failed to {action} {full_name}.'
self._log.error(err_msg)
raise
Expand All @@ -68,7 +68,7 @@ async def switch_enabled_state(
async def _get_switch_state(
self, name: Detection, endpoint: str
) -> tuple[bool, str]:
response = await self._api.request(endpoint, method='GET')
response = await self._api_client.request(endpoint, method='GET')
xml = response.text
xml_dict = xmltodict.parse(xml)
state: str = xml_dict[DETECTION_SWITCH_MAP[name]['method'].value]['enabled']
Expand Down
12 changes: 8 additions & 4 deletions hikcamerabot/clients/hikvision/endpoints/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class AlertStreamEndpoint(AbstractEndpoint):
async def __call__(self) -> AsyncGenerator[str, None]:
url = urljoin(self._api_client.host, Endpoint.ALERT_STREAM.value)
timeout = httpx.Timeout(CONN_TIMEOUT, read=300)
response: httpx.Response
async with self._api_client.session.stream(
'GET', url, timeout=timeout
) as response:
Expand All @@ -149,8 +150,11 @@ async def __call__(self) -> AsyncGenerator[str, None]:
class SwitchEndpoint(AbstractEndpoint):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self._switch = CameraConfigSwitch(api=self._api_client)
self._switch = CameraConfigSwitch(api_client=self._api_client)

async def __call__(self, trigger: Detection, enable: bool) -> Optional[str]:
"""Switch method to enable/disable Hikvision functions."""
return await self._switch.switch_enabled_state(trigger, enable)
async def __call__(self, trigger: Detection, state: bool) -> Optional[str]:
"""Switch method to enable/disable Hikvision functions.
:param state: Boolean value indicating on/off switch state.
"""
return await self._switch.switch_enabled_state(trigger, state)
1 change: 1 addition & 0 deletions hikcamerabot/config/schemas/main_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class CamAPIAuth(Schema):

class CamAPI(Schema):
host = f.Str(required=True, validate=non_empty_str)
port = f.Int(required=True, validate=int_min_1)
auth = f.Nested(CamAPIAuth(), required=True)
stream_timeout = f.Int(required=True, validate=int_min_1)

Expand Down
10 changes: 5 additions & 5 deletions hikcamerabot/event_engine/handlers/inbound.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,19 @@ async def _handle(self, event: DetectionConfEvent) -> None:
cam = event.cam
trigger = event.type
name = DETECTION_SWITCH_MAP[trigger]['name'].value
enable = event.switch
state = event.state

self._log.info(
'%s camera\'s %s has been requested',
'Enabling' if enable else 'Disabling',
'Enabling' if state else 'Disabling',
name,
)
text = await cam.services.alarm.trigger_switch(enable, trigger)
text = await cam.services.alarm.trigger_switch(trigger=trigger, state=state)
await self._result_queue.put(
DetectionConfOutboundEvent(
event=event.event,
type=event.type,
switch=event.switch,
state=state,
cam=cam,
message=event.message,
text=text,
Expand Down Expand Up @@ -156,6 +156,6 @@ async def _handle(self, event: IrcutConfEvent) -> None:
SendTextOutboundEvent(
event=Event.SEND_TEXT,
message=event.message,
text=bold('OK'),
text=bold(f'IrcutFilter set to "{event.filter_type.value}"'),
)
)
8 changes: 4 additions & 4 deletions hikcamerabot/services/alarm/alarm.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def _start_service_task(self) -> None:
async def _enable_triggers_on_camera(self) -> None:
for trigger in self.ALARM_TRIGGERS:
if self._conf[trigger].enabled:
await self.trigger_switch(enable=True, trigger=Detection(trigger))
await self.trigger_switch(trigger=Detection(trigger), state=True)

async def stop(self) -> None:
"""Disable alarm."""
Expand All @@ -95,12 +95,12 @@ async def alert_stream(self) -> AsyncGenerator[str, None]:
async for chunk in self._api.alert_stream():
yield chunk

async def trigger_switch(self, enable: bool, trigger: Detection) -> Optional[str]:
async def trigger_switch(self, trigger: Detection, state: bool) -> Optional[str]:
"""Trigger switch."""
full_name: str = DETECTION_SWITCH_MAP[trigger]['name'].value
self._log.debug('%s %s', 'Enabling' if enable else 'Disabling', full_name)
self._log.debug('%s %s', 'Enabling' if state else 'Disabling', full_name)
try:
return await self._api.switch(enable=enable, trigger=trigger)
return await self._api.switch(trigger=trigger, state=state)
except HikvisionAPIError as err:
err_msg = f'{full_name} Switch encountered an error: {err}'
self._log.error(err_msg)
Expand Down
2 changes: 1 addition & 1 deletion hikcamerabot/services/stream/dvr/file_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def __init__(self, filename: str, lock_count: int, cam: 'HikvisionCam') -> None:

self._storage_path: str = self._cam.conf.livestream.dvr.local_storage_path
self._full_path = os.path.join(self._storage_path, self._filename)
self._thumbnail = os.path.join(self._storage_path, f'{self.name}.jpg')
self._thumbnail = os.path.join(self._storage_path, f'{self.name}-thumb.jpg')

self._duration: Optional[int] = None
self._width: Optional[int] = None
Expand Down
2 changes: 1 addition & 1 deletion hikcamerabot/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.5.1'
__version__ = '1.5.2'
24 changes: 24 additions & 0 deletions releases/release_1.5.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Release info

Version: 1.5.2

Release date: October 17, 2022

# Important
1. Added required `port` config variable for camera API section. Defaults to `80` in the `config-template.json` template. Just add it to your existing config and you're good.

```json
...
"camera_list": {
"cam_1": {
"api": {
"host": "http://192.168.1.1",
"port": 80,
...
```

# New features
N/A

# Misc
N/A

0 comments on commit 11dbdba

Please sign in to comment.