Русский
Русский
English
Статистика
Реклама

Яндекс api

Пишем full stack монолит с помощью Angular Universal NestJS PostgreSQL

10.08.2020 02:14:34 | Автор: admin
Привет, Хабр!

В этой статье мы создадим готовый шаблон-монолит, который можно брать за основу нового fullstack приложения как скелет для навешивания функционала.


Эта статья будет полезна, если вы:


  • Начинающий fullstack-разработчик;
  • Стартапер, который пишет MVP чтобы проверить гипотезу.

Почему выбрал такой стек:


  • Angular: имею много опыта в нем, люблю строгую архитектуру и Typescript из коробки, выходец из .NET
  • NestJS: тот-же язык, та-же архитектура, быстрое написание REST API, возможность в дальнейшем пересесть на Serverless (дешевле виртуалки)
  • PostgreSQL: Собираюсь хоститься в Яндекс.Облаке, на минималках дешевле на 30% чем MongoDB

Прайс яндекса


Прежде чем написать статью, поискал на хабре статьи про подобный кейс, нашел следующее:



Из этого ничего не описывает "скопировал и вставил" или дает ссылки на то что еще нужно дорабатывать.


Оглавление:


1. Создаем Angular приложение и добавляем библиотеку компонентов ng-zorro
2. Устанавливаем NestJS и решаем проблемы с SSR
3. Делаем API на NestJS и подключаем к фронту
4. Подключаем базу данных PostgreSQL



1. Создаем Angular приложение


Установим Angular-CLI чтобы создавать SPA-сайты на Ангуляре:


npm install -g @angular/cli

Создадим Angular приложение с помощью следующей команды:


ng new angular-habr-nestjs

Далее переходим в папку приложения и запускаем, чтобы проверить работоспособность:


cd angular-habr-nestjsng serve --open

Статическое SPA-приложение на Angular


Приложение создалось. Подключаем библиотеку NG-Zorro:


ng add ng-zorro-antd

Далее выбираем следующие конфигурации библиотеки:


? Enable icon dynamic loading [ Detail: https://ng.ant.design/components/icon/en ] Yes? Set up custom theme file [ Detail: https://ng.ant.design/docs/customize-theme/en ] No? Choose your locale code: ru_RU? Choose template to create project: sidemenu

Эта конфигурация заменит содержимое app.component на дизайн с менюшкой слева, футером и хедером и подключит локализацию на русском языке:


Подключили NG-Zorro


В данной статье мы отобразим список данных для наглядности, поэтому добавим простенькую табличку в компоненте src/app/pages/welcome, который сгенерил NG-Zorro:
Пример взят отсюда:
https://ng.ant.design/components/table/en


// welcome.component.html<nz-table #basicTable [nzData]="items$ | async"> <thead> <tr>  <th>Name</th>  <th>Age</th>  <th>Address</th> </tr> </thead> <tbody> <tr *ngFor="let data of basicTable.data">  <td>{{ data.name }}</td>  <td>{{ data.age }}</td>  <td>{{ data.address }}</td> </tr> </tbody></nz-table>

// welcome.module.tsimport { NgModule } from '@angular/core';import { WelcomeRoutingModule } from './welcome-routing.module';import { WelcomeComponent } from './welcome.component';import { NzTableModule } from 'ng-zorro-antd';import { CommonModule } from '@angular/common';@NgModule({ imports: [  WelcomeRoutingModule,  NzTableModule, // Добавили для таблицы  CommonModule // Добавили для пайпа async ], declarations: [WelcomeComponent], exports: [WelcomeComponent]})export class WelcomeModule {}

// welcome.component.tsimport { Component, OnInit } from '@angular/core';import { Observable, of } from 'rxjs';import { HttpClient } from '@angular/common/http';import { share } from 'rxjs/operators';@Component({ selector: 'app-welcome', templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.scss']})export class WelcomeComponent implements OnInit { items$: Observable<Item[]> = of([  {name: 'Вася', age: 24, address: 'Москва'},  {name: 'Петя', age: 23, address: 'Лондон'},  {name: 'Миша', age: 21, address: 'Париж'},  {name: 'Вова', age: 23, address: 'Сидней'} ]); constructor(private http: HttpClient) { } ngOnInit() { } // Сразу напишем метод к бэку, понадобится позже getItems(): Observable<Item[]> {  return this.http.get<Item[]>('/api/items').pipe(share()); }}interface Item { name: string; age: number; address: string;}

Получилось следующее:


Табличка NG-Zorro



2. Устанавливаем NestJS


Далее установим NestJS таким образом, чтобы он предоставил Angular Universal (Server Side Rendering) из коробки и напишем пару ендпоинтов.


ng add @nestjs/ng-universal

После установки, запускаем наш SSR с помощью команды:


npm run serve

И вот уже первый косяк :) У нас появляется следующая ошибка:


TypeError: Cannot read property 'indexOf' of undefined  at D:\Projects\angular-habr-nestjs\node_modules\@nestjs\ng-universal\dist\utils\setup-universal.utils.js:35:43  at D:\Projects\angular-habr-nestjs\dist\server\main.js:107572:13  at View.engine (D:\Projects\angular-habr-nestjs\node_modules\@nestjs\ng-universal\dist\utils\setup-universal.utils.js:30:11)  at View.render (D:\Projects\angular-habr-nestjs\node_modules\express\lib\view.js:135:8)  at tryRender (D:\Projects\angular-habr-nestjs\node_modules\express\lib\application.js:640:10)  at Function.render (D:\Projects\angular-habr-nestjs\node_modules\express\lib\application.js:592:3)  at ServerResponse.render (D:\Projects\angular-habr-nestjs\node_modules\express\lib\response.js:1012:7)  at D:\Projects\angular-habr-nestjs\node_modules\@nestjs\ng-universal\dist\angular-universal.module.js:60:66  at Layer.handle [as handle_request] (D:\Projects\angular-habr-nestjs\node_modules\express\lib\router\layer.js:95:5)  at next (D:\Projects\angular-habr-nestjs\node_modules\express\lib\router\route.js:137:13)

Чтобы решить косяк, зайдем в файл server/app.module.ts и поменяем значение liveReload на false:


import { Module } from '@nestjs/common';import { AngularUniversalModule } from '@nestjs/ng-universal';import { join } from 'path';@Module({ imports: [  AngularUniversalModule.forRoot({   viewsPath: join(process.cwd(), 'dist/browser'),   bundle: require('../server/main'),   liveReload: false  }) ]})export class ApplicationModule {}

Также подтюним конфиг тайпскрипта, так-как эта конфигурация не взлетает с использованием Ivy рендера:


// tsconfig.server.json{ "extends": "./tsconfig.app.json", "compilerOptions": {  "outDir": "./out-tsc/server",  "target": "es2016",  "types": [   "node"  ] }, "files": [  "src/main.server.ts" ], "angularCompilerOptions": {  "enableIvy": false, // Добавили флажок  "entryModule": "./src/app/app.server.module#AppServerModule" }}

После пересоберем приложение командой ng run serve чтобы SSR заработал.


Angular SSR + NestJS


Ура! SSR подрубился, но как видимо в devtools он приходит с кривыми стилями.


Добавим extractCss: true, который позволит выносить стили не в styles.js, а в styles.css:


// angular.json..."architect": {    "build": {     "builder": "@angular-devkit/build-angular:browser",     "options": {      "outputPath": "dist/browser",      "index": "src/index.html",      "main": "src/main.ts",      "polyfills": "src/polyfills.ts",      "tsConfig": "tsconfig.app.json",      "aot": true,      "assets": [       "src/favicon.ico",       "src/assets",       {        "glob": "**/*",        "input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",        "output": "/assets/"       }      ],      "extractCss": true, // Добавили флажок      "styles": [       "./node_modules/ng-zorro-antd/ng-zorro-antd.min.css",       "src/styles.scss"      ],      "scripts": []     },...

Также подключим стили библиотеки в app.component.scss:


// app.component.scss@import "~ng-zorro-antd/ng-zorro-antd.min.css"; // Подключили стили:host { display: flex; text-rendering: optimizeLegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;}.app-layout { height: 100vh;}...

Теперь стили подключены, SSR отдает страничку со стилями, но мы видим что сначала у нас грузится SSR, потом страница моргает и отрисовывается CSR (Client Side Rendering). Это решается следующим способом:


import { NgModule } from '@angular/core';import { Routes, RouterModule } from '@angular/router';const routes: Routes = [ { path: '', pathMatch: 'full', redirectTo: '/welcome' }, { path: 'welcome', loadChildren: () => import('./pages/welcome/welcome.module').then(m => m.WelcomeModule) }];@NgModule({ imports: [RouterModule.forRoot(routes, {initialNavigation: 'enabled', scrollPositionRestoration: 'enabled'})], // Добавили initialNavigation, scrollPositionRestoration exports: [RouterModule]})export class AppRoutingModule { }

  • initialNavigation: 'enabled' дает инструкцию роутингу не отрисовывать страницу, если уже загружена через SSR
  • scrollPositionRestoration: 'enabled' скролит страницу наверх при каждом роутинге.


3. Сделаем пару ендпоинтов на NestJS


Перейдем в папку server и создадим первый контроллер items:


cd servernest g module itemsnest g controller items --no-spec

// items.module.tsimport { Module } from '@nestjs/common';import { ItemsController } from './items.controller';@Module({ controllers: [ItemsController]})export class ItemsModule {}

// items.controller.tsimport { Controller } from '@nestjs/common';@Controller('items')export class ItemsController {}

Контроллер и модуль создались. Создадим метод на получение списка items и на добавление объекта в список:


// server/src/items/items.controller.tsimport { Body, Controller, Get, Post } from '@nestjs/common';class Item { name: string; age: number; address: string;}@Controller('items')export class ItemsController { // для простоты данные взял из Angular private items: Item[] = [  {name: 'Вася', age: 24, address: 'Москва'},  {name: 'Петя', age: 23, address: 'Лондон'},  {name: 'Миша', age: 21, address: 'Париж'},  {name: 'Вова', age: 23, address: 'Сидней'} ]; @Get() getAll(): Item[] {  return this.items; } @Post() create(@Body() newItem: Item): void {  this.items.push(newItem); }}

Попробуем вызвать GET в Postman:


GET запросы апишки NestJS


Отлично, работает! Обратите внимание, вызываем метод GET items с префиксом api, который ставится автоматически в файле server/main.ts при установке NestJS:


// server/main.tsimport { NestFactory } from '@nestjs/core';import { ApplicationModule } from './app.module';async function bootstrap() { const app = await NestFactory.create(ApplicationModule); app.setGlobalPrefix('api'); // Это префикс await app.listen(4200);}bootstrap();

Теперь прикрутим бэк к фронту. Возвращаемся к файлу welcome.component.ts и делаем запрос списка к бэку:


// welcome.component.tsimport { Component, OnInit } from '@angular/core';import { Observable, of } from 'rxjs';import { HttpClient } from '@angular/common/http';import { share } from 'rxjs/operators';@Component({ selector: 'app-welcome', templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.scss']})export class WelcomeComponent implements OnInit { items$: Observable<Item[]> = this.getItems(); // прикрутили вызов бэка constructor(private http: HttpClient) { } ngOnInit() { } getItems(): Observable<Item[]> {  return this.http.get<Item[]>('/api/items').pipe(share()); }}interface Item { name: string; age: number; address: string;}

Можно увидеть что апиха на фронте дергается, но также дергается и в SSR, причем с ошибкой:


Дергание апихи в SSR


Ошибка при запросе в SSR решается следующим способом:


// welcome.component.tsimport { Component, OnInit } from '@angular/core';import { Observable, of } from 'rxjs';import { HttpClient } from '@angular/common/http';import { share } from 'rxjs/operators';@Component({ selector: 'app-welcome', templateUrl: './welcome.component.html', styleUrls: ['./welcome.component.scss']})export class WelcomeComponent implements OnInit { items$: Observable<Item[]> = this.getItems(); // прикрутили вызов бэка constructor(private http: HttpClient) { } ngOnInit() { } getItems(): Observable<Item[]> {  return this.http.get<Item[]>('http://localhost:4200/api/items').pipe(share()); // Прописали полный путь к апихе чтобы SSR не ругался }}interface Item { name: string; age: number; address: string;}

Чтобы исключить двойной запрос к апихе (один на SSR, другой на фронте), нужно проделать следующее:


  • Установим библиотеку @nguniversal/common:

npm i @nguniversal/common

  • В файле app/app.module.ts добавим модуль для запросов из SSR:

// app.module.tsimport { BrowserModule } from '@angular/platform-browser';import { NgModule } from '@angular/core';import { AppRoutingModule } from './app-routing.module';import { AppComponent } from './app.component';import { IconsProviderModule } from './icons-provider.module';import { NzLayoutModule } from 'ng-zorro-antd/layout';import { NzMenuModule } from 'ng-zorro-antd/menu';import { FormsModule } from '@angular/forms';import { HttpClientModule } from '@angular/common/http';import { BrowserAnimationsModule } from '@angular/platform-browser/animations';import { NZ_I18N } from 'ng-zorro-antd/i18n';import { ru_RU } from 'ng-zorro-antd/i18n';import { registerLocaleData } from '@angular/common';import ru from '@angular/common/locales/ru';import {TransferHttpCacheModule} from '@nguniversal/common';registerLocaleData(ru);@NgModule({ declarations: [  AppComponent ], imports: [  BrowserModule.withServerTransition({ appId: 'serverApp' }),  TransferHttpCacheModule, // Добавили  AppRoutingModule,  IconsProviderModule,  NzLayoutModule,  NzMenuModule,  FormsModule,  HttpClientModule,  BrowserAnimationsModule ], providers: [{ provide: NZ_I18N, useValue: ru_RU }], bootstrap: [AppComponent]})export class AppModule { }

Схожую операцию проделаем с app.server.module.ts:


// app.server.module.tsimport { NgModule } from '@angular/core';import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';import { AppModule } from './app.module';import { AppComponent } from './app.component';@NgModule({ imports: [  AppModule,  ServerModule,  ServerTransferStateModule, // Добавили ], bootstrap: [AppComponent],})export class AppServerModule {}

Хорошо. Теперь получаем данные из апи в SSR, отрисовываем на форме, отдаем на фронт и тот не делает повторных запросов.


Запроса нет, данные есть!



4. Подключим базу PostgreSQL


Подключим библиотеки для работы с PostgreSQL, также будем использовать TypeORM для работы с базой:


npm i pg typeorm @nestjs/typeorm

Внимание: у вас уже должна быть установлена PostgreSQL с базой внутри.


Описываем конфиг подключения к базе в server/app.module.ts:


// server/app.module.tsimport { Module } from '@nestjs/common';import { AngularUniversalModule } from '@nestjs/ng-universal';import { join } from 'path';import { ItemsController } from './src/items/items.controller';import { TypeOrmModule } from '@nestjs/typeorm';@Module({ imports: [  AngularUniversalModule.forRoot({   viewsPath: join(process.cwd(), 'dist/browser'),   bundle: require('../server/main'),   liveReload: false  }),  TypeOrmModule.forRoot({ // Конфиг подключения к базе   type: 'postgres',   host: 'localhost',   port: 5432,   username: 'postgres',   password: 'admin',   database: 'postgres',   entities: ['dist/**/*.entity{.ts,.js}'],   synchronize: true  }) ], controllers: [ItemsController]})export class ApplicationModule {}

Немного про поля конфига:


  • type: указываем название типа базы данных, к которой подключаемся
  • host и port: место где база хостится
  • username и password: аккаунт для этой базы
  • database: название базы
  • entities: путь, откуда будем брать сущности для схемы нашей базы

По последнему пункту, нужно создать сущность Item для мапинга полей в базу:


// server/src/items/item.entity.tsimport { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn } from 'typeorm/index';@Entity()export class ItemEntity { @PrimaryGeneratedColumn() id: number; @CreateDateColumn() createDate: string; @Column() name: string; @Column() age: number; @Column() address: string;}

Далее свяжем эту сущность с нашей базой.


// items.module.tsimport { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { ItemEntity } from './item.entity';import { ItemsController } from './items.controller';@Module({ imports: [  TypeOrmModule.forFeature([ItemEntity]) // Подключаем фича-модуль и указываем сущности базы ], controllers: [ItemsController]})export class ItemsModule {}

Теперь укажем в контроллере, что хотим работать с базой, а не кешем:


// items.controller.tsimport { Body, Controller, Get, Post } from '@nestjs/common';import { ItemEntity } from './item.entity';import { InjectRepository } from '@nestjs/typeorm';import { Repository } from 'typeorm/index';interface Item { name: string; age: number; address: string;}@Controller('items')export class ItemsController { constructor(@InjectRepository(ItemEntity)       private readonly itemsRepository: Repository<ItemEntity>) { // Подключили репозиторий } @Get() getAll(): Promise<Item[]> {  return this.itemsRepository.find(); } @Post() create(@Body() newItem: Item): Promise<Item> {  const item = this.itemsRepository.create(newItem);  return this.itemsRepository.save(item); }}

Проверим работу апихи в Postman:


POST к апихе с базой


Работает. Потыкали несколько раз постман, посмотрим что записалось в базе с помощью DBeaver:


Записи в базе


Отлично! В базе есть, посмотрим как выглядит на фронте:


Рабочее fullstack приложение


Готово! Мы сделали fullstack приложение, с которым можно работать дальше.


P.S. Сразу поясню следующее:


  • Вместо Ng-Zorro вы можете использовать любую другую библиотеку, например Angular Material. Мне она лично не зашла из-за сложности разработки;
  • Я знаю, что нужно на бэке использовать сервисы, а не напрямую дергать базу в контроллерах. Эта статья о том, как решив проблемы "влоб" получить MVP с которым можно работать, а не про архитектуру и паттерны;
  • Вместо вписывания на фронте http://localhost:4200/api возможно лучше написать интерсептор и проверять откуда мы стучимся

Полезные ссылки:


Подробнее..

Салют от Сбера в Яндекс.Облаке

03.12.2020 02:08:23 | Автор: admin


В сентябре 2020 г. Сбербанк переименовал себя просто в Сбер (т.н. ребрендинг), и на радостях запустил собственную платформу голосовых ассистентов под названием Салют. Особенностью Салюта является наличие сразу трёх голосовых ассистентов на выбор пользователей: Сбер мужчина, стиль обращения на вы, Афина женщина, обращается также на вы, и Джой девушка с дружеским ты.

Сбер (банк, не его тёзка голосовой ассистент) открыл эту платформу для сторонних разработчиков, пригласив их делать для неё приложения, т.н. смартапы аналог навыков голосовой помощницы Алисы, и учредив для них конкурс с весьма щедрым призовым фондом. В этом туториале мы рассмотрим как сделать смартап на Node.js, разместить его код в Яндекс.Облаке (используя функции), и, наконец, создать проект в Салюте, пройти там модерацию, и опубликовать наш смартап, чтобы он стал общедоступным.

А делать мы будем смартап под кодовым названием Умные Мысли. Моя любимая тема, поскольку это уже третья статья. До этого были: Алиса приобретает навык (про Умные Мысли для Алисы), и Строим Telegram-бот в Яндекс.Облаке (про создание @SmartThoughtsBot бота с таким же мирским именем). Итак, вот план на сегодня:
1. Сначала мы склонируем репозиторий с кодом нашего смартапа, и установим необходимые зависимости. Вы также сможете получить дополнительную полезную информацию прямо в комментариях кода.
2. Затем разместим наш код в Яндекс.Облаке, используя функции т.н. технологии serverless.
3. Потом мы создадим проект в SmartApp Studio веб-интерфейсе для создания приложений для Салюта, отправим наш смартап на модерацию, и дождавшись положительного ответа опубликуем его.
4. И наконец, посмотрим как наш смартап работает т.е. протестируем его.
5. Но после конца, будет ещё и заключение. Здесь я поделюсь своим мнением и впечатлением о новой платформе. Но это легко можно будет пропустить, просто не дочитав пару абзацев в конце статьи.

1. Код смартапа


Cклонируйте GitHub-репозиторий, перейдите в корневой каталог проекта, и установите зависимости, как показано ниже:

Необходимое ПО
Подразумевается, что на вашей рабочей машине (т.н. компьютере) должны быть установлены Node.js (версии не ниже 10), npm (по умолчанию устанавливается вместе с Node.js) и Git. Если чего-то не хватает сначала установите, а потом продолжайте. Кроме того, потребуется редактор для работы с кодом. У меня это Visual Studio Code.

git clone https://github.com/stmike/smart-thoughts-salute.git
cd smart-thoughts-salute
npm install

Код этого смартапа написан с использованием открытого SmartApp API и спецификации Yandex Cloud Functions, с которыми, я уверен, вы захотите познакомиться. После развёртывания, примерно вот так этот проект будет выглядеть у вас.

Код смартапа

Обратите внимание, что код испещрён комментариями, словно шумерские таблички клинописью. Думаю, что читать различные пояснения удобнее в контексте кода, чем поочерёдно глядеть то в код, то в текст статьи. Внесите в проект какие-нибудь изменения, например, в файл /src/lexicon-formal где собраны фразы смартапа с обращением на вы для Сбера и Афины, или в файл /src/lexicon-unformal где фразы с обращением на ты для Джой. После этого сделайте ZIP-архив, в который включите: файл index.js, каталог с файлами src, и каталог с файлами node_modules. Этот архив мы скоро зальём в Яндекс.Облако.

2. Яндекс.Облако


Перейдите в Яндекс.Облако. Если вы с ним ещё незнакомы следуйте там инструкциям для создания своего платежного аккаунта и прочих первоначальных настроек. В своей статье Алиса в стране Битрикс я уже подробно касался всего этого, поэтому, чтобы здесь не повторяться отсылаю читателей, которым надо больше информации на эту тему, к этой своей статье. Итак, в Яндекс.Облаке выбираем слева в меню Cloud functions, и нажимаем кнопку Создать функцию. Откроется страница создания функции.

Создание функции

Заполняем поля Имя и Описание и нажимаем на кнопку Создать. Теперь запустится мастер создания функции.

Выбор языка

Выберите Node.js 10-й версии, а также снимите галочку в чекбоксе Добавить файлы с примерами кода. Нажмите кнопку Продолжить.

Заполнение полей функции

На этом шаге мастера выберите опции и заполните поля, показанные на скриншоте, следующим образом:
1. Выберите вкладку ZIP-архив.
2. Нажмите кнопку Выбрать файл, и выберите на своей локальной машине ZIP-архив с нашим смартапом, который мы недавно создали.
3. В поле Точка входа укажите index.skill. Здесь index означает имя файла, а skill имя модуля в нашем index-файле.
4. В поле Таймаут укажите 7, потому что максимум 7 секунд Салют будет ждать ответа от этой функции (для сравнения Алисаждёт максимум 3 секунды).
5. Нажмите кнопку Создать версию.
Через несколько секунд новая функция сгенерируется, и вы окажетесь на странице Обзор.

Создана новая функция

Здесь надо включить переключатель Публичная функция на ON, т.е. сделать так, чтобы Салют мог её без проблем вызывать. Ссылку для вызова и Идентификатор функции держите в тайне, поскольку публичную функцию может вызвать любой, а платить будете вы. Запишите себе на манжете Ссылку для вызова она нам очень скоро потребуется.

3. Создание проекта для Салюта


Проекты для Салюта создаются с помощью SmartApp Studio.
Войдите в эту студию, если у вас уже есть Сбер ID, или зарегистрируйтесь в противном случае. Регистрация процесс тривиальный, останавливаться на этом не будем. Но отмечу важную особенность: осуществляется она по российскому номеру мобильного телефона, и если у вас этого нет (российского номера, мобильного телефона, или ничего из перечисленного), зарегистрироваться, как я понимаю, не получится.

Вход в Смартап студию по Сбер ID

Вошли. В моём аккаунте мы видим уже созданный и пока единственный смартап, кодом и способом создания которого я с вами здесь и сегодня делюсь. Кнопочка Изучить документацию вам, вероятно, понадобится в первую очередь, но для целей нашей статьи, мы всё же нажмём сейчас на Создать смартап.

Список смартапов

На изображении видно, что под названием смартапа имеются надписи Webhook и Запущено. Первая надпись говорит о том, что наше приложение работает на стороннем сервисе (у нас это Яндекс.Облако), а вторая что приложение опубликовано (я бы так и написал вместо Запущено). Перейдём же к созданию своего смартапа. Вводим название (дублировать нельзя, поэтому выберите своё, например, Мудрые фразы). Затем выберите Chat App (да, там есть ещё тип Canvas App и в планах, по-видимому, Native App, но сегодня мы будем делать именно с Chat App, как более простым в изготовлении, а об остальных типах почитайте в документации).

Выбор типа смартапа

Затем в разделе страницы Выбор инструмента, выберите Есть готовое приложение (и это абсолютная правда), а в поле Webhook вставьте Ссылку для вызова нашей функции в Яндекс.Облаке, которую вы совсем недавно записали на своём манжете.

Ввод вебхука

Ну и наконец, нажмите на кнопку Создать смартап, после чего сгенерируется новый проект, а на странице Параметры вы должны увидеть нечто похожее на изображенное ниже. Добавьте описание, заполните другие поля, и смело отправляйте свой новый смартап на модерацию. Но знайте: если не примут по причине однотипности с уже имеющимся приложением какую-либо ответственность за это я нести отказываюсь. Однако, в любом случае примут или не примут, подали вы смартап на модерацию или нет в вашем аккаунте ваш собственный смартап с этого момента уже будет работать! Но об этом чуть ниже.

Модерация

Обратите также внимание, что вверху на вкладке Каталог присутствует бейджик скоро. Если вы попытаетесь разместить иконку и скриншоты смартапа ничего не выйдет. Когда я подавал свой смартап на модерацию в первой половине ноября 2020 г., после его принятия, сотрудник (возможно это также и модератор в одном лице) просил прислать иконку по почте, а за скриншоты вообще ничего не говорил. Но зато говорил, что скоро такое положение дел изменится, и всё заработает полноценно. Будем надеятся.

Каталог смартапов

4. Тестирование


Пришло время проверить наш смартап в деле. Установите мобильное приложение СБЕР Салют для Android или СБЕР Салют для iOS. Это приложение главным образом предназначено для управления телевизионной приставкой SberBox, но даже без приставки в нём можно полноценно общаться с голосовыми ассистентами, и запускать смартапы сторонних разработчиков. Для этого откройте на своём смартфоне приложение СБЕР Салют, войдите под своим Сбер ID именно тем ID, под которым вы залогинены в SmartApp Studio! Нажмите внизу на круглую иконку, символизирующую голосового ассистента, и прослушайте его приветствие (ассистентом по умолчанию там выбран Сбер). Затем скажите: Включи... или Запусти... и произнесите название, которое вы дали своему смартапу. Повторяю: вы можете тестировать свои неопубликованные смартапы, если Сбер ID, с которым вы вошли в мобильное приложение СБЕР Салют тот же самый, что и Сбер ID, с которым вы входили в SmartApp Studio, когда создавали свой смартап. Если вы ещё ничего своего не создали, но уже хотите посмотреть, что в реальности производит программный код, который вам здесь предлагают, скажите ассистенту: Включи Умные Мысли. Запустится простой смартап, который позволит вам немного с ним пообщаться. Обратите внимание, что в Салюте не обязательно говорить слова смартап или навык, как это необходимо при общении с Алисой, например: Включи смартап Умные Мысли, или Запусти навык Умные Мысли. Эти слова здесь можно опускать, что делает общение с голосовым ассистентом более естественным, а значит приятным.

Умные мысли запущены

Когда немного пообщаетесь со Сбером (ассистентом), скажите: Позови Афину. А затем: Позови Джой. Убедитесь, что каждый из них имеет разный стиль общения: кто-то на вы, а кто-то на ты. Это значит, что ваш смартап (или Умные Мысли) эту фичу Салюта корректно обрабатывет.

5. Заключение


Очевидно (по крайней мере мне), что платформа Салют, находится ещё в детском или, в лучшем случае, отроческом возрасте. Если сравнить те же Умные Мысли здесь и у Алисы у Алисы будет покруче там ещё и музыка, и картинки. В Салюте всё это недоступно. Говорят, что пока. И это похоже на правду уж с очень большой энергией и напором там взялись за голосовых помощников! Кроме того, в Салюте организована необычайно активная и оперативная поддержка для независимых разработчиков часто просто в реальном времени в Telegram-канале SmartApp Studio Community & Support. Подозреваю, что Сбер (банк) задался целью в самое ближайшее время стать в России номером один в рыночной нише голосовых ассистентов. Поэтому, если вы понимаете что такое т.н. окна возможностей и немного авантюристичны по своей природе можете подключаться к процессу. Работать на Салют ещё трудно, но уже реально.

На сегодня это всё. Другие материалы следуют. Кому подобное читать интересно подписывайтесь на уведомления о новых публикациях. Подписаться можно на этом сайте (кнопка Подписаться внизу), или на Telegram-канал IT Туториал Захар, или на одноимённое сообщество в VK, или Twitter @mikezaharov.
Подробнее..

Не спешите выкидывать свой PolyCom

20.11.2020 18:16:58 | Автор: admin

Если у вас где-то в углу неприкаянно грустит телефон компании Polycom не спешите от него избавляться! Он еще сможет вам послужить. По крайней мере ковыряние с ним может доставить массу удовольствия. Все нижеописанное тестировалось на устаревшей модели Polycom SoundPoint IP 450(от 1500 рублей за БУ на ebay), но также подходит и для большинства более современных моделей т.к. все они работают под управлением одной и той-же операционной системы UC.

Да, именно так и вы не ослышались даже древний офисный телефон работает под управлением проприетарной ОС.

Итак, что-же полезного и интересного можно сделать с телефоном Polycom в домашних условиях:

  • Вывод полезной информации на экран телефона и использование внутреннего браузера

  • SIP телефония & Lync integration(Skype for Business) для SIP моделей

  • Интеграция с Google Chrome набор номера через расширение для google chrome

  • Кастомизация UI телефона

  • Интеграция с LDAP и AD и использование телефонных книг

Итак, обо все по прядку.

Встроенный браузер

Большинство моделей телефонов polycom оснащены тем или иным экранчиком, а прошивка поддерживает xhtml-браузер. У браузера есть 2 режима работы: idle отображается контент во время ожидания и active отображается контент во время использования телефона.

Телефон поддерживает только самые базовые html-теги, а также несколько собственных тегов для управления навигацией:

<html>

<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/></head>

<body>

<softkey index="1" label="Back" action="SoftKey:Back" />

<softkey index="2" label="Weather" action="SoftKey:Fetch;http://raspberrypi:88" />

<softkey index="3" label="Refresh" action="SoftKey:Refresh" />

<a href="Tel://89264341830">Taxi</a>|

<a href="Tel://89652991881">Суши</a>|

<a href="Tel://84965246699">Пицца</a>|

<br/>

<a href="Key:DoNotDisturb">DNDSettings</a>

</body>

</html>

Современные модели телефонов имеют расширенную поддержку HTML и даже умеют работать с Javascript. Используя встроенный браузер телефона довольно легко каcтомизировать UI/UX под свои предпочтения. В моем случае - это отображение прогноза погоды на основе Yandex Weather API и несколько статических страниц с наиболее часто используемыми телефонами.

Вместо погоды можно выводить последние новости, курсы валют, биржевые индексы и т.д. В моем случае страницы размещены на raspberry PI в домашней сети, но никто не мешает их разместить на любом бесплатном сервисе.

Самый простой способ настроить работу внутреннего браузера это зайти на внутренний сайт телефона (просто введите IP телефона и дефолтный пароль 456) и на вкладке Settings/Microbrowser вы найдете все необходимые настройки:

SIP

Если ваша модель поддерживает SIP протокол вам открывается масса интересных возможностей.

Большинство телефонов может поддерживает несколько линий. Если у вас мегафон, то на одну линию можно завести номер мобильного телефона, предварительно подключив бесплатную услугу мультифон и переадресацию звонков на телефон и SIP как описано здесь.

Используя тариф без абонентской связи вы получаете условно-бесплатный домашний телефон.

На второй линии у меня заведен мой личный номер мобильного телефона, который используется для исходящих звонков или для приема входящих звонков в случае проблем с сотовым покрытием. Ну а на третью линию можно подключить международного SIP провайдера и иметь за доллар или два номер в США или любой другой стране. Это позволит сэкономить колоссальные деньги на международных звонках. Настройку можно осуществить как через упоминаемый ранее внутренний сайт телефона, так и через прямое редактирование файла конфигурации телефона.

Чтобы выгрузить файл конфигурации зайдите в Utilities/Import-Export configuration в раздел Export.

Выгруженный файл конфигурации содержит все настройки телефона кроме паролей!

Информацию о содержимом файла конфигурации можно найти здесь.

В качестве альтернативы можно использовать свободную линию для подключение к Microsoft Lync серверу для интеграции со Skype For Business.

Интеграция с браузером Chrome

Пожалуй основной причиной по которой стационарные телефоны стоят без дела является их крайне неудобная интеграция с телефонными книгами, браузером и поисковиками. В рамках офиса есть возможность подключить к телефон к AD, но искать нужный контакт в телефоне это та еще боль.

Конечно для домашнего использования можно организовать LDAP интеграцию с Google contacts или же загрузить список контактов используя provisioning, но основную проблему с ужасным UX это не исправляет.

Для решения этой проблемы я сделал небольшое расширение для Google chrome, которое позволяет выделить номерна любой страничке и стартовать звонок со стационарного телефона.

Телефоны Polycom поддерживают, так называемые, интеграционные вызовы, когда отправив POST запрос извне на телефон можно заставить его выполнить то или иное действие: Позвонить на указанный номер, перейти в режим Не беспокоить, включить переадресацию, вывести сообщения на экран и т.д. и т.п.

К примеру такой запрос эмулирует нажатие кнопки Status на телефоне:

curl --digest -u Push:Push -d "<PolycomIPPhone><Data priority=\"All\">Key:Status</Data></PolycomIPPhone>" --header "Content-Type: application/x-com-polycom-spipx" -v http://192.168.0.226/push

А такой стартует звонок по указанному номеру на линии 1.:

curl --digest -u Push:Push -d "<PolycomIPPhone><Data priority=\"All\">tel:\\2222222;Line1 </Data></PolycomIPPhone>" --header "Content-Type: application/x-com-polycom-spipx" -v http://192.168.0.226/push

Соответственно все что требуется реализовать в расширении решить нетривиальный вопрос Digest аутентификации в JS, прочитать выделенный текст по вызову из контекстного меню и отправить Post запрос в телефон.

Предварительно необходимо разрешить интеграциионные вызовыв телефоне. Зайдите в раздел Settings/Applications/Push и выберите All в разделе Allow Push Message и не забудьте указать логин и пароль для авторизации вызовов. Без этого рабоать не будет.

В работе это примерно так:

Исходники расширения здесь а само раширение ищите в Google Chrome Extension Store.

Настройка UI телефона

UI Телефона поддается некоторой кастомизации. Кроме замены фона(на ЧБ экране это все равно выглядит ужасно) и отображения web- страниц во встроенном браузере -можно переназначать действие кнопок навигации, предварительно разрешив их переопределение с помощью этих ключей в конфигурации телефона.

feature.enhancedFeatureKeys.enabled="1"

feature.EFKLineKey.enabled="1"

Несколько примеров подобного переопределения:

Открытие странички с погодой во внутреннем браузере телефона:

softkey.1.action="http://personeltest.ru/away/192.168.0.228:88"

softkey.1.enable="1"

softkey.1.label="Инфо"

softkey.1.use.idle="1"

Переход в режим DoNotDesturb

softkey.1.action="Key:DoNotDisturb"

softkey.1.enable="1"

softkey.1.label="DnD"

softkey.1.use.idle="1"

Вызов 1-го контакта из списка быстрого набора

softkey.3.action="$S1$"

softkey.3.enable="1"

softkey.3.label="Такси"

softkey.3.use.idle="1"

Более подробную информацию о переназначении софтверных кнопок можно подчерпнуть здесь.

Телефон также поддерживает т.н. push сообщения, когда он сбрасывает на указанный адрес сообщения для выбранных событий, но практического применения данной функциональности в домашних условиях я не нашёл.

Подробнее..

Перевод Эволюция PHP от 5.6 до 8.0 (Часть 1)

20.10.2020 14:07:37 | Автор: admin

Перевод статьи подготовлен в преддверии старта курсаBackend-разработчик на PHP.

Шпаргалка по изменениям в PHP v7.x

PHP_v8.0PHP_v8.0

После релиза PHP версии 7.3 я решил уделить больше внимания развитию PHP: что собственно развивается и в каком направлении искать понимание потенциала и оптимизации этого невероятно популярного языка программирования.

В результате поиска лаконичного списка функций, реализованных PHP за время разработки PHP v7.x, я решил составить список сам - приятное дополнение, которое, я думаю, кому-то тоже может пригодиться.

За основу мы возьмем PHP 5.6 и рассмотрим, что было добавлено и изменено. Я также добавил везде где смог ссылки на официальную документацию по каждой из упомянутых фич, поэтому, если вам вдруг понадобится получить более подробную информацию - пожалуйста.

PHP 7.0

Поддержка анонимных классов

Анонимный класс может использоваться вместо именованного класса:

  • Когда класс не нужно документировать

  • Когда класс во время выполнения используется только один раз

new class($i) {   public function __construct($i) {       $this->i = $i;   }}

Функция целочисленного деления - безопасный способ деления (даже на 0).

Возвращает целочисленный результат деления первого операнда на второй. Если делитель (второй операнд) равен нулю, функция пробрасывает EWARNING и возвращает FALSE.

intdiv(int $numerator, int $divisor)

Добавлен новый оператор объединения с null - ??

$x = NULL;$y = NULL;$z = 3;vardump($x ?? $y ?? $z); // int(3)$x = ["c" => "meaningfulvalue"];vardump($x["a"] ?? $x["b"] ?? $x["c"]); // string(16) "meaningfulvalue"

Добавлен новый оператор - spaceship (космический корабль)(<=>)

Используется для оптимизации и упрощения операций сравнения.

// Преждеf unction orderfunc($a, $b) {return ($a < $b) ? -1 : (($a > $b) ? 1 : 0);}// Используя оператор <=>function orderfunc($a, $b) {return $a <=> $b;}

Объявления скалярных типов

Это всего лишь первый шаг к реализации преимуществ более строго типизированного языка программирования в PHP - v0.5.

function add(float $a, float $b): float {return $a + $b;}add(1, 2); // float(3)

Объявления типов возвращаемых значений

Добавлена возможность возвращать типы помимо скалярных - классы, включая наследование. Хех, упустив при этом возможность сделать это необязательным (что будет введено в v7.1 :))

interface A {static function make(): A;}class B implements A {static function make(): A {return new B();}}

Групповые объявления use

// Явный use синтаксис:use FooLibrary\Bar\Baz\ClassA;use FooLibrary\Bar\Baz\ClassB;use FooLibrary\Bar\Baz\ClassC;use FooLibrary\Bar\Baz\ClassD as Fizbo;// Групповой use синтаксис:use FooLibrary\Bar\Baz{ ClassA, ClassB, ClassC, ClassD as Fizbo };

Делегация генератора

В теле функций генераторов разрешен следующий новый синтаксис:

yield from <expr>

Повышение производительности

PHP 7 почти вдвое быстрее, чем PHP 5.6.

Значительное сокращение использования памяти

Как видно из диаграмм, PHP 7.0 стал громадным шагом вперед с точки зрения производительности и использования памяти. Для страницы с запросами к базе данных версия 7.0.0 более чем в 3 раза быстрее, чем 5.6 с включенным opcache в 2.7 раза быстрее без opcache! С точки зрения использования памяти разница тоже существенная!

Интерфейс Throwable

Классы исключений были реструктурированы к более контринтуитивной схеме именования, которая приведет к меньшей путанице, особенно для новых пользователей.

Error и Exception теперь реализуют Throwable.

Иерархия Throwable:

interface Throwable|- Error implements Throwable|- ArithmeticError extends Error|- DivisionByZeroError extends ArithmeticError|- AssertionError extends Error|- ParseError extends Error|- TypeError extends Error|- ArgumentCountError extends TypeError|- Exception implements Throwable|- ClosedGeneratorException extends Exception|- DOMException extends Exception|- ErrorException extends Exception|- IntlException extends Exception|- LogicException extends Exception|- BadFunctionCallException extends LogicException|- BadMethodCallException extends BadFunctionCallException|- DomainException extends LogicException|- InvalidArgumentException extends LogicException|- LengthException extends LogicException|- OutOfRangeException extends LogicException|- PharException extends Exception|- ReflectionException extends Exception|- RuntimeException extends Exception|- OutOfBoundsException extends RuntimeException|- OverflowException extends RuntimeException|- PDOException extends RuntimeException|- RangeException extends RuntimeException|- UnderflowException extends RuntimeException|- UnexpectedValueException extends RuntimeException

Внимание! Вы можете реализовать Throwable только через Error и Exception.

Синтаксис кодирования Unicode \u{xxxxx}

echo "\u{202E}Reversed text"; // выводит Reversed textecho "maana"; // "ma\u{00F1}ana"echo "maana"; // "man\u{0303}ana" "n" комбинирована с символом ~ (U+0303)

Чувствительный к контексту лексер

С этим нововведением глобально зарезервированные словами стала полу-зарезервированными:

callable class trait extends implements static abstract final public protected private constenddeclare endfor endforeach endif endwhile and global goto instanceof insteadof interfacenamespace new or xor try use var exit list clone include includeonce throw arrayprint echo require requireonce return else elseif default break continue switch yieldfunction if endswitch finally for foreach declare case do while as catch die self parent

За исключением того, что по-прежнему запрещено определять константу класса с именем class из-за разрешения имени класса ::class.

Выражения return в генераторах

Единый синтаксис переменных

Поддержка уровня вложенности для функции dirname()

PHP 7.1

Обнуляемые типы

function answer(): ?int {return null; // ок}function answer(): ?int {return 42; // ок}function answer(): ?int {return new stdclass(); // ошибка}function say(?string $msg) {if ($msg) {echo $msg;}}say('hello'); // ок - выводит hellosay(null); // ок - ничего не выводитsay(); // ошибка - отсутствует параметрsay(new stdclass); // ошибка - не подходящий тип

Ничего не возвращающие функции

function shouldreturnnothing(): void {return 1; // Fatal error: A void function must not return a value}

В отличие от других типов, которые принудительно применяются при вызове функции, этот тип проверяется во время компиляции, что означает, что ошибка возникает без необходимости вызова функции.

Функция с типом возврата void или void функция может либо возвращать неявно, либо иметь оператор возврата без значения:

function lacksreturn(): void {// валидно}

Псевдотип Iterable

Обычно функция принимает или возвращает array или объект, реализующий Traversable, который используется с foreach. Однако, поскольку array является примитивным типом, а Traversable - интерфейсом, в настоящее время нет способа использовать объявление типа для параметра или возвращаемого типа, чтобы указать, что значение является итерируемым.

function foo(iterable $iterable) {foreach ($iterable as $value) {// }}

iterable также может использоваться в качестве возвращаемого типа, чтобы указать, что функция вернет итерируемое значение. Если возвращаемое значение не является массивом или экземпляром Traversable, будет проброшена TypeError.

function bar(): iterable {return [1, 2, 3];}

Параметры, объявленные как iterable могут использовать null или массив в качестве значения по умолчанию.

function foo(iterable $iterable = []) {// }

Closure из callable

class Closure {public static function fromCallable(callable $callable) : Closure {}}

Синтаксис квадратных скобок для деструктуририрующего присваивания в массиве

$array = [1, 2, 3];// Присваивает $a, $b и $c значения соответствующих элементов массива $array с ключами, пронумерованными от нуля[$a, $b, $c] = $array;// Присваивает $a, $b и $c значения элементов массива $array с ключами "a", "b" и "c" соответственно["a" => $a, "b" => $b, "c" => $c] = $array;

Синтаксис квадратных скобок для list()

$powersOfTwo = [1 => 2, 2 => 4, 3 => 8];list(1 => $oneBit, 2 => $twoBit, 3 => $threeBit) = $powersOfTwo;

Видимость констант класса

class Token {    // Константы по умолчанию public   const PUBLICCONST = 0;// Константы также могут иметь определенную пользователем видимостьprivate const PRIVATECONST = 0;protected const PROTECTEDCONST = 0;public const PUBLICCONSTTWO = 0;// Константы могут иметь только один список объявлений видимостиprivate const FOO = 1, BAR = 2;}

Перехват нескольких типов исключений

try {// Какой-то код} catch (ExceptionType1 | ExceptionType2 $e) {// Код обработки исключения} catch (\Exception $e) {// }

В ближайшие дни мы опубликуем вторую часть статьи, а прямо сейчас приглашаем вас записаться на бесплатный вебинар в рамках которого наши эксперты ответят на все интересующие вас вопросы.

Читать ещё:

Подробнее..

Управление Яндекс.Станцией и другими колонками с Алисой из Home Assistant

26.06.2020 10:22:09 | Автор: admin

Мы привыкли называть умными устройства, которыми можем управлять, не вставая с дивана. Включить лампочку, вентилятор, кофеварку или стиральную машину.


Колонки с Яндекс Алисой хоть и называются умными, но вы не можете изменить громкость не находясь рядом с колонкой (орать через всю комнату не считается). Вы не можете перемотать песню из мобильного приложения Яндекса. Или остановить сказку, запущенную на колонке в детской, из своей кровати в спальне.


В январе 2020 кто-то обнаружил, что Яндекс.Станция поддерживает некий локальный протокол. На GitHub начали появляться проекты по управлению Яндекс.Станцией. Мне хватило пару часов, чтоб разобраться и выпустить первую версию компонента для Home Assistant. Это достаточно популярная система домашней автоматизации, написанная на языке Python.


На сегодняшний день компонент поддерживает управление всеми колонками с Яндекс Алисой и при желании может выглядеть так:



Или так:



А работать так:



Инструкции по установке, настройке и использованию компонента можете найти на GitHub странице проекта.


Локальный протокол


Устройства Яндекса обнаруживаются в локальной сети по протоколу mDNS и имени _yandexio._tcp.local..


Локальный протокол представляет собой подключение к станции по WebSocket и обмен JSON-сообщениями в две стороны. Создавался он для приложения Яндекс.Музыки и поддерживает полный перечень команд управления станцией, как медиа-устройством: включить песню по ID из каталога Яндекс.Музыки, перемотать, изменить громкость и т.п.


Первым сообщением нужно отправить на станцию токен, полученный с серверов Яндекса, используя акаунт Яндекса, к которому привязана станция. Так что кому угодно управлять вашей станцией не получится.


Кстати подключение к колонкам Google через протокол Chromecast не ограничено какими-либо паролями или аккаунтами. Управление колонкой доступно любому пользователю той же локальной сети.


К многим устройствам с AirPlay первой версии подключение также не ограничено паролем.


Помимо медиа команд протокол поддерживает функцию, делающую Яндекс.Станцию совершенно уникальным устройством на рынке. Это возможность отправить на колонку текстовую команду. И колонка её выполнит, будто услышала команду через микрофон.


Вы можете включить плейлист дня с Яндекс.Музыки, лайкнуть песню, спросить погоду, вызвать такси, управлять умными устройствами, подключенными напрямую в экосистему Яндекса.


И вишенкой на торте вы можете попросить станцию произнести любую фразу голосом Алисы. Это тот самый голос, который по праву признан лучшим голосом TTS для русского языка на сегодняшний день. Этот голос является эксклюзивом Яндекс Алисы и его нет даже в Yandex SpeechKit.


Но и это ещё не всё! Помимо зачечательного голоса вам доступна настройка генератора речи и библиотека звуков из платформы Яндекс.Диалоги.


Облачное управление


На сегодняшний день локальный протокол поддерживает только большая станция и модуль. Почти пол года сообщество ждало появление протокола в остальных колонках с Алисой, но это так и не случилось.


В начале мая Яндекс выпустил очень интересное обновление для своей платформы. В сценарии умного дома добавили возможность отправить на любую колонку пользователя любую текстовую команду. Как и в случае с большой станцией колонка её выполняет.


По задумке авторов сценарий выполняется по заданной активационной фразе, которую пользователь произносит своему устройству с Алисой. Это может быть колонка, мобильное приложение Яндекс или Яндекс.Браузер с Алисой на компьютере. Но в интерфейсе управления сценариями есть кнопка ручного запуска любого пользовательского сценария.


Интерфейс умного дома Яндекса представляет собой обычное веб-приложение, которое можно запустить в том числе на компьютере.


Я ранее сталкивался с внутренними API этого приложения для реализации функции управления HDMI выходом большой станции. Да, есть ещё такая недокументированная возможность и компонент её поддерживает.


Научить компонент создавать сценарии и выполнять их не составило особого труда. Через сценарии можно как выполнять команды, так и произносить любые фразы.


Единственным большим минусом такого подхода является отсутствие обратной связи от колонки. Неизвестно что играет станция на данный момент и вообще играет ли она что-либо. У локального протокола такой проблемы нет, там выводится полная информация о исполнителе и красивая обложка из Яндекс.Музыки.


YandexStation 2.0


Некоторое время ушло на объединение локального и облачного режима работы. При старте Home Assistant все колонки включаются в облачном режиме и запускается поиск колонок, поддерживающих локальный режим. mDNS довольно капризный протокол и иногда может искать колонку довольно долго. Но благодаря наличию облачного режима управление станцией сохранится, пусть и в обрезанном режиме без обратной связи. При обнаружении локальной колонки управление переключается на локальный протокол.


Похожим образом работает другой мой компонент для управления устройствами eWeLink (Sonoff) на оригинальной прошивке SonoffLAN. В линейке популярных китайских реле также есть устройства, которые поддерживают локальное и облачное управление. И устройства, которые поддерживают только облачное управление.


Могу написать отдельную статью про компонент, если интересно.


Home Assistant Windows Portable


Для пользователей, испытывающих трудности в установке Home Assistant, я собрал портативную версию Home Assistant под Windows на базе WinPython HassWP. Эта версия подойдёт для ознакомления и экспериментов. В ней уже установлен Home Assistant Community Store (HACS) и компоненты YandexStation и SonoffLAN.


Демонстрация

Для повседневного использования всё же рекомендую установить Hass.io на Raspberry Pi, NUC (или аналог) или виртуальную машину с Linux. Но слышал у VirtualBox есть проблемы с Multicast. Это тот самый mDNS без которого в локальной сети НЕ найдутся ваши Яндекс.Станции, устройства Sonoff, колонки Google, плееры с поддержкой AirPlay и многие другие полезные гаджеты.


Заключение


Я знаю довольно много людей, кто купил колонки с Алисой благодаря выходу этого компонента. Решение показывает, что при наличии фукнционального API эти колонки могут стать почти обязательным устройством в каждом умном доме.


Это далеко не все крутые вещи, на которые способны колонки с Алисой и экосистема умного дома Яндекса. Просто у меня пока ещё не дошли руки реализовать всё задуманное.


За инсайдами можете следить на канале в Telegram. На моей странице GitHub вы можете найти и другие полезные компоненты для Home Assistant.

Подробнее..

Как плохая поддержка убивает неплохой железный продукт на примере Яндекс.Станции

30.12.2020 00:08:02 | Автор: admin
Да это немного ненависти пост, но с верой в новогоднее чудо, вдруг добрый дедушка мороз придёт и настучит по голове поможет продакт менеджеру расставить приоритеты в развитии продукта и мир станет чуточку лучше.



Яндекс.Станция представлена была уже более полутора лет назад. Мне изначально показалось что это может быть мегаудобная вещь для кухни, где гораздо важнее свободные руки и компактность, нежели качество звука и поэтому я был одним из первых покупателей. Это было вполне осознанным компромиссом, тем более можно было ожидать чтобы функционал расширялся и правда появился умный дом, радиостанции и куча всего. Но одна вещь все ещё не даёт мне покоя это именно качество звука.

Как изменить звук на Яндекс станции? Хочется басы убавить, а высокие частоты прибавить. Как это сделать?
Дмитрий Красножон, вопрос на сервисе Яндекс.Кью
5 июля 2019

Понятно, что изначально глупо было ждать от подобных устройств хорошего звука они созданы из компромиссов нужно обеспечить все направленность, работу микрофона, и прочее и прочее. Но вся оказалось как то совсем плохо. Наиболее популярное слово описывающее звук в обзорах пристойно. Не особо впечатляет.
Звучит колонка от Яндекса вполне пристойно. Алиса разговаривает разборчиво, вполне живым голосом. В колонке ничего не скрежещет и не дребезжит музыка тоже воспроизводится нормально.
Ведомости
Вполне логичным решениям для создателей аудио техники давным давно является возможность добавить в их устройство эквалайзер. Кто то сделает себе больше ВЧ, кто то добавит басов. Слух и чувство прекрасного у всех разное и даже на хорошей брендовой технике всегда есть возможность подкрутить звук под себя. Уж тем более она напрашивается в подобное устройство, которое с рождения имеет много проблем. Пользователи начали искать эту возможность и задавать вопросы яндексу. Тем более что наверное впервые устройство создано в одной с ними стране, людьми говорящими на одном с ними языке и наверное тут гораздо проще донести свой фидбэк.
Добрый день! К сожалению, сейчас на Станции нельзя пользоваться эквалайзером. Но эта функция входит в ТОП-10 пожеланий от пользователей :) Это значит, что она вполне может появиться в какой-то момент. Следите за обновлениями!)
Дарья Пашаева Менеджер проектов в Яндекс.Кью
17 июля 2019
Дамы и господа, знакомьтесь АЧХ яндекс станции (спасибо ixbt.com)



выглядит как кардиограмма гипертоника плохо. И звук соответствует. По сути это звук тут это сильно выпяченный лоу-мид говоря русским языком бубнёж.
Да басов прям через-чур (звук местами как из бочки)
bonus2k, Яндекс.Кью
2 октября 2019
Громкие басы мешают соседям (из-за длины басовой волны они слышат больше, чем я). Ночью стремно пользоваться. А учитывая то, что на станцию завязаны другие устройства, пользование становится вообще неудобным.

Дмитрий Пылаев, Яндекс.Кью
9 ноября 2019
Прошло более полутора лет, станция и Алиса в ней, научились делать много нового, но сделать эквалайзер и дать возможность людям наслаждаться музыкой на музыкальной колонке нет. Вместо этого разработчики избрали другой путь сделать хорошо всем и сразу. Периодически с каким то обновлениями звук меняется. Наверное можно даже сказать что он стал немного лучше за полтора года. Но нравится ли он мне? Нет. Он все ещё бубнит и ему дико не хватает верхов.
Пол-года прошло, разработчики, вы офигели? Звук бубнящий, голос плохо распознается ушами, и это настройки вшитые по умолчанию для диалоговой колонки?!
Simon Logic
6 января 2019
Уже год кормят эквалайзером. Это номер 1 из функций что надо сделать. Звук на станций так себе колонка от lg ее уделывает, но увы я станцию раньше купил
Keane09
18 января

При это это колонка может звучит намного лучше достаточно подключить её по блютуз и настроить эквалайзер на источнике и сразу всё становится намного лучше. Только смысл умной голосовой колонки как то пропадает сразу же. Да возможно увеличение ВЧ скажется отрицательно на итак не идеальном слухе этой колонки, но например я готов смириться с этим. В конце концов можно сделать пресеты и дать пользователям возможность выбирать

Дальше будут мои любимые цитаты А вы давно лазили внутрь современной умной техники чтобы заставить её работать хорошо?
Экспериментировал с откручиванием пассивных излучателей, запихиванием ваты и замены на пластиковые заглушки не помогло особо, впаял подстроечный резистр на 100 Ом в разрыв на большой динамик, опытным путем выставил на 25 Ом для меня она стала просто отлично звучать басы есть но не так много как раньше середины стало больше, верха остались как были. Из недостатков общая громкость упала и звук в кашу сваливается на максимуме, но я так никогда не слушал, так что результатом остался доволен.
Алексей Б.
10 февраля
Алексей Б., еще лучше было впаять не резистор, а неполярный конденсатор на 1 мкФ (или подобрать номинал по себя).
Игорь Беляев
день назад
Для этого откручиваем torx T10H крепление заглушки сбоку и вырезаем туда такую же из плотного картона. В итоге басы снижаются и так звучание музыки и фильмов мне нравится намного больше.
Kolandr, 4pda
14.02.20, 03:06
Яндекс, вы правда считаете нормальным что ваши пользователи занимаются подобной ерундой?
Вопрос ведь даже не в звуке как в таковом, вопрос в отношении к пользователям. Я сейчас даже не приводить самых злых цитат с того яндекс.кью, я думаю этого будет достаточно
скоро яндексовцы попросят по всем вопросам касательно их продуктов писать в спортлото
sus
27 марта
Что же до меня мне в какой то момент казалось что эта станция идеальный подарок, но теперь я так не считаю и даже скорее отговариваю людей от покупки. И да верю в чудо и пытаюсь достучаться до ответственных за это дело людей хотя бы тут.
Алиса, убавь басы
Настройка эквалайзера пока не доступна
я
пару минут назад
Подробнее..

Яндекс.Карты API, я устал. Я ухожу

22.12.2020 18:11:09 | Автор: admin
О последней и других каплях в чаше решения о прекращении использования Яндекс.Карты API.

Что случилось?


С 1 ноября Яндекс.Карты уменьшили лимиты на бесплатное использование HTTP API Геокодера с 25 000 до 1 000 запросов в сутки. Но не всех об этом уведомили.

В добавок, хоть новые лимиты и касаются только HTTP API Геокодера, он остался спаренным с сервисом встраивания карт. Теперь случайное превышение по использованию геокодера отрубает вам на сутки и карты, несмотря на прежний лимит для встраивания в 25 000.

Конечно же, у нас так и произошло. И не только у нас. Яндекс признал проблему и даже предложил компенсацию.

И вот по поводу компенсации начинается переписка с поддержкой.

Сначала она не видит, что мы вообще используем API. Но прозревает после нашего скриншота.

Потом она перенаправляет на оформление коммерческого использования.

Дальше она не видит, как мы используем геокодер, чем объясняет отсутствие уведомление о новых лимитах. Хотя у нас обычный характер использования геокодера это до 100 ежедневно со скачками до 1 000, и несколько раз в год до 25 000.

Разговор завершается отсылкой к договору, где написано, что Яндекс вам ничем не обязан все так и должно работать.

Переписка заняла 3-и дня, из которых первый день карты не работали, т.к. никто ничего не стал компенсировать даже на время разбирательства.

Но ноги этого начали расти чуть больше года назад. Почему появилась эта статья что-то напоминает да? :)

Версионирование Шрёдингера и др.


До лимитов основной проблемой было версионирование.

Вот вы если указываете номер версии, скажем, 1.65, какую рассчитываете получить в итоге?
Я думал, что 1.65. Но нет, в Яндекса.Картах свято уверены, что это может и 1.72, и 1.75, и т.д. Хорошо хоть только возрастает. Но это не точно.

На вопрос, где они узнали, что это хорошая практика, ответили ссылкой на свою же документацию о версионировании. Ловко да? :)

Таким образом вам прилетают баги, которых вы не ждали. Править их нужно немедленно вас опять же никто не уведомил, и откатиться некуда.

По началу я рационализировал такой подход задумкой быстрой обратной связи и внесением правок. Поэтому сообщал о всем, что находил. Но опять не угадал. Как я Вас понимаю, @ReDev1.

Поддержка могла реагировать годами. И даже поправленные баги могут возвращаться.

Т.е. вы должны сами отслеживать и знать все изменения, даже те, которые внутри кода, и вносить правки в соответствии с планом работ Яндекса.

Так и получается, что работаешь то с отсутствием отзывчивых стилей карт, то с неработающей потайловой загрузкой у объектных карт, то с недокэшем тайлов подложки и др.

Не буду уже тратить время на документацию и архитектуру кода, которая часто напоминает итальянское блюдо.

Это просто бизнес, ничего личного.


Циничный это подход. И бизнес выходит хреновый, когда маркетологи-двоечники ставят бабло выше репутации.

Лишнее тому подтверждение недавний срыв большой сделки с Яндексом. Все-таки кто-то считается с настроениями пользователей.

Свои проекты мы строим по-другому.

До этого API Яндекс.Карт использовался больше 7 лет из-за их покрытия и удобных лимитов. Но теперь, учитывая характер использования, новые лимиты, расценки и качество поддержки, в этом нет смысла. Значительно дешевле, удобней и гибче использовать собственные решения на основе других открытых и бесплатных продуктов.

Тем более, что и так приходилось писать свои обертки для решения проблем сервисов Яндекс.Карт. Например, тот же геокодер натренирован на статистике пользовательского ввода. Из-за чего нередко бывает, что точный адрес не гео-кодируется, а вот, например, без вида улицы находится.

Думаю, многие другие проекты среднего размера поступят также. В итоге, на Картах останутся только небольшие проекты, которые используют самую базовую функциональность, и коммерческие пользователи, которые будут получать все больше пропущенных багов. Как результат начнется и их отток.

Еще одно интересное следствие всей этой истории это то, что я перестал воспринимать сотрудников Яндекс как серьезных специалистов. И приходится делить, минимум на 10-ть, все что они говорят, даже если и появляется желание их послушать.

Из, возможно, забавного.


Как-то у Яндекса в Недвижимости появилась функциональность, которая очень напоминала то, что являлось уникальным предложением нашего проекта.

После чего внутренний параноик задумался о случайности совпадений. Т.к. мы за 5 лет до того начали решать проблему поиска жилья с учетом качества жизни. В частности, с учетом экологии, и используя интерактивные карты. А это до сих пор очень нишевый сервис.

P.S.
Есть что-то похожее и про Google. Думаю как-нибудь тоже написать, если допечет.

Кто знает, есть ли плагин для leaflet, чтобы можно было его использовать с backend'ом под API Яндекс.Карт? Если что я уже начал писать похожее решение.
Подробнее..

Визуализация статистики Яндекс Директ своими руками. От API до Data Studio

14.08.2020 22:08:54 | Автор: admin
Мне, как специалисту по рекламе, требуется постоянно присматривать за клиентами. В этом мне помогает Data Studio.
Однако специалистам по рекламе редко выделяют бюджеты на аналитику, поэтому приходится делать все своими руками.

Что нужно сделать чтобы визуализировать Яндекс Директ в Data Studio:

  1. Получить токен от своего аккаунта (за этим в справку Директа)
  2. На Python Написать запрос к серверу Яндекс Директ
  3. Сложить статистику в Pandas Data Frame
  4. Отправить данные в Google Big Query
  5. Построить визуализацию в Data Studio на основе данных в Google Big Query



Написать запрос к серверу Яндекс Директ и сложить данные в Pandas Data Frame



Раньше я ковырялся с запросами и потом редактировал данные, чтобы их можно было отправить. Можете попробовать, если вам нужны тонкие настройки (Писал про это ранее тут habr.com/ru/post/445734)

Для остальных есть способ намного проще мой python-пакет yadirstat. (обзор на него habr.com/ru/post/512902). При его использовании вы вводите, токен, логин, даты и получаете готовый Data Frame, в котором ничего не нужно менять.

Примерно так выглядит код:

from yadirstat import yadirstatx=yadirstat.yadirstat.campaign('FFFFFfffffFFFFggggGGGgg', 'client123123','2020-05-10','2020-07-15')print(x)


Отправить данные в Google Big Query


Это самый очевидный способ собирать данные для последующей визуализации в DataStudio, так как они прекрасно работают в паре

Для отправки я использую пакет pandas_gbq
Код выглядит примерно так:
import pandas_gbqpandas_gbq.to_gbq(x, 'YD_Days.test', project_id='red-abstraction-99999999',if_exists='replace', progress_bar=None)


Почему данные именно перезаписываются? Потому, что статистика в директе может со временем корректироваться и, если мы новые сроки будет просто добавлять, у нас будут расхождения в статистике.

Теперь проверим отправилась ли информация в Big Query. Если все прошло успешно, будет такой набор полей их типов



Построить визуализацию в Data Studio на основе данных в Google Big Query



Для этого можно сразу в Big Query создать запрос на получение всех данных:
нажимаете Отправить запрос к таблице, добавляете после SELECT * и убираете лимит. Примерно так выглядит запрос: SELECT * FROM `red-abstraction-239999.YD_Days.test`

В Data Studio Подключаемся к Google Big Query





В изменении источника увидим следующие поля


Требуется изменить следующие поля для корректной агрегации:

  • AvgCpc
  • ConversionRate
  • CostPerConversion
  • Ctr


Зачем это делается? Рассмотрим на примере CPC
Если у нас будет две строки со следующими данными:
  • 100 кликов Стоимость клика 100 руб Стоимость 10000
  • 2 клика Стоимость клика 10 руб Стоимость 20 руб

Обычное среднее скажет, что CPC по двум строкам 55
Поэтому, чтобы получить корректный CPC, следует поделить все расходы на все клики. В этом случае CPC получится 98,2

Просто скрываем эти поля и добавляем их аналоги:

  • CPC=SUM(Cost)/SUM(Clicks)
  • CTR = SUM(Clicks)/SUM(Impressions)*100
  • % конверсий = SUM(Conversions)/SUM(Clicks)*100
  • Стоимость конверсии=SUM(Cost)/SUM(Conversions)


Столбец AvgPageviews я вообще не использую

Процент отказов очень сложный столбец из-за того, что Яндекс использует разную статистику исходя из каких-то дополнительных данных.
Если коротко, я использую формулу, именно она дает мне минимальные отклонения от того, что показывает Яндекс:
% отказов = SUM(Bounce_clicks)/(SUM(Clicks)/100).
где Bounce_clicks количество отказных кликов в каждой из строк
Но этот вопрос выходит за рамки этой статьи :)


На данном этапе получаем следующий набор полей:



Визуализируем


Я делаю две страницы для каждого клиента: общая информация и информация по ключам.

Начнем с первой страницы общей информации
Тут я размещаю:

  • График с расходами по дням
  • Таблицу со статистикой в разрезе дат
  • Таблицу со статистикой в разрезе кампаний
  • Дашборд со статистикой за вчера (клики, стоимость, цена клика)


Для начала в углу разместите диапазон дат, чтобы пользователи могли выбрать под себя даты:

Теперь добавим график с расходами:

Задаем следующие настройки:

Получаем такой график


Для таблицы с датами задаем такие настройки:

Для таблицы с кампаниями меняем параметр Date на название кампаний

Для дашбордов я использую сводку


На выходе у меня получается такая страница со статистикой:


Получаем статистику по ключевым словам:



Все то же самое, только теперь запрос будет выглядеть так:

import pandas_gbqfrom yadirstat import yadirstatx = yadirstat.yadirstat.criteria('AgAAAAАААаввадцоутпдцупдI',client-12247235,'2020-05-10','2020-07-15')print(x)pandas_gbq.to_gbq(x, 'YD_criteria.test', project_id='red-abstraction-21239254613',if_exists='replace', progress_bar=None)
Подробнее..

Обзор python-пакета yadirstat самый простой способ получить статистику из API Яндекс Директ

29.07.2020 02:21:58 | Автор: admin
Здравствуйте, мне приходится собирать статистику из Яндекс Директ и, чтобы упростить работу, я опубликовал свой python-пакет, с помощью которого это можно делать очень просто.

Сначала вам следует получить токен для своего аккаунта (подробнее тут: yandex.ru/dev/oauth/doc/dg/tasks/get-oauth-token-docpage)

Пакет yadirstat уже опубликован на pypi вы сможете установить его с помощью pip

pip install yadirstat


Пакет позволяет получить следующую статистику:

  • Статистика по кампаниям
  • Статистика по условиям показов (например, ключевые слова и аудитории)
  • Статистика по поисковым запросам


Как запрашивается статистика:


yadirstat.yadirstat.тип запроса(Токен клиента, Логин клиента, Дата начала, Дата окончания)


Примерно так будет выглядеть запрос для получения статистики по поисковым запросам
query_report =yadirstat.yadirstat.query('блаблаБЛАБЛАблаблАбалблаблаб','клиент-1245234','2020-05-10','2020-07-15')print(query_report)


А так запрос для получения статистики по кампаниям:
campaign_report = yadirstat.yadirstat.campaign('блаблаБЛАБЛАблаблАбалблаблаб','клиент-1245234','2020-05-10','2020-07-15')print(campaign_report)


А так запрос для получения статистики по условиям показов:
criteria_report = yadirstat.yadirstat.criteria('блаблаБЛАБЛАблаблАбалблаблаб','клиент-1245234','2020-05-10','2020-07-15')print(criteria_report)


Пример моего запроса:
from yadirstat import yadirstatx = yadirstat.yadirstat.campaign('AgAAAxxxxxxxXXXXXXxxxxxXXXXXcI','BxxxxXXXX','2020-05-10','2020-07-15')print(x)


Вывод выглядит следующим образом


Если во время вывода данные не помещаются, можете использовать следующее:

import pandas as pdpd.set_option('display.max_columns', None)pd.set_option('display.expand_frame_repr', False)pd.set_option('max_colwidth', 80)pd.set_option('max_rows', 600000)


На выходе мы получаем DataFrame
Для полноценного использования я заменяю "--" на 0

Структура данных:



Поисковые запросы:

  • CampaignName
  • Query
  • Impressions
  • Clicks
  • Ctr
  • Cost
  • AvgCpc
  • ConversionRate
  • CostPerConversion
  • Conversions


Кампании*:

  • Date
  • CampaignName
  • Impressions
  • Clicks
  • Ctr
  • Cost
  • AvgCpc
  • BounceRate
  • AvgPageviews
  • ConversionRate
  • CostPerConversion
  • Conversions
  • Date


Условия показа:

  • CampaignName
  • Criterion
  • Impressions
  • Clicks
  • Ctr
  • Cost
  • AvgCpc
  • Date


*- Добавление даты последним столбцом позволяет не терять данные по датам при передачи DataFrame (Например, при передаче в BigQuery, теряется столбец с датами из-за того, что он индексный, чтобы избежать проблем я просто продублировал этот столбец).

Почему именно така структура? именно так я собираю статистику, чтобы потом отправлять ее в Google BigQuery и далее визуализировать в Google DataStudio.

Буду рад выслушать предложения по развитию данного пакета и ваш опыт сбора статистики.

P.S:
С агентскими аккаунтами это тоже работает
И с аккаунтами еламы
Подробнее..

Из песочницы Как разместить статический сайт с помощью Yandex.Cloud Object Storage

20.07.2020 12:12:02 | Автор: admin
Привет, Хабр!

В этой статье, я расскажу как легко и просто разместить статический сайт с помощью технологий Яндекса, а именно Object Storage.


В конце у вас будет размещенный в сети сайт, который будет доступен по внешней ссылке.


Эта статья будет полезна, если вы


  • Начинающий разработчик, который только обучается программированию;
  • Разработчик, который сделал портфолио и хочет разместить его в открытом доступе, чтобы показать друзьям и работодателям.

О себе


Недавно, я разрабатывал SaaS сервис, подобие маркетплейса, где люди находят спортивных тренеров для персональных тренировок. Использовал стек Amazon Web Services (далее AWS). Но чем глубже погружался в проект тем больше нюансов узнавал о разных процессах организации стартапа.


Я столкнулся с следующими проблемами:


  • AWS потреблял много денег. Поработав 3 года в Enterprise компаниях, я привык к таким радостям, как Docker, Kubernetes, CI/CD, blue green deployment, и, как начинающий программист-стартапер, захотел реализовать тоже самое. В итоге пришел к тому, что ежемесячно AWS потреблял по 300-400 баксов. Самым дорогим оказался Kubernetes, около 100 баксов, при минималке с одним кластером и одной нодой.
    P.S. На старте не нужно так делать.
  • Далее, задумавшись о юридической стороне, я узнал про закон 152-ФЗ, в котором говорилось примерно следующее: "Персональные данные граждан РФ должны храниться на территории РФ", иначе штрафы, чего мне не хотелось. Я решил заняться этими вопросами, пока "сверху" мне не прилетело :).

Вдохновленный статьей о мигрировании инфраструктуры из Amazon Web Services в Яндекс.Облако, я решил изучить стек Яндекса подробнее.


Для меня ключевыми особенностями Яндекс.Облака было следующее:



Я изучал других конкурентов этого сервиса, но на тот момент Яндекс выигрывал.


О себе рассказал, можно и перейти к делу.


Шаг 0. Подготовим сайт


Для начала нам понадобится сайт, который мы хотим разместить в интернете. Так как я Angular разработчик, я сделаю простой шаблон SPA приложения, который далее размещу в интернете.


P.S. Кто разбирается в Angular или знает про его документацию https://angular.io/guide/setup-local, переходите к Шагу 1.


Установим Angular-CLI чтобы создавать SPA-сайты на Ангуляре:


npm install -g @angular/cli

Создадим Angular приложение с помощью следующей команды:


ng new angular-habr-object-storage

Далее переходим в папку приложения и запускаем его, чтобы проверить работоспособность:


cd angular-habr-object-storageng serve --open

Статическое SPA-приложение на Angular


Приложение создано, но пока не готово к размещению на хостинге. Соберем приложение в небольшой билд (Production), чтобы убрать все лишнее и оставить только необходимые файлы.
В ангуляре это можно сделать следующей командой:


ng build --prod

В результате этой команды в корне приложения появилась папочка dist с нашим сайтом.


Работает. Теперь переходим к хостингу.



Шаг 1.


Переходим на сайт https://console.cloud.yandex.ru/ и жмем на кнопку "Подключиться".


Примечание:


  • Для пользования сервисом Яндекса может понадобится почта Яндекса (но это не точно)
  • Для некоторых функций придется положить деньги на счет в личном кабинете (минимум 500 рублей).

После успешной регистрации и авторизации, мы в личном кабинете.


Интерфейс личного кабинета Yandex.Cloud


Далее слева в меню нужно найти сервис "Object Storage", который мы как раз будем использовать для хостинга сайта.


Коротко по терминам:


  • Object Storage это хранилище файлов, совместимое с аналогичной технологией Амазона AWS S3, у которого также есть свой API для управления хранилищем из кода и его также как и AWS S3 можно использовать для размещения статического сайта.
  • В Object Storage мы создаем "бакеты" (bucket / Корзина), которые являются отдельными хранилищами наших файлов.

Интерфейс сервиса Yandex.Cloud Object Storage


Создадим один из них. Для этого в консоли сервиса жмем на кнопку "Создать бакет".


Интрфейс создания бакета в Yandex.Cloud


В форме создания бакета есть следующие поля, пробежимся по ним:


  • Имя бакета. Для простоты, назовем так же как и ангуляр проект angular-habr-object-storage
  • Макс. размер. Ставим столько, сколько у нас весит сайт, так как сайт хранится не бесплатно и за каждый выделенный гигабайт, мы будем платить Яндексу копеечку.
  • Доступ для чтения объектов. Ставим "Публичный", так как пользователь должен получать каждый файл нашего статического сайта, чтобы на нем правильно отрисовывалась верстка, отрабатывали скрипты и тд.
  • Доступ к списку объектов и Доступ на чтение настроек. Оставляем "Ограниченный". Это нужно для того, чтобы использовать бакет как внутреннее хранилище файлов для приложений.
  • Класс хранилища. Оставляем "Стандартный". Это означает, что наш сайт часто будут посещать, а значит и часто скачивать файлы, составляющие сайт. Плюс пункт влияет на производительность и оплату (вставить ссылку).

Жмем "Создать бакет" и бакет создан.


Yandex.Cloud Бакет создан


Теперь нужно загрузить наш сайт в бакет. Самый простой способ открыть рядом папочку dist нашего сайта и ручками перетащить прямо на страницу. Это удобнее, чем жать на кнопку "Загрузить объекты", потому что в таком случае папки не переносятся и их придется создавать ручками в правильной последовательности.


Загрузили в бакет наш сайт


Итак, сайт загружен в хранилище, тем можем предоставить пользователям возможность обращаться к хранилищу, как к сайту.
Для этого слева в меню жмем на вкладку "Веб-сайт".


Настройка бакета под сайт


На странице настройки бакета как сайта, выбираем таб "Хостинг". Здесь указываем главную страницу сайта, обычно это index.html. Если у вас SPA приложение, то вероятно все ошибки обрабатываются также на главной странице, поэтому укажем на странице ошибки также index.html.


Мы сразу видим, по какой ссылке будет доступен наш сайт. Жмем сохранить.


Через минут 5, перейдя по ссылке мы видим что теперь наш сайт доступен всем желающим.


Хостинг Angular приложения с помощью Yandex.Cloud Object Storage


Спасибо всем кто дочитал до конца! Это моя первая статья, планирую дальше описать другие сервисы Яндекса и их интеграцию с frontend и backend технологиями.


Напишите в комментариях насколько интересно вам узнать про другие сервисы Яндекса или про использование Angular в современной разработке.

Подробнее..

Запись разговоров на астериск и их распознавание на Yandex.Speech

14.12.2020 14:08:42 | Автор: admin

Небольшой проект. Простая реализация. Заметка по диалплану астериск, командам консоли и АПИ распознавания Яндекса. Вы прочитаете и не наступите на мои грабли, я прочитаю через полгода-год и вспомню, что делал.

Задача: получать текстовое представление разговоров, записанных на астериске.

Сначала запись разговора

MixMonitor записывает разговор. Обычно MixMonitor записывает в один канал обоих собеседников. Нам надо получить каждый канал в отдельном файле. Поэтому есть две опции r и t, где мы можем указать файлы для записи разных каналов.

Также используется параметр b - для начала записи в момент начала разговора.

С 16 астериска была опция S - для синхронизации t и r файлов, (в тот, который позже начался записываться добавлялась тишина в начало файла). С 18 астериска опцию S убрали, т.к. это стало поведением по умолчанию, а добавили контр-опцию n. Но я использую b, поэтому эти дополнительные пляски мне не потребовались.

MixMonitor(record-o.wav,br(record-r.wav)t(record-t.wav),command)

Затем также в команде MixMonitor'а мы укажем команду для выполнения после записи. В рамках этой команды мы нормализуем каждую запись - выровняем по уровню и затем смерджим две записи в один двухканальный файл.

sox --norm record-t.wav record-t-norm.wav // нормализация записи одной стороны разговора

sox --norm record-r.wav record-r-norm.wav // нормализация записи второй стороны разговора

sox record-r-norm.wav record-t-norm.wav --channels 2 --combine merge record.wav // сливаем записи в один файл

Теперь итоговый файл можно прослушать в стерео режиме - в одном ухе слышно одного участника разговора, во втором - второго участника разговора. Интересный эффект присутствия, как будто по ролям читают, но не всем нравится такой формат прослушивания.

Для прослушивания можно использовать файл record-o.wav - первый файл из MixMonitor'а, записан традиционным методом, привычным для уха.

Файлы в формате wav достаточно много места занимают. Поэтому для хранения я их конвертирую в mp3 и копирую на хранилище.

Еще пара вариантов как организовать раздельную запись каналов на астериске

https://howto.a17.su/asterisk/call-recording.html

https://voxlink.ru/kb/asterisk-configuration/integraciya-asterisk-so-speech-analytics/

Теперь распознавание

Для распознавания дальше использую сервис Яндекса.

У Яндекса есть несколько API для распознавания: короткие аудио, длинные аудио и потоковое. Короткие - это до 30 секунд, поэтому пользуюсь API для длинных аудио.

Длинные аудио - передать можно wav или ogg файлы. С wav файлами дело не пошло, и в самом API еще необходимо указывать дополнительные параметры для wav-формата, поэтому я прежде чем отправлять на распознавание переконвертирую в ogg. Яндекс определит, что ogg двухканальный и распознает его по двум каналам без дополнительных параметров

/usr/bin/ffmpeg -i record.wav -acodec libopus record.ogg // команда переконвертации в ogg

Также есть пара нюансов

Во-первых, длинные аудио (прежде чем распознать) необходимо закачать в облако Яндекса, а в сервис распознавания передать уже ссылку на запись в облаке.

Хранилище Яндекса является S3-совместимым, поэтому для закачивания подойдет любая утилита или библиотека работающая с S3-хранилищем Амазона. Для хранения файлов в хранилще используются buckets.

Документация на Яндекс.Storage

Второе, сначала мы создаем задание на распознавание, получаем его id. А уже потом по его id чекаем на наличие результата (при этом количество операций на проверку статуса заданий ограничено, немало, конечно, но и не бесконечно).

Документация на Яндекс.Облако Распознавание длинных аудио

Документация на Яндекс.Облако Отслеживание статуса операции

Так получилось, что мое тестирование было в августе 2020, и попало на какой-то факап очереди заданий. И поэтому распознавания производились очень долго - по 2 и более часов.

Т.к. в Яндекс.Облаке и поддержка платная, отдельной строкой, то на мой тикет не отвечали оперативно. В чате же Яндекс.Облака достаточно быстро сообщили, что есть проблемы. Ну, а позже и на тикет ответили. И очередь починили. в штатном режиме все работает вполне оперативно.

По тарификации распознавания

Хранилище: место и операции - это тарифицируется. Распознавание - тоже. Все понемногу. Использую корпоративный тариф.

Тарификация одноканальных и двухканальных записей одинаковая, т.е. по деньгам нет разницы распознать одну минуту одноканальной записи или одну минуту двух каналов (а вот три канала будет уже вдвое дороже).

Посчитать на Тарификаторе Яндекса (раздел SpeechKit)

Ключи доступа. Тут главное не запутаться, так как у вас будут ключи и от сервиса распознавания (API ключ), и от хранилища S3 (статический ключ). Оба вида ключа на сервисном аккаунте.

Надеюсь, заметка сэкономит вам несколько минут, и вы быстрее реализуете свой проект по необходимости.

Подробнее..

Сущности для платформы Яндекс.Диалоги

04.07.2020 16:09:21 | Автор: admin
В прошлую субботу состоялся онлайн-хакатон по разработке навыков Алисы. Жаль, что никто не написал здесь об итогах, любопытно почитать истории победителей. Но раз желающих не нашлось, то поделюсь своей историей.

Я делаю голосовой интерфейс для управления брокерским счётом, уже писал об этом на Хабре Алиса, купи акции Яндекс. В какой-то момент мне понадобилось извлекать из запроса цену в разных валютах. Уверен, я не первый, кто столкнулся такой задачей, поэтому попытался найти готовые интенты или именованные сущности на GitHub, но ничего не нашёл. На носу был хакатон, много разработчиков в одном месте, подумал я, если каждый поделится своими наработками, то сущностей наберётся на целую библиотеку. Так родилась идея для репозитория библиотека сущностей.



Пользовательские сущности в Диалогах


Когда я говорю умной колонке купи одну акцию Яндекс, то речь проходит через внутреннюю магию платформы Яндекс.Диалоги, затем попадает в веб-хук, который я указал в качестве обработчика навыка. Вот что приходит в обработчик:
  "request": {    "command": "купи одну акцию яндекс",    "original_utterance": "купи одну акцию яндекс",    "nlu": {      "tokens": [        "купи",        "1",        "акцию",        "яндекс"      ],      ...      "intents": {        "market.order": {          "slots": {            "amount": {              "type": "YANDEX.NUMBER",              "tokens": {                "start": 1,                "end": 2              },              "value": 1            },            "unit": {              "type": "OperationUnit",              "tokens": {                "start": 2,                "end": 3              },              "value": "share"            },            "figi": {              "type": "EFigi",              "tokens": {                "start": 3,                "end": 4              },              "value": "BBG006L8G4H1"            },            "operation": {              "type": "OperationType",              "tokens": {                "start": 0,                "end": 1              },              "value": "buy"            }          }        }      }    },    ...  },

Обратите внимание на слот figi, в котором содержится идентификатор акции Яндекс, так называемый FIGI (Financial Instrument Global Identifier), необходимый для взаимодействия с API торговой платформы Тинькофф Инвестиции. Тип данных EFigi, это пользовательская сущность, которую я описал в разделе Сущности при создании навыка в платформе Яндекс.Диалоги. Вот фрагмент описания:
entity EFigi:    values:        BBG005DXJS36:            %exact            TCS            %lemma            тиньков(банк)?            тинькоф(банк)?            тинькофф(банк)?            ти си эс (груп)?        BBG006L8G4H1:            %exact            YNDX            %lemma            яндекс            яндекса        BBG004730JJ5:            %exact            MOEX            %lemma            Московская Биржа            Мосбиржа        BBG002B2J5X0:            %exact            KRKNP            %lemma            [Саратовский НПЗ акции привилегированные]            [Саратовский нефтеперерабатывающий завод акции привилегированные]...

Благодаря механизму сущностей, в коде обработчика мне не нужно делать дополнительных манипуляций над входными данными, чтобы получить FIGI. Платформа Диалогов преобразует название ценной бумаги в FIGI за меня.

Я использую EFigi в качестве нетерминала грамматики и типа слота в интентах. Интенты это такие регулярные выражения на стероидах в Диалогах. Благодаря интентам Диалоги понимают, какие данные нужно извлечь из пользовательского запроса и передать в обработчик. Вот пример интента для команды покупки/продажи ценных бумаг на бирже по рыночной цене:
slots:    operation:        source: $Operation        type: OperationType    figi:        source: $Stock        type: Efigi    amount:        source: $Amount        type: YANDEX.NUMBER    unit:        source: $Unit        type: OperationUnitroot:    $Operation [$Amount $Unit $Stock]$Operation:    $OperationType$Amount:    $YANDEX.NUMBER$Unit:    $OperationUnit$Stock:    $EFigi

Это похоже на регулярные выражения.

Библиотека сущностей для Диалогов


Во время хакатона по разработке навыков для Алисы я создал репозиторий alice-entities-library, запушил туда сущность EFigi и отправился на GitHub искать репозитории, в которых есть описание пользовательских сущностей. Ожидал, что найду сотни репозиториев, свяжусь с разработчиками и предложу прислать пул-реквесты в библиотеку сущностей.

Репозитории искал по тегам: yandex-dialogs, alice-skills, yandex-alice и alice-sdk. Оказалось, мало кто использует теги на GitHub, мне удалось найти всего один репозиторий, содержащий файл с описанием сущности ELang. По стечению обстоятельств автором репозитория оказался Давид один из организаторов хакатона. Я предложил Давиду добавить сущность ELang в библиотеку и через несколько минут получил от него пул-реквест.

Другие участники онлайн-хакатона проигнорировали мои сообщения в чате с предложением пополнить библиотеку сущностей. Возможно, в разгар борьбы было не до этого. По правде говоря, я немного расстроился, но в конце добавил ссылку на репозиторий в sameoldmadness/awesome-alice.

Вместо заключения


Уважаемые разработчики навыков для Алисы, прошу вас по возможности выкладывать исходный код на GitHub, чтобы другие могли учиться.

Пожалуйста, добавляйте теги yandex-dialogs, alice-skills и yandex-alice к репозиториям, чтобы другие могли находить ваши навыки на GitHub.

Создайте в своём репозитории директорию entities и поместите туда файлы с описанием сущностей, которые вы написали для навыка, чтобы другие могли переиспользовать вашу работу.

Прежде чем описывать новую сущность, загляните в библиотеку сущностей, возможно, там уже есть то, что вам нужно. А если нет, то, пожалуйста, добавляйте свои сущности в библиотеку.
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru